Imported Debian patch 0.5.3-1.1
[debian/efibootmgr] / src / lib / efi.c
index ebfcdbc41318a935603119e5c47efbd39c3dcbbd..a760eb185b70f209f7d1b733ad7f847053591144 100644 (file)
@@ -18,8 +18,7 @@
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-#define _FILE_OFFSET_BITS 64
-
+#include <ctype.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <linux/sockios.h>
 #include <net/if.h>
-
-typedef unsigned long long u64; /* hack, so we may include kernel's ethtool.h */
-typedef __uint32_t u32;         /* ditto */
-typedef __uint16_t u16;         /* ditto */
-typedef __uint8_t u8;           /* ditto */
-
+#include <pci/pci.h>
+#include <asm/types.h>
 #include <linux/ethtool.h>
 #include "efi.h"
 #include "efichar.h"
@@ -49,6 +44,7 @@ typedef __uint8_t u8;           /* ditto */
 #include "efibootmgr.h"
 #include "efivars_procfs.h"
 #include "efivars_sysfs.h"
+#include "list.h"
 
 static struct efivar_kernel_calls *fs_kernel_calls;
 
@@ -175,9 +171,12 @@ create_or_edit_variable(efi_variable_t *var)
 static int
 select_boot_var_names(const struct dirent *d)
 {
-       int num, rc;
-       rc = sscanf(d->d_name, "Boot0%03x-%*s", &num);
-       return rc;
+       if (!strncmp(d->d_name, "Boot", 4) &&
+           isxdigit(d->d_name[4]) && isxdigit(d->d_name[5]) &&
+           isxdigit(d->d_name[6]) && isxdigit(d->d_name[7]) &&
+           d->d_name[8] == '-')
+               return 1;
+       return 0;
 }
 
 int
@@ -298,8 +297,55 @@ make_mac_addr_device_path(void *dest, char *mac, uint8_t iftype)
        return p.length;
 }
 
+struct device
+{
+       struct pci_dev *pci_dev;
+       struct list_head node;
+};
+
+static struct device *
+is_parent_bridge(struct pci_dev *p, unsigned int target_bus)
+{
+       struct device *d;
+       unsigned int primary, secondary;
+
+       if ( (pci_read_word(p, PCI_HEADER_TYPE) & 0x7f) != PCI_HEADER_TYPE_BRIDGE)
+               return NULL;
+
+       primary=pci_read_byte(p, PCI_PRIMARY_BUS);
+       secondary=pci_read_byte(p, PCI_SECONDARY_BUS);
+
+
+       if (secondary != target_bus)
+               return NULL;
+
+       d = malloc(sizeof(struct device));
+       if (!d)
+               return NULL;
+       memset(d, 0, sizeof(*d));
+       INIT_LIST_HEAD(&d->node);
+
+       d->pci_dev = p;
+
+       return d;
+}
+
+static struct device *
+find_parent(struct pci_access *pacc, unsigned int target_bus)
+{
+       struct device *dev;
+       struct pci_dev *p;
+
+       for (p=pacc->devices; p; p=p->next) {
+               dev = is_parent_bridge(p, target_bus);
+               if (dev)
+                       return dev;
+       }
+       return NULL;
+}
+
 static uint16_t
-make_pci_device_path(void *dest, uint8_t device, uint8_t function)
+make_one_pci_device_path(void *dest, uint8_t device, uint8_t function)
 {
        PCI_DEVICE_PATH p;
        memset(&p, 0, sizeof(p));
@@ -312,6 +358,47 @@ make_pci_device_path(void *dest, uint8_t device, uint8_t function)
        return p.length;
 }
 
+static uint16_t
+make_pci_device_path(void *dest, uint8_t bus, uint8_t device, uint8_t function)
+{
+       struct device *dev;
+       struct pci_access *pacc;
+       struct list_head *pos, *n;
+       LIST_HEAD(pci_parent_list);
+       char *p = dest;
+
+       pacc = pci_alloc();
+       if (!pacc)
+               return 0;
+
+       pci_init(pacc);
+       pci_scan_bus(pacc);
+
+       do {
+               dev = find_parent(pacc, bus);
+               if (dev) {
+                       list_add(&pci_parent_list, &dev->node);
+                       bus = dev->pci_dev->bus;
+               }
+       } while (dev && bus);
+
+
+       list_for_each_safe(pos, n, &pci_parent_list) {
+               dev = list_entry(pos, struct device, node);
+               p += make_one_pci_device_path(p,
+                                             dev->pci_dev->dev,
+                                             dev->pci_dev->func);
+               list_del(&dev->node);
+               free(dev);
+       }
+
+       p += make_one_pci_device_path(p, device, function);
+
+       pci_cleanup(pacc);
+
+       return ((void *)p - dest);
+}
+
 static uint16_t
 make_scsi_device_path(void *dest, uint16_t id, uint16_t lun)
 {
@@ -388,7 +475,7 @@ make_edd30_device_path(int fd, void *buffer)
        idlun_to_components(&idlun, &host, &channel, &id, &lun);
 
        p += make_acpi_device_path      (p, EISAID_PNP0A03, bus);
-       p += make_pci_device_path       (p, device, function);
+       p += make_pci_device_path       (p, bus, device, function);
        p += make_scsi_device_path      (p, id, lun);
        return ((void *)p - buffer);
 }
@@ -458,12 +545,13 @@ char *make_disk_load_option(char *p, char *disk)
  * make_net_load_option()
  * @data - load option returned
  *
- * Returns 0 on error, length of load option created on success.
+ * Returns NULL on error, or p advanced by length of load option
+ * created on success.
  */
 char *make_net_load_option(char *p, char *iface)
 {
     /* copied pretty much verbatim from the ethtool source */
-    int fd = 0, err;
+    int fd = 0, err; 
     int bus, slot, func;
     struct ifreq ifr;
     struct ethtool_drvinfo drvinfo;
@@ -476,29 +564,37 @@ char *make_net_load_option(char *p, char *iface)
     fd = socket(AF_INET, SOCK_DGRAM, 0);
     if (fd < 0) {
         perror("Cannot get control socket");
+       goto out;
     }
     err = ioctl(fd, SIOCETHTOOL, &ifr);
     if (err < 0) {
         perror("Cannot get driver information");
+       goto out;
     }
 
-    err = sscanf(drvinfo.bus_info, "%2x:%2x.%x", &bus, &slot, &func);
-    if (err == 0) {
-        perror("Couldn't parse device location string.");
+    /* The domain part was added in 2.6 kernels.  Test for that first. */
+    err = sscanf(drvinfo.bus_info, "%*x:%2x:%2x.%x", &bus, &slot, &func);
+    if (err != 3) {
+           err = sscanf(drvinfo.bus_info, "%2x:%2x.%x", &bus, &slot, &func);
+           if (err != 3) {
+                   perror("Couldn't parse device location string.");
+                   goto out;
+           }
     }
 
-    p += make_acpi_device_path(p, opts.acpi_hid, opts.acpi_uid);
-    p += make_pci_device_path(p, (uint8_t)slot, (uint8_t)func);
-
     err = ioctl(fd, SIOCGIFHWADDR, &ifr);
     if (err < 0) {
         perror("Cannot get hardware address.");
+       goto out;
     }
 
+    p += make_acpi_device_path(p, opts.acpi_hid, opts.acpi_uid);
+    p += make_pci_device_path(p, bus, (uint8_t)slot, (uint8_t)func);
     p += make_mac_addr_device_path(p, ifr.ifr_ifru.ifru_hwaddr.sa_data, 0);
     p += make_end_device_path       (p);
-
     return(p);
+ out:
+    return NULL;
 }
 
 /**
@@ -533,10 +629,11 @@ make_linux_load_option(void *data)
        if (opts.iface) {
              p = (char *)make_net_load_option(p, opts.iface);
        }
-
        else {
              p = (char *)make_disk_load_option(p, opts.iface);
        }
+       if (p == NULL)
+               return 0;
 
        load_option->file_path_list_length = p - q;
 
@@ -608,14 +705,57 @@ append_extra_args_unicode(void *data, unsigned long maxchars)
        return 0;
 }
 
+static unsigned long
+append_extra_args_file(void *data, unsigned long maxchars)
+{
+       char *p = data;
+       char *file = opts.extra_opts_file;
+       int fd = STDIN_FILENO;
+       ssize_t num_read=0;
+       unsigned long appended=0;
+
+       if (!data) return 0;
+
+       if (file && strncmp(file, "-", 1))
+               fd = open(file, O_RDONLY);
+
+       if (fd == -1) {
+               perror("Failed to open extra arguments file");
+               return 0;
+       }
+
+       do {
+               num_read = read(fd, p, maxchars - appended);
+               if (num_read < 0) {
+                       perror("Error reading extra arguments file");
+                       break;
+               }
+               else if (num_read>0) {
+                       appended += num_read;
+                       p += num_read;
+               }
+       } while (num_read > 0 && ((maxchars - appended) > 0));
+
+       if (fd != STDIN_FILENO)
+               close(fd);
+
+       return appended;
+}
+
 
 static unsigned long
 append_extra_args(void *data, unsigned long maxchars)
 {
-       if (opts.unicode)
-         return append_extra_args_unicode(data, maxchars);
+       unsigned long bytes_written=0;
+
+       if (opts.extra_opts_file)
+               bytes_written += append_extra_args_file(data, maxchars);
+
+       if  (opts.unicode)
+               bytes_written += append_extra_args_unicode(data, maxchars - bytes_written);
        else
-         return append_extra_args_ascii(data, maxchars);
+               bytes_written += append_extra_args_ascii(data, maxchars - bytes_written);
+       return bytes_written;
 }
 
 
@@ -632,7 +772,7 @@ make_linux_efi_variable(efi_variable_t *var,
        memset(buffer,    0, sizeof(buffer));
 
        /* VariableName needs to be BootXXXX */
-       sprintf(buffer, "Boot%04x", free_number);
+       sprintf(buffer, "Boot%04X", free_number);
 
        efichar_from_char(var->VariableName, buffer, 1024);