diff options
author | Kay Sievers <kay@vrfy.org> | 2013-02-14 04:25:11 +0100 |
---|---|---|
committer | Kay Sievers <kay@vrfy.org> | 2013-02-14 04:25:11 +0100 |
commit | 5b9b31c509e89a54d7672ad04668593fcc881706 (patch) | |
tree | 848e42b242e57725ed95e2fff9480544431a7522 | |
parent | 52a5ad57c94403b11307c705ad0ca91c21f6d922 (diff) |
setup: efivars - compose BootXXXX record
-rw-r--r-- | src/setup/efivars.c | 247 | ||||
-rw-r--r-- | src/setup/efivars.h | 11 | ||||
-rw-r--r-- | src/setup/setup.c | 9 |
3 files changed, 212 insertions, 55 deletions
diff --git a/src/setup/efivars.c b/src/setup/efivars.c index 4796553..884d4dc 100644 --- a/src/setup/efivars.c +++ b/src/setup/efivars.c @@ -122,7 +122,64 @@ int efi_get_variable( finish: if (fd >= 0) close(fd); + free(p); + return r; +} + +int efi_set_variable( + const uint8_t vendor[16], + const char *name, + const void *value, + size_t size) { + + struct var { + uint32_t attr; + char buf[]; + } __attribute__((packed)) *buf = NULL; + char *p = NULL; + int fd = -1; + int r; + + assert(vendor); + assert(name); + assert(value); + assert(size); + + if (asprintf(&p, + "/sys/firmware/efi/efivars/%s-%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + name, + vendor[0], vendor[1], vendor[2], vendor[3], vendor[4], vendor[5], vendor[6], vendor[7], + vendor[8], vendor[9], vendor[10], vendor[11], vendor[12], vendor[13], vendor[14], vendor[15]) < 0) + return -ENOMEM; + + fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC); + if (fd < 0) { + r = -errno; + goto finish; + } + + buf = malloc(sizeof(uint32_t) + size); + if (!buf) { + r = -errno; + goto finish; + } + + buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS; + memcpy(buf->buf, value, size); + + r = write(fd, buf, sizeof(uint32_t) + size); + if (r < 0) + goto finish; + if ((size_t)r != sizeof(uint32_t) + size) { + r = -EIO; + goto finish; + } + +finish: + if (fd >= 0) + close(fd); + free(buf); free(p); return r; } @@ -155,14 +212,15 @@ static size_t utf16_size(const uint16_t *s) { return (l+1) * sizeof(uint16_t); } +struct guid { + uint32_t u1; + uint16_t u2; + uint16_t u3; + uint8_t u4[8]; +} __attribute__((packed)); + static void efi_guid_to_id128(const void *guid, uint8_t *bytes) { - struct uuid { - uint32_t u1; - uint16_t u2; - uint16_t u3; - uint8_t u4[8]; - } __attribute__((packed)); - const struct uuid *uuid = guid; + const struct guid *uuid = guid; bytes[0] = (uuid->u1 >> 24) & 0xff; bytes[1] = (uuid->u1 >> 16) & 0xff; @@ -175,6 +233,15 @@ static void efi_guid_to_id128(const void *guid, uint8_t *bytes) { memcpy(bytes+8, uuid->u4, sizeof(uuid->u4)); } +static void id128_to_efi_guid(const uint8_t *bytes, void *guid) { + struct guid *uuid = guid; + + uuid->u1 = bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3]; + uuid->u2 = bytes[4] << 8 | bytes[5]; + uuid->u3 = bytes[6] << 8 | bytes[7]; + memcpy(uuid->u4, bytes+8, sizeof(uuid->u4)); +} + static char *tilt_slashes(char *s) { char *p; @@ -185,37 +252,41 @@ static char *tilt_slashes(char *s) { return s; } -int efi_get_boot_option( - uint16_t id, - char **title, - uint8_t part_uuid[16], - char **path) { - - struct boot_option { - uint32_t attr; - uint16_t path_len; - uint16_t title[]; - } __attribute__((packed)); - - struct drive_path { - uint32_t part_nr; - uint64_t part_start; - uint64_t part_size; - char signature[16]; - uint8_t mbr_type; - uint8_t signature_type; - } __attribute__((packed)); - - struct device_path { - uint8_t type; - uint8_t sub_type; - uint16_t length; - union { - uint16_t path[0]; - struct drive_path drive; - }; - } __attribute__((packed)); - +#define LOAD_OPTION_ACTIVE 0x00000001 +#define MEDIA_DEVICE_PATH 0x04 +#define MEDIA_HARDDRIVE_DP 0x01 +#define MEDIA_FILEPATH_DP 0x04 +#define SIGNATURE_TYPE_GUID 0x02 +#define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02 +#define END_DEVICE_PATH_TYPE 0x7f +#define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff + +struct boot_option { + uint32_t attr; + uint16_t path_len; + uint16_t title[]; +} __attribute__((packed)); + +struct drive_path { + uint32_t part_nr; + uint64_t part_start; + uint64_t part_size; + char signature[16]; + uint8_t mbr_type; + uint8_t signature_type; +} __attribute__((packed)); + +struct device_path { + uint8_t type; + uint8_t sub_type; + uint16_t length; + union { + uint16_t path[0]; + struct drive_path drive; + }; +} __attribute__((packed)); + +int efi_get_boot_option(uint16_t id, char **title, uint8_t part_uuid[16], char **path) { char boot_id[9]; uint8_t *buf = NULL; size_t l; @@ -264,31 +335,27 @@ int efi_get_boot_option( break; /* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */ - if (dpath->type == 0x7f && dpath->sub_type == 0xff) + if (dpath->type == END_DEVICE_PATH_TYPE && dpath->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE) break; dnext += dpath->length; - /* Type 0x04 – Media Device Path */ - if (dpath->type != 0x04) + if (dpath->type != MEDIA_DEVICE_PATH) continue; - /* Sub-Type 1 – Hard Drive */ - if (dpath->sub_type == 0x01) { - /* 0x02 – GUID Partition Table */ - if (dpath->drive.mbr_type != 0x02) + if (dpath->sub_type == MEDIA_HARDDRIVE_DP) { + /* GPT Partition Table */ + if (dpath->drive.mbr_type != MBR_TYPE_EFI_PARTITION_TABLE_HEADER) continue; - /* 0x02 – GUID signature */ - if (dpath->drive.signature_type != 0x02) + if (dpath->drive.signature_type != SIGNATURE_TYPE_GUID) continue; efi_guid_to_id128(dpath->drive.signature, p_uuid); continue; } - /* Sub-Type 4 – File Path */ - if (dpath->sub_type == 0x04) { + if (dpath->sub_type == MEDIA_FILEPATH_DP) { p = utf16_to_utf8(dpath->path, dpath->length-4); tilt_slashes(p); continue; @@ -318,6 +385,83 @@ err: return err; } +static void to_utf16(uint16_t *dest, const char *src) { + int i; + + for (i = 0; src[i] != '\0'; i++) + dest[i] = src[i]; + dest[i] = '\0'; +} + +int efi_set_boot_option(uint16_t id, const char *title, + uint32_t part, uint64_t pstart, uint64_t psize, + const uint8_t part_uuid[16], + const char *path) { + char boot_id[9]; + char *buf; + size_t size; + size_t title_len; + size_t path_len; + struct boot_option *option; + struct device_path *devicep; + int err; + + title_len = (strlen(title)+1) * 2; + path_len = (strlen(path)+1) * 2; + + buf = calloc(sizeof(struct boot_option) + title_len + + sizeof(struct drive_path) + + sizeof(struct device_path) + path_len, 1); + if (!buf) { + err = -ENOMEM; + goto finish; + } + + /* header */ + option = (struct boot_option *)buf; + option->attr = LOAD_OPTION_ACTIVE; + option->path_len = offsetof(struct device_path, drive) + sizeof(struct drive_path) + + offsetof(struct device_path, path) + path_len + + offsetof(struct device_path, path); + to_utf16(option->title, title); + size = offsetof(struct boot_option, title) + title_len; + + /* partition info */ + devicep = (struct device_path *)(buf + size); + devicep->type = MEDIA_DEVICE_PATH; + devicep->sub_type = MEDIA_HARDDRIVE_DP; + devicep->length = offsetof(struct device_path, drive) + sizeof(struct drive_path); + devicep->drive.part_nr = part; + devicep->drive.part_start = pstart; + devicep->drive.part_size = psize; + devicep->drive.signature_type = SIGNATURE_TYPE_GUID; + devicep->drive.mbr_type = MBR_TYPE_EFI_PARTITION_TABLE_HEADER; + id128_to_efi_guid(part_uuid, devicep->drive.signature); + size += devicep->length; + + /* path to loader */ + devicep = (struct device_path *)(buf + size); + devicep->type = MEDIA_DEVICE_PATH; + devicep->sub_type = MEDIA_FILEPATH_DP; + devicep->length = offsetof(struct device_path, path) + path_len; + to_utf16(devicep->path, path); + size += devicep->length; + + /* end of path */ + devicep = (struct device_path *)(buf + size); + devicep->type = END_DEVICE_PATH_TYPE; + devicep->sub_type = END_ENTIRE_DEVICE_PATH_SUBTYPE; + devicep->length = offsetof(struct device_path, path); + size += devicep->length; + + snprintf(boot_id, sizeof(boot_id), "Boot%04X", id); + err = efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, buf, size); + +finish: + free(buf); + return err; +} + int efi_get_boot_order(uint16_t **order) { void *buf; size_t l; @@ -342,10 +486,9 @@ int efi_get_boot_order(uint16_t **order) { } static int cmp_uint16(const void *a, const void *b) { - const uint16_t *i1 = a; - const uint16_t *i2 = a; + const uint16_t *i1 = a, *i2 = a; - return *i1 - *i2; + return (int)*i1 - (int)*i2; } static int boot_id_hex(const char s[4]) { diff --git a/src/setup/efivars.h b/src/setup/efivars.h index b5f102f..a3dd1a2 100644 --- a/src/setup/efivars.h +++ b/src/setup/efivars.h @@ -25,11 +25,22 @@ #include <sys/types.h> #include <inttypes.h> +#define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001 +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002 +#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004 + bool is_efi_boot(void); int efi_get_variable(const uint8_t vendor[16], const char *name, void **value, size_t *size); +int efi_set_variable( const uint8_t vendor[16], const char *name, const void *value, size_t size); int efi_get_variable_string(const uint8_t vendor[16], const char *name, char **p); int efi_get_boot_option(uint16_t id, char **title, uint8_t part_uuid[16], char **path); + int efi_get_boot_options(uint16_t **options); +int efi_set_boot_option(uint16_t id, const char *title, + uint32_t part, uint64_t pstart, uint64_t psize, + const uint8_t part_uuid[16], + const char *path); + int efi_get_boot_order(uint16_t **order); char *utf16_to_utf8(const void *s, size_t length); diff --git a/src/setup/setup.c b/src/setup/setup.c index 1dcdbde..c6f2bcb 100644 --- a/src/setup/setup.c +++ b/src/setup/setup.c @@ -744,8 +744,8 @@ static int copy_one_file(const char *name) { if (strncmp(name, "gummiboot", 9) == 0) { int k; - /* Create the fallback names for removable devices */ + /* Create the fallback names for removable devices */ if (asprintf(&v, "%s/EFI/BOOT/%s", arg_path, name + 5) < 0) { fprintf(stderr, "Out of memory.\n"); r = -ENOMEM; @@ -779,7 +779,6 @@ static int install_binaries(void) { * files (unless there are newer ones already), but we * won't create the directories for them in the first * place. */ - r = create_dirs(); if (r < 0) return r; @@ -820,6 +819,11 @@ static int install_variables(void) { return 0; } + //efi_set_boot_option(0xaffe, + // "Linux", + // 1, 0x800, 0x20000, + // (uint8_t *)"\x1f\xcb\xc5\x7f\x4b\xfc\x4c\x2b\x91\xa3\x9c\x84\xfb\xcd\x9a\xf1", + // "\\EFI\\gummiboot\\gummibootx64.efi"); return 0; } @@ -1020,7 +1024,6 @@ static int parse_argv(int argc, char *argv[]) { assert(argv); while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { - switch (c) { case 'h': |