2 efi.[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
33 #include <sys/socket.h>
34 #include <sys/types.h>
35 #include <sys/ioctl.h>
36 #include <linux/sockios.h>
39 typedef unsigned long long u64; /* hack, so we may include kernel's ethtool.h */
40 typedef __uint32_t u32; /* ditto */
41 typedef __uint16_t u16; /* ditto */
42 typedef __uint8_t u8; /* ditto */
44 #include <linux/ethtool.h>
47 #include "scsi_ioctls.h"
49 #include "efibootmgr.h"
52 load_option_path(EFI_LOAD_OPTION *option)
54 char *p = (char *) option;
55 return (EFI_DEVICE_PATH *)
56 (p + sizeof(uint32_t) /* Attributes */
57 + sizeof(uint16_t) /* FilePathListLength*/
58 + efichar_strsize(option->description)); /* Description */
62 efi_guid_unparse(efi_guid_t *guid, char *out)
64 sprintf(out, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
65 guid->b[3], guid->b[2], guid->b[1], guid->b[0],
66 guid->b[5], guid->b[4], guid->b[7], guid->b[6],
67 guid->b[8], guid->b[9], guid->b[10], guid->b[11],
68 guid->b[12], guid->b[13], guid->b[14], guid->b[15]);
74 read_variable(char *name, efi_variable_t *var)
80 if (!name || !var) return EFI_INVALID_PARAMETER;
82 newnamesize = strlen(PROC_DIR_EFI_VARS) + strlen(name) + 1;
83 newname = malloc(newnamesize);
84 if (!newname) return EFI_OUT_OF_RESOURCES;
85 sprintf(newname, "%s%s", PROC_DIR_EFI_VARS,name);
86 fd = open(newname, O_RDONLY);
91 readsize = read(fd, var, sizeof(*var));
92 if (readsize != sizeof(*var)) {
95 return EFI_INVALID_PARAMETER;
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");
124 * select_variable_names()
125 * @d - dirent to compare against
127 * This ignores "." and ".." entries, and selects all others.
131 select_variable_names(const struct dirent *d)
133 if (!strcmp(d->d_name, ".") ||
134 !strcmp(d->d_name, ".."))
140 * find_write_victim()
141 * @var - variable to be written
142 * @file - name of file to open for writing @var is returned.
144 * This ignores "." and ".." entries, and selects all others.
147 find_write_victim(efi_variable_t *var, char file[PATH_MAX])
149 struct dirent **namelist = NULL;
151 char testname[PATH_MAX], *p;
153 memset(testname, 0, sizeof(testname));
154 n = scandir(PROC_DIR_EFI_VARS, &namelist,
155 select_variable_names, alphasort);
157 perror("scandir " PROC_DIR_EFI_VARS);
158 fprintf(stderr, "You must 'modprobe efivars' first.\n");
163 efichar_to_char(p, var->VariableName, PATH_MAX);
165 p += sprintf(p, "-");
166 efi_guid_unparse(&var->VendorGuid, p);
168 for (i=0; i<n; i++) {
170 strncmp(testname, namelist[i]->d_name, sizeof(testname))) {
172 sprintf(file, "%s%s", PROC_DIR_EFI_VARS,
173 namelist[i]->d_name);
186 if (!found) return NULL;
192 write_variable(efi_variable_t *var)
196 char buffer[PATH_MAX], name[PATH_MAX], *p = NULL;
198 if (!var) return EFI_INVALID_PARAMETER;
199 if (opts.testfile) return write_variable_to_file(var);
200 memset(buffer, 0, sizeof(buffer));
201 memset(name, 0, sizeof(name));
203 p = find_write_victim(var, name);
204 if (!p) return EFI_INVALID_PARAMETER;
206 fd = open(name, O_WRONLY);
208 sprintf(buffer, "write_variable():open(%s)", name);
210 return EFI_INVALID_PARAMETER;
212 writesize = write(fd, var, sizeof(*var));
213 if (writesize != sizeof(*var)) {
215 sprintf(buffer, "write_variable():write(%s)", name);
217 dump_raw_data(var, sizeof(*var));
220 return EFI_INVALID_PARAMETER;
233 efi_guid_t guid = BLKX_UNKNOWN_GUID;
234 char name[80], text_guid[40];
235 ACPI_DEVICE_PATH *path = (ACPI_DEVICE_PATH *)&(var.Data);
238 /* Allow global user option override */
240 switch (opts.edd_version)
242 case 0: /* No EDD information */
245 case 1: /* EDD 1.0 */
248 case 3: /* EDD 3.0 */
256 memset(&var, 0, sizeof(efi_variable_t));
257 efi_guid_unparse(&guid, text_guid);
258 sprintf(name, "blk0-%s", text_guid);
260 status = read_variable(name, &var);
261 if (status != EFI_SUCCESS) {
264 if (path->type == 2 && path->subtype == 1) rc = 3;
270 EFI_DEVICE_PATH, 0x01 (Hardware), 0x04 (Vendor), length 0x0018
271 This needs to know what EFI device has the boot device.
274 make_edd10_device_path(void *dest, uint32_t hardware_device)
276 VENDOR_DEVICE_PATH *hw;
277 char buffer[EDD10_HARDWARE_VENDOR_PATH_LENGTH];
278 efi_guid_t guid = EDD10_HARDWARE_VENDOR_PATH_GUID;
280 memset(buffer, 0, sizeof(buffer));
281 hw = (VENDOR_DEVICE_PATH *)buffer;
282 data = (uint32_t *)hw->data;
283 hw->type = 0x01; /* Hardware Device Path */
284 hw->subtype = 0x04; /* Vendor */
285 hw->length = EDD10_HARDWARE_VENDOR_PATH_LENGTH;
286 memcpy(&(hw->vendor_guid), &guid, sizeof(guid));
287 *data = hardware_device;
288 memcpy(dest, buffer, hw->length);
293 make_end_device_path(void *dest)
296 memset(&p, 0, sizeof(p));
297 p.type = 0x7F; /* End of Hardware Device Path */
298 p.subtype = 0xFF; /* End Entire Device Path */
299 p.length = sizeof(p);
300 memcpy(dest, &p, p.length);
305 make_acpi_device_path(void *dest, uint32_t _HID, uint32_t _UID)
308 memset(&p, 0, sizeof(p));
311 p.length = sizeof(p);
314 memcpy(dest, &p, p.length);
319 make_mac_addr_device_path(void *dest, char *mac, uint8_t iftype)
323 MAC_ADDR_DEVICE_PATH p;
324 memset(&p, 0, sizeof(p));
327 p.length = sizeof(p);
328 for (i=0; i < 14; i++) {
329 p.macaddr[i] = mac[i];
332 memcpy(dest, &p, p.length);
337 make_pci_device_path(void *dest, uint8_t device, uint8_t function)
340 memset(&p, 0, sizeof(p));
343 p.length = sizeof(p);
345 p.function = function;
346 memcpy(dest, &p, p.length);
351 make_scsi_device_path(void *dest, uint16_t id, uint16_t lun)
354 memset(&p, 0, sizeof(p));
357 p.length = sizeof(p);
360 memcpy(dest, &p, p.length);
365 make_harddrive_device_path(void *dest, uint32_t num, uint64_t start, uint64_t size,
367 uint8_t mbr_type, uint8_t signature_type)
369 HARDDRIVE_DEVICE_PATH p;
370 memset(&p, 0, sizeof(p));
373 p.length = sizeof(p);
377 if (signature) memcpy(p.signature, signature, 16);
378 p.mbr_type = mbr_type;
379 p.signature_type = signature_type;
380 memcpy(dest, &p, p.length);
385 make_file_path_device_path(void *dest, efi_char16_t *name)
387 FILE_PATH_DEVICE_PATH *p;
389 int namelen = efichar_strlen(name, -1);
390 int namesize = efichar_strsize(name);
392 memset(buffer, 0, sizeof(buffer));
393 p = (FILE_PATH_DEVICE_PATH *)buffer;
396 p->length = 4 + namesize;
397 efichar_strncpy(p->path_name,
400 memcpy(dest, buffer, p->length);
408 make_edd30_device_path(int fd, void *buffer)
411 unsigned char bus=0, device=0, function=0;
413 unsigned char host=0, channel=0, id=0, lun=0;
417 rc = disk_get_pci(fd, &bus, &device, &function);
420 memset(&idlun, 0, sizeof(idlun));
421 rc = get_scsi_idlun(fd, &idlun);
423 idlun_to_components(&idlun, &host, &channel, &id, &lun);
425 p += make_acpi_device_path (p, EISAID_PNP0A03, bus);
426 p += make_pci_device_path (p, device, function);
427 p += make_scsi_device_path (p, id, lun);
428 return ((void *)p - buffer);
432 * make_disk_load_option()
435 * Returns 0 on error, length of load option created on success.
437 char *make_disk_load_option(char *p, char *disk)
442 int rc, edd_version=0;
443 uint8_t mbr_type=0, signature_type=0;
444 uint64_t start=0, size=0;
445 efi_char16_t os_loader_path[40];
447 memset(signature, 0, sizeof(signature));
449 disk_fd = open(opts.disk, O_RDWR);
451 sprintf(buffer, "Could not open disk %s", opts.disk);
456 if (opts.edd_version) {
457 edd_version = get_edd_version();
459 if (edd_version == 3) {
460 p += make_edd30_device_path(disk_fd, p);
462 else if (edd_version == 1) {
463 p += make_edd10_device_path(p, opts.edd10_devicenum);
467 rc = disk_get_partition_info (disk_fd, opts.part,
468 &start, &size, signature,
469 &mbr_type, &signature_type);
474 fprintf(stderr, "Error: no partition information on disk %s.\n"
475 " Cowardly refusing to create a boot option.\n",
480 p += make_harddrive_device_path (p, opts.part,
483 mbr_type, signature_type);
485 efichar_from_char(os_loader_path, opts.loader, sizeof(os_loader_path));
486 p += make_file_path_device_path (p, os_loader_path);
487 p += make_end_device_path (p);
493 * make_net_load_option()
494 * @data - load option returned
496 * Returns 0 on error, length of load option created on success.
498 char *make_net_load_option(char *p, char *iface)
500 /* copied pretty much verbatim from the ethtool source */
504 struct ethtool_drvinfo drvinfo;
506 memset(&ifr, 0, sizeof(ifr));
507 strcpy(ifr.ifr_name, iface);
508 drvinfo.cmd = ETHTOOL_GDRVINFO;
509 ifr.ifr_data = (caddr_t)&drvinfo;
510 /* Open control socket */
511 fd = socket(AF_INET, SOCK_DGRAM, 0);
513 perror("Cannot get control socket");
515 err = ioctl(fd, SIOCETHTOOL, &ifr);
517 perror("Cannot get driver information");
520 err = sscanf(drvinfo.bus_info, "%2x:%2x.%x", &bus, &slot, &func);
522 perror("Couldn't parse device location string.");
525 p += make_acpi_device_path(p, opts.acpi_hid, opts.acpi_uid);
526 p += make_pci_device_path(p, (uint8_t)slot, (uint8_t)func);
528 err = ioctl(fd, SIOCGIFHWADDR, &ifr);
530 perror("Cannot get hardware address.");
533 p += make_mac_addr_device_path(p, ifr.ifr_ifru.ifru_hwaddr.sa_data, 0);
534 p += make_end_device_path (p);
540 * make_linux_load_option()
541 * @data - load option returned
543 * Returns 0 on error, length of load option created on success.
546 make_linux_load_option(void *data)
548 EFI_LOAD_OPTION *load_option = data;
550 efi_char16_t description[64];
551 unsigned long datasize=0;
553 /* Write Attributes */
554 if (opts.active) load_option->attributes = LOAD_OPTION_ACTIVE;
555 else load_option->attributes = 0;
557 p += sizeof(uint32_t);
558 /* skip writing file_path_list_length */
559 p += sizeof(uint16_t);
560 /* Write description. This is the text that appears on the screen for the load option. */
561 memset(description, 0, sizeof(description));
562 efichar_from_char(description, opts.label, sizeof(description));
563 efichar_strncpy(load_option->description, description, sizeof(description));
564 p += efichar_strsize(load_option->description);
569 p = (char *)make_net_load_option(p, opts.iface);
573 p = (char *)make_disk_load_option(p, opts.iface);
576 load_option->file_path_list_length = p - q;
578 datasize = (uint8_t *)p - (uint8_t *)data;
583 * append_extra_args()
584 * appends all arguments from argv[] not snarfed by getopt
585 * as one long string onto data, up to maxchars. allow for nulls
589 append_extra_args_ascii(void *data, unsigned long maxchars)
593 unsigned long usedchars=0;
597 for (i=opts.optind; i < opts.argc && usedchars < maxchars; i++) {
598 p = strncpy(p, opts.argv[i], maxchars-usedchars-1);
602 usedchars = p - (char *)data;
604 /* Put a space between args */
605 if (i < (opts.argc-1)) {
607 p = strncpy(p, " ", maxchars-usedchars-1);
609 usedchars = p - (char *)data;
613 /* Remember the NULL */
614 if (appended) return strlen(data) + 1;
619 append_extra_args_unicode(void *data, unsigned long maxchars)
623 unsigned long usedchars=0;
627 for (i=opts.optind; i < opts.argc && usedchars < maxchars; i++) {
628 p += efichar_from_char((efi_char16_t *)p, opts.argv[i],
630 usedchars = efichar_strsize(data) - sizeof(efi_char16_t);
633 /* Put a space between args */
634 if (i < (opts.argc-1)) {
635 p += efichar_from_char((efi_char16_t *)p, " ",
637 usedchars = efichar_strsize(data) -
638 sizeof(efi_char16_t);
642 if (appended) return efichar_strsize( (efi_char16_t *)data );
648 append_extra_args(void *data, unsigned long maxchars)
651 return append_extra_args_unicode(data, maxchars);
653 return append_extra_args_ascii(data, maxchars);
659 make_linux_efi_variable(efi_variable_t *var,
660 unsigned int free_number)
662 efi_guid_t guid = EFI_GLOBAL_VARIABLE;
664 unsigned char *optional_data=NULL;
665 unsigned long load_option_size = 0, opt_data_size=0;
667 memset(buffer, 0, sizeof(buffer));
669 /* VariableName needs to be BootXXXX */
670 sprintf(buffer, "Boot%04x", free_number);
672 efichar_from_char(var->VariableName, buffer, 1024);
674 memcpy(&(var->VendorGuid), &guid, sizeof(guid));
676 EFI_VARIABLE_NON_VOLATILE |
677 EFI_VARIABLE_BOOTSERVICE_ACCESS |
678 EFI_VARIABLE_RUNTIME_ACCESS;
680 /* Set Data[] and DataSize */
682 load_option_size = make_linux_load_option(var->Data);
684 if (!load_option_size) return 0;
686 /* Set OptionalData (passed as binary to the called app) */
687 optional_data = var->Data + load_option_size;
688 opt_data_size = append_extra_args(optional_data,
689 sizeof(var->Data) - load_option_size);
690 var->DataSize = load_option_size + opt_data_size;
691 return var->DataSize;