diff --git a/arch/efi/configs/efi_defconfig b/arch/efi/configs/efi_defconfig index 456f70d..3c9a9dc 100644 --- a/arch/efi/configs/efi_defconfig +++ b/arch/efi/configs/efi_defconfig @@ -7,6 +7,7 @@ CONFIG_AUTO_COMPLETE=y CONFIG_MENU=y # CONFIG_TIMESTAMP is not set +CONFIG_BLSPEC=y CONFIG_CONSOLE_ACTIVATE_ALL=y CONFIG_PARTITION_DISK_EFI=y CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW=y diff --git a/arch/efi/efi/efi-block-io.c b/arch/efi/efi/efi-block-io.c index 85603d9..e02d3b4 100644 --- a/arch/efi/efi/efi-block-io.c +++ b/arch/efi/efi/efi-block-io.c @@ -147,7 +147,7 @@ efi_bio_print_info(priv); priv->dev = &efidev->dev; - priv->blk.cdev.name = asprintf("disk%d", cdev_find_free_index("disk")); + priv->blk.cdev.name = xasprintf("disk%d", cdev_find_free_index("disk")); priv->blk.blockbits = ffs(media->block_size) - 1; priv->blk.num_blocks = media->last_block + 1; priv->blk.ops = &efi_bio_ops; diff --git a/arch/efi/efi/efi-image.c b/arch/efi/efi/efi-image.c index f7bda8d..b6437f4 100644 --- a/arch/efi/efi/efi-image.c +++ b/arch/efi/efi/efi-image.c @@ -17,6 +17,7 @@ * */ +#include #include #include #include @@ -37,14 +38,62 @@ #include #include -static int efi_execute_image(const char *file) +struct linux_kernel_header { + /* first sector of the image */ + uint8_t code1[0x0020]; + uint16_t cl_magic; /**< Magic number 0xA33F */ + uint16_t cl_offset; /**< The offset of command line */ + uint8_t code2[0x01F1 - 0x0020 - 2 - 2]; + uint8_t setup_sects; /**< The size of the setup in sectors */ + uint16_t root_flags; /**< If the root is mounted readonly */ + uint16_t syssize; /**< obsolete */ + uint16_t swap_dev; /**< obsolete */ + uint16_t ram_size; /**< obsolete */ + uint16_t vid_mode; /**< Video mode control */ + uint16_t root_dev; /**< Default root device number */ + uint16_t boot_flag; /**< 0xAA55 magic number */ + + /* second sector of the image */ + uint16_t jump; /**< Jump instruction (this is code!) */ + uint32_t header; /**< Magic signature "HdrS" */ + uint16_t version; /**< Boot protocol version supported */ + uint32_t realmode_swtch; /**< Boot loader hook */ + uint16_t start_sys; /**< The load-low segment (obsolete) */ + uint16_t kernel_version; /**< Points to kernel version string */ + uint8_t type_of_loader; /**< Boot loader identifier */ + uint8_t loadflags; /**< Boot protocol option flags */ + uint16_t setup_move_size; /**< Move to high memory size */ + uint32_t code32_start; /**< Boot loader hook */ + uint32_t ramdisk_image; /**< initrd load address */ + uint32_t ramdisk_size; /**< initrd size */ + uint32_t bootsect_kludge; /**< obsolete */ + uint16_t heap_end_ptr; /**< Free memory after setup end */ + uint8_t ext_loader_ver; /**< boot loader's extension of the version number */ + uint8_t ext_loader_type; /**< boot loader's extension of its type */ + uint32_t cmd_line_ptr; /**< Points to the kernel command line */ + uint32_t initrd_addr_max; /**< Highest address for initrd */ + uint32_t kernel_alignment; /**< Alignment unit required by the kernel */ + uint8_t relocatable_kernel; /** */ + uint8_t min_alignment; /** */ + uint16_t xloadflags; /** */ + uint32_t cmdline_size; /** */ + uint32_t hardware_subarch; /** */ + uint64_t hardware_subarch_data; /** */ + uint32_t payload_offset; /** */ + uint32_t payload_length; /** */ + uint64_t setup_data; /** */ + uint64_t pref_address; /** */ + uint32_t init_size; /** */ + uint32_t handover_offset; /** */ +} __attribute__ ((packed)); + +int efi_load_image(const char *file, efi_loaded_image_t **loaded_image, + efi_handle_t *h) { void *exe; size_t size; efi_handle_t handle; - efi_status_t efiret; - const char *options; - efi_loaded_image_t *loaded_image; + efi_status_t efiret = EFI_SUCCESS; exe = read_file(file, &size); if (!exe) @@ -54,30 +103,153 @@ &handle); if (EFI_ERROR(efiret)) { pr_err("failed to LoadImage: %s\n", efi_strerror(efiret)); - return -efi_errno(efiret);; + goto out; }; efiret = BS->open_protocol(handle, &efi_loaded_image_protocol_guid, - (void **)&loaded_image, + (void **)loaded_image, efi_parent_image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); - if (EFI_ERROR(efiret)) - return -efi_errno(efiret); + if (EFI_ERROR(efiret)) { + pr_err("failed to OpenProtocol: %s\n", efi_strerror(efiret)); + BS->unload_image(handle); + goto out; + } - options = linux_bootargs_get(); - loaded_image->load_options = strdup_char_to_wchar(options); - loaded_image->load_options_size = (strlen(options) + 1) * sizeof(wchar_t); + *h = handle; +out: + memset(exe, 0, size); + free(exe); + return -efi_errno(efiret); +} + +static int efi_execute_image(const char *file) +{ + efi_handle_t handle; + efi_loaded_image_t *loaded_image; + efi_status_t efiret; + struct linux_kernel_header *image_header; + const char *options; + int ret; + + ret = efi_load_image(file, &loaded_image, &handle); + if (ret) + return ret; + + image_header = (struct linux_kernel_header *)loaded_image->image_base; + if (image_header->boot_flag == 0xAA55 && + image_header->header == 0x53726448) { + pr_debug("Linux kernel detected. Adding bootargs."); + options = linux_bootargs_get(); + pr_err("add linux options '%s'\n", options); + loaded_image->load_options = xstrdup_char_to_wchar(options); + loaded_image->load_options_size = + (strlen(options) + 1) * sizeof(wchar_t); + } efiret = BS->start_image(handle, NULL, NULL); + if (EFI_ERROR(efiret)) + pr_err("failed to StartImage: %s\n", efi_strerror(efiret)); + + BS->unload_image(handle); efi_connect_all(); efi_register_devices(); - return 0; + return -efi_errno(efiret); } +#ifdef __x86_64__ +typedef void(*handover_fn)(void *image, efi_system_table_t *table, + struct linux_kernel_header *header); + +static inline void linux_efi_handover(efi_handle_t handle, + struct linux_kernel_header *header) +{ + handover_fn handover; + + asm volatile ("cli"); + handover = (handover_fn)((long)header->code32_start + 512 + + header->handover_offset); + handover(handle, efi_sys_table, header); +} +#else +typedef void(*handover_fn)(VOID *image, EFI_SYSTEM_TABLE *table, + struct SetupHeader *setup) __attribute__((regparm(0))); + +static inline void linux_efi_handover(efi_handle_t handle, + struct linux_kernel_header *header) +{ + handover_fn handover; + + handover = (handover_fn)((long)header->code32_start + + header->handover_offset); + handover(handle, efi_sys_table, header); +} +#endif + static int do_bootm_efi(struct image_data *data) { - return efi_execute_image(data->os_file); + void *tmp; + void *initrd; + size_t size; + efi_handle_t handle; + int ret; + const char *options; + efi_loaded_image_t *loaded_image; + struct linux_kernel_header *image_header, *boot_header; + + ret = efi_load_image(data->os_file, &loaded_image, &handle); + if (ret) + return ret; + + image_header = (struct linux_kernel_header *)loaded_image->image_base; + + if (image_header->boot_flag != 0xAA55 || + image_header->header != 0x53726448 || + image_header->version < 0x20b || + !image_header->relocatable_kernel) { + pr_err("Not a valid kernel image!\n"); + BS->unload_image(handle); + return -EINVAL; + } + + boot_header = xmalloc(0x4000); + memset(boot_header, 0, 0x4000); + memcpy(boot_header, image_header, sizeof(*image_header)); + + boot_header->type_of_loader = 0xff; + + if (data->initrd_file) { + tmp = read_file(data->initrd_file, &size); + initrd = xmemalign(PAGE_SIZE, PAGE_ALIGN(size)); + memcpy(initrd, tmp, size); + memset(initrd + size, 0, PAGE_ALIGN(size) - size); + free(tmp); + boot_header->ramdisk_image = (uint64_t)initrd; + boot_header->ramdisk_size = PAGE_ALIGN(size); + } + + options = linux_bootargs_get(); + boot_header->cmd_line_ptr = (uint64_t)options; + boot_header->cmdline_size = strlen(options); + + boot_header->code32_start = (uint64_t)loaded_image->image_base + + (image_header->setup_sects+1) * 512; + + if (bootm_verbose(data)) { + printf("\nStarting kernel at 0x%p", loaded_image->image_base); + if (data->initrd_file) + printf(", initrd at 0x%08x", + boot_header->ramdisk_image); + printf("...\n"); + } + + efi_set_variable_usec("LoaderTimeExecUSec", &efi_systemd_vendor_guid, + get_time_ns()/1000); + + linux_efi_handover(handle, boot_header); + + return 0; } static struct image_handler efi_handle_tr = { diff --git a/arch/efi/efi/efi.c b/arch/efi/efi/efi.c index d351775..d3f520f 100644 --- a/arch/efi/efi/efi.c +++ b/arch/efi/efi/efi.c @@ -52,7 +52,7 @@ efi_status_t efiret; void *buf; unsigned long size = 0; - s16 *name16 = strdup_char_to_wchar(name); + s16 *name16 = xstrdup_char_to_wchar(name); efiret = RT->get_variable(name16, vendor, NULL, &size, NULL); @@ -83,6 +83,33 @@ return buf; } +int efi_set_variable(char *name, efi_guid_t *vendor, uint32_t attributes, + void *buf, unsigned long size) +{ + efi_status_t efiret = EFI_SUCCESS; + s16 *name16 = xstrdup_char_to_wchar(name); + + efiret = RT->set_variable(name16, vendor, attributes, size, buf); + + free(name16); + + return -efi_errno(efiret); +} + +int efi_set_variable_usec(char *name, efi_guid_t *vendor, uint64_t usec) +{ + char buf[20]; + wchar_t buf16[40]; + + snprintf(buf, sizeof(buf), "%lld", usec); + strcpy_char_to_wchar(buf16, buf); + + return efi_set_variable(name, vendor, + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, buf16, + (strlen(buf)+1) * sizeof(wchar_t)); +} + struct efi_boot { u32 attributes; u16 file_path_len; @@ -98,7 +125,7 @@ int size; char *name; - name = asprintf("Boot%04X", num); + name = xasprintf("Boot%04X", num); buf = efi_get_global_var(name, &size); @@ -119,7 +146,7 @@ ptr += sizeof(u16); - boot->description = strdup_wchar_to_char(ptr); + boot->description = xstrdup_wchar_to_char(ptr); ptr += (strlen(boot->description) + 1) * 2; @@ -299,8 +326,13 @@ static int efi_init(void) { + char *env; + defaultenv_append_directory(env_efi); + env = xasprintf("/efivars/barebox-env-%pUl", &efi_barebox_vendor_guid); + default_environment_path_set(env); + return 0; } device_initcall(efi_init); @@ -310,8 +342,10 @@ */ efi_status_t efi_main(efi_handle_t image, efi_system_table_t *sys_table) { - void *mem; + efi_physical_addr_t mem; + size_t memsize; efi_status_t efiret; + char *uuid; #ifdef DEBUG sys_table->con_out->output_string(sys_table->con_out, L"barebox\n"); @@ -332,10 +366,38 @@ fixup_tables(); - BS->allocate_pool(efi_loaded_image->image_data_type, SZ_16M, &mem); - mem_malloc_init(mem, mem + SZ_16M); + mem = 0x3fffffff; + for (memsize = SZ_256M; memsize >= SZ_8M; memsize /= 2) { + efiret = BS->allocate_pages(EFI_ALLOCATE_MAX_ADDRESS, + EFI_LOADER_DATA, + memsize/PAGE_SIZE, &mem); + if (!EFI_ERROR(efiret)) + break; + if (efiret != EFI_OUT_OF_RESOURCES) + panic("failed to allocate malloc pool: %s\n", + efi_strerror(efiret)); + } + if (EFI_ERROR(efiret)) + panic("failed to allocate malloc pool: %s\n", + efi_strerror(efiret)); + mem_malloc_init((void *)mem, (void *)mem + memsize); efi_clocksource_init(); + efi_set_variable_usec("LoaderTimeInitUSec", &efi_systemd_vendor_guid, + get_time_ns()/1000); + + uuid = device_path_to_partuuid(device_path_from_handle( + efi_loaded_image->device_handle)); + if (uuid) { + wchar_t *uuid16 = xstrdup_char_to_wchar(uuid); + efi_set_variable("LoaderDevicePartUUID", + &efi_systemd_vendor_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + uuid16, (strlen(uuid)+1) * sizeof(wchar_t)); + free(uuid); + free(uuid16); + } start_barebox(); diff --git a/arch/efi/include/mach/efi.h b/arch/efi/include/mach/efi.h index 1e9782a..2b25cf1 100644 --- a/arch/efi/include/mach/efi.h +++ b/arch/efi/include/mach/efi.h @@ -21,4 +21,8 @@ return efi_get_variable(name, &efi_global_variable_guid, var_size); } +int efi_set_variable(char *name, efi_guid_t *vendor, uint32_t attributes, + void *buf, unsigned long size); +int efi_set_variable_usec(char *name, efi_guid_t *vendor, uint64_t usec); + #endif /* __MACH_EFI_H */ diff --git a/common/efi-devicepath.c b/common/efi-devicepath.c index a53c6d2..54c2f4e 100644 --- a/common/efi-devicepath.c +++ b/common/efi-devicepath.c @@ -1383,3 +1383,33 @@ return device_path_type(dev_path); } + +char *device_path_to_partuuid(struct efi_device_path *dev_path) +{ + struct efi_device_path *dev_path_node; + struct harddrive_device_path *hd; + char *str = NULL;; + + dev_path = unpack_device_path(dev_path); + + for (dev_path_node = dev_path; !is_device_path_end(dev_path_node); + dev_path_node = next_device_path_node(dev_path_node)) { + + if (device_path_type(dev_path_node) != MEDIA_DEVICE_PATH) + continue; + + if (dev_path_node->sub_type != MEDIA_HARDDRIVE_DP) + continue; + + hd = (struct harddrive_device_path *)dev_path_node; + + if (hd->signature_type != SIGNATURE_TYPE_GUID) + continue; + + str = xasprintf("%pUl", (efi_guid_t *)&(hd->signature[0])); + break; + } + + return str; +} + diff --git a/common/efi-guid.c b/common/efi-guid.c index f6b0404..64f3b1f 100644 --- a/common/efi-guid.c +++ b/common/efi-guid.c @@ -9,6 +9,8 @@ efi_guid_t efi_null_guid = EFI_NULL_GUID; efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID; efi_guid_t efi_block_io_protocol_guid = EFI_BLOCK_IO_PROTOCOL_GUID; +efi_guid_t efi_barebox_vendor_guid = EFI_BAREBOX_VENDOR_GUID; +efi_guid_t efi_systemd_vendor_guid = EFI_SYSTEMD_VENDOR_GUID; #define EFI_GUID_STRING(guid, short, long) do { \ if (!efi_guidcmp(guid, *g)) \ diff --git a/fs/efi.c b/fs/efi.c index 8f4739a..a7adcb9 100644 --- a/fs/efi.c +++ b/fs/efi.c @@ -114,9 +114,9 @@ wchar_t *ret; if (!*path) - return strdup_char_to_wchar("\\"); + return xstrdup_char_to_wchar("\\"); - dst = strdup_char_to_wchar(path); + dst = xstrdup_char_to_wchar(path); if (!dst) return NULL; diff --git a/fs/efivarfs.c b/fs/efivarfs.c index ebace8b..c7a282b 100644 --- a/fs/efivarfs.c +++ b/fs/efivarfs.c @@ -153,7 +153,7 @@ inode->vendor = vendor; - name8 = strdup_wchar_to_char(inode->name); + name8 = xstrdup_wchar_to_char(inode->name); inode->full_name = asprintf("%s-%pUl", name8, &inode->vendor); free(name8); @@ -216,7 +216,7 @@ return -ENOENT; efiret = RT->get_variable(efile->name, &efile->vendor, - &efile->attributes, &efile->size, NULL); + NULL, &efile->size, NULL); if (EFI_ERROR(efiret) && efiret != EFI_BUFFER_TOO_SMALL) { ret = -efi_errno(efiret); goto out; @@ -228,8 +228,9 @@ goto out; } - efiret = RT->get_variable(efile->name, &efile->vendor, NULL, &efile->size, - efile->buf); + efiret = RT->get_variable(efile->name, &efile->vendor, + &efile->attributes, &efile->size, + efile->buf); if (EFI_ERROR(efiret)) { ret = -efi_errno(efiret); goto out; @@ -269,6 +270,7 @@ static int efivarfs_write(struct device_d *_dev, FILE *f, const void *buf, size_t insize) { struct efivars_file *efile = f->priv; + efi_status_t efiret; if (efile->size < f->pos + insize) { efile->buf = realloc(efile->buf, f->pos + insize); @@ -277,8 +279,11 @@ memcpy(efile->buf + f->pos, buf, insize); - RT->set_variable(efile->name, &efile->vendor, efile->attributes, - efile->size ? efile->size : 1, efile->buf); + efiret = RT->set_variable(efile->name, &efile->vendor, + efile->attributes, + efile->size ? efile->size : 1, efile->buf); + if (EFI_ERROR(efiret)) + return -efi_errno(efiret); return insize; } @@ -286,12 +291,16 @@ static int efivarfs_truncate(struct device_d *dev, FILE *f, ulong size) { struct efivars_file *efile = f->priv; + efi_status_t efiret; efile->size = size; efile->buf = realloc(efile->buf, efile->size + sizeof(uint32_t)); - RT->set_variable(efile->name, &efile->vendor, efile->attributes, - efile->size ? efile->size : 1, efile->buf); + efiret = RT->set_variable(efile->name, &efile->vendor, + efile->attributes, + efile->size ? efile->size : 1, efile->buf); + if (EFI_ERROR(efiret)) + return -efi_errno(efiret); f->size = efile->size; @@ -391,11 +400,11 @@ break; inode = xzalloc(sizeof(*inode)); - inode->name = strdup_wchar(name); + inode->name = xstrdup_wchar(name); inode->vendor = vendor; - name8 = strdup_wchar_to_char(inode->name); + name8 = xstrdup_wchar_to_char(inode->name); inode->full_name = asprintf("%s-%pUl", name8, &vendor); free(name8); diff --git a/include/efi.h b/include/efi.h index 9b4f16b..b2e965b 100644 --- a/include/efi.h +++ b/include/efi.h @@ -211,7 +211,7 @@ unsigned long *exitdata_size, s16 **exitdata); efi_status_t(EFIAPI *exit)(efi_handle_t handle, efi_status_t exit_status, unsigned long exitdata_size, s16 *exitdata); - void *unload_image; + efi_status_t (EFIAPI *unload_image)(efi_handle_t handle); efi_status_t (EFIAPI *exit_boot_services)(efi_handle_t, unsigned long); void *get_next_monotonic_count; efi_status_t (EFIAPI *stall)(unsigned long usecs); @@ -473,6 +473,10 @@ #define EFI_BAREBOX_VENDOR_GUID \ EFI_GUID(0x5b91f69c, 0x8b88, 0x4a2b, 0x92, 0x69, 0x5f, 0x1d, 0x80, 0x2b, 0x51, 0x75) +/* for systemd */ +#define EFI_SYSTEMD_VENDOR_GUID \ + EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf, 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f) + extern efi_guid_t efi_file_info_id; extern efi_guid_t efi_simple_file_system_protocol_guid; extern efi_guid_t efi_device_path_protocol_guid; @@ -481,6 +485,8 @@ extern efi_guid_t efi_null_guid; extern efi_guid_t efi_global_variable_guid; extern efi_guid_t efi_block_io_protocol_guid; +extern efi_guid_t efi_barebox_vendor_guid; +extern efi_guid_t efi_systemd_vendor_guid; #define EFI_SYSTEM_TABLE_SIGNATURE ((u64)0x5453595320494249ULL) @@ -622,8 +628,10 @@ return memcmp(a, b, sizeof(efi_guid_t)); } +struct efi_device_path *device_path_from_handle(efi_handle_t Handle); char *device_path_to_str(struct efi_device_path *dev_path); u8 device_path_to_type(struct efi_device_path *dev_path); +char *device_path_to_partuuid(struct efi_device_path *dev_path); const char *efi_guid_string(efi_guid_t *g); diff --git a/include/xfuncs.h b/include/xfuncs.h index 940a1d6..e3f120a 100644 --- a/include/xfuncs.h +++ b/include/xfuncs.h @@ -2,6 +2,8 @@ #define __XFUNCS_H #include +#include +#include void *xmalloc(size_t size); void *xrealloc(void *ptr, size_t size); @@ -10,5 +12,11 @@ char *xstrndup(const char *s, size_t size); void* xmemalign(size_t alignment, size_t bytes); void* xmemdup(const void *orig, size_t size); +char *xasprintf(const char *fmt, ...) __attribute__ ((format(__printf__, 1, 2))); +char *xvasprintf(const char *fmt, va_list ap); + +wchar_t *xstrdup_wchar(const wchar_t *src); +wchar_t *xstrdup_char_to_wchar(const char *src); +char *xstrdup_wchar_to_char(const wchar_t *src); #endif /* __XFUNCS_H */ diff --git a/lib/xfuncs.c b/lib/xfuncs.c index f0219c4..152081c 100644 --- a/lib/xfuncs.c +++ b/lib/xfuncs.c @@ -22,6 +22,7 @@ #include #include #include +#include void *xmalloc(size_t size) { @@ -103,3 +104,57 @@ return buf; } EXPORT_SYMBOL(xmemdup); + +char *xvasprintf(const char *fmt, va_list ap) +{ + char *p; + + p = vasprintf(fmt, ap); + if (!p) + panic("ERROR: out of memory\n"); + return p; +} +EXPORT_SYMBOL(xvasprintf); + +char *xasprintf(const char *fmt, ...) +{ + va_list ap; + char *p; + + va_start(ap, fmt); + p = xvasprintf(fmt, ap); + va_end(ap); + + return p; +} +EXPORT_SYMBOL(xasprintf); + +wchar_t *xstrdup_wchar(const wchar_t *s) +{ + wchar_t *p = strdup_wchar(s); + + if (!p) + panic("ERROR: out of memory\n"); + return p; +} +EXPORT_SYMBOL(xstrdup_wchar); + +wchar_t *xstrdup_char_to_wchar(const char *s) +{ + wchar_t *p = strdup_char_to_wchar(s); + + if (!p) + panic("ERROR: out of memory\n"); + return p; +} +EXPORT_SYMBOL(xstrdup_char_to_wchar); + +char *xstrdup_wchar_to_char(const wchar_t *s) +{ + char *p = strdup_wchar_to_char(s); + + if (!p) + panic("ERROR: out of memory\n"); + return p; +} +EXPORT_SYMBOL(xstrdup_wchar_to_char);