Skip to content

Commit

Permalink
efi: add support for initrd loading
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Olbrich <[email protected]>
Signed-off-by: Sascha Hauer <[email protected]>
  • Loading branch information
michaelolbrich authored and saschahauer committed Jul 23, 2015
1 parent 356aaef commit 55da0cf
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 14 deletions.
193 changes: 180 additions & 13 deletions arch/efi/efi/efi-image.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,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)
Expand All @@ -54,30 +102,149 @@ 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");
}
linux_efi_handover(handle, boot_header);

return 0;
}

static struct image_handler efi_handle_tr = {
Expand Down
2 changes: 1 addition & 1 deletion include/efi.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ typedef struct {
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);
Expand Down

0 comments on commit 55da0cf

Please sign in to comment.