bootstd: Add a simple bootmeth for ChromiumOS

It is possible to boot x86-based ChromeOS machines by parsing a table and
locating the kernel and command line. Add a bootmeth for this.

Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
This commit is contained in:
Simon Glass 2023-07-12 09:04:45 -06:00 committed by Bin Meng
parent d0dfbf548d
commit c88d67d021
4 changed files with 225 additions and 0 deletions

View file

@ -463,6 +463,17 @@ config BOOTMETH_GLOBAL
EFI bootmgr, since they take full control over which bootdevs are EFI bootmgr, since they take full control over which bootdevs are
selected to boot. selected to boot.
config BOOTMETH_CROS
bool "Bootdev support for Chromium OS"
depends on X86 || SANDBOX
default y
help
Enables support for booting Chromium OS using bootdevs. This uses the
kernel A slot and obtains the kernel command line from the parameters
provided there.
Note that only x86 devices are supported at present.
config BOOTMETH_EXTLINUX config BOOTMETH_EXTLINUX
bool "Bootdev support for extlinux boot" bool "Bootdev support for extlinux boot"
select PXE_UTILS select PXE_UTILS

View file

@ -27,6 +27,7 @@ obj-$(CONFIG_$(SPL_TPL_)BOOTSTD) += bootstd-uclass.o
obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_EXTLINUX) += bootmeth_extlinux.o obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_EXTLINUX) += bootmeth_extlinux.o
obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_EXTLINUX_PXE) += bootmeth_pxe.o obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_EXTLINUX_PXE) += bootmeth_pxe.o
obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_EFILOADER) += bootmeth_efi.o obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_EFILOADER) += bootmeth_efi.o
obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_CROS) += bootmeth_cros.o
obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_SANDBOX) += bootmeth_sandbox.o obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_SANDBOX) += bootmeth_sandbox.o
obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_SCRIPT) += bootmeth_script.o obj-$(CONFIG_$(SPL_TPL_)BOOTMETH_SCRIPT) += bootmeth_script.o
ifdef CONFIG_$(SPL_TPL_)BOOTSTD_FULL ifdef CONFIG_$(SPL_TPL_)BOOTSTD_FULL

212
boot/bootmeth_cros.c Normal file
View file

@ -0,0 +1,212 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Bootmethod for ChromiumOS
*
* Copyright 2023 Google LLC
* Written by Simon Glass <sjg@chromium.org>
*/
#define LOG_CATEGORY UCLASS_BOOTSTD
#include <common.h>
#include <blk.h>
#include <bootdev.h>
#include <bootflow.h>
#include <bootmeth.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>
enum {
/* Offsets in the kernel-partition header */
KERN_START = 0x4f0,
KERN_SIZE = 0x518,
SETUP_OFFSET = 0x1000, /* bytes before base */
CMDLINE_OFFSET = 0x2000, /* bytes before base */
OFFSET_BASE = 0x100000, /* assumed kernel load-address */
};
static int cros_check(struct udevice *dev, struct bootflow_iter *iter)
{
/* This only works on block and network devices */
if (bootflow_iter_check_blk(iter))
return log_msg_ret("blk", -ENOTSUPP);
return 0;
}
static int copy_cmdline(const char *from, const char *uuid, char **bufp)
{
const int maxlen = 2048;
char buf[maxlen];
char *cmd, *to, *end;
int len;
/* Allow space for cmdline + UUID */
len = strnlen(from, sizeof(buf));
if (len >= maxlen)
return -E2BIG;
log_debug("uuid %d %s\n", uuid ? (int)strlen(uuid) : 0, uuid);
for (to = buf, end = buf + maxlen - UUID_STR_LEN - 1; *from; from++) {
if (to >= end)
return -E2BIG;
if (from[0] == '%' && from[1] == 'U' && uuid &&
strlen(uuid) == UUID_STR_LEN) {
strcpy(to, uuid);
to += UUID_STR_LEN;
from++;
} else {
*to++ = *from;
}
}
*to = '\0';
len = to - buf;
cmd = strdup(buf);
if (!cmd)
return -ENOMEM;
free(*bufp);
*bufp = cmd;
return 0;
}
static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow)
{
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;
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);
if (ret)
return log_msg_ret("part", ret);
/* Make a buffer for the header information */
num_blks = SZ_4K >> 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);
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);
if (memcmp("CHROMEOS", hdr, 8))
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;
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);
if (ret != num_blks)
return log_msg_ret("inf", ret);
base = map_to_sysmem(buf);
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, cmdline, kern_base);
#ifdef CONFIG_X86
const char *version;
version = zimage_get_kernel_version(map_sysmem(setup, 0),
map_sysmem(kern_base, 0));
log_debug("version %s\n", version);
if (version)
bflow->name = strdup(version);
#endif
if (!bflow->name)
bflow->name = strdup("ChromeOS");
if (!bflow->name)
return log_msg_ret("nam", -ENOMEM);
bflow->os_name = strdup("ChromeOS");
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->state = BOOTFLOWST_READY;
bflow->buf = buf;
bflow->x86_setup = map_sysmem(setup, 0);
return 0;
}
static int cros_read_file(struct udevice *dev, struct bootflow *bflow,
const char *file_path, ulong addr, ulong *sizep)
{
return -ENOSYS;
}
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
return log_msg_ret("go", -EFAULT);
}
static int cros_bootmeth_bind(struct udevice *dev)
{
struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
plat->desc = "ChromiumOS boot";
return 0;
}
static struct bootmeth_ops cros_bootmeth_ops = {
.check = cros_check,
.read_bootflow = cros_read_bootflow,
.read_file = cros_read_file,
.boot = cros_boot,
};
static const struct udevice_id cros_bootmeth_ids[] = {
{ .compatible = "u-boot,cros" },
{ }
};
U_BOOT_DRIVER(bootmeth_cros) = {
.name = "bootmeth_cros",
.id = UCLASS_BOOTMETH,
.of_match = cros_bootmeth_ids,
.ops = &cros_bootmeth_ops,
.bind = cros_bootmeth_bind,
};

View file

@ -10,6 +10,7 @@ CONFIG_FIT=y
CONFIG_TIMESTAMP=y CONFIG_TIMESTAMP=y
CONFIG_FIT_SIGNATURE=y CONFIG_FIT_SIGNATURE=y
# CONFIG_BOOTSTD_FULL is not set # CONFIG_BOOTSTD_FULL is not set
# CONFIG_BOOTMETH_CROS is not set
# CONFIG_BOOTMETH_VBE is not set # CONFIG_BOOTMETH_VBE is not set
CONFIG_USE_BOOTCOMMAND=y CONFIG_USE_BOOTCOMMAND=y
CONFIG_BOOTCOMMAND="run distro_bootcmd" CONFIG_BOOTCOMMAND="run distro_bootcmd"