/*
- 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 <Matt_Domsch@dell.com>
#define _FILE_OFFSET_BITS 64
+typedef unsigned long long u64; /* hack to allow include of ethtool.h */
+
+#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 <linux/ethtool.h>
#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)
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)
{
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; i<n; i++) {
- if (namelist[i] &&
- strncmp(testname, namelist[i]->d_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);
}
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));
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)
{
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);
}
* 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;
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;
}
/**
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;
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;
}
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);
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);
+}