X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=src%2Flib%2Fefi.c;h=a4ce5cfe21cdd6fe8ea942cd133f05dba025bddb;hb=438378f384ede73602fcdfd288e45ac41d933bb0;hp=0ab0339b23a852d591dfa38236b4394f99a5da88;hpb=98db23750431fa650821f391aca2ce1898cb8020;p=debian%2Fefibootmgr diff --git a/src/lib/efi.c b/src/lib/efi.c index 0ab0339..a4ce5cf 100644 --- a/src/lib/efi.c +++ b/src/lib/efi.c @@ -1,5 +1,5 @@ /* - efi.[ch] - Manipulates EFI variables as exported in /proc/efi/vars + efivars_proc.[ch] - Manipulates EFI variables as exported in /proc/efi/vars Copyright (C) 2001,2003 Dell Computer Corporation @@ -18,8 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define _FILE_OFFSET_BITS 64 - +#include #include #include #include @@ -34,19 +33,21 @@ #include #include #include +#include #include - -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 +#include #include #include "efi.h" #include "efichar.h" #include "scsi_ioctls.h" #include "disk.h" #include "efibootmgr.h" +#include "efivars_procfs.h" +#include "efivars_sysfs.h" +#include "list.h" + +static struct efivar_kernel_calls *fs_kernel_calls; EFI_DEVICE_PATH * load_option_path(EFI_LOAD_OPTION *option) @@ -69,36 +70,33 @@ efi_guid_unparse(efi_guid_t *guid, char *out) return out; } - -efi_status_t -read_variable(char *name, efi_variable_t *var) +void +set_fs_kernel_calls() { - int newnamesize; - char *newname; - int fd; - size_t readsize; - if (!name || !var) return EFI_INVALID_PARAMETER; - - newnamesize = strlen(PROC_DIR_EFI_VARS) + strlen(name) + 1; - newname = malloc(newnamesize); - if (!newname) return EFI_OUT_OF_RESOURCES; - sprintf(newname, "%s%s", PROC_DIR_EFI_VARS,name); - fd = open(newname, O_RDONLY); - if (fd == -1) { - free(newname); - return EFI_NOT_FOUND; + char name[PATH_MAX]; + DIR *dir; + snprintf(name, PATH_MAX, "%s", SYSFS_DIR_EFI_VARS); + dir = opendir(name); + if (dir) { + closedir(dir); + fs_kernel_calls = &sysfs_kernel_calls; + return; } - readsize = read(fd, var, sizeof(*var)); - if (readsize != sizeof(*var)) { - free(newname); - close(fd); - return EFI_INVALID_PARAMETER; + + snprintf(name, PATH_MAX, "%s", PROCFS_DIR_EFI_VARS); + dir = opendir(name); + if (dir) { + closedir(dir); + fs_kernel_calls = &procfs_kernel_calls; + return; } - close(fd); - free(newname); - return var->Status; + fprintf(stderr, "Fatal: Couldn't open either sysfs or procfs directories for accessing EFI variables.\n"); + fprintf(stderr, "Try 'modprobe efivars' as root.\n"); + exit(1); } + + static efi_status_t write_variable_to_file(efi_variable_t *var) { @@ -120,108 +118,75 @@ write_variable_to_file(efi_variable_t *var) close(fd); return EFI_SUCCESS; } -/** - * select_variable_names() - * @d - dirent to compare against - * - * This ignores "." and ".." entries, and selects all others. - */ -static int -select_variable_names(const struct dirent *d) +efi_status_t +read_variable(const char *name, efi_variable_t *var) { - if (!strcmp(d->d_name, ".") || - !strcmp(d->d_name, "..")) - return 0; - return 1; + if (!name || !var) return EFI_INVALID_PARAMETER; + return fs_kernel_calls->read(name, var); } -/** - * find_write_victim() - * @var - variable to be written - * @file - name of file to open for writing @var is returned. - * - * This ignores "." and ".." entries, and selects all others. - */ -static char * -find_write_victim(efi_variable_t *var, char file[PATH_MAX]) -{ - struct dirent **namelist = NULL; - int i, n, found=0; - char testname[PATH_MAX], *p; - - memset(testname, 0, sizeof(testname)); - n = scandir(PROC_DIR_EFI_VARS, &namelist, - select_variable_names, alphasort); - if (n < 0) { - perror("scandir " PROC_DIR_EFI_VARS); - fprintf(stderr, "You must 'modprobe efivars' first.\n"); - return NULL; - } +efi_status_t +create_variable(efi_variable_t *var) +{ + if (!var) return EFI_INVALID_PARAMETER; + if (opts.testfile) return write_variable_to_file(var); + return fs_kernel_calls->create(var); +} - p = testname; - efichar_to_char(p, var->VariableName, PATH_MAX); - p += strlen(p); - p += sprintf(p, "-"); - efi_guid_unparse(&var->VendorGuid, p); +efi_status_t +delete_variable(efi_variable_t *var) +{ + if (!var) return EFI_INVALID_PARAMETER; + if (opts.testfile) return write_variable_to_file(var); + return fs_kernel_calls->delete(var); +} - for (i=0; id_name, sizeof(testname))) { - found++; - sprintf(file, "%s%s", PROC_DIR_EFI_VARS, - namelist[i]->d_name); - break; - } - } - while (n--) { - if (namelist[n]) { - free(namelist[n]); - namelist[n] = NULL; - } - } - free(namelist); +efi_status_t +edit_variable(efi_variable_t *var) +{ + char name[PATH_MAX]; + if (!var) return EFI_INVALID_PARAMETER; + if (opts.testfile) return write_variable_to_file(var); - if (!found) return NULL; - return file; + variable_to_name(var, name); + return fs_kernel_calls->edit(name, var); } - efi_status_t -write_variable(efi_variable_t *var) +create_or_edit_variable(efi_variable_t *var) { - int fd; - size_t writesize; - char buffer[PATH_MAX], name[PATH_MAX], *p = NULL; + efi_variable_t testvar; + char name[PATH_MAX]; - if (!var) return EFI_INVALID_PARAMETER; - if (opts.testfile) return write_variable_to_file(var); - memset(buffer, 0, sizeof(buffer)); - memset(name, 0, sizeof(name)); + memcpy(&testvar, var, sizeof(*var)); + variable_to_name(var, name); - p = find_write_victim(var, name); - if (!p) return EFI_INVALID_PARAMETER; + if (read_variable(name, &testvar) == EFI_SUCCESS) + return edit_variable(var); + else + return create_variable(var); +} - fd = open(name, O_WRONLY); - if (fd == -1) { - sprintf(buffer, "write_variable():open(%s)", name); - perror(buffer); - return EFI_INVALID_PARAMETER; - } - writesize = write(fd, var, sizeof(*var)); - if (writesize != sizeof(*var)) { -#if 0 - sprintf(buffer, "write_variable():write(%s)", name); - perror(buffer); - dump_raw_data(var, sizeof(*var)); -#endif - close(fd); - return EFI_INVALID_PARAMETER; +static int +select_boot_var_names(const struct dirent *d) +{ + 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; +} - } - close(fd); - return EFI_SUCCESS; +int +read_boot_var_names(struct dirent ***namelist) +{ + if (!fs_kernel_calls || !namelist) return -1; + return scandir(fs_kernel_calls->path, + namelist, select_boot_var_names, + alphasort); } @@ -333,8 +298,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)); @@ -347,6 +359,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) { @@ -423,7 +476,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); } @@ -493,12 +546,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; @@ -511,29 +565,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; } /** @@ -568,10 +630,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; @@ -643,14 +706,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"); + exit(1); + } + + 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; } @@ -667,7 +773,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); @@ -690,3 +796,15 @@ make_linux_efi_variable(efi_variable_t *var, var->DataSize = load_option_size + opt_data_size; return var->DataSize; } + + +int +variable_to_name(efi_variable_t *var, char *name) +{ + char *p = name; + efichar_to_char(p, var->VariableName, PATH_MAX); + p += strlen(p); + p += sprintf(p, "-"); + efi_guid_unparse(&var->VendorGuid, p); + return strlen(name); +}