From 08b92cb68b9f2371c663367e9d529391960e6106 Mon Sep 17 00:00:00 2001 From: Bdale Garbee Date: Sat, 1 Apr 2006 22:00:02 -0800 Subject: [PATCH] Imported Debian patch 0.5.3-1 --- AUTHORS | 4 + Makefile | 7 +- README | 2 + debian/changelog | 6 + debian/compat | 1 + debian/control | 4 +- debian/rules | 2 - doc/ChangeLog | 30 + src/efibootmgr/efibootmgr.c | 20 +- src/efibootmgr/efibootmgr.c.orig | 1045 +++++++++++++++++++++++++++++ src/include/efi.h | 2 +- src/include/efibootmgr.h | 1 + src/lib/disk.c | 2 - src/lib/efi.c | 77 ++- src/lib/efi.c.orig | 765 +++++++++++++++++++++ src/lib/efivars_procfs.c | 2 - src/lib/efivars_sysfs.c | 2 - src/lib/gpt.c | 3 +- src/man/man8/efibootmgr.8 | 22 +- src/man/man8/efibootmgr.8.docbook | 16 +- 20 files changed, 1974 insertions(+), 39 deletions(-) create mode 100644 debian/compat create mode 100644 src/efibootmgr/efibootmgr.c.orig create mode 100644 src/lib/efi.c.orig diff --git a/AUTHORS b/AUTHORS index 40dd425..1bf3318 100644 --- a/AUTHORS +++ b/AUTHORS @@ -20,3 +20,7 @@ Joshua Giles Alex Williamson - Patch to efi.c and efibootmgr.c for handling BootXXXX values using uppercase hex rather than lowercase, per EFI 1.10 spec. + +Rogerio Timmers +- add option -@ for passing extra variable options in from a file, + necessary for setting up some boot entries for Microsoft Windows. diff --git a/Makefile b/Makefile index 23bc9d8..a52eef2 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,15 @@ default: all - RELEASE_DATE := "08-Mar-2005" + RELEASE_DATE := "09-Nov-2005" RELEASE_MAJOR := 0 RELEASE_MINOR := 5 - RELEASE_SUBLEVEL := 1 + RELEASE_SUBLEVEL := 3 RELEASE_EXTRALEVEL := RELEASE_NAME := efibootmgr RELEASE_STRING := $(RELEASE_NAME)-$(RELEASE_MAJOR).$(RELEASE_MINOR).$(RELEASE_SUBLEVEL)$(RELEASE_EXTRALEVEL) - CFLAGS += -DEFIBOOTMGR_VERSION=\"$(RELEASE_MAJOR).$(RELEASE_MINOR).$(RELEASE_SUBLEVEL)$(RELEASE_EXTRALEVEL)\" -Wall + CFLAGS += -DEFIBOOTMGR_VERSION=\"$(RELEASE_MAJOR).$(RELEASE_MINOR).$(RELEASE_SUBLEVEL)$(RELEASE_EXTRALEVEL)\" \ + -Wall -g -D_FILE_OFFSET_BITS=64 MODULES := src diff --git a/README b/README index 9ee586b..89a6445 100644 --- a/README +++ b/README @@ -37,6 +37,8 @@ usage: efibootmgr [options] -v | --verbose print additional information -V | --version return version and exit -w | --write-signature write unique sig to MBR if needed + -@ | --append-binary-args append extra variable args from + file (use - to read from stdin). Typical usage: diff --git a/debian/changelog b/debian/changelog index 1ceec3d..1c86251 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +efibootmgr (0.5.3-1) unstable; urgency=low + + * new upstream version, closes: #357884 + + -- Bdale Garbee Sat, 1 Apr 2006 22:00:02 -0800 + efibootmgr (0.5.1-1) unstable; urgency=low * new upstream version. diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +5 diff --git a/debian/control b/debian/control index baaa98b..516728f 100644 --- a/debian/control +++ b/debian/control @@ -2,8 +2,8 @@ Source: efibootmgr Section: admin Priority: optional Maintainer: Bdale Garbee -Build-Depends: debhelper (>> 3.0.0), docbook-to-man, pciutils-dev -Standards-Version: 3.6.1.0 +Build-Depends: debhelper (>> 5), docbook-to-man, pciutils-dev +Standards-Version: 3.6.2.2 Package: efibootmgr Architecture: amd64 i386 ia64 diff --git a/debian/rules b/debian/rules index 6b02157..4d06695 100755 --- a/debian/rules +++ b/debian/rules @@ -1,7 +1,5 @@ #!/usr/bin/make -f -export DH_COMPAT=3 - configure: configure-stamp configure-stamp: dh_testdir diff --git a/doc/ChangeLog b/doc/ChangeLog index 2780443..eada84f 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,33 @@ +* Wed Nov 9 2005 Matt Domsch +- released v0.5.2.2 as v0.5.3, no changes + +* Thu Aug 11 2005 Matt Domsch +- applied patch from Rogerio Timmers which adds a new option -@ , + which takes extra variable parameters from , or - from stdin. + This lets you pass binary (non-unicode, non-ascii) formatted options to + your bootloader. +- cleaned up Rogerio's patch some. +- moved definition of _FILE_OFFSET_BITS=64 into Makefile and out of the + individual .c files. This fixes a bug reported by Red Flag, where + variable data was getting incorrectly set with a 32-bit copy of + efibootmgr on a 32-bit kernel. +- made efi_variable_t.DataSize be an unsigned long to match the kernel. + This lets a 32-bit copy of efibootmgr run on a 32-bit kernel. This + means you've got to have a 32-bit efibootmgr on a 32-bit kernel, and a + 64-bit efibootmgr on a 64-bit kernel, but since efi_status_t is also a + long, this was really going to be the case anyway. +- valgrind caught the app exiting without freeing some malloc'd + structures, fix that. +- v0.5.2.2 released for testing + + +* Fri May 20 2005 Matt Domsch +- applied patch from Andreas Schwab to properly parse PCI + domain:bus:device.fn info in netboot entries. Fixed up return value + when creating network boot entries for nonexistant devices, so the + creation now fails, rather than succeeding with incorrect data. +- v0.5.2 released + * Tue Mar 08 2005 Matt Domsch - applied patch from Alex Williamson for handling BootXXXX values using uppercase hex rather than lowercase, per EFI 1.10 spec. diff --git a/src/efibootmgr/efibootmgr.c b/src/efibootmgr/efibootmgr.c index 5058f92..7aeb186 100644 --- a/src/efibootmgr/efibootmgr.c +++ b/src/efibootmgr/efibootmgr.c @@ -96,6 +96,18 @@ fill_var(efi_variable_t *var, const char *name) | EFI_VARIABLE_RUNTIME_ACCESS; } +static void +free_vars(list_t *head) +{ + list_t *pos, *n; + var_entry_t *boot; + + list_for_each_safe(pos, n, head) { + boot = list_entry(pos, var_entry_t, list); + list_del(&(boot->list)); + free(boot); + } +} static void read_vars(struct dirent **namelist, @@ -750,6 +762,7 @@ usage() printf("\t-v | --verbose print additional information\n"); printf("\t-V | --version return version and exit\n"); printf("\t-w | --write-signature write unique sig to MBR if needed\n"); + printf("\t-@ | --append-binary-args file append extra args from file (use \"-\" for stdin)\n"); } static void @@ -809,17 +822,21 @@ parse_opts(int argc, char **argv) {"verbose", optional_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {"write-signature", no_argument, 0, 'w'}, + {"append-binary-args", required_argument, 0, '@'}, {0, 0, 0, 0} }; c = getopt_long (argc, argv, - "AaBb:cd:e:E:gH:i:l:L:n:No:Op:qt:TuU:v::Vw", + "AaBb:cd:e:E:gH:i:l:L:n:No:Op:qt:TuU:v::Vw@:", long_options, &option_index); if (c == -1) break; switch (c) { + case '@': + opts.extra_opts_file = optarg; + break; case 'a': opts.active = 1; break; @@ -1027,6 +1044,7 @@ main(int argc, char **argv) } } free_dirents(boot_names, num_boot_names); + free_vars(&boot_entry_list); return 0; } diff --git a/src/efibootmgr/efibootmgr.c.orig b/src/efibootmgr/efibootmgr.c.orig new file mode 100644 index 0000000..5a1a0d9 --- /dev/null +++ b/src/efibootmgr/efibootmgr.c.orig @@ -0,0 +1,1045 @@ +/* + efibootmgr.c - Manipulates EFI variables as exported in /proc/efi/vars + + Copyright (C) 2001-2004 Dell, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + This must tie the EFI_DEVICE_PATH to /boot/efi/elilo.efi + The EFI_DEVICE_PATH will look something like: + ACPI device path, length 12 bytes + Hardware Device Path, PCI, length 6 bytes + Messaging Device Path, SCSI, length 8 bytes, or ATAPI, length ?? + Media Device Path, Hard Drive, partition XX, length 30 bytes + Media Device Path, File Path, length ?? + End of Hardware Device Path, length 4 + Arguments passed to elilo, as UCS-2 characters, length ?? + +*/ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "list.h" +#include "efi.h" +#include "efichar.h" +#include "unparse_path.h" +#include "disk.h" +#include "efibootmgr.h" + + +#ifndef EFIBOOTMGR_VERSION +#define EFIBOOTMGR_VERSION "unknown (fix Makefile!)" +#endif + + +typedef struct _var_entry { + struct dirent *name; + uint16_t num; + efi_variable_t var_data; + list_t list; +} var_entry_t; + + +/* global variables */ +static LIST_HEAD(boot_entry_list); +static LIST_HEAD(blk_list); +efibootmgr_opt_t opts; + +static inline void +var_num_from_name(const char *pattern, char *name, uint16_t *num) +{ + sscanf(name, pattern, num); +} + +static void +fill_bootvar_name(char *dest, size_t len, const char *name) +{ + efi_guid_t guid = EFI_GLOBAL_VARIABLE; + char text_uuid[40]; + efi_guid_unparse(&guid, text_uuid); + snprintf(dest, len, "%s-%s", name, text_uuid); +} + +static void +fill_var(efi_variable_t *var, const char *name) +{ + efi_guid_t guid = EFI_GLOBAL_VARIABLE; + + efichar_from_char(var->VariableName, name, 1024); + memcpy(&var->VendorGuid, &guid, sizeof(guid)); + var->Attributes = EFI_VARIABLE_NON_VOLATILE + | EFI_VARIABLE_BOOTSERVICE_ACCESS + | EFI_VARIABLE_RUNTIME_ACCESS; +} + +static void +free_vars(list_t *head) +{ + list_t *pos, *n; + var_entry_t *boot; + + list_for_each_safe(pos, n, head) { + boot = list_entry(pos, var_entry_t, list); + list_del(&(boot->list)); + free(boot); + } +} + +static void +read_vars(struct dirent **namelist, + int num_boot_names, + list_t *head) +{ + efi_status_t status; + var_entry_t *entry; + int i; + + if (!namelist) return; + + for (i=0; i < num_boot_names; i++) + { + if (namelist[i]) { + entry = malloc(sizeof(var_entry_t)); + if (!entry) return; + memset(entry, 0, sizeof(var_entry_t)); + + status = read_variable(namelist[i]->d_name, + &entry->var_data); + if (status != EFI_SUCCESS) break; + entry->name = namelist[i]; + list_add_tail(&entry->list, head); + } + } + return; +} + + + + + +static void +free_dirents(struct dirent **ptr, int num_dirents) +{ + int i; + if (!ptr) return; + for (i=0; i < num_dirents; i++) { + if (ptr[i]) { + free(ptr[i]); + ptr[i] = NULL; + } + } + free(ptr); +} + + + +static int +compare(const void *a, const void *b) +{ + int rc = -1; + uint32_t n1, n2; + memcpy(&n1, a, sizeof(n1)); + memcpy(&n2, b, sizeof(n2)); + if (n1 < n2) rc = -1; + if (n1 == n2) rc = 0; + if (n2 > n2) rc = 1; + return rc; +} + + +/* + Return an available boot variable number, + or -1 on failure. +*/ +static int +find_free_boot_var(list_t *boot_list) +{ + int num_vars=0, i=0, found; + uint16_t *vars, free_number; + list_t *pos; + var_entry_t *boot; + list_for_each(pos, boot_list) { + num_vars++; + } + vars = malloc(sizeof(uint16_t) * num_vars); + if (!vars) return -1; + memset(vars, 0, sizeof(uint16_t) * num_vars); + + list_for_each(pos, boot_list) { + boot = list_entry(pos, var_entry_t, list); + vars[i] = boot->num; + i++; + } + qsort(vars, i, sizeof(uint16_t), compare); + found = 1; + + num_vars = i; + for (free_number = 0; free_number < num_vars && found; free_number++) { + found = 0; + list_for_each(pos, boot_list) { + boot = list_entry(pos, var_entry_t, list); + if (boot->num == free_number) { + found = 1; + break; + } + } + if (!found) break; + } + if (found && num_vars) free_number = vars[num_vars-1] + 1; + free(vars); + return free_number; +} + + +static void +warn_duplicate_name(list_t *boot_list) +{ + list_t *pos; + var_entry_t *boot; + EFI_LOAD_OPTION *load_option; + + list_for_each(pos, boot_list) { + boot = list_entry(pos, var_entry_t, list); + load_option = (EFI_LOAD_OPTION *) + boot->var_data.Data; + if (!efichar_char_strcmp(opts.label, + load_option->description)) { + fprintf(stderr, "** Warning ** : %.8s has same label %s\n", + boot->name->d_name, + opts.label); + } + } +} + + +static var_entry_t * +make_boot_var(list_t *boot_list) +{ + var_entry_t *boot; + int free_number; + list_t *pos; + + if (opts.bootnum == -1) + free_number = find_free_boot_var(boot_list); + else { + list_for_each(pos, boot_list) { + boot = list_entry(pos, var_entry_t, list); + if (boot->num == opts.bootnum) { + fprintf(stderr, "** Warning ** : bootnum %04X " + "already exists\n", opts.bootnum); + return NULL; + } + } + free_number = opts.bootnum; + } + + if (free_number == -1) return NULL; + + /* Create a new var_entry_t object + and populate it. + */ + + boot = malloc(sizeof(*boot)); + if (!boot) return NULL; + memset(boot, 0, sizeof(*boot)); + boot->num = free_number; + if (!make_linux_efi_variable(&boot->var_data, free_number)) { + free(boot); + return NULL; + } + create_variable(&boot->var_data); + list_add_tail(&boot->list, boot_list); + return boot; +} + + + +static efi_status_t +read_boot(efi_variable_t *var, const char *name) +{ + char name_guid[PATH_MAX]; + + memset(var, 0, sizeof(*var)); + fill_bootvar_name(name_guid, sizeof(name_guid), name); + return read_variable(name_guid, var); +} + +static efi_status_t +read_boot_order(efi_variable_t *boot_order) +{ + efi_status_t status; + + status = read_boot(boot_order, "BootOrder"); + if (status != EFI_SUCCESS && status != EFI_NOT_FOUND) + return status; + + if (status == EFI_NOT_FOUND) { + fill_var(boot_order, "BootOrder"); + } + return EFI_SUCCESS; +} + + +static efi_status_t +add_to_boot_order(uint16_t num) +{ + efi_status_t status; + efi_variable_t boot_order; + uint64_t new_data_size; + uint16_t *new_data, *old_data; + + status = read_boot_order(&boot_order); + if (status != EFI_SUCCESS) return status; + + /* We've now got an array (in boot_order.Data) of the + boot order. First add our entry, then copy the old array. + */ + old_data = (uint16_t *)&(boot_order.Data); + new_data_size = boot_order.DataSize + sizeof(uint16_t); + new_data = malloc(new_data_size); + + new_data[0] = num; + memcpy(new_data+1, old_data, boot_order.DataSize); + + /* Now new_data has what we need */ + memcpy(&(boot_order.Data), new_data, new_data_size); + boot_order.DataSize = new_data_size; + return create_or_edit_variable(&boot_order); +} + + +static efi_status_t +remove_from_boot_order(uint16_t num) +{ + efi_status_t status; + efi_variable_t boot_order; + uint64_t new_data_size; + uint16_t *new_data, *old_data; + int old_i,new_i; + char boot_order_name[PATH_MAX]; + + status = read_boot_order(&boot_order); + if (status != EFI_SUCCESS) return status; + /* If it's empty, yea! */ + if (!boot_order.DataSize) return EFI_SUCCESS; + + fill_bootvar_name(boot_order_name, sizeof(boot_order_name), + "BootOrder"); + + /* We've now got an array (in boot_order.Data) of the + boot order. Simply copy the array, skipping the + entry we're deleting. + */ + old_data = (uint16_t *)&(boot_order.Data); + /* Start with the same size */ + new_data_size = boot_order.DataSize; + new_data = malloc(new_data_size); + for (old_i=0,new_i=0; + old_i < boot_order.DataSize / sizeof(uint16_t); + old_i++) { + if (old_data[old_i] != num) { + /* Copy this value */ + new_data[new_i] = old_data[old_i]; + new_i++; + } + } + + /* Now new_data has what we need */ + new_data_size = new_i * sizeof(uint16_t); + memset(&(boot_order.Data), 0, boot_order.DataSize); + memcpy(&(boot_order.Data), new_data, new_data_size); + boot_order.DataSize = new_data_size; + + return edit_variable(&boot_order); +} + +static efi_status_t +delete_var(const char *name) +{ + efi_variable_t var; + + memset(&var, 0, sizeof(var)); + fill_var(&var, name); + return delete_variable(&var); +} + +static int +read_boot_u16(const char *name) +{ + efi_status_t status; + efi_variable_t var; + uint16_t *n = (uint16_t *)(var.Data); + + memset(&var, 0, sizeof(var)); + status = read_boot(&var, name); + if (status) return -1; + return *n; +} + +static efi_status_t +set_boot_u16(const char *name, uint16_t num) +{ + efi_variable_t var; + uint16_t *n = (uint16_t *)var.Data; + + memset(&var, 0, sizeof(var)); + + fill_var(&var, name); + *n = num; + var.DataSize = sizeof(uint16_t); + return create_or_edit_variable(&var); +} + +static efi_status_t +delete_boot_var(uint16_t num) +{ + efi_status_t status; + efi_variable_t var; + char name[16]; + list_t *pos, *n; + var_entry_t *boot; + + snprintf(name, sizeof(name), "Boot%04X", num); + memset(&var, 0, sizeof(var)); + fill_var(&var, name); + status = delete_variable(&var); + + /* For backwards compatibility, try to delete abcdef entries as well */ + if (status) { + snprintf(name, sizeof(name), "Boot%04x", num); + memset(&var, 0, sizeof(var)); + fill_var(&var, name); + status = delete_variable(&var); + } + + if (status) return status; + list_for_each_safe(pos, n, &boot_entry_list) { + boot = list_entry(pos, var_entry_t, list); + if (boot->num == num) { + status = remove_from_boot_order(num); + if (status) return status; + list_del(&(boot->list)); + break; /* short-circuit since it was found */ + } + } + return EFI_SUCCESS; +} + + +static void +set_var_nums(const char *pattern, list_t *list) +{ + list_t *pos; + var_entry_t *var; + int num=0, rc; + char *name; + int warn=0; + + list_for_each(pos, list) { + var = list_entry(pos, var_entry_t, list); + rc = sscanf(var->name->d_name, pattern, &num); + if (rc == 1) { + var->num = num; + name = var->name->d_name; /* shorter name */ + if ((isalpha(name[4]) && islower(name[4])) || + (isalpha(name[5]) && islower(name[5])) || + (isalpha(name[6]) && islower(name[6])) || + (isalpha(name[7]) && islower(name[7]))) { + fprintf(stderr, "** Warning ** : %.8s is not " + "EFI 1.10 compliant (lowercase hex in name)\n", name); + warn++; + } + } + } + if (warn) { + fprintf(stderr, "** Warning ** : please recreate these using efibootmgr to remove this warning.\n"); + } +} + +#if 0 +static efi_variable_t * +find_pci_scsi_disk_blk(int fd, int bus, int device, int func, + list_t *blk_list) +{ + list_t *pos; + int rc; + Scsi_Idlun idlun; + unsigned char host, channel, id, lun; + var_entry_t *blk; + efi_variable_t *blk_var; + long size = 0; + + memset(&idlun, 0, sizeof(idlun)); + rc = get_scsi_idlun(fd, &idlun); + if (rc) return NULL; + + rc = disk_get_size(fd, &size); + + idlun_to_components(&idlun, &host, &channel, &id, &lun); + + list_for_each(pos, blk_list) { + blk = list_entry(pos, var_entry_t, list); + blk_var = blk->var_data; + + if (!compare_pci_scsi_disk_blk(blk_var, + bus, device, func, + host, channel, id, lun, + 0, size)) { + return blk_var; + } + } + return NULL; +} + + + + +/* The right blkX variable contains: + 1) the PCI and SCSI information for the disk passed in disk_name + 2) Does not contain a partition field +*/ + + +static efi_variable_t * +find_disk_blk(char *disk_name, list_t *blk_list) +{ + efi_variable_t *disk_blk = NULL; + int fd, rc; + unsigned char bus=0,device=0,func=0; + int interface_type=interface_type_unknown; + unsigned int controllernum=0, disknum=0; + unsigned char part=0; + + fd = open(disk_name, O_RDONLY|O_DIRECT); + rc = disk_get_pci(fd, &bus, &device, &func); + if (rc) { + fprintf(stderr, "disk_get_pci() failed.\n"); + return NULL; + } + rc = disk_info_from_fd(fd, + &interface_type, + &controllernum, + &disknum, + &part); + if (rc) { + fprintf(stderr, "disk_info_from_fd() failed.\n"); + return NULL; + } + switch (interface_type) + { + case scsi: + return find_pci_scsi_disk_blk(fd,bus,device,func,blk_list); + break; + case ata: + return find_pci_ata_disk_blk(fd,bus,device,func,blk_list); + break; + case i2o: + return find_pci_i2o_disk_blk(fd,bus,device,func,blk_list); + break; + case md: + return find_pci_md_disk_blk(fd,bus,device,func,blk_list); + break; + default: + break; + } + return NULL; +} +#endif + +static void +unparse_boot_order(uint16_t *order, int length) +{ + int i; + printf("BootOrder: "); + for (i=0; ivar_data.Data; + efichar_to_char(description, + load_option->description, sizeof(description)); + memset(text_path, 0, sizeof(text_path)); + path = load_option_path(load_option); + if (boot->name) + printf("%.8s", boot->name->d_name); + else + printf("Boot%04X", boot->num); + + if (load_option->attributes & LOAD_OPTION_ACTIVE) + printf("* "); + else printf(" "); + printf("%s", description); + + if (opts.verbose) { + unparse_path(text_path, path, + load_option->file_path_list_length); + /* Print optional data */ + optional_data_len = + boot->var_data.DataSize - + load_option->file_path_list_length - + ((char *)path - (char *)load_option); + if (optional_data_len) { + p = text_path; + p += strlen(text_path); + unparse_raw_text(p, ((uint8_t *)path) + + load_option->file_path_list_length, + optional_data_len); + } + + printf("\t%s", text_path); + } + printf("\n"); + } +} + + + +static void +show_boot_order() +{ + efi_status_t status; + efi_variable_t boot_order; + uint16_t *data; + + status = read_boot_order(&boot_order); + + if (status != EFI_SUCCESS) { + perror("show_boot_order()"); + return; + } + + /* We've now got an array (in boot_order.Data) of the + boot order. First add our entry, then copy the old array. + */ + data = (uint16_t *)&(boot_order.Data); + if (boot_order.DataSize) + unparse_boot_order(data, boot_order.DataSize / sizeof(uint16_t)); + +} + +static efi_status_t +set_active_state() +{ + list_t *pos; + var_entry_t *boot; + EFI_LOAD_OPTION *load_option; + + list_for_each(pos, &boot_entry_list) { + boot = list_entry(pos, var_entry_t, list); + load_option = (EFI_LOAD_OPTION *) + boot->var_data.Data; + if (boot->num == opts.bootnum) { + if (opts.active == 1) { + if (load_option->attributes + & LOAD_OPTION_ACTIVE) return EFI_SUCCESS; + else { + load_option->attributes + |= LOAD_OPTION_ACTIVE; + return edit_variable(&boot->var_data); + } + } + else if (opts.active == 0) { + if (!(load_option->attributes + & LOAD_OPTION_ACTIVE)) + return EFI_SUCCESS; + else { + load_option->attributes + &= ~LOAD_OPTION_ACTIVE; + return edit_variable(&boot->var_data); + } + } + } + } + return EFI_SUCCESS; +} + + + + +static void +usage() +{ + printf("efibootmgr version %s\n", EFIBOOTMGR_VERSION); + printf("usage: efibootmgr [options]\n"); + printf("\t-a | --active sets bootnum active\n"); + printf("\t-A | --inactive sets bootnum inactive\n"); + printf("\t-b | --bootnum XXXX modify BootXXXX (hex)\n"); + printf("\t-B | --delete-bootnum delete bootnum (hex)\n"); + printf("\t-c | --create create new variable bootnum and add to bootorder\n"); + printf("\t-d | --disk disk (defaults to /dev/sda) containing loader\n"); + printf("\t-e | --edd [1|3|-1] force EDD 1.0 or 3.0 creation variables, or guess\n"); + printf("\t-E | --device num EDD 1.0 device number (defaults to 0x80)\n"); + printf("\t-g | --gpt force disk with invalid PMBR to be treated as GPT\n"); + printf("\t-H | --acpi_hid XXXX set the ACPI HID (used with -i)\n"); + printf("\t-i | --iface name create a netboot entry for the named interface\n"); + printf("\t-l | --loader name (defaults to \\elilo.efi)\n"); + printf("\t-L | --label label Boot manager display label (defaults to \"Linux\")\n"); + printf("\t-n | --bootnext XXXX set BootNext to XXXX (hex)\n"); + printf("\t-N | --delete-bootnext delete BootNext\n"); + printf("\t-o | --bootorder XXXX,YYYY,ZZZZ,... explicitly set BootOrder (hex)\n"); + printf("\t-O | --delete-bootorder delete BootOrder\n"); + printf("\t-p | --part part (defaults to 1) containing loader\n"); + printf("\t-q | --quiet be quiet\n"); + printf("\t | --test filename don't write to NVRAM, write to filename.\n"); + printf("\t-t | --timeout seconds set boot manager timeout waiting for user input.\n"); + printf("\t-T | --delete-timeout delete Timeout.\n"); + printf("\t-u | --unicode | --UCS-2 pass extra args as UCS-2 (default is ASCII)\n"); + printf("\t-U | --acpi_uid XXXX set the ACPI UID (used with -i)\n"); + printf("\t-v | --verbose print additional information\n"); + printf("\t-V | --version return version and exit\n"); + printf("\t-w | --write-signature write unique sig to MBR if needed\n"); +} + +static void +set_default_opts() +{ + memset(&opts, 0, sizeof(opts)); + opts.bootnum = -1; /* auto-detect */ + opts.bootnext = -1; /* Don't set it */ + opts.active = -1; /* Don't set it */ + opts.timeout = -1; /* Don't set it */ + opts.edd10_devicenum = 0x80; + opts.loader = "\\elilo.efi"; + opts.label = "Linux"; + opts.disk = "/dev/sda"; + opts.iface = NULL; + opts.part = 1; + opts.acpi_hid = -1; + opts.acpi_uid = -1; +} + +static void +parse_opts(int argc, char **argv) +{ + int c, num, rc; + int option_index = 0; + + while (1) + { + static struct option long_options[] = + /* name, has_arg, flag, val */ + { + {"active", no_argument, 0, 'a'}, + {"inactive", no_argument, 0, 'A'}, + {"bootnum", required_argument, 0, 'b'}, + {"delete-bootnum", no_argument, 0, 'B'}, + {"create", no_argument, 0, 'c'}, + {"disk", required_argument, 0, 'd'}, + {"iface", required_argument, 0, 'i'}, + {"acpi_hid", required_argument, 0, 'H'}, + {"edd-device", required_argument, 0, 'E'}, + {"edd30", required_argument, 0, 'e'}, + {"gpt", no_argument, 0, 'g'}, + {"loader", required_argument, 0, 'l'}, + {"label", required_argument, 0, 'L'}, + {"bootnext", required_argument, 0, 'n'}, + {"delete-bootnext", no_argument, 0, 'N'}, + {"bootorder", required_argument, 0, 'o'}, + {"delete-bootorder", no_argument, 0, 'O'}, + {"part", required_argument, 0, 'p'}, + {"quiet", no_argument, 0, 'q'}, + {"test", required_argument, 0, 1}, + {"timeout", required_argument, 0, 't'}, + {"delete-timeout", no_argument, 0, 'T'}, + {"unicode", no_argument, 0, 'u'}, + {"UCS-2", no_argument, 0, 'u'}, + {"acpi_uid", required_argument, 0, 'U'}, + {"verbose", optional_argument, 0, 'v'}, + {"version", no_argument, 0, 'V'}, + {"write-signature", no_argument, 0, 'w'}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, + "AaBb:cd:e:E:gH:i:l:L:n:No:Op:qt:TuU:v::Vw", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 'a': + opts.active = 1; + break; + case 'A': + opts.active = 0; + break; + case 'B': + opts.delete_boot = 1; + break; + case 'b': + rc = sscanf(optarg, "%X", &num); + if (rc == 1) opts.bootnum = num; + break; + case 'c': + opts.create = 1; + break; + case 'd': + opts.disk = optarg; + break; + case 'e': + rc = sscanf(optarg, "%d", &num); + if (rc == 1) opts.edd_version = num; + break; + case 'E': + rc = sscanf(optarg, "%x", &num); + if (rc == 1) opts.edd10_devicenum = num; + break; + case 'g': + opts.forcegpt = 1; + break; + case 'H': + rc = sscanf(optarg, "%x", &num); + if (rc == 1) opts.acpi_hid = num; + break; + case 'i': + opts.iface = optarg; + break; + case 'l': + opts.loader = optarg; + break; + case 'L': + opts.label = optarg; + break; + case 'N': + opts.delete_bootnext = 1; + break; + case 'n': + rc = sscanf(optarg, "%x", &num); + if (rc == 1) opts.bootnext = num; + break; + case 'o': + opts.bootorder = optarg; + break; + case 'O': + opts.delete_bootorder = 1; + break; + case 'p': + rc = sscanf(optarg, "%u", &num); + if (rc == 1) opts.part = num; + break; + case 'q': + opts.quiet = 1; + break; + case 1: + opts.testfile = optarg; + break; + case 't': + rc = sscanf(optarg, "%u", &num); + if (rc == 1) { + opts.timeout = num; + opts.set_timeout = 1; + } + break; + case 'T': + opts.delete_timeout = 1; + break; + case 'u': + opts.unicode = 1; + break; + + case 'U': + rc = sscanf(optarg, "%x", &num); + if (rc == 1) opts.acpi_uid = num; + break; + case 'v': + opts.verbose = 1; + if (optarg) { + if (!strcmp(optarg, "v")) opts.verbose = 2; + if (!strcmp(optarg, "vv")) opts.verbose = 3; + rc = sscanf(optarg, "%d", &num); + if (rc == 1) opts.verbose = num; + } + break; + case 'V': + opts.showversion = 1; + break; + + case 'w': + opts.write_signature = 1; + break; + + default: + usage(); + exit(1); + } + } + + if (optind < argc) { + opts.argc = argc; + opts.argv = argv; + opts.optind = optind; + } +} + + +int +main(int argc, char **argv) +{ + struct dirent **boot_names = NULL; + var_entry_t *new_boot = NULL; + int num, num_boot_names=0; + + set_default_opts(); + parse_opts(argc, argv); + if (opts.showversion) { + printf("version %s\n", EFIBOOTMGR_VERSION); + return 0; + } + + if (opts.iface && opts.acpi_hid == -1 && opts.acpi_uid == -1) { + fprintf(stderr, "\nYou must specify the ACPI HID and UID when using -i.\n\n"); + return 1; + } + + if (!opts.testfile) + set_fs_kernel_calls(); + + if (!opts.testfile) { + num_boot_names = read_boot_var_names(&boot_names); + read_vars(boot_names, num_boot_names, &boot_entry_list); + set_var_nums("Boot%04X-%*s", &boot_entry_list); + + if (opts.delete_boot) { + if (opts.bootnum == -1) + fprintf(stderr, "\nYou must specify a boot entry to delete (see the -b option).\n\n"); + else + delete_boot_var(opts.bootnum); + } + + if (opts.active >= 0) { + set_active_state(); + } + } + + if (opts.create) { + warn_duplicate_name(&boot_entry_list); + new_boot = make_boot_var(&boot_entry_list); + /* Put this boot var in the right BootOrder */ + if (!opts.testfile && new_boot) + add_to_boot_order(new_boot->num); + } + + if (!opts.testfile) { + + if (opts.delete_bootorder) { + delete_var("BootOrder"); + } + + if (opts.bootorder) { + set_boot_order(); + } + + + if (opts.delete_bootnext) { + delete_var("BootNext"); + } + + if (opts.delete_timeout) { + delete_var("Timeout"); + } + + if (opts.bootnext >= 0) { + set_boot_u16("BootNext", opts.bootnext & 0xFFFF); + } + + if (opts.set_timeout) { + set_boot_u16("Timeout", opts.timeout); + } + + if (!opts.quiet) { + num = read_boot_u16("BootNext"); + if (num != -1 ) { + printf("BootNext: %04X\n", num); + } + num = read_boot_u16("BootCurrent"); + if (num != -1) { + printf("BootCurrent: %04X\n", num); + } + num = read_boot_u16("Timeout"); + if (num != -1) { + printf("Timeout: %u seconds\n", num); + } + show_boot_order(); + show_boot_vars(); + } + } + free_dirents(boot_names, num_boot_names); + free_vars(&boot_entry_list); + return 0; +} + diff --git a/src/include/efi.h b/src/include/efi.h index 5fc52ff..be667ae 100644 --- a/src/include/efi.h +++ b/src/include/efi.h @@ -98,7 +98,7 @@ typedef uint16_t efi_char16_t; /* UNICODE character */ typedef struct _efi_variable_t { efi_char16_t VariableName[1024/sizeof(efi_char16_t)]; efi_guid_t VendorGuid; - uint64_t DataSize; + unsigned long DataSize; uint8_t Data[1024]; efi_status_t Status; uint32_t Attributes; diff --git a/src/include/efibootmgr.h b/src/include/efibootmgr.h index 3546834..9ab6802 100644 --- a/src/include/efibootmgr.h +++ b/src/include/efibootmgr.h @@ -31,6 +31,7 @@ typedef struct { char *label; char *bootorder; char *testfile; + char *extra_opts_file; uint32_t part; int edd_version; int edd10_devicenum; diff --git a/src/lib/disk.c b/src/lib/disk.c index 6581040..883864f 100644 --- a/src/lib/disk.c +++ b/src/lib/disk.c @@ -18,8 +18,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define _FILE_OFFSET_BITS 64 - #include #include #include diff --git a/src/lib/efi.c b/src/lib/efi.c index ac782c9..7dd2ab3 100644 --- a/src/lib/efi.c +++ b/src/lib/efi.c @@ -548,7 +548,8 @@ 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) { @@ -566,29 +567,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, bus, (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; } /** @@ -623,10 +632,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; @@ -698,14 +708,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; } diff --git a/src/lib/efi.c.orig b/src/lib/efi.c.orig new file mode 100644 index 0000000..2257e9f --- /dev/null +++ b/src/lib/efi.c.orig @@ -0,0 +1,765 @@ +/* + efivars_proc.[ch] - Manipulates EFI variables as exported in /proc/efi/vars + + Copyright (C) 2001,2003 Dell Computer Corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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) +{ + char *p = (char *) option; + return (EFI_DEVICE_PATH *) + (p + sizeof(uint32_t) /* Attributes */ + + sizeof(uint16_t) /* FilePathListLength*/ + + efichar_strsize(option->description)); /* Description */ +} + +char * +efi_guid_unparse(efi_guid_t *guid, char *out) +{ + sprintf(out, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + guid->b[3], guid->b[2], guid->b[1], guid->b[0], + guid->b[5], guid->b[4], guid->b[7], guid->b[6], + guid->b[8], guid->b[9], guid->b[10], guid->b[11], + guid->b[12], guid->b[13], guid->b[14], guid->b[15]); + return out; +} + +void +set_fs_kernel_calls() +{ + 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; + } + + snprintf(name, PATH_MAX, "%s", PROCFS_DIR_EFI_VARS); + dir = opendir(name); + if (dir) { + closedir(dir); + fs_kernel_calls = &procfs_kernel_calls; + return; + } + 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) +{ + int fd, byteswritten; + if (!var || !opts.testfile) return EFI_INVALID_PARAMETER; + + printf("Test mode: Writing to %s\n", opts.testfile); + fd = creat(opts.testfile, S_IRWXU); + if (fd == -1) { + perror("Couldn't write to testfile"); + return EFI_INVALID_PARAMETER; + } + + byteswritten = write(fd, var, sizeof(*var)); + if (byteswritten == -1) { + perror("Writing to testfile"); + + } + close(fd); + return EFI_SUCCESS; +} + +efi_status_t +read_variable(const char *name, efi_variable_t *var) +{ + if (!name || !var) return EFI_INVALID_PARAMETER; + return fs_kernel_calls->read(name, var); +} + +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); +} + +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); +} + + +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); + + variable_to_name(var, name); + return fs_kernel_calls->edit(name, var); +} + +efi_status_t +create_or_edit_variable(efi_variable_t *var) +{ + efi_variable_t testvar; + char name[PATH_MAX]; + + memcpy(&testvar, var, sizeof(*var)); + variable_to_name(var, name); + + if (read_variable(name, &testvar) == EFI_SUCCESS) + return edit_variable(var); + else + return create_variable(var); +} + +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; +} + +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); +} + + +static int +get_edd_version() +{ + efi_status_t status; + efi_variable_t var; + efi_guid_t guid = BLKX_UNKNOWN_GUID; + char name[80], text_guid[40]; + ACPI_DEVICE_PATH *path = (ACPI_DEVICE_PATH *)&(var.Data); + int rc = 0; + + /* Allow global user option override */ + + switch (opts.edd_version) + { + case 0: /* No EDD information */ + return 0; + break; + case 1: /* EDD 1.0 */ + return 1; + break; + case 3: /* EDD 3.0 */ + return 3; + break; + default: + break; + } + + + memset(&var, 0, sizeof(efi_variable_t)); + efi_guid_unparse(&guid, text_guid); + sprintf(name, "blk0-%s", text_guid); + + status = read_variable(name, &var); + if (status != EFI_SUCCESS) { + return 0; + } + if (path->type == 2 && path->subtype == 1) rc = 3; + else rc = 1; + return rc; +} + +/* + EFI_DEVICE_PATH, 0x01 (Hardware), 0x04 (Vendor), length 0x0018 + This needs to know what EFI device has the boot device. +*/ +static uint16_t +make_edd10_device_path(void *dest, uint32_t hardware_device) +{ + VENDOR_DEVICE_PATH *hw; + char buffer[EDD10_HARDWARE_VENDOR_PATH_LENGTH]; + efi_guid_t guid = EDD10_HARDWARE_VENDOR_PATH_GUID; + uint32_t *data; + memset(buffer, 0, sizeof(buffer)); + hw = (VENDOR_DEVICE_PATH *)buffer; + data = (uint32_t *)hw->data; + hw->type = 0x01; /* Hardware Device Path */ + hw->subtype = 0x04; /* Vendor */ + hw->length = EDD10_HARDWARE_VENDOR_PATH_LENGTH; + memcpy(&(hw->vendor_guid), &guid, sizeof(guid)); + *data = hardware_device; + memcpy(dest, buffer, hw->length); + return hw->length; +} + +static uint16_t +make_end_device_path(void *dest) +{ + END_DEVICE_PATH p; + memset(&p, 0, sizeof(p)); + p.type = 0x7F; /* End of Hardware Device Path */ + p.subtype = 0xFF; /* End Entire Device Path */ + p.length = sizeof(p); + memcpy(dest, &p, p.length); + return p.length; +} + +static uint16_t +make_acpi_device_path(void *dest, uint32_t _HID, uint32_t _UID) +{ + ACPI_DEVICE_PATH p; + memset(&p, 0, sizeof(p)); + p.type = 2; + p.subtype = 1; + p.length = sizeof(p); + p._HID = _HID; + p._UID = _UID; + memcpy(dest, &p, p.length); + return p.length; +} + +static uint16_t +make_mac_addr_device_path(void *dest, char *mac, uint8_t iftype) +{ + + int i; + MAC_ADDR_DEVICE_PATH p; + memset(&p, 0, sizeof(p)); + p.type = 3; + p.subtype = 11; + p.length = sizeof(p); + for (i=0; i < 14; i++) { + p.macaddr[i] = mac[i]; + } + p.iftype = iftype; + memcpy(dest, &p, p.length); + 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_one_pci_device_path(void *dest, uint8_t device, uint8_t function) +{ + PCI_DEVICE_PATH p; + memset(&p, 0, sizeof(p)); + p.type = 1; + p.subtype = 1; + p.length = sizeof(p); + p.device = device; + p.function = function; + memcpy(dest, &p, p.length); + 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) +{ + SCSI_DEVICE_PATH p; + memset(&p, 0, sizeof(p)); + p.type = 3; + p.subtype = 2; + p.length = sizeof(p); + p.id = id; + p.lun = lun; + memcpy(dest, &p, p.length); + return p.length; +} + +static uint16_t +make_harddrive_device_path(void *dest, uint32_t num, uint64_t start, uint64_t size, + uint8_t *signature, + uint8_t mbr_type, uint8_t signature_type) +{ + HARDDRIVE_DEVICE_PATH p; + memset(&p, 0, sizeof(p)); + p.type = 4; + p.subtype = 1; + p.length = sizeof(p); + p.part_num = num; + p.start = start; + p.size = size; + if (signature) memcpy(p.signature, signature, 16); + p.mbr_type = mbr_type; + p.signature_type = signature_type; + memcpy(dest, &p, p.length); + return p.length; +} + +static uint16_t +make_file_path_device_path(void *dest, efi_char16_t *name) +{ + FILE_PATH_DEVICE_PATH *p; + char buffer[1024]; + int namelen = efichar_strlen(name, -1); + int namesize = efichar_strsize(name); + + memset(buffer, 0, sizeof(buffer)); + p = (FILE_PATH_DEVICE_PATH *)buffer; + p->type = 4; + p->subtype = 4; + p->length = 4 + namesize; + efichar_strncpy(p->path_name, + name, namelen); + + memcpy(dest, buffer, p->length); + return p->length; + +} + + + +static long +make_edd30_device_path(int fd, void *buffer) +{ + int rc=0; + unsigned char bus=0, device=0, function=0; + Scsi_Idlun idlun; + unsigned char host=0, channel=0, id=0, lun=0; + char *p = buffer; + + + rc = disk_get_pci(fd, &bus, &device, &function); + if (rc) return 0; + + memset(&idlun, 0, sizeof(idlun)); + rc = get_scsi_idlun(fd, &idlun); + if (rc) return 0; + idlun_to_components(&idlun, &host, &channel, &id, &lun); + + p += make_acpi_device_path (p, EISAID_PNP0A03, bus); + p += make_pci_device_path (p, bus, device, function); + p += make_scsi_device_path (p, id, lun); + return ((void *)p - buffer); +} + +/** + * make_disk_load_option() + * @disk disk + * + * Returns 0 on error, length of load option created on success. + */ +char *make_disk_load_option(char *p, char *disk) +{ + int disk_fd=0; + char buffer[80]; + char signature[16]; + int rc, edd_version=0; + uint8_t mbr_type=0, signature_type=0; + uint64_t start=0, size=0; + efi_char16_t os_loader_path[40]; + + memset(signature, 0, sizeof(signature)); + + disk_fd = open(opts.disk, O_RDWR); + if (disk_fd == -1) { + sprintf(buffer, "Could not open disk %s", opts.disk); + perror(buffer); + return 0; + } + + if (opts.edd_version) { + edd_version = get_edd_version(); + + if (edd_version == 3) { + p += make_edd30_device_path(disk_fd, p); + } + else if (edd_version == 1) { + p += make_edd10_device_path(p, opts.edd10_devicenum); + } + } + + rc = disk_get_partition_info (disk_fd, opts.part, + &start, &size, signature, + &mbr_type, &signature_type); + + close(disk_fd); + + if (rc) { + fprintf(stderr, "Error: no partition information on disk %s.\n" + " Cowardly refusing to create a boot option.\n", + opts.disk); + return 0; + } + + p += make_harddrive_device_path (p, opts.part, + start, size, + signature, + mbr_type, signature_type); + + efichar_from_char(os_loader_path, opts.loader, sizeof(os_loader_path)); + p += make_file_path_device_path (p, os_loader_path); + p += make_end_device_path (p); + + return(p); +} + +/** + * make_net_load_option() + * @data - load option returned + * + * 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 bus, slot, func; + struct ifreq ifr; + struct ethtool_drvinfo drvinfo; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, iface); + drvinfo.cmd = ETHTOOL_GDRVINFO; + ifr.ifr_data = (caddr_t)&drvinfo; + /* Open control socket */ + 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; + } + + /* 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; + } + } + + 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; +} + +/** + * make_linux_load_option() + * @data - load option returned + * + * Returns 0 on error, length of load option created on success. + */ +static unsigned long +make_linux_load_option(void *data) +{ + EFI_LOAD_OPTION *load_option = data; + char *p = data, *q; + efi_char16_t description[64]; + unsigned long datasize=0; + + /* Write Attributes */ + if (opts.active) load_option->attributes = LOAD_OPTION_ACTIVE; + else load_option->attributes = 0; + + p += sizeof(uint32_t); + /* skip writing file_path_list_length */ + p += sizeof(uint16_t); + /* Write description. This is the text that appears on the screen for the load option. */ + memset(description, 0, sizeof(description)); + efichar_from_char(description, opts.label, sizeof(description)); + efichar_strncpy(load_option->description, description, sizeof(description)); + p += efichar_strsize(load_option->description); + + q = p; + + 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; + + datasize = (uint8_t *)p - (uint8_t *)data; + return datasize; +} + +/* + * append_extra_args() + * appends all arguments from argv[] not snarfed by getopt + * as one long string onto data, up to maxchars. allow for nulls + */ + +static unsigned long +append_extra_args_ascii(void *data, unsigned long maxchars) +{ + char *p = data; + int i, appended=0; + unsigned long usedchars=0; + if (!data) return 0; + + + for (i=opts.optind; i < opts.argc && usedchars < maxchars; i++) { + p = strncpy(p, opts.argv[i], maxchars-usedchars-1); + p += strlen(p); + appended=1; + + usedchars = p - (char *)data; + + /* Put a space between args */ + if (i < (opts.argc-1)) { + + p = strncpy(p, " ", maxchars-usedchars-1); + p += strlen(p); + usedchars = p - (char *)data; + } + + } + /* Remember the NULL */ + if (appended) return strlen(data) + 1; + return 0; +} + +static unsigned long +append_extra_args_unicode(void *data, unsigned long maxchars) +{ + char *p = data; + int i, appended=0; + unsigned long usedchars=0; + if (!data) return 0; + + + for (i=opts.optind; i < opts.argc && usedchars < maxchars; i++) { + p += efichar_from_char((efi_char16_t *)p, opts.argv[i], + maxchars-usedchars); + usedchars = efichar_strsize(data) - sizeof(efi_char16_t); + appended=1; + + /* Put a space between args */ + if (i < (opts.argc-1)) { + p += efichar_from_char((efi_char16_t *)p, " ", + maxchars-usedchars); + usedchars = efichar_strsize(data) - + sizeof(efi_char16_t); + } + } + + if (appended) return efichar_strsize( (efi_char16_t *)data ); + return 0; +} + + +static unsigned long +append_extra_args(void *data, unsigned long maxchars) +{ + if (opts.unicode) + return append_extra_args_unicode(data, maxchars); + else + return append_extra_args_ascii(data, maxchars); +} + + + +int +make_linux_efi_variable(efi_variable_t *var, + unsigned int free_number) +{ + efi_guid_t guid = EFI_GLOBAL_VARIABLE; + char buffer[16]; + unsigned char *optional_data=NULL; + unsigned long load_option_size = 0, opt_data_size=0; + + memset(buffer, 0, sizeof(buffer)); + + /* VariableName needs to be BootXXXX */ + sprintf(buffer, "Boot%04X", free_number); + + efichar_from_char(var->VariableName, buffer, 1024); + + memcpy(&(var->VendorGuid), &guid, sizeof(guid)); + var->Attributes = + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS; + + /* Set Data[] and DataSize */ + + load_option_size = make_linux_load_option(var->Data); + + if (!load_option_size) return 0; + + /* Set OptionalData (passed as binary to the called app) */ + optional_data = var->Data + load_option_size; + opt_data_size = append_extra_args(optional_data, + sizeof(var->Data) - load_option_size); + 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); +} diff --git a/src/lib/efivars_procfs.c b/src/lib/efivars_procfs.c index 2ff5d64..54fe290 100644 --- a/src/lib/efivars_procfs.c +++ b/src/lib/efivars_procfs.c @@ -18,8 +18,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define _FILE_OFFSET_BITS 64 - #include #include #include diff --git a/src/lib/efivars_sysfs.c b/src/lib/efivars_sysfs.c index 642c5ad..182c70f 100644 --- a/src/lib/efivars_sysfs.c +++ b/src/lib/efivars_sysfs.c @@ -18,8 +18,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define _FILE_OFFSET_BITS 64 - #include #include #include diff --git a/src/lib/gpt.c b/src/lib/gpt.c index 85aefed..02985f4 100644 --- a/src/lib/gpt.c +++ b/src/lib/gpt.c @@ -22,8 +22,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define _FILE_OFFSET_BITS 64 - #include #include #include @@ -236,6 +234,7 @@ read_lba(int fd, uint64_t lba, void *buffer, size_t bytes) lseek(fd, offset, SEEK_SET); bytesread = read(fd, aligned, bytes); memcpy(buffer, aligned, bytesread); + free(unaligned); /* Kludge. This is necessary to read/write the last block of an odd-sized disk, until Linux 2.5.x kernel fixes. diff --git a/src/man/man8/efibootmgr.8 b/src/man/man8/efibootmgr.8 index e65a659..214ab89 100644 --- a/src/man/man8/efibootmgr.8 +++ b/src/man/man8/efibootmgr.8 @@ -3,13 +3,13 @@ .\" .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng . -.TH "EFIBOOTMGR" "8" "06 February 2004" "" "" +.TH "EFIBOOTMGR" "8" "11 August 2005" "" "" .SH NAME efibootmgr \- manipulate the EFI Boot Manager .SH SYNOPSIS -\fBefibootmgr\fR [ \fB-a\fR ] [ \fB-A\fR ] [ \fB-b \fIXXXX\fB\fR ] [ \fB-B \fIXXXX\fB\fR ] [ \fB-c\fR ] [ \fB-d \fIDISK\fB\fR ] [ \fB-e \fI1|3|-1\fB\fR ] [ \fB-E \fINUM\fB\fR ] [ \fB-g\fR ] [ \fB-H \fIXXXX\fB\fR ] [ \fB-i \fINAME\fB\fR ] [ \fB-l \fINAME\fB\fR ] [ \fB-L \fILABEL\fB\fR ] [ \fB-n \fIXXXX\fB\fR ] [ \fB-N\fR ] [ \fB-o \fIXXXX\fB,\fIYYYY\fB,\fIZZZZ\fB\fR\fI ...\fR ] [ \fB-O\fR ] [ \fB-p \fIPART\fB\fR ] [ \fB-q\fR ] [ \fB-t \fIseconds\fB\fR ] [ \fB-T\fR ] [ \fB-u\fR ] [ \fB-U \fIXXXX\fB\fR ] [ \fB-v\fR ] [ \fB-V\fR ] [ \fB-w\fR ] +\fBefibootmgr\fR [ \fB-a\fR ] [ \fB-A\fR ] [ \fB-b \fIXXXX\fB\fR ] [ \fB-B \fIXXXX\fB\fR ] [ \fB-c\fR ] [ \fB-d \fIDISK\fB\fR ] [ \fB-e \fI1|3|-1\fB\fR ] [ \fB-E \fINUM\fB\fR ] [ \fB-g\fR ] [ \fB-H \fIXXXX\fB\fR ] [ \fB-i \fINAME\fB\fR ] [ \fB-l \fINAME\fB\fR ] [ \fB-L \fILABEL\fB\fR ] [ \fB-n \fIXXXX\fB\fR ] [ \fB-N\fR ] [ \fB-o \fIXXXX\fB,\fIYYYY\fB,\fIZZZZ\fB\fR\fI ...\fR ] [ \fB-O\fR ] [ \fB-p \fIPART\fB\fR ] [ \fB-q\fR ] [ \fB-t \fIseconds\fB\fR ] [ \fB-T\fR ] [ \fB-u\fR ] [ \fB-U \fIXXXX\fB\fR ] [ \fB-v\fR ] [ \fB-V\fR ] [ \fB-w\fR ] [ \fB-@ \fIfile\fB\fR ] .SH "DESCRIPTION" .PP @@ -93,10 +93,10 @@ Partition number containing the bootloader (defaults to 1) Quiet mode - supresses output. .TP \fB--test \fIfilename\fB\fR -Don't write to NVRAM, write to \fIfilename\fR. +Don't write to NVRAM, write to \fIfilename\fR\&. .TP \fB-t | --timeout \fIseconds\fB\fR -Boot Manager timeout, in \fIseconds\fR. +Boot Manager timeout, in \fIseconds\fR\&. .TP \fB-T | --delete-timeout\fR Delete Timeout variable. @@ -116,6 +116,12 @@ Just print version string and exit. .TP \fB-w | --write-signature\fR write unique signature to the MBR if needed +.TP +\fB-@ | --append-binary-args \fR +append extra variable args from file (use - to read +from stdin). Data in file is appended as command line +arguments to the boot loader command, with no modification to +the data, so you can pass any binary or text data necessary. .SH "EXAMPLES" .TP 3 1. @@ -164,12 +170,12 @@ flag (* means active) and the name displayed on the screen. 2. .SS "CREATING A NEW BOOT OPTION" .PP -An OS installer would call \fBefibootmgr -c\fR. +An OS installer would call \fBefibootmgr -c\fR\&. This assumes that \fI/boot/efi\fR is your EFI System -Partition, and is mounted at \fI/dev/sda1\fR. This +Partition, and is mounted at \fI/dev/sda1\fR\&. This creates a new boot option, called "Linux", and puts it at the top of the boot order list. Options may be passed to modify the default -behavior. The default OS Loader is \fIelilo.efi\fR. +behavior. The default OS Loader is \fIelilo.efi\fR\&. .TP 3 3. .SS "CHANGING THE BOOT ORDER" @@ -212,7 +218,7 @@ You create the boot entry with: .SH "BUGS" .PP Please direct any bugs, features, patches, etc. to Matt Domsch -. +\&. .SH "AUTHOR" .PP This man page was generated by dann frazier for the diff --git a/src/man/man8/efibootmgr.8.docbook b/src/man/man8/efibootmgr.8.docbook index 0eec0f7..aef4444 100644 --- a/src/man/man8/efibootmgr.8.docbook +++ b/src/man/man8/efibootmgr.8.docbook @@ -1,4 +1,3 @@ - dannf@debian.org, @@ -17,7 +16,7 @@ dann"> frazier"> - 2004-02-05"> + 2005-08-11"> 8"> @@ -82,6 +81,7 @@ -v -V -w + -@ file @@ -279,7 +279,19 @@ write unique signature to the MBR if needed + + | + + + + append extra variable args from file (use - to read + from stdin). Data in file is appended as command line + arguments to the boot loader command, with no modification to + the data, so you can pass any binary or text data necessary. + + + -- 2.30.2