]> git.proxmox.com Git - grub2.git/commitdiff
Automatically determine prefix when netbooted on EFI
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sat, 2 Jul 2011 14:56:35 +0000 (16:56 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sat, 2 Jul 2011 14:56:35 +0000 (16:56 +0200)
grub-core/disk/efi/efidisk.c
grub-core/kern/efi/efi.c
grub-core/kern/efi/init.c
grub-core/net/drivers/efi/efinet.c
grub-core/net/net.c
include/grub/efi/api.h
include/grub/efi/efi.h
include/grub/net.h

index 5dc7530462164f35035c7c30e52d93a120eeeeaf..07f0f68593a7c26150ccd07a8c3a591b3fbea818 100644 (file)
@@ -84,54 +84,6 @@ find_last_device_path (const grub_efi_device_path_t *dp)
   return p;
 }
 
-/* Compare device paths.  */
-static int
-compare_device_paths (const grub_efi_device_path_t *dp1,
-                     const grub_efi_device_path_t *dp2)
-{
-  if (! dp1 || ! dp2)
-    /* Return non-zero.  */
-    return 1;
-
-  while (1)
-    {
-      grub_efi_uint8_t type1, type2;
-      grub_efi_uint8_t subtype1, subtype2;
-      grub_efi_uint16_t len1, len2;
-      int ret;
-
-      type1 = GRUB_EFI_DEVICE_PATH_TYPE (dp1);
-      type2 = GRUB_EFI_DEVICE_PATH_TYPE (dp2);
-
-      if (type1 != type2)
-       return (int) type2 - (int) type1;
-
-      subtype1 = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp1);
-      subtype2 = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp2);
-
-      if (subtype1 != subtype2)
-       return (int) subtype1 - (int) subtype2;
-
-      len1 = GRUB_EFI_DEVICE_PATH_LENGTH (dp1);
-      len2 = GRUB_EFI_DEVICE_PATH_LENGTH (dp2);
-
-      if (len1 != len2)
-       return (int) len1 - (int) len2;
-
-      ret = grub_memcmp (dp1, dp2, len1);
-      if (ret != 0)
-       return ret;
-
-      if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp1))
-       break;
-
-      dp1 = (grub_efi_device_path_t *) ((char *) dp1 + len1);
-      dp2 = (grub_efi_device_path_t *) ((char *) dp2 + len2);
-    }
-
-  return 0;
-}
-
 static struct grub_efidisk_data *
 make_devices (void)
 {
@@ -214,7 +166,7 @@ find_parent_device (struct grub_efidisk_data *devices,
       if (parent == d)
        continue;
 
-      if (compare_device_paths (parent->device_path, dp) == 0)
+      if (grub_efi_compare_device_paths (parent->device_path, dp) == 0)
        {
          /* Found.  */
          if (! parent->last_device_path)
@@ -249,7 +201,7 @@ iterate_child_devices (struct grub_efidisk_data *devices,
       ldp->length[0] = sizeof (*ldp);
       ldp->length[1] = 0;
 
-      if (compare_device_paths (dp, d->device_path) == 0)
+      if (grub_efi_compare_device_paths (dp, d->device_path) == 0)
        if (hook (p))
          {
            grub_free (dp);
@@ -273,11 +225,11 @@ add_device (struct grub_efidisk_data **devices, struct grub_efidisk_data *d)
     {
       int ret;
 
-      ret = compare_device_paths (find_last_device_path ((*p)->device_path),
-                                 find_last_device_path (d->device_path));
+      ret = grub_efi_compare_device_paths (find_last_device_path ((*p)->device_path),
+                                          find_last_device_path (d->device_path));
       if (ret == 0)
-       ret = compare_device_paths ((*p)->device_path,
-                                   d->device_path);
+       ret = grub_efi_compare_device_paths ((*p)->device_path,
+                                            d->device_path);
       if (ret == 0)
        return;
       else if (ret > 0)
@@ -706,7 +658,35 @@ grub_efidisk_get_device_handle (grub_disk_t disk)
 char *
 grub_efidisk_get_device_name (grub_efi_handle_t *handle)
 {
-  grub_efi_device_path_t *dp, *ldp;
+  grub_efi_device_path_t *dp, *ldp, *sdp;
+  /* This is a hard disk partition.  */
+  grub_disk_t parent = 0;
+  auto int find_parent_disk (const char *name);
+
+  /* Find the disk which is the parent of a given hard disk partition.  */
+  int find_parent_disk (const char *name)
+  {
+    grub_disk_t disk;
+
+    disk = grub_disk_open (name);
+    if (! disk)
+      return 1;
+
+    if (disk->dev->id == GRUB_DISK_DEVICE_EFIDISK_ID)
+      {
+       struct grub_efidisk_data *d;
+
+       d = disk->data;
+       if (grub_efi_compare_device_paths (d->device_path, sdp) == 0)
+         {
+           parent = disk;
+           return 1;
+         }
+      }
+
+    grub_disk_close (disk);
+    return 0;
+  }
 
   dp = grub_efi_get_device_path (handle);
   if (! dp)
@@ -720,40 +700,12 @@ grub_efidisk_get_device_name (grub_efi_handle_t *handle)
       && (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp)
          == GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE))
     {
-      /* This is a hard disk partition.  */
-      grub_disk_t parent = 0;
       grub_partition_t tpart = NULL;
       char *device_name;
       grub_efi_device_path_t *dup_dp, *dup_ldp;
       grub_efi_hard_drive_device_path_t hd;
-      auto int find_parent_disk (const char *name);
       auto int find_partition (grub_disk_t disk, const grub_partition_t part);
 
-      /* Find the disk which is the parent of a given hard disk partition.  */
-      int find_parent_disk (const char *name)
-       {
-         grub_disk_t disk;
-
-         disk = grub_disk_open (name);
-         if (! disk)
-           return 1;
-
-         if (disk->dev->id == GRUB_DISK_DEVICE_EFIDISK_ID)
-           {
-             struct grub_efidisk_data *d;
-
-             d = disk->data;
-             if (compare_device_paths (d->device_path, dup_dp) == 0)
-               {
-                 parent = disk;
-                 return 1;
-               }
-           }
-
-         grub_disk_close (disk);
-         return 0;
-       }
-
       /* Find the identical partition.  */
       int find_partition (grub_disk_t disk __attribute__ ((unused)),
                          const grub_partition_t part)
@@ -780,6 +732,8 @@ grub_efidisk_get_device_name (grub_efi_handle_t *handle)
       dup_ldp->length[0] = sizeof (*dup_ldp);
       dup_ldp->length[1] = 0;
 
+      sdp = dup_dp;
+
       grub_efidisk_iterate (find_parent_disk);
       grub_free (dup_dp);
 
@@ -816,36 +770,15 @@ grub_efidisk_get_device_name (grub_efi_handle_t *handle)
   else
     {
       /* This should be an entire disk.  */
-      auto int find_disk (const char *name);
       char *device_name = 0;
 
-      int find_disk (const char *name)
-       {
-         grub_disk_t disk;
-
-         disk = grub_disk_open (name);
-         if (! disk)
-           return 1;
-
-         if (disk->dev->id == GRUB_DISK_DEVICE_EFIDISK_ID)
-           {
-             struct grub_efidisk_data *d;
-
-             d = disk->data;
-             if (compare_device_paths (d->device_path, dp) == 0)
-               {
-                 device_name = grub_strdup (disk->name);
-                 grub_disk_close (disk);
-                 return 1;
-               }
-           }
-
-         grub_disk_close (disk);
-         return 0;
+      sdp = dp;
 
-       }
-
-      grub_efidisk_iterate (find_disk);
+      grub_efidisk_iterate (find_parent_disk);
+      if (!parent)
+       return NULL;
+      device_name = grub_strdup (parent->name);
+      grub_disk_close (parent);
       return device_name;
     }
 
index c950587331d4552549201115a75d0f2e19f3b51b..d0994a940671abc708b44f19967b4022c6188219 100644 (file)
@@ -746,3 +746,51 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp)
       dp = (grub_efi_device_path_t *) ((char *) dp + len);
     }
 }
+
+/* Compare device paths.  */
+int
+grub_efi_compare_device_paths (const grub_efi_device_path_t *dp1,
+                              const grub_efi_device_path_t *dp2)
+{
+  if (! dp1 || ! dp2)
+    /* Return non-zero.  */
+    return 1;
+
+  while (1)
+    {
+      grub_efi_uint8_t type1, type2;
+      grub_efi_uint8_t subtype1, subtype2;
+      grub_efi_uint16_t len1, len2;
+      int ret;
+
+      type1 = GRUB_EFI_DEVICE_PATH_TYPE (dp1);
+      type2 = GRUB_EFI_DEVICE_PATH_TYPE (dp2);
+
+      if (type1 != type2)
+       return (int) type2 - (int) type1;
+
+      subtype1 = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp1);
+      subtype2 = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp2);
+
+      if (subtype1 != subtype2)
+       return (int) subtype1 - (int) subtype2;
+
+      len1 = GRUB_EFI_DEVICE_PATH_LENGTH (dp1);
+      len2 = GRUB_EFI_DEVICE_PATH_LENGTH (dp2);
+
+      if (len1 != len2)
+       return (int) len1 - (int) len2;
+
+      ret = grub_memcmp (dp1, dp2, len1);
+      if (ret != 0)
+       return ret;
+
+      if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp1))
+       break;
+
+      dp1 = (grub_efi_device_path_t *) ((char *) dp1 + len1);
+      dp2 = (grub_efi_device_path_t *) ((char *) dp2 + len2);
+    }
+
+  return 0;
+}
index a7325acc64cb9732ad69bcb823dcfc674b6fa3ad..4dfb06284464bb590f54a0ad246b363f860c311f 100644 (file)
@@ -42,6 +42,10 @@ grub_efi_init (void)
   grub_efidisk_init ();
 }
 
+void (*grub_efi_net_config) (grub_efi_handle_t hnd, 
+                            char **device,
+                            char **path);
+
 void
 grub_machine_get_bootlocation (char **device, char **path)
 {
@@ -53,6 +57,8 @@ grub_machine_get_bootlocation (char **device, char **path)
     return;
   *device = grub_efidisk_get_device_name (image->device_handle);
   *path = grub_efi_get_filename (image->file_path);
+  if (!*device && grub_efi_net_config)
+    grub_efi_net_config (image->device_handle, device, path);
 
   /* Get the directory.  */
   p = grub_strrchr (*path, '/');
index a6e005601fbb703a925ed5597dca8c5136fdb958..29bc661833a8e4796655b643167fca22bac532f0 100644 (file)
@@ -27,14 +27,14 @@ GRUB_MOD_LICENSE ("GPLv3+");
 
 /* GUID.  */
 static grub_efi_guid_t net_io_guid = GRUB_EFI_SIMPLE_NETWORK_GUID;
-
+static grub_efi_guid_t pxe_io_guid = GRUB_EFI_PXE_GUID;
 
 static grub_err_t
 send_card_buffer (const struct grub_net_card *dev,
                  struct grub_net_buff *pack)
 {
   grub_efi_status_t st;
-  grub_efi_simple_network_t *net = dev->data;
+  grub_efi_simple_network_t *net = dev->efi_net;
   st = efi_call_7 (net->transmit, net, 0, pack->tail - pack->data,
                   pack->data, NULL, NULL, NULL);
   if (st != GRUB_EFI_SUCCESS)
@@ -46,7 +46,7 @@ static grub_ssize_t
 get_card_packet (const struct grub_net_card *dev,
                 struct grub_net_buff *nb)
 {
-  grub_efi_simple_network_t *net = dev->data;
+  grub_efi_simple_network_t *net = dev->efi_net;
   grub_err_t err;
   grub_efi_status_t st;
   grub_efi_uintn_t bufsize = 1500;
@@ -139,21 +139,62 @@ grub_efinet_findcards (void)
       grub_memcpy (card->default_address.mac,
                   net->mode->current_address,
                   sizeof (card->default_address.mac));
-      card->data = net;
+      card->efi_net = net;
+      card->efi_handle = *handle;
 
       grub_net_card_register (card);
     }
   grub_free (handles);
 }
 
+static void
+grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
+                         char **path)
+{
+  struct grub_net_card *card;
+  grub_efi_device_path_t *dp;
+
+  dp = grub_efi_get_device_path (hnd);
+  if (! dp)
+    return;
+
+  FOR_NET_CARDS (card)
+  {
+    grub_efi_device_path_t *cdp;
+    struct grub_efi_pxe *pxe;
+    struct grub_efi_pxe_mode *pxe_mode;
+    if (card->driver != &efidriver)
+      continue;
+    cdp = grub_efi_get_device_path (card->efi_handle);
+    if (! cdp)
+      continue;
+    if (grub_efi_compare_device_paths (dp, cdp) != 0)
+      continue;
+    pxe = grub_efi_open_protocol (hnd, &pxe_io_guid,
+                                 GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+    if (! pxe)
+      continue;
+    pxe_mode = pxe->mode;
+    grub_net_configure_by_dhcp_ack (card->name, card, 0,
+                                   (struct grub_net_bootp_packet *)
+                                   &pxe_mode->dhcp_ack,
+                                   sizeof (pxe_mode->dhcp_ack),
+                                   1, device, path);
+    return;
+  }
+}
+
+
 GRUB_MOD_INIT(efinet)
 {
   grub_efinet_findcards ();
+  grub_efi_net_config = grub_efi_net_config_real;
 }
 
 GRUB_MOD_FINI(ofnet)
 {
   struct grub_net_card *card;
+  grub_efi_net_config = 0;
   FOR_NET_CARDS (card) 
     if (card->driver && !grub_strcmp (card->driver->name, "efinet"))
       {
index 34148f52c0da9a5fd5ace7466cf363e98e2b2e13..2ecb709b97e5d3a0d94c1e4342b57ecdb62e56d2 100644 (file)
@@ -936,7 +936,8 @@ grub_net_configure_by_dhcp_ack (const char *name,
                                const struct grub_net_card *card,
                                grub_net_interface_flags_t flags,
                                const struct grub_net_bootp_packet *bp,
-                               grub_size_t size)
+                               grub_size_t size,
+                               int is_def, char **device, char **path)
 {
   grub_net_network_level_address_t addr;
   grub_net_link_level_address_t hwaddr;
@@ -945,6 +946,11 @@ grub_net_configure_by_dhcp_ack (const char *name,
   addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
   addr.ipv4 = bp->your_ip;
 
+  if (device)
+    *device = 0;
+  if (path)
+    *path = 0;
+
   grub_memcpy (hwaddr.mac, bp->mac_addr,
               bp->hw_len < sizeof (hwaddr.mac) ? bp->hw_len
               : sizeof (hwaddr.mac));
@@ -975,26 +981,57 @@ grub_net_configure_by_dhcp_ack (const char *name,
   if (size > OFFSET_OF (boot_file, bp))
     set_env_limn_ro (name, "boot_file", (char *) bp->boot_file,
                     sizeof (bp->boot_file));
+  if (is_def)
+    default_server = 0;
   if (size > OFFSET_OF (server_name, bp)
       && bp->server_name[0])
     {
       set_env_limn_ro (name, "dhcp_server_name", (char *) bp->server_name,
                       sizeof (bp->server_name));
-      if (!default_server)
+      if (is_def && !default_server)
        {
          default_server = grub_strdup (bp->server_name);
-         grub_errno = GRUB_ERR_NONE;
-       }         
+         grub_print_error ();
+       }
+      if (device && !*device)
+       {
+         *device = grub_xasprintf ("tftp,%s", bp->server_name);
+         grub_print_error ();
+       }
     }
-  if (!default_server)
+  if (is_def && !default_server)
     {
       default_server = grub_xasprintf ("%d.%d.%d.%d",
                                       ((grub_uint8_t *) &bp->server_ip)[0],
                                       ((grub_uint8_t *) &bp->server_ip)[1],
                                       ((grub_uint8_t *) &bp->server_ip)[2],
                                       ((grub_uint8_t *) &bp->server_ip)[3]);
-      grub_errno = GRUB_ERR_NONE;
-    }    
+      grub_print_error ();
+    }
+
+  if (device && !*device)
+    {
+      *device = grub_xasprintf ("tftp,%d.%d.%d.%d",
+                               ((grub_uint8_t *) &bp->server_ip)[0],
+                               ((grub_uint8_t *) &bp->server_ip)[1],
+                               ((grub_uint8_t *) &bp->server_ip)[2],
+                               ((grub_uint8_t *) &bp->server_ip)[3]);
+      grub_print_error ();
+    }
+  if (size > OFFSET_OF (boot_file, bp) && path)
+    {
+      *path = grub_strndup (bp->boot_file, sizeof (bp->boot_file));
+      grub_print_error ();
+      if (*path)
+       {
+         char *slash;
+         slash = grub_strrchr (*path, '/');
+         if (slash)
+           *slash = 0;
+         else
+           **path = 0;
+       }
+    }
   if (size > OFFSET_OF (vendor, bp))
     parse_dhcp_vendor (name, &bp->vendor, size - OFFSET_OF (vendor, bp));
 
@@ -1025,7 +1062,7 @@ grub_net_process_dhcp (struct grub_net_buff *nb,
     }
   grub_net_configure_by_dhcp_ack (name, card,
                                  0, (const struct grub_net_bootp_packet *) nb->data,
-                                 (nb->tail - nb->data));
+                                 (nb->tail - nb->data), 0, 0, 0);
   grub_free (name);
   if (grub_errno)
     grub_print_error ();
index a3dde0effbcc7d544eae5d0095b52dbf794ba460..5cd1686ea415fbe3b77f339b89c1c7f87e7595c3 100644 (file)
     { 0x9a, 0x2d, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \
   }
 
+#define GRUB_EFI_PXE_GUID      \
+  { 0x03c4e603, 0xac28, 0x11d3, \
+    { 0x9a, 0x2d, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \
+  }
+
 #define GRUB_EFI_DEVICE_PATH_GUID      \
   { 0x09576e91, 0x6d3f, 0x11d2, \
     { 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \
@@ -1118,6 +1123,36 @@ struct grub_efi_simple_text_output_interface
 };
 typedef struct grub_efi_simple_text_output_interface grub_efi_simple_text_output_interface_t;
 
+typedef grub_uint8_t grub_efi_pxe_packet_t[1472];
+
+typedef struct grub_efi_pxe_mode
+{
+  grub_uint8_t unused[52];
+  grub_efi_pxe_packet_t dhcp_discover;
+  grub_efi_pxe_packet_t dhcp_ack;
+  grub_efi_pxe_packet_t proxy_offer;
+  grub_efi_pxe_packet_t pxe_discover;
+  grub_efi_pxe_packet_t pxe_reply;
+} grub_efi_pxe_mode_t;
+
+typedef struct grub_efi_pxe
+{
+  grub_uint64_t rev;
+  void (*start) (void);
+  void (*stop) (void);
+  void (*dhcp) (void);
+  void (*discover) (void);
+  void (*mftp) (void);
+  void (*udpwrite) (void);
+  void (*udpread) (void);
+  void (*setipfilter) (void);
+  void (*arp) (void);
+  void (*setparams) (void);
+  void (*setstationip) (void);
+  void (*setpackets) (void);
+  struct grub_efi_pxe_mode *mode;
+} grub_efi_pxe_t;
+
 #define GRUB_EFI_BLACK         0x00
 #define GRUB_EFI_BLUE          0x01
 #define GRUB_EFI_GREEN         0x02
index e9c57dd11d4279200eafa4354e154375642a2e66..e98f99507378a6df1cd9263e85a4652aa4818a1d 100644 (file)
@@ -62,6 +62,14 @@ grub_err_t EXPORT_FUNC (grub_efi_set_virtual_address_map) (grub_efi_uintn_t memo
                                                           grub_efi_uint32_t descriptor_version,
                                                           grub_efi_memory_descriptor_t *virtual_map);
 
+int
+EXPORT_FUNC (grub_efi_compare_device_paths) (const grub_efi_device_path_t *dp1,
+                                            const grub_efi_device_path_t *dp2);
+
+extern void (*EXPORT_VAR(grub_efi_net_config)) (grub_efi_handle_t hnd, 
+                                               char **device,
+                                               char **path);
+
 void grub_efi_mm_init (void);
 void grub_efi_mm_fini (void);
 void grub_efi_init (void);
index ee94ebcc6664e7c7f66e3412d1adce3bd4089da3..6aaf391d5a7af57c32fd893ba84554bf4c50a626 100644 (file)
@@ -109,6 +109,11 @@ struct grub_net_card
   grub_net_card_flags_t flags;
   union
   {
+    struct
+    {
+      struct grub_efi_simple_network *efi_net;
+      void *efi_handle;
+    };
     void *data;
     int data_num;
   };
@@ -404,7 +409,8 @@ grub_net_configure_by_dhcp_ack (const char *name,
                                const struct grub_net_card *card,
                                grub_net_interface_flags_t flags,
                                const struct grub_net_bootp_packet *bp,
-                               grub_size_t size);
+                               grub_size_t size,
+                               int is_def, char **device, char **path);
 
 void
 grub_net_process_dhcp (struct grub_net_buff *nb,