blob: 59a63d8f4ff42c1f51be9e1dd3cba40da3ba3577 [file] [log] [blame]
/*
* 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"
/*
* Note: The below header definitions are taken from
* system/core/mkbootimg/bootimg.h
*/
typedef struct boot_img_hdr boot_img_hdr;
#define BOOT_MAGIC {'A','N','D','R','O','I','D','!'}
#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
*/
/* 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|. Stores allocated bytes for |next| in
* |out_bytes|.
*
* @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,
OUT UINTN* out_bytes) {
// Number of bytes from initial path to current walker.
UINTN walker_bytes = (UINT8 *)NextDevicePathNode(walker) - (UINT8 *)init;
*out_bytes = sizeof(EFI_DEVICE_PATH) + walker_bytes;
*next = (EFI_DEVICE_PATH*)bub_malloc_(*out_bytes);
if (*next == NULL) {
*out_bytes = 0;
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 > BUB_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;
}
/* 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* block_handle,
OUT EFI_BLOCK_IO** block_io,
OUT EFI_DISK_IO** disk_io,
OUT EFI_DEVICE_PATH** io_path) {
EFI_STATUS err;
EFI_HANDLE disk_handle;
UINTN path_bytes;
EFI_DEVICE_PATH *disk_path;
EFI_DEVICE_PATH *walker_path;
EFI_DEVICE_PATH *init_path;
GPTHeader gpt_header = {{0}};
init_path = DevicePathFromHandle(block_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, &(*io_path), &path_bytes);
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(*io_path));
#endif
disk_path = (EFI_DEVICE_PATH*)bub_malloc_(path_bytes);
bub_memcpy(disk_path, *io_path, path_bytes);
err = uefi_call_wrapper(BS->LocateDevicePath, NUM_ARGS_LOCATE_DEVICE_PATH,
&BlockIoProtocol,
&(*io_path),
&block_handle);
if (EFI_ERROR(err)) {
bub_warning("LocateDevicePath, BLOCK_IO_PROTOCOL.\n");
bub_free(*io_path);
bub_free(disk_path);
continue;
}
err = uefi_call_wrapper(BS->LocateDevicePath, NUM_ARGS_LOCATE_DEVICE_PATH,
&DiskIoProtocol,
&disk_path,
&disk_handle);
if (EFI_ERROR(err)) {
bub_warning("LocateDevicePath, DISK_IO_PROTOCOL.\n");
bub_free(*io_path);
bub_free(disk_path);
continue;
}
// Handle Block and Disk i/o.
// Attempt to get handle on device, must be Block/Disk Io type.
err = uefi_call_wrapper(BS->HandleProtocol, NUM_ARGS_HANDLE_PROTOCOL,
block_handle,
&BlockIoProtocol,
(VOID **)&(*block_io));
if (EFI_ERROR(err)) {
bub_warning("Cannot get handle on block device.\n");
bub_free(*io_path);
bub_free(disk_path);
continue;
}
err = uefi_call_wrapper(BS->HandleProtocol, NUM_ARGS_HANDLE_PROTOCOL,
disk_handle,
&DiskIoProtocol,
(VOID **)&(*disk_io));
if (EFI_ERROR(err)) {
bub_warning("Cannot get handle on disk device.\n");
bub_free(*io_path);
bub_free(disk_path);
continue;
}
if ((*block_io)->Media->LogicalPartition ||
!(*block_io)->Media->MediaPresent) {
bub_warning("Logical partion or No Media Present, continue...\n");
bub_free(*io_path);
bub_free(disk_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(*io_path);
bub_free(disk_path);
continue;
}
err = validate_gpt(&gpt_header);
if (EFI_ERROR(err)) {
bub_warning("Invalid GPTHeader\n");
bub_free(*io_path);
bub_free(disk_path);
continue;
}
#ifdef BUB_ENABLE_DEBUG
Print(L"Walking Device Path3 : %s\n", DevicePathToStr(disk_path));
Print(L"Validated GPT\n");
#endif
return EFI_SUCCESS;
}
(*block_io) = NULL;
return EFI_NOT_FOUND;
}
EFI_STATUS bub_partition_entry_by_name(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);
bub_free(*entry_buf);
*entry_buf = NULL;
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);
bub_free(*entry_buf);
*entry_buf = NULL;
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);
bub_free(*entry_buf);
*entry_buf = NULL;
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);
bub_free(*entry_buf);
*entry_buf = NULL;
return EFI_NOT_FOUND;
}
// Find matching partition name.
UINT8 i;
for (i = 0; i < gpt_header->entry_count; ++i)
if ((StrLen(partition_name_ucs2) ==
StrLen(all_gpt_entries[i].name)) &&
!bub_memcmp(all_gpt_entries[i].name,
partition_name_ucs2,
sizeof(CHAR16) * bub_strlen(partition_name))) {
#ifdef BUB_ENABLE_DEBUG
Print(L"Requested Partition: %s\n", partition_name_ucs2);
Print(L"Found Partition Name is: %s\n", all_gpt_entries[i].name);
Print(L"Found 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);
bub_free(*entry_buf);
*entry_buf = NULL;
return EFI_NOT_FOUND;
}
int bub_init(MyBubOps* bub, EFI_HANDLE app_image) {
EFI_STATUS err;
EFI_LOADED_IMAGE *loaded_app_image = NULL;
EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL;
bub->efi_image_handle = app_image;
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 0;
}
#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 disk and block i/o.
err = getDiskBlockIo(loaded_app_image->DeviceHandle,
&bub->block_io,
&bub->disk_io,
&bub->path);
if (EFI_ERROR(err)) {
bub_warning("Could not acquire block or disk device handle.\n");
return 0;
}
bub->parent.read_from_partition = bub_read_from_partition;
bub->parent.write_to_partition = bub_write_to_partition;
return 1;
}
BubBootResult bub_boot_kernel(MyBubOps* bub, const char* boot_partition_name) {
EFI_STATUS err;
GPTEntry* partition_entry;
UINT8* kernel_buf = NULL;
boot_img_hdr* head_buf = NULL;
UINTN num_bytes_read;
EFI_HANDLE kernel_image;
EFI_LOADED_IMAGE *loaded_kernel_image = NULL;
err = uefi_call_wrapper(BS->AllocatePool, NUM_ARGS_ALLOCATE_POOL,
EfiLoaderCode,
sizeof(boot_img_hdr),
&head_buf);
if (EFI_ERROR(err)) {
bub_warning("Could not allocate for kernel buffer.\n");
return BUB_BOOT_ERROR_OOM;
}
if (bub->parent.read_from_partition((BubOps *)bub,
boot_partition_name,
head_buf,
0,
sizeof(boot_img_hdr), &num_bytes_read)) {
bub_warning("Could not read boot image header.\n");
return BUB_BOOT_ERROR_IO;
}
#ifdef BUB_ENABLE_DEBUG
// Print Header info
UINT8 i = 0;
Print(L"magic: %c", head_buf->magic[0]);
for (i = 1; i < 8; ++i) Print(L"%c", head_buf->magic[i]);
Print(L"\nkernel 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);
Print(L"id: %x\n", head_buf->id[0]);
for (i = 1; i < 8; ++i) Print(L" %x ", head_buf->id[i]);
Print(L"\n");
#endif
// Retrieve Gpt partition data to check address boundaries.
err = bub_partition_entry_by_name(bub->block_io,
boot_partition_name,
&partition_entry);
if (EFI_ERROR(err)) {
bub_warning("Could not find boot partition GPT entry.\n");
return BUB_BOOT_ERROR_IO;
}
#ifdef BUB_ENABLE_DEBUG
Print(L"Block IO media block size: %d\n", bub->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,bub->block_io));
#endif
// Check boot image header magic field.
if (bub_memcmp((UINT8[8])BOOT_MAGIC, head_buf->magic, 8)) {
bub_warning("Wrong boot image header magic.\n");
return BUB_BOOT_ERROR_PARTITION_INVALID_FORMAT;
}
// Checks on buffer overflow.
if (head_buf->kernel_size > IMG_SIZE(partition_entry, bub->block_io)) {
bub_warning("Kernel size beyond allowed boundary.\n");
return BUB_BOOT_ERROR_PARTITION_INVALID_FORMAT;
}
if (head_buf->page_size >
(IMG_SIZE(partition_entry, bub->block_io) - sizeof(boot_img_hdr))) {
bub_warning("Page size too big.\n");
return BUB_BOOT_ERROR_PARTITION_INVALID_FORMAT;
}
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 BUB_BOOT_ERROR_OOM;
}
bub_debug("Reading kernel image.\n");
if (bub->parent.read_from_partition((BubOps *)bub,
boot_partition_name,
kernel_buf,
head_buf->page_size,
head_buf->kernel_size,
&num_bytes_read)) {
bub_warning("Could not read kernel image.\n");
return BUB_BOOT_ERROR_IO;
}
bub_debug("Loading kernel image.\n");
err = uefi_call_wrapper(BS->LoadImage, NUM_ARGS_LOAD_IMAGE,
FALSE,
bub->efi_image_handle,
bub->path,
(void *)(kernel_buf),
head_buf->kernel_size,
&kernel_image);
if (EFI_ERROR(err)) {
bub_warning("Could not load kernel image.\n");
return BUB_BOOT_ERROR_LOAD_KERNEL;
}
bub_debug("Loaded kernel image.\n");
// Load parameters
err = LoadParameters(kernel_image, head_buf, &loaded_kernel_image);
if (EFI_ERROR(err))
return BUB_BOOT_ERROR_PARAMETER_LOAD;
err = uefi_call_wrapper(BS->StartImage, 3, kernel_image, NULL, NULL);
if (EFI_ERROR(err)) {
bub_warning("Could not start kernel image.\n");
return BUB_BOOT_ERROR_START_KERNEL;
}
return BUB_BOOT_RESULT_OK;
}