diff options
-rw-r--r-- | arch/x86/include/asm/zimage.h | 37 | ||||
-rw-r--r-- | arch/x86/lib/zimage.c | 8 | ||||
-rw-r--r-- | boot/Kconfig | 4 | ||||
-rw-r--r-- | boot/bootflow.c | 17 | ||||
-rw-r--r-- | boot/bootm.c | 37 | ||||
-rw-r--r-- | boot/bootmeth-uclass.c | 12 | ||||
-rw-r--r-- | boot/bootmeth_cros.c | 367 | ||||
-rw-r--r-- | boot/bootmeth_cros.h | 197 | ||||
-rw-r--r-- | cmd/bootflow.c | 47 | ||||
-rw-r--r-- | doc/usage/cmd/bootflow.rst | 139 | ||||
-rw-r--r-- | include/bootflow.h | 15 | ||||
-rw-r--r-- | include/bootm.h | 47 | ||||
-rw-r--r-- | include/bootmeth.h | 25 |
13 files changed, 845 insertions, 107 deletions
diff --git a/arch/x86/include/asm/zimage.h b/arch/x86/include/asm/zimage.h index 9ad74dc..655675b 100644 --- a/arch/x86/include/asm/zimage.h +++ b/arch/x86/include/asm/zimage.h @@ -62,41 +62,4 @@ struct boot_params *load_zimage(char *image, unsigned long kernel_size, int setup_zimage(struct boot_params *setup_base, char *cmd_line, int auto_boot, ulong initrd_addr, ulong initrd_size, ulong cmdline_force); -/** - * zimage_dump() - Dump the metadata of a zimage - * - * This shows all available information in a zimage that has been loaded. - * - * @base_ptr: Pointer to the boot parameters, typically at address - * DEFAULT_SETUP_BASE - */ -void zimage_dump(struct boot_params *base_ptr); - -/** - * zboot_start() - Boot a zimage - * - * Boot a zimage, given the component parts - * - * @addr: Address where the bzImage is moved before booting, either - * BZIMAGE_LOAD_ADDR or ZIMAGE_LOAD_ADDR - * @base: Pointer to the boot parameters, typically at address - * DEFAULT_SETUP_BASE - * @initrd: Address of the initial ramdisk, or 0 if none - * @initrd_size: Size of the initial ramdisk, or 0 if none - * @cmdline: Command line to use for booting - * Return: -EFAULT on error (normally it does not return) - */ -int zboot_start(ulong addr, ulong size, ulong initrd, ulong initrd_size, - ulong base, char *cmdline); - -/* - * zimage_get_kernel_version() - Get the version string from a kernel - * - * @params: boot_params pointer - * @kernel_base: base address of kernel - * Return: Kernel version as a NUL-terminated string - */ -const char *zimage_get_kernel_version(struct boot_params *params, - void *kernel_base); - #endif diff --git a/arch/x86/lib/zimage.c b/arch/x86/lib/zimage.c index 062e3d3..a41e1cc 100644 --- a/arch/x86/lib/zimage.c +++ b/arch/x86/lib/zimage.c @@ -692,7 +692,7 @@ static void show_loader(struct setup_header *hdr) printf("\n"); } -void zimage_dump(struct boot_params *base_ptr) +void zimage_dump(struct boot_params *base_ptr, bool show_cmdline) { struct setup_header *hdr; const char *version; @@ -703,7 +703,7 @@ void zimage_dump(struct boot_params *base_ptr) printf("E820: %d entries\n", base_ptr->e820_entries); if (base_ptr->e820_entries) { - printf("%18s %16s %s\n", "Addr", "Size", "Type"); + printf("%12s %10s %s\n", "Addr", "Size", "Type"); for (i = 0; i < base_ptr->e820_entries; i++) { struct e820_entry *entry = &base_ptr->e820_map[i]; @@ -749,7 +749,7 @@ void zimage_dump(struct boot_params *base_ptr) print_num("Ext loader ver", hdr->ext_loader_ver); print_num("Ext loader type", hdr->ext_loader_type); print_num("Command line ptr", hdr->cmd_line_ptr); - if (hdr->cmd_line_ptr) { + if (show_cmdline && hdr->cmd_line_ptr) { printf(" "); /* Use puts() to avoid limits from CONFIG_SYS_PBSIZE */ puts((char *)(ulong)hdr->cmd_line_ptr); @@ -787,7 +787,7 @@ static int do_zboot_dump(struct cmd_tbl *cmdtp, int flag, int argc, printf("No zboot setup_base\n"); return CMD_RET_FAILURE; } - zimage_dump(base_ptr); + zimage_dump(base_ptr, true); return 0; } diff --git a/boot/Kconfig b/boot/Kconfig index b00d7a8..5e2d428 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -464,8 +464,8 @@ config BOOTMETH_GLOBAL config BOOTMETH_CROS bool "Bootdev support for Chromium OS" - depends on X86 || SANDBOX - default y + depends on X86 || ARM || SANDBOX + default y if !ARM help Enables support for booting Chromium OS using bootdevs. This uses the kernel A slot and obtains the kernel command line from the parameters diff --git a/boot/bootflow.c b/boot/bootflow.c index 81b5829..6ef62e1 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -432,6 +432,7 @@ void bootflow_free(struct bootflow *bflow) free(bflow->buf); free(bflow->os_name); free(bflow->fdt_fname); + free(bflow->bootmeth_priv); } void bootflow_remove(struct bootflow *bflow) @@ -444,6 +445,22 @@ void bootflow_remove(struct bootflow *bflow) free(bflow); } +#if CONFIG_IS_ENABLED(BOOTSTD_FULL) +int bootflow_read_all(struct bootflow *bflow) +{ + int ret; + + if (bflow->state != BOOTFLOWST_READY) + return log_msg_ret("rd", -EPROTO); + + ret = bootmeth_read_all(bflow->method, bflow); + if (ret) + return log_msg_ret("rd2", ret); + + return 0; +} +#endif /* BOOTSTD_FULL */ + int bootflow_boot(struct bootflow *bflow) { int ret; diff --git a/boot/bootm.c b/boot/bootm.c index 75f0b4a..b1c3afe 100644 --- a/boot/bootm.c +++ b/boot/bootm.c @@ -823,6 +823,43 @@ err: return ret; } +int bootm_boot_start(ulong addr, const char *cmdline) +{ + static struct cmd_tbl cmd = {"bootm"}; + char addr_str[30]; + char *argv[] = {addr_str, NULL}; + int states; + int ret; + + /* + * TODO(sjg@chromium.org): This uses the command-line interface, but + * should not. To clean this up, the various bootm states need to be + * passed an info structure instead of cmdline flags. Then this can + * set up the required info and move through the states without needing + * the command line. + */ + states = BOOTM_STATE_START | BOOTM_STATE_FINDOS | BOOTM_STATE_PRE_LOAD | + BOOTM_STATE_FINDOTHER | BOOTM_STATE_LOADOS | + BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO | + BOOTM_STATE_OS_GO; + if (IS_ENABLED(CONFIG_SYS_BOOT_RAMDISK_HIGH)) + states |= BOOTM_STATE_RAMDISK; + if (IS_ENABLED(CONFIG_PPC) || IS_ENABLED(CONFIG_MIPS)) + states |= BOOTM_STATE_OS_CMDLINE; + images.state |= states; + + snprintf(addr_str, sizeof(addr_str), "%lx", addr); + + ret = env_set("bootargs", cmdline); + if (ret) { + printf("Failed to set cmdline\n"); + return ret; + } + ret = do_bootm_states(&cmd, 0, 1, argv, states, &images, 1); + + return ret; +} + #if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT) /** * image_get_kernel - verify legacy format kernel image diff --git a/boot/bootmeth-uclass.c b/boot/bootmeth-uclass.c index 175eb1d..1d157d5 100644 --- a/boot/bootmeth-uclass.c +++ b/boot/bootmeth-uclass.c @@ -61,6 +61,18 @@ int bootmeth_set_bootflow(struct udevice *dev, struct bootflow *bflow, return ops->set_bootflow(dev, bflow, buf, size); } +#if CONFIG_IS_ENABLED(BOOTSTD_FULL) +int bootmeth_read_all(struct udevice *dev, struct bootflow *bflow) +{ + const struct bootmeth_ops *ops = bootmeth_get_ops(dev); + + if (!ops->read_all) + return -ENOSYS; + + return ops->read_all(dev, bflow); +} +#endif /* BOOTSTD_FULL */ + int bootmeth_boot(struct udevice *dev, struct bootflow *bflow) { const struct bootmeth_ops *ops = bootmeth_get_ops(dev); diff --git a/boot/bootmeth_cros.c b/boot/bootmeth_cros.c index aa19ae0..1776fb1 100644 --- a/boot/bootmeth_cros.c +++ b/boot/bootmeth_cros.c @@ -12,24 +12,76 @@ #include <blk.h> #include <bootdev.h> #include <bootflow.h> +#include <bootm.h> #include <bootmeth.h> +#include <display_options.h> #include <dm.h> #include <malloc.h> #include <mapmem.h> #include <part.h> -#ifdef CONFIG_X86 -#include <asm/zimage.h> -#endif #include <linux/sizes.h> +#include "bootmeth_cros.h" + +/* + * Layout of the ChromeOS kernel + * + * Partitions 2 and 4 contain kernels + * + * Contents are: + * + * Offset Contents + * 0 struct vb2_keyblock + * m struct vb2_kernel_preamble + * m + n kernel buffer + * + * m is keyblock->keyblock_size + * n is preamble->preamble_size + * + * The kernel buffer itself consists of various parts: + * + * Offset Contents + * m + n kernel image (Flat vmlinux binary or FIT) + * b - 8KB Command line text + * b - 4KB X86 setup block (struct boot_params, extends for about 16KB) + * b X86 bootloader (continuation of setup block) + * b + 16KB X86 setup block (copy, used for hold data pointed to) + * + * b is m + n + preamble->bootloader_address - preamble->body_load_address + * + * Useful metadata extends from b - 8KB through to b + 32 KB + */ enum { - /* Offsets in the kernel-partition header */ - KERN_START = 0x4f0, - KERN_SIZE = 0x518, + PROBE_SIZE = SZ_4K, /* initial bytes read from partition */ - SETUP_OFFSET = 0x1000, /* bytes before base */ - CMDLINE_OFFSET = 0x2000, /* bytes before base */ - OFFSET_BASE = 0x100000, /* assumed kernel load-address */ + X86_SETUP_OFFSET = -0x1000, /* setup offset relative to base */ + CMDLINE_OFFSET = -0x2000, /* cmdline offset relative to base */ + X86_KERNEL_OFFSET = 0x4000, /* kernel offset relative to base */ +}; + +/** + * struct cros_priv - Private data + * + * This is read from the disk and recorded for use when the full kernel must + * be loaded and booted + * + * @body_offset: Offset of kernel body from start of partition (in bytes) + * @body_size: Size of kernel body in bytes + * @part_start: Block offset of selected partition from the start of the disk + * @body_load_address: Nominal load address for kernel body + * @bootloader_address: Address of bootloader, after body is loaded at + * body_load_address + * @bootloader_size: Size of bootloader in bytes + * @info_buf: Buffer containing ChromiumOS info + */ +struct cros_priv { + ulong body_offset; + ulong body_size; + lbaint_t part_start; + ulong body_load_address; + ulong bootloader_address; + ulong bootloader_size; + void *info_buf; }; static int cros_check(struct udevice *dev, struct bootflow_iter *iter) @@ -77,61 +129,83 @@ static int copy_cmdline(const char *from, const char *uuid, char **bufp) return 0; } -static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow) +/** + * scan_part() - Scan a kernel partition to see if has a ChromeOS header + * + * This reads the first PROBE_SIZE of a partition, loookng for + * VB2_KEYBLOCK_MAGIC + * + * @blk: Block device to scan + * @partnum: Partition number to scan + * @info: Please to put partition info + * @hdrp: Return allocated keyblock header on success + */ +static int scan_part(struct udevice *blk, int partnum, + struct disk_partition *info, struct vb2_keyblock **hdrp) { - struct blk_desc *desc = dev_get_uclass_plat(bflow->blk); - ulong base, start, size, setup, cmdline, num_blks, kern_base; - struct disk_partition info; - const char *uuid = NULL; - void *buf, *hdr; + struct blk_desc *desc = dev_get_uclass_plat(blk); + struct vb2_keyblock *hdr; + ulong num_blks; int ret; - log_debug("starting, part=%d\n", bflow->part); - - /* We consider the whole disk, not any one partition */ - if (bflow->part) - return log_msg_ret("max", -ENOENT); - - /* Check partition 2 */ - ret = part_get_info(desc, 2, &info); + ret = part_get_info(desc, partnum, info); if (ret) return log_msg_ret("part", ret); /* Make a buffer for the header information */ - num_blks = SZ_4K >> desc->log2blksz; + num_blks = PROBE_SIZE >> desc->log2blksz; log_debug("Reading header, blk=%s, start=%lx, blocks=%lx\n", - bflow->blk->name, (ulong)info.start, num_blks); - hdr = memalign(SZ_1K, SZ_4K); + blk->name, (ulong)info->start, num_blks); + hdr = memalign(SZ_1K, PROBE_SIZE); if (!hdr) return log_msg_ret("hdr", -ENOMEM); - ret = blk_read(bflow->blk, info.start, num_blks, hdr); - if (ret != num_blks) - return log_msg_ret("inf", ret); + ret = blk_read(blk, info->start, num_blks, hdr); + if (ret != num_blks) { + free(hdr); + return log_msg_ret("inf", -EIO); + } - if (memcmp("CHROMEOS", hdr, 8)) + if (memcmp(VB2_KEYBLOCK_MAGIC, hdr->magic, VB2_KEYBLOCK_MAGIC_SIZE)) { + free(hdr); return -ENOENT; + } - log_info("Header at %lx\n", (ulong)map_to_sysmem(hdr)); - start = *(u32 *)(hdr + KERN_START); - size = ALIGN(*(u32 *)(hdr + KERN_SIZE), desc->blksz); - log_debug("Reading start %lx size %lx\n", start, size); - bflow->size = size; + *hdrp = hdr; + + return 0; +} + +/** + * cros_read_buf() - Read information into a buf and parse it + * + * @bflow: Bootflow to update + * @buf: Buffer to use + * @size: Size of buffer and number of bytes to read thereinto + * @start: Start offset to read from on disk + * @before_base: Number of bytes to read before the bootloader base + * @uuid: UUID string if supported, else NULL + * Return: 0 if OK, -ENOMEM if out of memory, -EIO on read failure + */ +static int cros_read_buf(struct bootflow *bflow, void *buf, ulong size, + loff_t start, ulong before_base, const char *uuid) +{ + struct blk_desc *desc = dev_get_uclass_plat(bflow->blk); + ulong base, setup, cmdline, kern_base; + ulong num_blks; + int ret; - buf = memalign(SZ_1K, size); - if (!buf) - return log_msg_ret("buf", -ENOMEM); num_blks = size >> desc->log2blksz; - log_debug("Reading data, blk=%s, start=%lx, blocks=%lx\n", - bflow->blk->name, (ulong)info.start, num_blks); - ret = blk_read(bflow->blk, (ulong)info.start + 0x80, num_blks, buf); + log_debug("Reading info to %lx, blk=%s, size=%lx, blocks=%lx\n", + (ulong)map_to_sysmem(buf), bflow->blk->name, size, num_blks); + ret = blk_read(bflow->blk, start, num_blks, buf); if (ret != num_blks) - return log_msg_ret("inf", ret); - base = map_to_sysmem(buf); + return log_msg_ret("inf", -EIO); + base = map_to_sysmem(buf) + before_base; - setup = base + start - OFFSET_BASE - SETUP_OFFSET; - cmdline = base + start - OFFSET_BASE - CMDLINE_OFFSET; - kern_base = base + start - OFFSET_BASE + SZ_16K; - log_debug("base %lx setup %lx, cmdline %lx, kern_base %lx\n", base, + setup = base + X86_SETUP_OFFSET; + cmdline = base + CMDLINE_OFFSET; + kern_base = base + X86_KERNEL_OFFSET; + log_debug("base %lx setup %lx cmdline %lx kern_base %lx\n", base, setup, cmdline, kern_base); #ifdef CONFIG_X86 @@ -151,35 +225,211 @@ static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow) if (!bflow->os_name) return log_msg_ret("os", -ENOMEM); -#if CONFIG_IS_ENABLED(PARTITION_UUIDS) - uuid = info.uuid; -#endif ret = copy_cmdline(map_sysmem(cmdline, 0), uuid, &bflow->cmdline); if (ret) return log_msg_ret("cmd", ret); + bflow->x86_setup = map_sysmem(setup, 0); + + return 0; +} + +/** + * cros_read_info() - Read information and fill out the bootflow + * + * @bflow: Bootflow to update + * @uuid: UUID string if supported, else NULL + * @preamble: Kernel preamble information + * Return: 0 if OK, -ENOMEM if out of memory, -EIO on read failure + */ +static int cros_read_info(struct bootflow *bflow, const char *uuid, + const struct vb2_kernel_preamble *preamble) +{ + struct cros_priv *priv = bflow->bootmeth_priv; + struct udevice *blk = bflow->blk; + struct blk_desc *desc = dev_get_uclass_plat(blk); + ulong offset, size, before_base; + void *buf; + int ret; + + log_debug("Kernel preamble at %lx, version major %x, minor %x\n", + (ulong)map_to_sysmem(preamble), + preamble->header_version_major, + preamble->header_version_minor); + + log_debug(" - load_address %lx, bl_addr %lx, bl_size %lx\n", + (ulong)preamble->body_load_address, + (ulong)preamble->bootloader_address, + (ulong)preamble->bootloader_size); + + priv->body_size = preamble->body_signature.data_size; + priv->body_load_address = preamble->body_load_address; + priv->bootloader_address = preamble->bootloader_address; + priv->bootloader_size = preamble->bootloader_size; + log_debug("Kernel body at %lx size %lx\n", priv->body_offset, + priv->body_size); + + /* Work out how many bytes to read before the bootloader base */ + before_base = -CMDLINE_OFFSET; + + /* Read the cmdline through to the end of the bootloader */ + size = priv->bootloader_size + before_base; + offset = priv->body_offset + + (priv->bootloader_address - priv->body_load_address) + + CMDLINE_OFFSET; + buf = malloc(size); + if (!buf) + return log_msg_ret("buf", -ENOMEM); + + ret = cros_read_buf(bflow, buf, size, + priv->part_start + (offset >> desc->log2blksz), + before_base, uuid); + if (ret) { + /* Clear this since the buffer is invalid */ + bflow->x86_setup = NULL; + free(buf); + return log_msg_ret("pro", ret); + } + priv->info_buf = buf; + + return 0; +} + +static int cros_read_kernel(struct bootflow *bflow) +{ + struct blk_desc *desc = dev_get_uclass_plat(bflow->blk); + struct cros_priv *priv = bflow->bootmeth_priv; + ulong base, setup; + ulong num_blks; + void *buf; + int ret; + + bflow->size = priv->body_size; + + buf = memalign(SZ_1K, priv->body_size); + if (!buf) + return log_msg_ret("buf", -ENOMEM); + + /* Check that the header is not smaller than permitted */ + if (priv->body_offset < PROBE_SIZE) + return log_msg_ret("san", EFAULT); + + /* Read kernel body */ + num_blks = priv->body_size >> desc->log2blksz; + log_debug("Reading body to %lx, blk=%s, size=%lx, blocks=%lx\n", + (ulong)map_to_sysmem(buf), bflow->blk->name, priv->body_size, + num_blks); + ret = blk_read(bflow->blk, + priv->part_start + (priv->body_offset >> desc->log2blksz), + num_blks, buf); + if (ret != num_blks) + return log_msg_ret("inf", -EIO); + base = map_to_sysmem(buf) + priv->bootloader_address - + priv->body_load_address; + setup = base + X86_SETUP_OFFSET; - bflow->state = BOOTFLOWST_READY; bflow->buf = buf; bflow->x86_setup = map_sysmem(setup, 0); return 0; } +static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow) +{ + const struct vb2_kernel_preamble *preamble; + struct disk_partition info; + struct vb2_keyblock *hdr; + const char *uuid = NULL; + struct cros_priv *priv; + int part, ret; + + log_debug("starting, part=%d\n", bflow->part); + + /* We consider the whole disk, not any one partition */ + if (bflow->part) + return log_msg_ret("max", -ENOENT); + + /* Check partition 2 then 4 */ + part = 2; + ret = scan_part(bflow->blk, part, &info, &hdr); + if (ret) { + part = 4; + ret = scan_part(bflow->blk, part, &info, &hdr); + if (ret) + return log_msg_ret("scan", ret); + } + bflow->part = part; + + priv = malloc(sizeof(struct cros_priv)); + if (!priv) { + free(hdr); + return log_msg_ret("buf", -ENOMEM); + } + bflow->bootmeth_priv = priv; + + log_info("Selected partition %d, header at %lx\n", bflow->part, + (ulong)map_to_sysmem(hdr)); + + /* Grab a few things from the preamble */ + preamble = (void *)hdr + hdr->keyblock_size; + priv->body_offset = hdr->keyblock_size + preamble->preamble_size; + priv->part_start = info.start; + + /* Now read everything we can learn about kernel */ +#if CONFIG_IS_ENABLED(PARTITION_UUIDS) + uuid = info.uuid; +#endif + ret = cros_read_info(bflow, uuid, preamble); + preamble = NULL; + free(hdr); + if (ret) + return log_msg_ret("inf", ret); + bflow->size = priv->body_size; + bflow->state = BOOTFLOWST_READY; + + return 0; +} + static int cros_read_file(struct udevice *dev, struct bootflow *bflow, const char *file_path, ulong addr, ulong *sizep) { return -ENOSYS; } +#if CONFIG_IS_ENABLED(BOOSTD_FULL) +static int cros_read_all(struct udevice *dev, struct bootflow *bflow) +{ + int ret; + + if (bflow->buf) + return log_msg_ret("ld", -EALREADY); + ret = cros_read_kernel(bflow); + if (ret) + return log_msg_ret("rd", ret); + + return 0; +} +#endif /* BOOSTD_FULL */ + static int cros_boot(struct udevice *dev, struct bootflow *bflow) { -#ifdef CONFIG_X86 - zboot_start(map_to_sysmem(bflow->buf), bflow->size, 0, 0, - map_to_sysmem(bflow->x86_setup), - bflow->cmdline); -#endif + int ret; + + if (!bflow->buf) { + ret = cros_read_kernel(bflow); + if (ret) + return log_msg_ret("rd", ret); + } + + if (IS_ENABLED(CONFIG_X86)) { + ret = zboot_start(map_to_sysmem(bflow->buf), bflow->size, 0, 0, + map_to_sysmem(bflow->x86_setup), + bflow->cmdline); + } else { + ret = bootm_boot_start(map_to_sysmem(bflow->buf), + bflow->cmdline); + } - return log_msg_ret("go", -EFAULT); + return log_msg_ret("go", ret); } static int cros_bootmeth_bind(struct udevice *dev) @@ -196,6 +446,9 @@ static struct bootmeth_ops cros_bootmeth_ops = { .read_bootflow = cros_read_bootflow, .read_file = cros_read_file, .boot = cros_boot, +#if CONFIG_IS_ENABLED(BOOSTD_FULL) + .read_all = cros_read_all, +#endif /* BOOSTD_FULL */ }; static const struct udevice_id cros_bootmeth_ids[] = { diff --git a/boot/bootmeth_cros.h b/boot/bootmeth_cros.h new file mode 100644 index 0000000..8e30385 --- /dev/null +++ b/boot/bootmeth_cros.h @@ -0,0 +1,197 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Structures used by the ChromiumOS bootmeth + * + * See docs at: + * https://www.chromium.org/chromium-os/chromiumos-design-docs/verified-boot-data-structures/ + * + * Original code at: + * https://chromium.googlesource.com/chromiumos/platform/vboot_reference/+/refs/heads/main/firmware/2lib/include/2struct.h + * + * Code taken from vboot_reference commit 5b8596ce file 2struct.h + * + * Copyright 2023 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#ifndef __BOOTMETH_CROS_H +#define __BOOTMETH_CROS_H + +/* Signature data (a secure hash, possibly signed) */ +struct vb2_signature { + /* Offset of signature data from start of this struct */ + uint32_t sig_offset; + uint32_t reserved0; + + /* Size of signature data in bytes */ + uint32_t sig_size; + uint32_t reserved1; + + /* Size of the data block which was signed in bytes */ + uint32_t data_size; + uint32_t reserved2; +} __attribute__((packed)); + +#define EXPECTED_VB2_SIGNATURE_SIZE 24 + +/* Packed public key data */ +struct vb2_packed_key { + /* Offset of key data from start of this struct */ + uint32_t key_offset; + uint32_t reserved0; + + /* Size of key data in bytes (NOT strength of key in bits) */ + uint32_t key_size; + uint32_t reserved1; + + /* Signature algorithm used by the key (enum vb2_crypto_algorithm) */ + uint32_t algorithm; + uint32_t reserved2; + + /* Key version */ + uint32_t key_version; + uint32_t reserved3; + + /* TODO: when redoing this struct, add a text description of the key */ +} __attribute__((packed)); + +#define EXPECTED_VB2_PACKED_KEY_SIZE 32 + +#define VB2_KEYBLOCK_MAGIC "CHROMEOS" +#define VB2_KEYBLOCK_MAGIC_SIZE 8 + +/* + * Keyblock, containing the public key used to sign some other chunk of data. + * + * This should be followed by: + * 1) The data_key key data, pointed to by data_key.key_offset. + * 2) The checksum data for (vb2_keyblock + data_key data), pointed to + * by keyblock_checksum.sig_offset. + * 3) The signature data for (vb2_keyblock + data_key data), pointed to + * by keyblock_signature.sig_offset. + */ +struct vb2_keyblock { + /* Magic number */ + uint8_t magic[VB2_KEYBLOCK_MAGIC_SIZE]; + + /* Version of this header format */ + uint32_t header_version_major; + uint32_t header_version_minor; + + /* + * Length of this entire keyblock, including keys, signatures, and + * padding, in bytes + */ + uint32_t keyblock_size; + uint32_t reserved0; + + /* + * Signature for this keyblock (header + data pointed to by data_key) + * For use with signed data keys + */ + struct vb2_signature keyblock_signature; + + /* + * SHA-512 hash for this keyblock (header + data pointed to by + * data_key) For use with unsigned data keys. + * + * Only supported for kernel keyblocks, not firmware keyblocks. + */ + struct vb2_signature keyblock_hash; + + /* Flags for key (VB2_KEYBLOCK_FLAG_*) */ + uint32_t keyblock_flags; + uint32_t reserved1; + + /* Key to verify the chunk of data */ + struct vb2_packed_key data_key; +} __attribute__((packed)); + +#define EXPECTED_VB2_KEYBLOCK_SIZE 112 + +/* + * Preamble block for kernel, version 2.2 + * + * This should be followed by: + * 1) The signature data for the kernel body, pointed to by + * body_signature.sig_offset. + * 2) The signature data for (vb2_kernel_preamble + body signature data), + * pointed to by preamble_signature.sig_offset. + * 3) The 16-bit vmlinuz header, which is used for reconstruction of + * vmlinuz image. + */ +struct vb2_kernel_preamble { + /* + * Size of this preamble, including keys, signatures, vmlinuz header, + * and padding, in bytes + */ + uint32_t preamble_size; + uint32_t reserved0; + + /* Signature for this preamble (header + body signature) */ + struct vb2_signature preamble_signature; + + /* Version of this header format */ + uint32_t header_version_major; + uint32_t header_version_minor; + + /* Kernel version */ + uint32_t kernel_version; + uint32_t reserved1; + + /* Load address for kernel body */ + uint64_t body_load_address; + /* TODO (vboot 2.1): we never used that */ + + /* Address of bootloader, after body is loaded at body_load_address */ + uint64_t bootloader_address; + /* TODO (vboot 2.1): should be a 32-bit offset */ + + /* Size of bootloader in bytes */ + uint32_t bootloader_size; + uint32_t reserved2; + + /* Signature for the kernel body */ + struct vb2_signature body_signature; + + /* + * TODO (vboot 2.1): fields for kernel offset and size. Right now the + * size is implicitly the same as the size of data signed by the body + * signature, and the offset is implicitly at the end of the preamble. + * But that forces us to pad the preamble to 64KB rather than just + * having a tiny preamble and an offset field. + */ + + /* + * Fields added in header version 2.1. You must verify the header + * version before reading these fields! + */ + + /* + * Address of 16-bit header for vmlinuz reassembly. Readers should + * return 0 for header version < 2.1. + */ + uint64_t vmlinuz_header_address; + + /* Size of 16-bit header for vmlinuz in bytes. Readers should return 0 + for header version < 2.1 */ + uint32_t vmlinuz_header_size; + uint32_t reserved3; + + /* + * Fields added in header version 2.2. You must verify the header + * version before reading these fields! + */ + + /* + * Flags; see VB2_KERNEL_PREAMBLE_*. Readers should return 0 for + * header version < 2.2. Flags field is currently defined as: + * [31:2] - Reserved (for future use) + * [1:0] - Kernel image type (0b00 - CrOS, + * 0b01 - bootimg, + * 0b10 - multiboot) + */ + uint32_t flags; +} __attribute__((packed)); + +#endif /* __BOOTMETH_CROS_H */ diff --git a/cmd/bootflow.c b/cmd/bootflow.c index c0aa4f8..3c3abaf 100644 --- a/cmd/bootflow.c +++ b/cmd/bootflow.c @@ -9,6 +9,7 @@ #include <common.h> #include <bootdev.h> #include <bootflow.h> +#include <bootm.h> #include <bootstd.h> #include <command.h> #include <console.h> @@ -303,11 +304,14 @@ static int do_bootflow_info(struct cmd_tbl *cmdtp, int flag, int argc, { struct bootstd_priv *std; struct bootflow *bflow; + bool x86_setup = false; bool dump = false; int ret; - if (argc > 1 && *argv[1] == '-') + if (argc > 1 && *argv[1] == '-') { dump = strchr(argv[1], 'd'); + x86_setup = strchr(argv[1], 's'); + } ret = bootstd_get_priv(&std); if (ret) @@ -319,6 +323,12 @@ static int do_bootflow_info(struct cmd_tbl *cmdtp, int flag, int argc, } bflow = std->cur_bootflow; + if (IS_ENABLED(CONFIG_X86) && x86_setup) { + zimage_dump(bflow->x86_setup, false); + + return 0; + } + printf("Name: %s\n", bflow->name); printf("Device: %s\n", bflow->dev->name); printf("Block dev: %s\n", bflow->blk ? bflow->blk->name : "(none)"); @@ -369,6 +379,35 @@ static int do_bootflow_info(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static int do_bootflow_read(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct bootstd_priv *std; + struct bootflow *bflow; + int ret; + + ret = bootstd_get_priv(&std); + if (ret) + return CMD_RET_FAILURE; + + /* + * Require a current bootflow. Users can use 'bootflow scan -b' to + * automatically scan and boot, if needed. + */ + if (!std->cur_bootflow) { + printf("No bootflow selected\n"); + return CMD_RET_FAILURE; + } + bflow = std->cur_bootflow; + ret = bootflow_read_all(bflow); + if (ret) { + printf("Failed: err=%dE\n", ret); + return CMD_RET_FAILURE; + } + + return 0; +} + static int do_bootflow_boot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -508,8 +547,9 @@ static char bootflow_help_text[] = "scan [-abeGl] [bdev] - scan for valid bootflows (-l list, -a all, -e errors, -b boot, -G no global)\n" "bootflow list [-e] - list scanned bootflows (-e errors)\n" "bootflow select [<num>|<name>] - select a bootflow\n" - "bootflow info [-d] - show info on current bootflow (-d dump bootflow)\n" - "bootflow boot - boot current bootflow (or first available if none selected)\n" + "bootflow info [-ds] - show info on current bootflow (-d dump bootflow)\n" + "bootflow read - read all current-bootflow files\n" + "bootflow boot - boot current bootflow\n" "bootflow menu [-t] - show a menu of available bootflows\n" "bootflow cmdline [set|get|clear|delete|auto] <param> [<value>] - update cmdline"; #else @@ -523,6 +563,7 @@ U_BOOT_CMD_WITH_SUBCMDS(bootflow, "Boot flows", bootflow_help_text, U_BOOT_SUBCMD_MKENT(list, 2, 1, do_bootflow_list), U_BOOT_SUBCMD_MKENT(select, 2, 1, do_bootflow_select), U_BOOT_SUBCMD_MKENT(info, 2, 1, do_bootflow_info), + U_BOOT_SUBCMD_MKENT(read, 1, 1, do_bootflow_read), U_BOOT_SUBCMD_MKENT(boot, 1, 1, do_bootflow_boot), U_BOOT_SUBCMD_MKENT(menu, 2, 1, do_bootflow_menu), U_BOOT_SUBCMD_MKENT(cmdline, 4, 1, do_bootflow_cmdline), diff --git a/doc/usage/cmd/bootflow.rst b/doc/usage/cmd/bootflow.rst index a8af1f8..ead493d 100644 --- a/doc/usage/cmd/bootflow.rst +++ b/doc/usage/cmd/bootflow.rst @@ -11,7 +11,8 @@ Synopis bootflow scan [-abelGH] [bootdev] bootflow list [-e] bootflow select [<num|name>] - bootflow info [-d] + bootflow info [-ds] + bootflow read bootflow boot bootflow cmdline [set|get|clear|delete|auto] <param> [<value>] @@ -191,11 +192,29 @@ Error Use the `-d` flag to dump out the contents of the bootfile file. +The `-s` flag shows any x86 setup block, instead of the above. + + +bootflow read +~~~~~~~~~~~~~ + +This reads any files related to the bootflow. Some bootflows with large files +avoid doing this when the bootflow is scanned, since it uses a lot of memory +and takes extra time. The files are then automatically read when `bootflow boot` +is used. + +This command reads these files immediately. Typically this fills in the bootflow +`buf` property, which can be used to examine the bootflow. + +Note that reading the files does not result in any extra parsing, nor loading of +images in the files. This is purely used to read in the data ready for +booting, or examination. + bootflow boot ~~~~~~~~~~~~~ -This boots the current bootflow. +This boots the current bootflow, reading any required files first. bootflow cmdline @@ -522,6 +541,122 @@ the cmdline is word-wrapped here and some parts of the command line are elided:: [ 0.000000] Command line: loglevel=7 ... usb-storage.quirks=13fe:6500:u earlycon=uart8250,mmio32,0xfe03e000,115200n8 [ 0.000000] x86/split lock detection: warning about user-space split_locks +This shows looking at x86 setup information:: + + => bootfl sel 0 + => bootfl i -s + Setup located at 77b56010: + + ACPI RSDP addr : 0 + E820: 2 entries + Addr Size Type + 0 1000 RAM + fffff000 1000 Reserved + Setup sectors : 1e + Root flags : 1 + Sys size : 63420 + RAM size : 0 + Video mode : ffff + Root dev : 0 + Boot flag : 0 + Jump : 66eb + Header : 53726448 + Kernel V2 + Version : 20d + Real mode switch : 0 + Start sys seg : 1000 + Kernel version : 38cc + @00003acc: + Type of loader : ff + unknown + Load flags : 1 + : loaded-high + Setup move size : 8000 + Code32 start : 100000 + Ramdisk image : 0 + Ramdisk size : 0 + Bootsect kludge : 0 + Heap end ptr : 5160 + Ext loader ver : 0 + Ext loader type : 0 + Command line ptr : 735000 + Initrd addr max : 7fffffff + Kernel alignment : 200000 + Relocatable kernel : 1 + Min alignment : 15 + : 200000 + Xload flags : 3 + : 64-bit-entry can-load-above-4gb + Cmdline size : 7ff + Hardware subarch : 0 + HW subarch data : 0 + Payload offset : 26e + Payload length : 612045 + Setup data : 0 + Pref address : 1000000 + Init size : 1383000 + Handover offset : 0 + +This shows reading a bootflow to examine the kernel:: + + => bootfl i 0 + Name: + Device: emmc@1c,0.bootdev + Block dev: emmc@1c,0.blk + Method: cros + State: ready + Partition: 2 + Subdir: (none) + Filename: <NULL> + Buffer: 0 + Size: 63ee00 (6548992 bytes) + OS: ChromeOS + Cmdline: console= loglevel=7 init=/sbin/init cros_secure oops=panic panic=-1 root=PARTUUID=35c775e7-3735-d745-93e5-d9e0238f7ed0/PARTNROFF=1 rootwait rw dm_verity.error_behavior=3 dm_verity.max_bios=-1 dm_verity.dev_wait=0 dm="1 vroot none rw 1,0 3788800 verity payload=ROOT_DEV hashtree=HASH_DEV hashstart=3788800 alg=sha1 root_hexdigest=55052b629d3ac889f25a9583ea12cdcd3ea15ff8 salt=a2d4d9e574069f4fed5e3961b99054b7a4905414b60a25d89974a7334021165c" noinitrd vt.global_cursor_default=0 kern_guid=35c775e7-3735-d745-93e5-d9e0238f7ed0 add_efi_memmap boot=local noresume noswap i915.modeset=1 tpm_tis.force=1 tpm_tis.interrupts=0 nmi_watchdog=panic,lapic disablevmx=off + X86 setup: 77b56010 + Logo: (none) + FDT: <NULL> + Error: 0 + +Note that `Buffer` is 0 so it has not be read yet. Using `bootflow read`:: + + => bootfl read + => bootfl info + Name: + Device: emmc@1c,0.bootdev + Block dev: emmc@1c,0.blk + Method: cros + State: ready + Partition: 2 + Subdir: (none) + Filename: <NULL> + Buffer: 77b7e400 + Size: 63ee00 (6548992 bytes) + OS: ChromeOS + Cmdline: console= loglevel=7 init=/sbin/init cros_secure oops=panic panic=-1 root=PARTUUID=35c775e7-3735-d745-93e5-d9e0238f7ed0/PARTNROFF=1 rootwait rw dm_verity.error_behavior=3 dm_verity.max_bios=-1 dm_verity.dev_wait=0 dm="1 vroot none rw 1,0 3788800 verity payload=ROOT_DEV hashtree=HASH_DEV hashstart=3788800 alg=sha1 root_hexdigest=55052b629d3ac889f25a9583ea12cdcd3ea15ff8 salt=a2d4d9e574069f4fed5e3961b99054b7a4905414b60a25d89974a7334021165c" noinitrd vt.global_cursor_default=0 kern_guid=35c775e7-3735-d745-93e5-d9e0238f7ed0 add_efi_memmap boot=local noresume noswap i915.modeset=1 tpm_tis.force=1 tpm_tis.interrupts=0 nmi_watchdog=panic,lapic disablevmx=off + X86 setup: 781b4400 + Logo: (none) + FDT: <NULL> + Error: 0 + +Now the buffer can be accessed:: + + => md 77b7e400 + 77b7e400: 1186f6fc 40000002 b8fa0c75 00000018 .......@u....... + 77b7e410: c08ed88e a68dd08e 000001e8 000000e8 ................ + 77b7e420: ed815d00 00000021 62c280b8 89e80100 .]..!......b.... + 77b7e430: 22f7e8c4 c0850061 22ec850f eb890061 ..."a......"a... + 77b7e440: 0230868b 01480000 21d0f7c3 00fb81c3 ..0...H....!.... + 77b7e450: 7d010000 0000bb05 c3810100 00d4f000 ...}............ + 77b7e460: 8130858d 85890061 00618132 3095010f ..0.a...2.a....0 + 77b7e470: 0f006181 c883e020 e0220f20 e000bb8d .a.. ... ."..... + 77b7e480: c0310062 001800b9 8dabf300 62e000bb b.1............b + 77b7e490: 07878d00 89000010 00bb8d07 8d0062f0 .............b.. + 77b7e4a0: 00100787 0004b900 07890000 00100005 ................ + 77b7e4b0: 08c78300 8df37549 630000bb 0183b800 ....Iu.....c.... + 77b7e4c0: 00b90000 89000008 00000507 c7830020 ............ ... + 77b7e4d0: f3754908 e000838d 220f0062 0080b9d8 .Iu.....b..".... + 77b7e4e0: 320fc000 08e8ba0f c031300f b8d0000f ...2.....01..... + 77b7e4f0: 00000020 6ad8000f 00858d10 50000002 ......j.......P Return value diff --git a/include/bootflow.h b/include/bootflow.h index 4152577..44d3741 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -83,6 +83,7 @@ enum bootflow_flags_t { * @flags: Flags for the bootflow (see enum bootflow_flags_t) * @cmdline: OS command line, or NULL if not known (allocated) * @x86_setup: Pointer to x86 setup block inside @buf, NULL if not present + * @bootmeth_priv: Private data for the bootmeth */ struct bootflow { struct list_head bm_node; @@ -107,7 +108,8 @@ struct bootflow { ulong fdt_addr; int flags; char *cmdline; - char *x86_setup; + void *x86_setup; + void *bootmeth_priv; }; /** @@ -351,6 +353,17 @@ void bootflow_free(struct bootflow *bflow); int bootflow_boot(struct bootflow *bflow); /** + * bootflow_read_all() - Read all bootflow files + * + * Some bootmeths delay reading of large files until booting is requested. This + * causes those files to be read. + * + * @bflow: Bootflow to read + * Return: result of trying to read + */ +int bootflow_read_all(struct bootflow *bflow); + +/** * bootflow_run_boot() - Try to boot a bootflow * * @iter: Current iteration (or NULL if none). Used to disable a bootmeth if the diff --git a/include/bootm.h b/include/bootm.h index 044a479..c3c7336 100644 --- a/include/bootm.h +++ b/include/bootm.h @@ -9,6 +9,7 @@ #include <image.h> +struct boot_params; struct cmd_tbl; #define BOOTM_ERR_RESET (-1) @@ -124,4 +125,50 @@ int bootm_process_cmdline(char *buf, int maxlen, int flags); */ int bootm_process_cmdline_env(int flags); +/** + * zboot_start() - Boot a zimage + * + * Boot a zimage, given the component parts + * + * @addr: Address where the bzImage is moved before booting, either + * BZIMAGE_LOAD_ADDR or ZIMAGE_LOAD_ADDR + * @base: Pointer to the boot parameters, typically at address + * DEFAULT_SETUP_BASE + * @initrd: Address of the initial ramdisk, or 0 if none + * @initrd_size: Size of the initial ramdisk, or 0 if none + * @cmdline: Command line to use for booting + * Return: -EFAULT on error (normally it does not return) + */ +int zboot_start(ulong addr, ulong size, ulong initrd, ulong initrd_size, + ulong base, char *cmdline); + +/* + * zimage_get_kernel_version() - Get the version string from a kernel + * + * @params: boot_params pointer + * @kernel_base: base address of kernel + * Return: Kernel version as a NUL-terminated string + */ +const char *zimage_get_kernel_version(struct boot_params *params, + void *kernel_base); + +/** + * zimage_dump() - Dump the metadata of a zimage + * + * This shows all available information in a zimage that has been loaded. + * + * @base_ptr: Pointer to the boot parameters, typically at address + * DEFAULT_SETUP_BASE + * @show_cmdline: true to show the full command line + */ +void zimage_dump(struct boot_params *base_ptr, bool show_cmdline); + +/* + * bootm_boot_start() - Boot an image at the given address + * + * @addr: Image address + * @cmdline: Command line to set + */ +int bootm_boot_start(ulong addr, const char *cmdline); + #endif diff --git a/include/bootmeth.h b/include/bootmeth.h index 7cb7da3..d3d8d60 100644 --- a/include/bootmeth.h +++ b/include/bootmeth.h @@ -119,7 +119,16 @@ struct bootmeth_ops { */ int (*read_file)(struct udevice *dev, struct bootflow *bflow, const char *file_path, ulong addr, ulong *sizep); - +#if CONFIG_IS_ENABLED(BOOTSTD_FULL) + /** + * readall() - read all files for a bootflow + * + * @dev: Bootmethod device to boot + * @bflow: Bootflow to read + * Return: 0 if OK, -EIO on I/O error, other -ve on other error + */ + int (*read_all)(struct udevice *dev, struct bootflow *bflow); +#endif /* BOOTSTD_FULL */ /** * boot() - boot a bootflow * @@ -224,6 +233,20 @@ int bootmeth_read_file(struct udevice *dev, struct bootflow *bflow, const char *file_path, ulong addr, ulong *sizep); /** + * bootmeth_read_all() - read all bootflow files + * + * Some bootmeths delay reading of large files until booting is requested. This + * causes those files to be read. + * + * @dev: Bootmethod device to use + * @bflow: Bootflow to read + * Return: does not return on success, since it should boot the + * Operating Systemn. Returns -EFAULT if that fails, other -ve on + * other error + */ +int bootmeth_read_all(struct udevice *dev, struct bootflow *bflow); + +/** * bootmeth_boot() - boot a bootflow * * @dev: Bootmethod device to boot |