2 efivars_proc.[ch] - Manipulates EFI variables as exported in /proc/efi/vars
4 Copyright (C) 2001,2003 Dell Computer Corporation <Matt_Domsch@dell.com>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #define _FILE_OFFSET_BITS 64
23 typedef unsigned long long u64; /* hack to allow include of ethtool.h */
36 #include <sys/socket.h>
37 #include <sys/types.h>
38 #include <sys/ioctl.h>
39 #include <linux/sockios.h>
42 #include <linux/ethtool.h>
45 #include "scsi_ioctls.h"
47 #include "efibootmgr.h"
48 #include "efivars_procfs.h"
49 #include "efivars_sysfs.h"
52 static struct efivar_kernel_calls *fs_kernel_calls;
55 load_option_path(EFI_LOAD_OPTION *option)
57 char *p = (char *) option;
58 return (EFI_DEVICE_PATH *)
59 (p + sizeof(uint32_t) /* Attributes */
60 + sizeof(uint16_t) /* FilePathListLength*/
61 + efichar_strsize(option->description)); /* Description */
65 efi_guid_unparse(efi_guid_t *guid, char *out)
67 sprintf(out, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
68 guid->b[3], guid->b[2], guid->b[1], guid->b[0],
69 guid->b[5], guid->b[4], guid->b[7], guid->b[6],
70 guid->b[8], guid->b[9], guid->b[10], guid->b[11],
71 guid->b[12], guid->b[13], guid->b[14], guid->b[15]);
80 snprintf(name, PATH_MAX, "%s", SYSFS_DIR_EFI_VARS);
84 fs_kernel_calls = &sysfs_kernel_calls;
88 snprintf(name, PATH_MAX, "%s", PROCFS_DIR_EFI_VARS);
92 fs_kernel_calls = &procfs_kernel_calls;
95 fprintf(stderr, "Fatal: Couldn't open either sysfs or procfs directories for accessing EFI variables.\n");
96 fprintf(stderr, "Try 'modprobe efivars' as root.\n");
103 write_variable_to_file(efi_variable_t *var)
105 int fd, byteswritten;
106 if (!var || !opts.testfile) return EFI_INVALID_PARAMETER;
108 printf("Test mode: Writing to %s\n", opts.testfile);
109 fd = creat(opts.testfile, S_IRWXU);
111 perror("Couldn't write to testfile");
112 return EFI_INVALID_PARAMETER;
115 byteswritten = write(fd, var, sizeof(*var));
116 if (byteswritten == -1) {
117 perror("Writing to testfile");
125 read_variable(const char *name, efi_variable_t *var)
127 if (!name || !var) return EFI_INVALID_PARAMETER;
128 return fs_kernel_calls->read(name, var);
132 create_variable(efi_variable_t *var)
134 if (!var) return EFI_INVALID_PARAMETER;
135 if (opts.testfile) return write_variable_to_file(var);
136 return fs_kernel_calls->create(var);
140 delete_variable(efi_variable_t *var)
142 if (!var) return EFI_INVALID_PARAMETER;
143 if (opts.testfile) return write_variable_to_file(var);
144 return fs_kernel_calls->delete(var);
149 edit_variable(efi_variable_t *var)
152 if (!var) return EFI_INVALID_PARAMETER;
153 if (opts.testfile) return write_variable_to_file(var);
155 variable_to_name(var, name);
156 return fs_kernel_calls->edit(name, var);
160 create_or_edit_variable(efi_variable_t *var)
162 efi_variable_t testvar;
165 memcpy(&testvar, var, sizeof(*var));
166 variable_to_name(var, name);
168 if (read_variable(name, &testvar) == EFI_SUCCESS)
169 return edit_variable(var);
171 return create_variable(var);
175 select_boot_var_names(const struct dirent *d)
177 if (!strncmp(d->d_name, "Boot", 4) &&
178 isxdigit(d->d_name[4]) && isxdigit(d->d_name[5]) &&
179 isxdigit(d->d_name[6]) && isxdigit(d->d_name[7]) &&
186 read_boot_var_names(struct dirent ***namelist)
188 if (!fs_kernel_calls || !namelist) return -1;
189 return scandir(fs_kernel_calls->path,
190 namelist, select_boot_var_names,
200 efi_guid_t guid = BLKX_UNKNOWN_GUID;
201 char name[80], text_guid[40];
202 ACPI_DEVICE_PATH *path = (ACPI_DEVICE_PATH *)&(var.Data);
205 /* Allow global user option override */
207 switch (opts.edd_version)
209 case 0: /* No EDD information */
212 case 1: /* EDD 1.0 */
215 case 3: /* EDD 3.0 */
223 memset(&var, 0, sizeof(efi_variable_t));
224 efi_guid_unparse(&guid, text_guid);
225 sprintf(name, "blk0-%s", text_guid);
227 status = read_variable(name, &var);
228 if (status != EFI_SUCCESS) {
231 if (path->type == 2 && path->subtype == 1) rc = 3;
237 EFI_DEVICE_PATH, 0x01 (Hardware), 0x04 (Vendor), length 0x0018
238 This needs to know what EFI device has the boot device.
241 make_edd10_device_path(void *dest, uint32_t hardware_device)
243 VENDOR_DEVICE_PATH *hw;
244 char buffer[EDD10_HARDWARE_VENDOR_PATH_LENGTH];
245 efi_guid_t guid = EDD10_HARDWARE_VENDOR_PATH_GUID;
247 memset(buffer, 0, sizeof(buffer));
248 hw = (VENDOR_DEVICE_PATH *)buffer;
249 data = (uint32_t *)hw->data;
250 hw->type = 0x01; /* Hardware Device Path */
251 hw->subtype = 0x04; /* Vendor */
252 hw->length = EDD10_HARDWARE_VENDOR_PATH_LENGTH;
253 memcpy(&(hw->vendor_guid), &guid, sizeof(guid));
254 *data = hardware_device;
255 memcpy(dest, buffer, hw->length);
260 make_end_device_path(void *dest)
263 memset(&p, 0, sizeof(p));
264 p.type = 0x7F; /* End of Hardware Device Path */
265 p.subtype = 0xFF; /* End Entire Device Path */
266 p.length = sizeof(p);
267 memcpy(dest, &p, p.length);
272 make_acpi_device_path(void *dest, uint32_t _HID, uint32_t _UID)
275 memset(&p, 0, sizeof(p));
278 p.length = sizeof(p);
281 memcpy(dest, &p, p.length);
286 make_mac_addr_device_path(void *dest, char *mac, uint8_t iftype)
290 MAC_ADDR_DEVICE_PATH p;
291 memset(&p, 0, sizeof(p));
294 p.length = sizeof(p);
295 for (i=0; i < 14; i++) {
296 p.macaddr[i] = mac[i];
299 memcpy(dest, &p, p.length);
305 struct pci_dev *pci_dev;
306 struct list_head node;
309 static struct device *
310 is_parent_bridge(struct pci_dev *p, unsigned int target_bus)
313 unsigned int primary, secondary;
315 if ( (pci_read_word(p, PCI_HEADER_TYPE) & 0x7f) != PCI_HEADER_TYPE_BRIDGE)
318 primary=pci_read_byte(p, PCI_PRIMARY_BUS);
319 secondary=pci_read_byte(p, PCI_SECONDARY_BUS);
322 if (secondary != target_bus)
325 d = malloc(sizeof(struct device));
328 memset(d, 0, sizeof(*d));
329 INIT_LIST_HEAD(&d->node);
336 static struct device *
337 find_parent(struct pci_access *pacc, unsigned int target_bus)
342 for (p=pacc->devices; p; p=p->next) {
343 dev = is_parent_bridge(p, target_bus);
351 make_one_pci_device_path(void *dest, uint8_t device, uint8_t function)
354 memset(&p, 0, sizeof(p));
357 p.length = sizeof(p);
359 p.function = function;
360 memcpy(dest, &p, p.length);
365 make_pci_device_path(void *dest, uint8_t bus, uint8_t device, uint8_t function)
368 struct pci_access *pacc;
369 struct list_head *pos, *n;
370 LIST_HEAD(pci_parent_list);
381 dev = find_parent(pacc, bus);
383 list_add(&pci_parent_list, &dev->node);
384 bus = dev->pci_dev->bus;
386 } while (dev && bus);
389 list_for_each_safe(pos, n, &pci_parent_list) {
390 dev = list_entry(pos, struct device, node);
391 p += make_one_pci_device_path(p,
394 list_del(&dev->node);
398 p += make_one_pci_device_path(p, device, function);
402 return ((void *)p - dest);
406 make_scsi_device_path(void *dest, uint16_t id, uint16_t lun)
409 memset(&p, 0, sizeof(p));
412 p.length = sizeof(p);
415 memcpy(dest, &p, p.length);
420 make_harddrive_device_path(void *dest, uint32_t num, uint64_t start, uint64_t size,
422 uint8_t mbr_type, uint8_t signature_type)
424 HARDDRIVE_DEVICE_PATH p;
425 memset(&p, 0, sizeof(p));
428 p.length = sizeof(p);
432 if (signature) memcpy(p.signature, signature, 16);
433 p.mbr_type = mbr_type;
434 p.signature_type = signature_type;
435 memcpy(dest, &p, p.length);
440 make_file_path_device_path(void *dest, efi_char16_t *name)
442 FILE_PATH_DEVICE_PATH *p;
444 int namelen = efichar_strlen(name, -1);
445 int namesize = efichar_strsize(name);
447 memset(buffer, 0, sizeof(buffer));
448 p = (FILE_PATH_DEVICE_PATH *)buffer;
451 p->length = 4 + namesize;
452 efichar_strncpy(p->path_name,
455 memcpy(dest, buffer, p->length);
463 make_edd30_device_path(int fd, void *buffer)
466 unsigned char bus=0, device=0, function=0;
468 unsigned char host=0, channel=0, id=0, lun=0;
472 rc = disk_get_pci(fd, &bus, &device, &function);
475 memset(&idlun, 0, sizeof(idlun));
476 rc = get_scsi_idlun(fd, &idlun);
478 idlun_to_components(&idlun, &host, &channel, &id, &lun);
480 p += make_acpi_device_path (p, EISAID_PNP0A03, bus);
481 p += make_pci_device_path (p, bus, device, function);
482 p += make_scsi_device_path (p, id, lun);
483 return ((void *)p - buffer);
487 * make_disk_load_option()
490 * Returns 0 on error, length of load option created on success.
492 char *make_disk_load_option(char *p, char *disk)
497 int rc, edd_version=0;
498 uint8_t mbr_type=0, signature_type=0;
499 uint64_t start=0, size=0;
500 efi_char16_t os_loader_path[40];
502 memset(signature, 0, sizeof(signature));
504 disk_fd = open(opts.disk, O_RDWR);
506 sprintf(buffer, "Could not open disk %s", opts.disk);
511 if (opts.edd_version) {
512 edd_version = get_edd_version();
514 if (edd_version == 3) {
515 p += make_edd30_device_path(disk_fd, p);
517 else if (edd_version == 1) {
518 p += make_edd10_device_path(p, opts.edd10_devicenum);
522 rc = disk_get_partition_info (disk_fd, opts.part,
523 &start, &size, signature,
524 &mbr_type, &signature_type);
529 fprintf(stderr, "Error: no partition information on disk %s.\n"
530 " Cowardly refusing to create a boot option.\n",
535 p += make_harddrive_device_path (p, opts.part,
538 mbr_type, signature_type);
540 efichar_from_char(os_loader_path, opts.loader, sizeof(os_loader_path));
541 p += make_file_path_device_path (p, os_loader_path);
542 p += make_end_device_path (p);
548 * make_net_load_option()
549 * @data - load option returned
551 * Returns NULL on error, or p advanced by length of load option
552 * created on success.
554 char *make_net_load_option(char *p, char *iface)
556 /* copied pretty much verbatim from the ethtool source */
560 struct ethtool_drvinfo drvinfo;
562 memset(&ifr, 0, sizeof(ifr));
563 strcpy(ifr.ifr_name, iface);
564 drvinfo.cmd = ETHTOOL_GDRVINFO;
565 ifr.ifr_data = (caddr_t)&drvinfo;
566 /* Open control socket */
567 fd = socket(AF_INET, SOCK_DGRAM, 0);
569 perror("Cannot get control socket");
572 err = ioctl(fd, SIOCETHTOOL, &ifr);
574 perror("Cannot get driver information");
578 /* The domain part was added in 2.6 kernels. Test for that first. */
579 err = sscanf(drvinfo.bus_info, "%*x:%2x:%2x.%x", &bus, &slot, &func);
581 err = sscanf(drvinfo.bus_info, "%2x:%2x.%x", &bus, &slot, &func);
583 perror("Couldn't parse device location string.");
588 err = ioctl(fd, SIOCGIFHWADDR, &ifr);
590 perror("Cannot get hardware address.");
594 p += make_acpi_device_path(p, opts.acpi_hid, opts.acpi_uid);
595 p += make_pci_device_path(p, bus, (uint8_t)slot, (uint8_t)func);
596 p += make_mac_addr_device_path(p, ifr.ifr_ifru.ifru_hwaddr.sa_data, 0);
597 p += make_end_device_path (p);
604 * make_linux_load_option()
605 * @data - load option returned
607 * Returns 0 on error, length of load option created on success.
610 make_linux_load_option(void *data)
612 EFI_LOAD_OPTION *load_option = data;
614 efi_char16_t description[64];
615 unsigned long datasize=0;
617 /* Write Attributes */
618 if (opts.active) load_option->attributes = LOAD_OPTION_ACTIVE;
619 else load_option->attributes = 0;
621 p += sizeof(uint32_t);
622 /* skip writing file_path_list_length */
623 p += sizeof(uint16_t);
624 /* Write description. This is the text that appears on the screen for the load option. */
625 memset(description, 0, sizeof(description));
626 efichar_from_char(description, opts.label, sizeof(description));
627 efichar_strncpy(load_option->description, description, sizeof(description));
628 p += efichar_strsize(load_option->description);
633 p = (char *)make_net_load_option(p, opts.iface);
636 p = (char *)make_disk_load_option(p, opts.iface);
641 load_option->file_path_list_length = p - q;
643 datasize = (uint8_t *)p - (uint8_t *)data;
648 * append_extra_args()
649 * appends all arguments from argv[] not snarfed by getopt
650 * as one long string onto data, up to maxchars. allow for nulls
654 append_extra_args_ascii(void *data, unsigned long maxchars)
658 unsigned long usedchars=0;
662 for (i=opts.optind; i < opts.argc && usedchars < maxchars; i++) {
663 p = strncpy(p, opts.argv[i], maxchars-usedchars-1);
667 usedchars = p - (char *)data;
669 /* Put a space between args */
670 if (i < (opts.argc-1)) {
672 p = strncpy(p, " ", maxchars-usedchars-1);
674 usedchars = p - (char *)data;
678 /* Remember the NULL */
679 if (appended) return strlen(data) + 1;
684 append_extra_args_unicode(void *data, unsigned long maxchars)
688 unsigned long usedchars=0;
692 for (i=opts.optind; i < opts.argc && usedchars < maxchars; i++) {
693 p += efichar_from_char((efi_char16_t *)p, opts.argv[i],
695 usedchars = efichar_strsize(data) - sizeof(efi_char16_t);
698 /* Put a space between args */
699 if (i < (opts.argc-1)) {
700 p += efichar_from_char((efi_char16_t *)p, " ",
702 usedchars = efichar_strsize(data) -
703 sizeof(efi_char16_t);
707 if (appended) return efichar_strsize( (efi_char16_t *)data );
712 append_extra_args_file(void *data, unsigned long maxchars)
715 char *file = opts.extra_opts_file;
716 int fd = STDIN_FILENO;
718 unsigned long appended=0;
722 if (file && strncmp(file, "-", 1))
723 fd = open(file, O_RDONLY);
726 perror("Failed to open extra arguments file");
731 num_read = read(fd, p, maxchars - appended);
733 perror("Error reading extra arguments file");
736 else if (num_read>0) {
737 appended += num_read;
740 } while (num_read > 0 && ((maxchars - appended) > 0));
742 if (fd != STDIN_FILENO)
750 append_extra_args(void *data, unsigned long maxchars)
752 unsigned long bytes_written=0;
754 if (opts.extra_opts_file)
755 bytes_written += append_extra_args_file(data, maxchars);
758 bytes_written += append_extra_args_unicode(data, maxchars - bytes_written);
760 bytes_written += append_extra_args_ascii(data, maxchars - bytes_written);
761 return bytes_written;
767 make_linux_efi_variable(efi_variable_t *var,
768 unsigned int free_number)
770 efi_guid_t guid = EFI_GLOBAL_VARIABLE;
772 unsigned char *optional_data=NULL;
773 unsigned long load_option_size = 0, opt_data_size=0;
775 memset(buffer, 0, sizeof(buffer));
777 /* VariableName needs to be BootXXXX */
778 sprintf(buffer, "Boot%04X", free_number);
780 efichar_from_char(var->VariableName, buffer, 1024);
782 memcpy(&(var->VendorGuid), &guid, sizeof(guid));
784 EFI_VARIABLE_NON_VOLATILE |
785 EFI_VARIABLE_BOOTSERVICE_ACCESS |
786 EFI_VARIABLE_RUNTIME_ACCESS;
788 /* Set Data[] and DataSize */
790 load_option_size = make_linux_load_option(var->Data);
792 if (!load_option_size) return 0;
794 /* Set OptionalData (passed as binary to the called app) */
795 optional_data = var->Data + load_option_size;
796 opt_data_size = append_extra_args(optional_data,
797 sizeof(var->Data) - load_option_size);
798 var->DataSize = load_option_size + opt_data_size;
799 return var->DataSize;
804 variable_to_name(efi_variable_t *var, char *name)
807 efichar_to_char(p, var->VariableName, PATH_MAX);
809 p += sprintf(p, "-");
810 efi_guid_unparse(&var->VendorGuid, p);