diff options
author | Kevin Chavez <kechavez@google.com> | 2016-07-27 19:09:52 -0400 |
---|---|---|
committer | David Zeuthen <zeuthen@google.com> | 2016-08-04 21:35:34 +0000 |
commit | ddd8ee3bd6e0b44d00c672e847985cc360d5cb36 (patch) | |
tree | 4aa54f7765830105061ddb5523241fa9f62937ff | |
parent | 4c863e2fcc809a2536ea6791598c81e957e7ee01 (diff) | |
download | brillo-ddd8ee3bd6e0b44d00c672e847985cc360d5cb36.tar.gz |
brillo: UEFI boot loader kernel booting.
Booting the Android kernel image in a UEFI environment. This CL provides
capabilities for the Brillo UEFI boot loader to specify a bootable partition in
order to load and boot it. This will be used to toggle between booting
into different partitions in A/B implementation.
BUG=29072323
TEST=Built in assertions passing, manually testing kernel booting using qemu.
Ongoing unit test development.
Change-Id: I0bb50019a4bd2cb7bfc54460172b38ec50819a0a
-rw-r--r-- | brillo_uefi_x86_64/boot_loader/Makefile | 11 | ||||
-rwxr-xr-x | brillo_uefi_x86_64/boot_loader/brillo_boot_loader.efi | bin | 42643 -> 54396 bytes | |||
-rw-r--r-- | brillo_uefi_x86_64/boot_loader/bub_boot_kernel.c | 629 | ||||
-rw-r--r-- | brillo_uefi_x86_64/boot_loader/bub_boot_kernel.h | 42 | ||||
-rw-r--r-- | brillo_uefi_x86_64/boot_loader/bub_main.c (renamed from brillo_uefi_x86_64/boot_loader/main.c) | 16 | ||||
-rw-r--r-- | brillo_uefi_x86_64/boot_loader/bub_sysdeps.h | 174 | ||||
-rw-r--r-- | brillo_uefi_x86_64/boot_loader/bub_sysdeps_uefi.c | 102 | ||||
-rw-r--r-- | brillo_uefi_x86_64/boot_loader/bub_util.c | 61 | ||||
-rw-r--r-- | brillo_uefi_x86_64/boot_loader/bub_util.h | 50 |
9 files changed, 1079 insertions, 6 deletions
diff --git a/brillo_uefi_x86_64/boot_loader/Makefile b/brillo_uefi_x86_64/boot_loader/Makefile index af55a56..1d134e5 100644 --- a/brillo_uefi_x86_64/boot_loader/Makefile +++ b/brillo_uefi_x86_64/boot_loader/Makefile @@ -14,7 +14,7 @@ # limitations under the License. # -# Note: This is not invoked by Android build system and is innteded to aid +# Note: This is not invoked by Android build system and is intended to aid # users to locally build the boot loader application. # TODO: Port this as an Android.mk and add gnu-efi to external/. @@ -23,7 +23,7 @@ EFI_CC = gcc EFI_LD = ld EFI_OBJCOPY = objcopy -EFI_SRC_FILES = main.c +EFI_SRC_FILES = bub_main.c bub_boot_kernel.c bub_sysdeps_uefi.c bub_util.c EFI_OBJ_FILES = $(patsubst %.c,%.o,$(EFI_SRC_FILES)) EFI_TARGET = brillo_boot_loader.efi EFI_SHARED_OBJ = $(patsubst %.efi,%.so,$(EFI_TARGET)) @@ -48,7 +48,7 @@ EFI_OBJCOPY_TARGET = --target=efi-app-x86_64 all: $(EFI_TARGET) $(EFI_OBJ_FILES): $(EFI_SRC_FILES) - $(EFI_CC) $(EFI_CFLAGS) -c -o $(EFI_OBJ_FILES) $(EFI_SRC_FILES) + $(EFI_CC) $(EFI_CFLAGS) -c $(EFI_SRC_FILES) $(EFI_SHARED_OBJ): $(EFI_OBJ_FILES) $(EFI_LD) $(EFI_LDFLAGS) $(EFI_OBJ_FILES) -o $@ $(EFI_LDLIBS) @@ -57,4 +57,7 @@ $(EFI_TARGET): $(EFI_SHARED_OBJ) $(EFI_OBJCOPY) $(EFI_OBJCOPY_SECTIONS) $(EFI_OBJCOPY_TARGET) $^ $@ clean: - rm $(EFI_OBJ_FILES) $(EFI_SHARED_OBJ) $(EFI_TARGET)
\ No newline at end of file + rm $(EFI_OBJ_FILES) $(EFI_SHARED_OBJ) $(EFI_TARGET) + +keep: + rm $(EFI_OBJ_FILES) $(EFI_SHARED_OBJ) diff --git a/brillo_uefi_x86_64/boot_loader/brillo_boot_loader.efi b/brillo_uefi_x86_64/boot_loader/brillo_boot_loader.efi Binary files differindex 1e10013..6ce6a7e 100755 --- a/brillo_uefi_x86_64/boot_loader/brillo_boot_loader.efi +++ b/brillo_uefi_x86_64/boot_loader/brillo_boot_loader.efi diff --git a/brillo_uefi_x86_64/boot_loader/bub_boot_kernel.c b/brillo_uefi_x86_64/boot_loader/bub_boot_kernel.c new file mode 100644 index 0000000..0befed7 --- /dev/null +++ b/brillo_uefi_x86_64/boot_loader/bub_boot_kernel.c @@ -0,0 +1,629 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* TODO: + * 1) Perform substitution of the kernel command-line fields with the + * appropriate values based on bvb_boot_image_header.h + */ + +#include <efi.h> +#include <efilib.h> +#include "bub_boot_kernel.h" +#include "bub_sysdeps.h" + +// GPT related constants +#define GPT_REVISION 0x00010000 +#define GPT_MAGIC "EFI PART" +#define GPT_MIN_SIZE 92 +#define GPT_ENTRIES_LBA 2 +#define BLOCK_SIZE 512 +#define ENTRIES_PER_BLOCK 4 +#define ENTRY_NAME_LEN 36 +#define MAX_GPT_ENTRIES 128 + +typedef struct { + UINT8 signature[8]; + UINT32 revision; + UINT32 header_size; + UINT32 header_crc32; + UINT32 reserved; + UINT64 header_lba; + UINT64 alternate_header_lba; + UINT64 first_usable_lba; + UINT64 last_usable_lba; + UINT8 disk_guid[16]; + UINT64 entry_lba; + UINT32 entry_count; + UINT32 entry_size; + UINT32 entry_crc32; + UINT8 reserved2[420]; +} GPTHeader; + +typedef struct { + UINT8 type_GUID[16]; + UINT8 unique_GUID[16]; + UINT64 first_lba; + UINT64 last_lba; + UINT64 flags; + CHAR16 name[ENTRY_NAME_LEN]; // UTF-16LE encoding, NULL terminated +} GPTEntry; + +/* + * Note: The below header definitions are taken from + * system/core/mkbootimg/bootimg.h + */ +typedef struct boot_img_hdr boot_img_hdr; + +#define BOOT_MAGIC "ANDROID!" +#define BOOT_MAGIC_SIZE 8 +#define BOOT_NAME_SIZE 16 +#define BOOT_ARGS_SIZE 512 +#define BOOT_EXTRA_ARGS_SIZE 1024 + +struct boot_img_hdr +{ + UINT8 magic[BOOT_MAGIC_SIZE]; + + UINT32 kernel_size; /* size in bytes */ + UINT32 kernel_addr; /* physical load addr */ + + UINT32 ramdisk_size; /* size in bytes */ + UINT32 ramdisk_addr; /* physical load addr */ + + UINT32 second_size; /* size in bytes */ + UINT32 second_addr; /* physical load addr */ + + UINT32 tags_addr; /* physical addr for kernel tags */ + UINT32 page_size; /* flash page size we assume */ + UINT32 unused; /* reserved for future expansion: MUST be 0 */ + + /* operating system version and security patch level; for + * version "A.B.C" and patch level "Y-M-D": + * ver = A << 14 | B << 7 | C (7 bits for each of A, B, C) + * lvl = ((Y - 2000) & 127) << 4 | M (7 bits for Y, 4 bits for M) + * os_version = ver << 11 | lvl */ + UINT32 os_version; + + UINT8 name[BOOT_NAME_SIZE]; /* asciiz product name */ + + UINT8 cmdline[BOOT_ARGS_SIZE]; + + UINT32 id[8]; /* timestamp / checksum / sha1 / etc */ + + /* Supplemental command line data; kept here to maintain + * binary compatibility with older versions of mkbootimg */ + UINT8 extra_cmdline[BOOT_EXTRA_ARGS_SIZE]; +} __attribute__((packed)); + +/* +** +-----------------+ +** | boot header | 1 page +** +-----------------+ +** | kernel | n pages +** +-----------------+ +** | ramdisk | m pages +** +-----------------+ +** | second stage | o pages +** +-----------------+ +** +** n = (kernel_size + page_size - 1) / page_size +** m = (ramdisk_size + page_size - 1) / page_size +** o = (second_size + page_size - 1) / page_size +** +** 0. all entities are page_size aligned in flash +** 1. kernel and ramdisk are required (size != 0) +** 2. second is optional (second_size == 0 -> no second) +** 3. load each element (kernel, ramdisk, second) at +** the specified physical address (kernel_addr, etc) +** 4. prepare tags at tag_addr. kernel_args[] is +** appended to the kernel commandline in the tags. +** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr +** 6. if second_size != 0: jump to second_addr +** else: jump to kernel_addr +*/ + +#define IMG_SIZE(entry, block) \ + (entry->last_lba - entry->first_lba) * block->Media->BlockSize + +#define SIZE_BLOCK_ALIGN(bytes, block_size) \ + ((bytes + block_size - 1) / block_size) * block_size + +#define OFFSET_BLOCK_ALIGN(bytes, block_size) \ + (bytes / block_size) * block_size + +/* uefi_call_wrapper's second arguments is the number of argumets for the + * called function + */ +#define NUM_ARGS_HANDLE_PROTOCOL 3 +#define NUM_ARGS_ALLOCATE_POOL 3 +#define NUM_ARGS_LOCATE_DEVICE_PATH 3 +#define NUM_ARGS_READ_BLOCKS 5 +#define NUM_ARGS_LOAD_IMAGE 6 + +/* Helper method to get the parent path to the current |walker| path given the + * initial path, |init|. Resulting path is stored in |next|. Caller is + * responsible for freeing |next|. + * + * @return EFI_STATUS Standard UEFI error code, EFI_SUCCESS on success. + */ +static EFI_STATUS walk_path(IN EFI_DEVICE_PATH *init, + IN EFI_DEVICE_PATH *walker, + OUT EFI_DEVICE_PATH **next) { + EFI_STATUS err; + + // Number of bytes from initial path to current walker. + UINTN walker_bytes = (UINT8 *)NextDevicePathNode(walker) - (UINT8 *)init; + + *next = (EFI_DEVICE_PATH*)bub_malloc_(sizeof(EFI_DEVICE_PATH) + walker_bytes); + if (*next == NULL) + return EFI_NOT_FOUND; + + // Copy in the previous paths. + bub_memcpy((*next), init, walker_bytes); + // Copy in the new ending of the path. + bub_memcpy((UINT8 *)(*next) + walker_bytes, + EndDevicePath, + sizeof(EFI_DEVICE_PATH)); + return EFI_SUCCESS; +} + +/* Helper method to validate a GPT header, |gpth|. + * + * @return EFI_STATUS EFI_SUCCESS on success. + */ +static EFI_STATUS validate_gpt(const IN GPTHeader *gpth) { + if (bub_memcmp(gpth->signature, GPT_MAGIC, sizeof(gpth->signature)) + != 0) { + bub_warning("GPT signature does not match.\n"); + return EFI_NOT_FOUND; + } + // Make sure GPT header bytes are within minimun and block size. + if (gpth->header_size < GPT_MIN_SIZE) { + bub_warning("GPT header too small.\n"); + return EFI_NOT_FOUND; + } + if (gpth->header_size > BLOCK_SIZE) { + bub_warning("GPT header too big.\n"); + return EFI_NOT_FOUND; + } + + GPTHeader gpth_tmp = {0}; + bub_memcpy(&gpth_tmp, gpth, sizeof(GPTHeader)); + UINT32 gpt_header_crc = gpth_tmp.header_crc32; + gpth_tmp.header_crc32 = 0; + UINT32 gpt_header_crc_calc = + CalculateCrc((UINT8*)&gpth_tmp, gpth_tmp.header_size); + +#ifdef BUB_ENABLE_DEBUG + Print(L"GPT Header CRC: %d\n", gpt_header_crc); + Print(L"GPT Header CRC calculated: %d\n", gpt_header_crc_calc); +#endif + if(gpt_header_crc != gpt_header_crc_calc) { + bub_warning("GPT header crc invalid.\n"); + return EFI_NOT_FOUND; + } + + if (gpth->revision != GPT_REVISION) { + bub_warning("GPT header wrong revision.\n"); + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + +/* Looks through |block_io| to search for a GPT entry named |partition_name|, a + * NUULL-terminated string. Allocates a GPTEntry struct for |entry_buf|. Caller + * is responsible for freeing |entry_buf|. + * + * @return EFI_NOT_FOUND on error, EFI SUCCESS on success. + */ +static EFI_STATUS PartitionEntryByName(IN EFI_BLOCK_IO *block_io, + const char* partition_name, + GPTEntry** entry_buf) { + EFI_STATUS err; + GPTHeader* gpt_header = NULL; + GPTEntry all_gpt_entries[MAX_GPT_ENTRIES]; + CHAR16* partition_name_ucs2 = NULL; + UINTN partition_name_bytes; + + gpt_header = (GPTHeader*)bub_malloc_(sizeof(GPTHeader)); + if (gpt_header == NULL) { + bub_warning("Could not allocate for GPT header\n"); + return EFI_NOT_FOUND; + } + + *entry_buf = (GPTEntry*)bub_malloc_(sizeof(GPTEntry) * ENTRIES_PER_BLOCK); + if (entry_buf == NULL) { + bub_warning("Could not allocate for partition entry\n"); + bub_free(gpt_header); + return EFI_NOT_FOUND; + } + + err = uefi_call_wrapper(block_io->ReadBlocks, NUM_ARGS_READ_BLOCKS, + block_io, + block_io->Media->MediaId, + 1, + sizeof(GPTHeader), + gpt_header); + if (EFI_ERROR(err)) { + bub_warning("Could not ReadBlocks for gpt header\n"); + bub_free(gpt_header); + return EFI_NOT_FOUND; + } + + partition_name_bytes = bub_strlen(partition_name) + 1; + partition_name_ucs2 = + bub_calloc(sizeof(CHAR16) * partition_name_bytes); + if (partition_name_ucs2 == NULL) { + bub_warning ("Could not allocate for ucs2 partition name\n"); + bub_free(gpt_header); + return EFI_NOT_FOUND; + } + if (utf8_to_ucs2(partition_name, + partition_name_bytes, + partition_name_ucs2, + sizeof(CHAR16) * partition_name_bytes)) { + bub_warning("Could not convert partition name to UCS-2\n"); + bub_free(gpt_header); + bub_free(partition_name_ucs2); + return EFI_NOT_FOUND; + } + +#ifdef BUB_ENABLE_DEBUG + Print(L"\nENTRY: %d, BLOCKSIZE: %d, GPTEntry: %d\n", + gpt_header->entry_count, + block_io->Media->BlockSize, + sizeof(GPTEntry)); +#endif + + // Block-aligned bytes for entries. + UINTN entries_num_bytes = block_io->Media->BlockSize * + (MAX_GPT_ENTRIES / ENTRIES_PER_BLOCK); + + err = uefi_call_wrapper(block_io->ReadBlocks, NUM_ARGS_READ_BLOCKS, + block_io, + block_io->Media->MediaId, + GPT_ENTRIES_LBA, + entries_num_bytes, + &all_gpt_entries); + if (EFI_ERROR(err)) { + bub_warning("Could not ReadBlocks for GPT header\n"); + bub_free(gpt_header); + bub_free(partition_name_ucs2); + return EFI_NOT_FOUND; + } + + // Find matching partition name. + UINT8 i; + for (i = 0; i < gpt_header->entry_count; ++i) + if (!bub_memcmp(all_gpt_entries[i].name, + partition_name_ucs2, + sizeof(CHAR16) * bub_strlen(partition_name))) { +#ifdef BUB_ENABLE_DEBUG + Print(L"Boot Partition Name is: %s\n", all_gpt_entries[i].name); + Print(L"Boot Partition LBA is: %d\n", all_gpt_entries[i].first_lba); +#endif + bub_memcpy((*entry_buf), &all_gpt_entries[i], sizeof(GPTEntry)); + bub_free(partition_name_ucs2); + bub_free(gpt_header); + return EFI_SUCCESS; + } + + bub_free(partition_name_ucs2); + bub_free(gpt_header); + return EFI_NOT_FOUND; +} + +/* Allocates a pool of memory in the EfiLoaderData region for the LoadOptions + * member of |loaded_image|. The LoadOptions member is needed by next boot + * stage. In the case of Linux kernel images, the EFI_STUB is this stage. The + * stub is expected to pass the stored string (CHAR16 type) to the kernel. + * + * |image_header| is assumed to contain the kernel command line string. + * |image| is the kernel image handle to be booted. + * + * + * @return EFI_STATUS EFI_NOT_FOUND on fail. EFI_SUCCESS on success. + * + * TODO: Perform substitution of the kernel command-line fields with the + * appropriate values based on bvb_boot_image_header.h here. + */ +static EFI_STATUS LoadParameters(EFI_HANDLE image, + boot_img_hdr *image_header, + EFI_LOADED_IMAGE **loaded_image) { + EFI_STATUS err; + EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL; + err = uefi_call_wrapper(BS->HandleProtocol, NUM_ARGS_HANDLE_PROTOCOL, + image, + &loaded_image_protocol, + (VOID **) &(*loaded_image)); + if (EFI_ERROR(err)) { + bub_warning("Could not get loaded boot image\n"); + return EFI_NOT_FOUND; + } + + err = uefi_call_wrapper(BS->AllocatePool, NUM_ARGS_ALLOCATE_POOL, + EfiLoaderData, + BOOT_ARGS_SIZE * sizeof(UINT16), + &(*loaded_image)->LoadOptions); + if (EFI_ERROR(err)) { + bub_warning("Could not allocate for kernel parameters\n"); + return EFI_NOT_FOUND; + } + + UINT32 i; + for (i = 0; image_header->cmdline[i] != '\0'; ++i) + ((CHAR16 *)((*loaded_image)->LoadOptions))[i] = + (CHAR16)image_header->cmdline[i]; + ((CHAR16 *)((*loaded_image)->LoadOptions))[i] = L'\0'; + + (*loaded_image)->LoadOptionsSize = i * sizeof(UINT16); + + return EFI_SUCCESS; +} + +/* Queries |disk_handle| for a |block_io| device and the corresponding path, + * |block_path|. The |block_io| device is found by iteratively querying parent + * devices and checking for a GPT Header. This ensures the resulting + * |block_io| device is the top level block device having access to partition + * entries. + * + * @return EFI_STATUS EFI_NOT_FOUND on fail, EFI_SUCCESS otherwise. + */ +static EFI_STATUS getDiskBlockIO(IN EFI_HANDLE* disk_handle, + OUT EFI_BLOCK_IO** block_io, + OUT EFI_DEVICE_PATH** block_path) { + EFI_STATUS err; + EFI_DEVICE_PATH *walker_path; + EFI_DEVICE_PATH *init_path; + GPTHeader gpt_header = {{0}}; + init_path = DevicePathFromHandle(disk_handle); + +#ifdef BUB_ENABLE_DEBUG + Print(L"Initial Device Path: %s\n", DevicePathToStr(init_path)); +#endif + + if (!init_path) + return EFI_NOT_FOUND; + + walker_path = init_path; + while(!IsDevicePathEnd(walker_path)) { + walker_path = NextDevicePathNode(walker_path); + +#ifdef BUB_ENABLE_DEBUG + Print(L"DevicePathType: %x\n", DevicePathType(walker_path)); +#endif + + err = walk_path(init_path, walker_path, &(*block_path)); + if (EFI_ERROR(err)) { + bub_warning("Cannot walk device path.\n"); + return EFI_NOT_FOUND; + } + +#ifdef BUB_ENABLE_DEBUG + Print(L"Walking Device Path : %s\n", DevicePathToStr(*block_path)); +#endif + err = uefi_call_wrapper(BS->LocateDevicePath, NUM_ARGS_LOCATE_DEVICE_PATH, + &BlockIoProtocol, + &(*block_path), + &disk_handle); + if (EFI_ERROR(err)) { + bub_warning("LocateDevicePath, BLOCK_IO_PROTOCOL.\n"); + bub_free(*block_path); + continue; + } + + // Handle Block i/o + // Attempt to get handle on device, must be BlockIo type. + err = uefi_call_wrapper(BS->HandleProtocol, NUM_ARGS_HANDLE_PROTOCOL, + disk_handle, + &BlockIoProtocol, + (VOID **)&(*block_io)); + if (EFI_ERROR(err)) { + bub_warning("HandleProtocol, BLOCK_IO_PROTOCOL.\n"); + bub_free(*block_path); + continue; + } + + if ((*block_io)->Media->LogicalPartition || + !(*block_io)->Media->MediaPresent) { + bub_warning("Logical partion or No Media Present, continue...\n"); + bub_free(*block_path); + continue; + } + + err = uefi_call_wrapper((*block_io)->ReadBlocks, NUM_ARGS_READ_BLOCKS, + (*block_io), + (*block_io)->Media->MediaId, + 1, + sizeof(GPTHeader), + &gpt_header); + + if (EFI_ERROR(err)) { + bub_warning("ReadBlocks, Block Media error.\n"); + bub_free(*block_path); + continue; + } + + err = validate_gpt(&gpt_header); + if (EFI_ERROR(err)) { + bub_warning("Invalid GPTHeader\n"); + bub_free(*block_path); + continue; + } + +#ifdef BUB_ENABLE_DEBUG + Print(L"Validated GPT\n"); +#endif + return EFI_SUCCESS; + } + + (*block_io) = NULL; + return EFI_NOT_FOUND; +} + +int bub_boot_kernel(EFI_HANDLE app_image, const char* boot_partition_name) { + EFI_STATUS err; + EFI_LOADED_IMAGE *loaded_app_image = NULL; + EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL; + EFI_BLOCK_IO* block_io; + EFI_DEVICE_PATH* block_io_path; + GPTEntry* partition_entry; + UINT8* boot_buf = NULL; + UINT8* kernel_buf = NULL; + boot_img_hdr* head_buf = NULL; + EFI_HANDLE kernel_image; + EFI_LOADED_IMAGE *loaded_kernel_image = NULL; + + err = uefi_call_wrapper(BS->HandleProtocol, NUM_ARGS_HANDLE_PROTOCOL, + app_image, + &loaded_image_protocol, + (VOID **) &loaded_app_image); + if (EFI_ERROR(err)) { + bub_warning("HandleProtocol, LOADED_IMAGE_PROTOCOL.\n"); + return 1; + } + +#ifdef BUB_ENABLE_DEBUG + Print(L"Image base : %lx\n", loaded_app_image->ImageBase); + Print(L"Image size : %lx\n", loaded_app_image->ImageSize); + Print(L"Image file : %s\n", + DevicePathToStr(loaded_app_image->FilePath)); + Print(L"Path Type : %x\n", loaded_app_image->FilePath->Type); + Print(L"Path Sub-type : %x\n", loaded_app_image->FilePath->SubType); + Print(L"LoadOptionsSize : %d\n", loaded_app_image->LoadOptionsSize); +#endif + + // Get parent device block i/o. + err = getDiskBlockIO(loaded_app_image->DeviceHandle, + &block_io, + &block_io_path); + if (EFI_ERROR(err)) { + bub_warning("Could not block device handle.\n"); + return 1; + } + + + // Get lba of partition based on name. + err = PartitionEntryByName(block_io, boot_partition_name ,&partition_entry); + if (EFI_ERROR(err)) { + bub_warning("Could not find Image LBA offset.\n"); + return 1; + } + +#ifdef BUB_ENABLE_DEBUG + Print(L"Block IO media block size: %d\n", block_io->Media->BlockSize); + Print(L"Kernel Image LBA: 0x%x\n", partition_entry->first_lba); + Print(L"Kernel size: 0x%x\n", IMG_SIZE(partition_entry,block_io)); +#endif + + boot_buf = (UINT8*)bub_malloc_(IMG_SIZE(partition_entry, block_io)); + if (boot_buf == NULL) { + bub_warning("Could not allocate for boot buffer.\n"); + return 1; + } + +#ifdef BUB_ENABLE_DEBUG + Print(L"Loading Boot Image...\n"); +#endif + + err = uefi_call_wrapper(block_io->ReadBlocks, NUM_ARGS_READ_BLOCKS, + block_io, + block_io->Media->MediaId, + partition_entry->first_lba, + IMG_SIZE(partition_entry, block_io), + boot_buf); + + if (EFI_ERROR(err)) { + bub_warning("Could not read load partition image.\n"); + return 1; + } + + head_buf = (boot_img_hdr *)boot_buf; + err = uefi_call_wrapper(BS->AllocatePool, NUM_ARGS_ALLOCATE_POOL, + EfiLoaderCode, + head_buf->kernel_size, + &kernel_buf); + if (EFI_ERROR(err)) { + bub_warning("Could not allocate for kernel buffer.\n"); + return 1; + } + +#ifdef BUB_ENABLE_DEBUG + // Print Header info + Print(L"kernel size: %x\n", head_buf->kernel_size); + Print(L"kernel_addr: %x\n", head_buf->kernel_addr); + Print(L"ramdisk_size: %x\n", head_buf->ramdisk_size); + Print(L"ramdisk_addr: %x\n", head_buf->ramdisk_addr); + Print(L"second_size: %x\n", head_buf->second_size); + Print(L"second_addr: %x\n", head_buf->second_addr); + Print(L"tags_addr: %x\n", head_buf->tags_addr); + Print(L"page_size: %x\n", head_buf->page_size); + Print(L"os_version: %x\n", head_buf->os_version); +#endif + + // Checks on buffer overflow. + if (head_buf->kernel_size > IMG_SIZE(partition_entry, block_io)) { + bub_warning("Kernel size beyond allowed boundary.\n"); + return 1; + } + if (head_buf->page_size > + (IMG_SIZE(partition_entry, block_io) - sizeof(boot_img_hdr))) { + bub_warning("Page size too big.\n"); + return 1; + } + + bub_memcpy(kernel_buf, + boot_buf + head_buf->page_size, + head_buf->kernel_size); + + err = uefi_call_wrapper(BS->LoadImage, NUM_ARGS_LOAD_IMAGE, + FALSE, + app_image, + block_io_path, + (void *)(kernel_buf), + head_buf->kernel_size, + &kernel_image); + if (EFI_ERROR(err)) { + bub_warning("Could not LOAD kernel_image.\n"); + return 1; + } + +#ifdef BUB_ENABLE_DEBUG + Print(L"LOADED Kernel Image.\n"); +#endif + + // Load parameters + err = LoadParameters(kernel_image, head_buf, &loaded_kernel_image); + if (EFI_ERROR(err)) + return 1; + + err = uefi_call_wrapper(BS->StartImage, 3, kernel_image, NULL, NULL); + if (EFI_ERROR(err)) { + bub_warning("Could not START kernel_image.\n"); + return 1; + } + + return 0; +}
\ No newline at end of file diff --git a/brillo_uefi_x86_64/boot_loader/bub_boot_kernel.h b/brillo_uefi_x86_64/boot_loader/bub_boot_kernel.h new file mode 100644 index 0000000..7ae85a3 --- /dev/null +++ b/brillo_uefi_x86_64/boot_loader/bub_boot_kernel.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef BUB_BOOT_KERNEL_H_ +#define BUB_BOOT_KERNEL_H_ + +#include <efi.h> +#include <efilib.h> + +// For printing debug statements. +#define BUB_ENABLE_DEBUG + +/* Boots a UEFI kernel image given a |boot_partition_name| string belonging to a + * bootable partition entry. The partition must be on the same block device as + * the current UEFI application, |app_image|. |app_image| is given at the entry + * point, efi_main(), of the UEFI application. + * + * @return int 1 upon failure. 0 on success. + */ +int bub_boot_kernel(EFI_HANDLE app_image, const char* boot_partition_name); + +#endif /* BUB_BOOT_KERNEL_H_ */
\ No newline at end of file diff --git a/brillo_uefi_x86_64/boot_loader/main.c b/brillo_uefi_x86_64/boot_loader/bub_main.c index b12950e..5889b18 100644 --- a/brillo_uefi_x86_64/boot_loader/main.c +++ b/brillo_uefi_x86_64/boot_loader/bub_main.c @@ -24,11 +24,23 @@ #include <efi.h> #include <efilib.h> +#include "bub_boot_kernel.h" +#include "bub_sysdeps.h" + EFI_STATUS EFIAPI efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable) { + int err; + InitializeLib(ImageHandle, SystemTable); - Print(L"Brillo EFI A/B BOOT LOADER\n"); + bub_print("Brillo UEFI A/B BOOT LOADER\n"); + + err = bub_boot_kernel(ImageHandle, "boot_a"); + if (err) { + bub_error("Error loading kernel.\n"); + uefi_call_wrapper(BS->Stall, 1, 15 * 1000 * 1000); + return EFI_LOAD_ERROR; + } return EFI_SUCCESS; -} +}
\ No newline at end of file diff --git a/brillo_uefi_x86_64/boot_loader/bub_sysdeps.h b/brillo_uefi_x86_64/boot_loader/bub_sysdeps.h new file mode 100644 index 0000000..d3ccc6d --- /dev/null +++ b/brillo_uefi_x86_64/boot_loader/bub_sysdeps.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// NOTE: See avb_sysdeps.h + +// #if !defined(BUB_INSIDE_LIBBUB_H) && !defined(BUB_COMPILATION) +// #error "Never include this file directly, include libbub.h instead." +// #endif + +#ifndef BUB_SYSDEPS_H_ +#define BUB_SYSDEPS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Change these includes to match your platform to bring in the + * equivalent types available in a normal C runtime, as well as + * printf()-format specifiers such as PRIx64. + */ +#include <inttypes.h> +#include <stddef.h> +#include <stdint.h> +#include "bub_util.h" + +/* If you don't have gcc or clang, these attribute macros may need to + * be adjusted. + */ +#define BUB_ATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#define BUB_ATTR_PACKED __attribute__((packed)) +#define BUB_ATTR_FORMAT_PRINTF(format_idx, arg_idx) \ + __attribute__((format(printf, format_idx, arg_idx))) +#define BUB_ATTR_NO_RETURN __attribute__((noreturn)) + +#define WIDEN2(x) L ## x +#define WIDEN(x) WIDEN2(x) +#define WFILE WIDEN(__FILE__) + +#ifdef BUB_ENABLE_DEBUG +/* Aborts the program if |expr| is false. + * + * This has no effect unless BUB_ENABLE_DEBUG is defined. + */ +#define bub_assert(expr) \ + do { \ + if (!(expr)) { \ + bub_error("assert fail: \n"); \ + } \ + } while (0) +#else +#define bub_assert(expr) +#endif /* BUB_ENABLE_DEBUG */ + +/* Size in bytes used for word-alignment. + * + * Change this to match your architecture - must be a power of two. + */ +#define BUB_WORD_ALIGNMENT_SIZE 8 + +/* Aborts the program if |addr| is not word-aligned. + * + * This has no effect unless BUB_ENABLE_DEBUG is defined. + */ +#define bub_assert_word_aligned(addr) \ +// bub_assert((((uintptr_t)addr) & (BUB_WORD_ALIGNMENT_SIZE - 1)) == 0) + +/* Compare |n| bytes in |src1| and |src2|. + * + * Returns an integer less than, equal to, or greater than zero if the + * first |n| bytes of |src1| is found, respectively, to be less than, + * to match, or be greater than the first |n| bytes of |src2|. */ +int bub_memcmp(const void* src1, const void* src2, + size_t n) BUB_ATTR_WARN_UNUSED_RESULT; + +/* Compare two strings. + * + * Return an integer less than, equal to, or greater than zero if |s1| + * is found, respectively, to be less than, to match, or be greater + * than |s2|. + */ +int bub_strcmp(const char* s1, const char* s2); + +/* Copy |n| bytes from |src| to |dest|. */ +void* bub_memcpy(void* dest, const void* src, size_t n); + +/* Set |n| bytes starting at |s| to |c|. Returns |dest|. */ +void* bub_memset(void* dest, const int c, size_t n); + +/* Compare |n| bytes starting at |s1| with |s2| and return 0 if they + * match, 1 if they don't. Returns 0 if |n|==0, since no bytes + * mismatched. + * + * Time taken to perform the comparison is only dependent on |n| and + * not on the relationship of the match between |s1| and |s2|. + * + * Note that unlike bub_memcmp(), this only indicates inequality, not + * whether |s1| is less than or greater than |s2|. + */ +int bub_safe_memcmp(const void* s1, const void* s2, + size_t n) BUB_ATTR_WARN_UNUSED_RESULT; + +#ifdef BUB_ENABLE_DEBUG +/* printf()-style function, used for diagnostics. + * + * This has no effect unless BUB_ENABLE_DEBUG is defined. + */ +#define bub_debug(format) \ + do { \ + bub_print("DEBUG: " format); \ + } while (0) +#else +#define bub_debug(format) +#endif /* BUB_ENABLE_DEBUG */ + +/* Prints out a message (defined by |format|, printf()-style). + */ +void bub_print(const char* format); + +/* Prints out a message (defined by |format|, printf()-style). This is + * typically used if a runtime-error occurs. + */ +#define bub_warning(format) \ + do { \ + bub_print("WARNING: " format); \ + } while (0) + + +/* Prints out a message (defined by |format|, printf()-style) and + * calls bub_abort(). + */ +#define bub_error(format) \ + do { \ + bub_print("ERROR: " format); \ + bub_abort(); \ + } while (0) + +/* Aborts the program or reboots the device. */ +void bub_abort(void) BUB_ATTR_NO_RETURN; + +/* Allocates |size| bytes. Returns NULL if no memory is available, + * otherwise a pointer to the allocated memory. + * + * The memory is not initialized. + * + * The pointer returned is guaranteed to be word-aligned. + * + * The memory should be freed with bub_free() when you are done with it. + */ +void* bub_malloc_(size_t size) BUB_ATTR_WARN_UNUSED_RESULT; + +/* Frees memory previously allocated with bub_malloc(). */ +void bub_free(void* ptr); + +/* Returns the lenght of |str|, excluding the terminating NUL-byte. */ +size_t bub_strlen(const char* str) BUB_ATTR_WARN_UNUSED_RESULT; + +#ifdef __cplusplus +} +#endif + +#endif /* BUB_SYSDEPS_H_ */
\ No newline at end of file diff --git a/brillo_uefi_x86_64/boot_loader/bub_sysdeps_uefi.c b/brillo_uefi_x86_64/boot_loader/bub_sysdeps_uefi.c new file mode 100644 index 0000000..051a748 --- /dev/null +++ b/brillo_uefi_x86_64/boot_loader/bub_sysdeps_uefi.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <efi.h> +#include <efilib.h> +#include "bub_sysdeps.h" + +int bub_memcmp(const void* src1, const void* src2, size_t n) { + return (int)CompareMem(src1, src2, (UINTN)n); +} + +int bub_strcmp(const char* s1, const char* s2) { + return (int)StrCmp(s1, s2); +} + +void* bub_memcpy(void* dest, const void* src, size_t n) { + CopyMem(dest, src, (UINTN)n); +} + +void* bub_memset(void* dest, const int c, size_t n) { + SetMem(dest, (UINTN)n, (UINT8)c); +} + +int bub_safe_memcmp(const void* s1, const void* s2, size_t n) { + const unsigned char* us1 = s1; + const unsigned char* us2 = s2; + int result = 0; + + if (0 == n) return 0; + + /* + * Code snippet without data-dependent branch due to Nate Lawson + * (nate@root.org) of Root Labs. + */ + while (n--) result |= *us1++ ^ *us2++; + + return result != 0; +} + +void bub_print(const char* format) { + size_t utf8_bytes = bub_strlen(format) + 1; + size_t max_ucs2_bytes = utf8_bytes * 2; + uint16_t* format_ucs2 = (uint16_t*)bub_calloc(max_ucs2_bytes); + if (format_ucs2 == NULL) + return; + if (!utf8_to_ucs2(format, utf8_bytes, format_ucs2, max_ucs2_bytes)) + Print(format_ucs2); + bub_free(format_ucs2); +} + +void bub_abort(void) { + bub_print("\nABORTING...\n"); + uefi_call_wrapper(BS->Stall, 1, 5 * 1000 * 1000); + uefi_call_wrapper(BS->Exit, 4, + NULL, + EFI_NOT_FOUND, + 0, + NULL); +} + +void* bub_malloc_(size_t size) { + EFI_STATUS err; + void *x; + + err = uefi_call_wrapper(BS->AllocatePool, 3, + EfiBootServicesData, + (UINTN)size, + &x); + if (EFI_ERROR(err)) { + return NULL; + } + + return x; + +} + +void bub_free(void* ptr) { + EFI_STATUS err; + err = uefi_call_wrapper(BS->FreePool, 1, ptr); + + if (EFI_ERROR(err)) { + Print(L"Warning: Bad bub_free: %r\n", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + } +} + +size_t bub_strlen(const char* str) { + return strlena(str); +}
\ No newline at end of file diff --git a/brillo_uefi_x86_64/boot_loader/bub_util.c b/brillo_uefi_x86_64/boot_loader/bub_util.c new file mode 100644 index 0000000..7a8e63b --- /dev/null +++ b/brillo_uefi_x86_64/boot_loader/bub_util.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "bub_sysdeps.h" + +int utf8_to_ucs2(const uint8_t* utf8_data, + size_t utf8_num_bytes, + uint16_t* ucs2_data, + size_t ucs2_data_capacity_num_bytes) { + uint32_t i8 = 0; + uint32_t i2 = 0; + do { + // cannot represent this character, too long + if ((utf8_data[i8] & 0xF8) == 0xF0) + return 1; + else if ((utf8_data[i8] & 0xF0) == 0xE0) { + ucs2_data[i2] = (uint16_t)((uint16_t)utf8_data[i8] << 12) | + (uint16_t)(((uint16_t)utf8_data[i8 + 1] << 6) & 0x0FC0) | + (uint16_t)((uint16_t)utf8_data[i8 + 2 ] & 0x003F); + i8 += 3; + } + else if ((utf8_data[i8] & 0xE0) == 0XC0) { + ucs2_data[i2] = (uint16_t)(((uint16_t)utf8_data[i8] << 6) & 0x07C0) | + (uint16_t)((uint16_t)utf8_data[i8 + 1] & 0x003F); + i8 += 2; + } + else if (!(utf8_data[i8] >> 7)) { + ucs2_data[i2] = (uint16_t)((uint16_t)utf8_data[i8] & 0x00FF); + //bub_print("(1) ucs2 = %lc, utf8 = %c ",ucs2_data[i2], utf8_data[i8]); + i8++; + } + // invalid utf-8 + else + return 1; + //bub_print("\nloop\n"); + i2++; + } while (i8 < utf8_num_bytes - 1); + return 0; +} + +void* bub_calloc(size_t size) { + void *x = bub_malloc_(size); + + if (x != NULL) + bub_memset(x, 0, size); + + return x; +}
\ No newline at end of file diff --git a/brillo_uefi_x86_64/boot_loader/bub_util.h b/brillo_uefi_x86_64/boot_loader/bub_util.h new file mode 100644 index 0000000..78978d8 --- /dev/null +++ b/brillo_uefi_x86_64/boot_loader/bub_util.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef BUB_UTIL_H_ +#define BUB_UTIL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Converts a UTF-8 string, |utf8_data|, to UCS-2 stored in |ucs2_data|. Caller + * must specify |utf8_num_bytes| as the number of bytes of the null-terminated + * |utf8_data|. Caller must have allocated enough bytes for the resulting UCS-2 + * specified by |ucs2_data_capacity_num_bytes|. 2 * len(|utf8_data|) + 1 would + * be the upper bound of |ucs2_data_capacity_num_bytes|. + * + */ +int utf8_to_ucs2(const uint8_t* utf8_data, + size_t utf8_num_bytes, + uint16_t* ucs2_data, + size_t ucs2_data_capacity_num_bytes); + +/* Allocates |size| bytes set to all zeros. Returns NULL if no memory is + * available, otherwise a pointer to the allocated memory. + * + * The memory is not initialized. + * + * The pointer returned is guaranteed to be word-aligned. + * + * The memory should be freed with bub_free() when you are done with it. + */ +void* bub_calloc(size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* BUB_UTIL_H_ */
\ No newline at end of file |