summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2015-08-06 12:33:05 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2015-08-06 12:33:05 +0200
commit66f4cbdc895260a21e76eeb7769ade2e2dc62213 (patch)
treef3ad7a02e001e946dd793ce528d54eea0787853f /arch
parent937c85a302722b4e70a677cbaffad8c54af165e4 (diff)
parent89aef0fb8583789c12238468f4af47a278ff0c9d (diff)
downloadbarebox-66f4cbdc895260a21e76eeb7769ade2e2dc62213.tar.gz
barebox-66f4cbdc895260a21e76eeb7769ade2e2dc62213.tar.xz
Merge branch 'for-next/efi'
Diffstat (limited to 'arch')
-rw-r--r--arch/efi/configs/efi_defconfig1
-rw-r--r--arch/efi/efi/efi-block-io.c2
-rw-r--r--arch/efi/efi/efi-image.c198
-rw-r--r--arch/efi/efi/efi.c74
-rw-r--r--arch/efi/include/mach/efi.h4
5 files changed, 259 insertions, 20 deletions
diff --git a/arch/efi/configs/efi_defconfig b/arch/efi/configs/efi_defconfig
index 456f70d32b..3c9a9dcb95 100644
--- a/arch/efi/configs/efi_defconfig
+++ b/arch/efi/configs/efi_defconfig
@@ -7,6 +7,7 @@ CONFIG_CMDLINE_EDITING=y
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 85603d913d..e02d3b49cc 100644
--- a/arch/efi/efi/efi-block-io.c
+++ b/arch/efi/efi/efi-block-io.c
@@ -147,7 +147,7 @@ int efi_bio_probe(struct efi_device *efidev)
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 f7bda8dfcb..b6437f4078 100644
--- a/arch/efi/efi/efi-image.c
+++ b/arch/efi/efi/efi-image.c
@@ -17,6 +17,7 @@
*
*/
+#include <clock.h>
#include <common.h>
#include <linux/sizes.h>
#include <memory.h>
@@ -37,14 +38,62 @@
#include <mach/efi.h>
#include <mach/efi-device.h>
-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 @@ static int efi_execute_image(const char *file)
&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 d351775a28..d3f520f60f 100644
--- a/arch/efi/efi/efi.c
+++ b/arch/efi/efi/efi.c
@@ -52,7 +52,7 @@ void *efi_get_variable(char *name, efi_guid_t *vendor, int *var_size)
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 @@ out:
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 @@ struct efi_boot *efi_get_boot(int num)
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 @@ struct efi_boot *efi_get_boot(int num)
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 void fixup_tables(void)
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 @@ device_initcall(efi_init);
*/
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 @@ efi_status_t efi_main(efi_handle_t image, efi_system_table_t *sys_table)
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 1e9782a136..2b25cf1868 100644
--- a/arch/efi/include/mach/efi.h
+++ b/arch/efi/include/mach/efi.h
@@ -21,4 +21,8 @@ static inline void *efi_get_global_var(char *name, int *var_size)
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 */