]> Git Repo - u-boot.git/blobdiff - boot/bootmeth_cros.c
Revert "Merge patch series "arm: dts: am62-beagleplay: Fix Beagleplay Ethernet""
[u-boot.git] / boot / bootmeth_cros.c
index 05284713f67b4764592c90d7dabab1125a95b85c..f015f2e1c75faad5862ef875b54b0d869aa21feb 100644 (file)
 #include <blk.h>
 #include <bootdev.h>
 #include <bootflow.h>
+#include <bootm.h>
 #include <bootmeth.h>
 #include <display_options.h>
 #include <dm.h>
+#include <efi.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"
 
+static const efi_guid_t cros_kern_type = PARTITION_CROS_KERNEL;
+
+/*
+ * Layout of the ChromeOS kernel
+ *
+ * Partitions 2 and 4 contain kernels with type GUID_CROS_KERNEL
+ *
+ * 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 {
        PROBE_SIZE      = SZ_4K,        /* initial bytes read from partition */
 
@@ -32,6 +62,31 @@ enum {
        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)
 {
        /* This only works on block and network devices */
@@ -93,13 +148,25 @@ static int scan_part(struct udevice *blk, int partnum,
 {
        struct blk_desc *desc = dev_get_uclass_plat(blk);
        struct vb2_keyblock *hdr;
+       struct uuid type;
        ulong num_blks;
        int ret;
 
+       if (!partnum)
+               return log_msg_ret("efi", -ENOENT);
+
        ret = part_get_info(desc, partnum, info);
        if (ret)
                return log_msg_ret("part", ret);
 
+       /* Check for kernel partition type */
+       log_debug("part %x: type=%s\n", partnum, info->type_guid);
+       if (uuid_str_to_bin(info->type_guid, (u8 *)&type, UUID_STR_FORMAT_GUID))
+               return log_msg_ret("typ", -EINVAL);
+
+       if (memcmp(&cros_kern_type, &type, sizeof(type)))
+               return log_msg_ret("typ", -ENOEXEC);
+
        /* Make a buffer for the header information */
        num_blks = PROBE_SIZE >> desc->log2blksz;
        log_debug("Reading header, blk=%s, start=%lx, blocks=%lx\n",
@@ -115,6 +182,7 @@ static int scan_part(struct udevice *blk, int partnum,
 
        if (memcmp(VB2_KEYBLOCK_MAGIC, hdr->magic, VB2_KEYBLOCK_MAGIC_SIZE)) {
                free(hdr);
+               log_debug("no magic\n");
                return -ENOENT;
        }
 
@@ -123,38 +191,82 @@ static int scan_part(struct udevice *blk, int partnum,
        return 0;
 }
 
-static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow)
+/**
+ * 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, num_blks, kern_base;
-       const struct vb2_kernel_preamble *preamble;
-       ulong body_offset, body_size;
-       struct disk_partition info;
-       const char *uuid = NULL;
-       struct vb2_keyblock *hdr;
-       int part, ret;
-       void *buf;
+       ulong base, setup, cmdline, kern_base;
+       ulong num_blks;
+       int ret;
+
+       num_blks = size >> desc->log2blksz;
+       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", -EIO);
+       base = map_to_sysmem(buf) + before_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);
 
-       log_debug("starting, part=%d\n", bflow->part);
+#ifdef CONFIG_X86
+       const char *version;
 
-       /* We consider the whole disk, not any one partition */
-       if (bflow->part)
-               return log_msg_ret("max", -ENOENT);
+       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);
 
-       /* 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;
+       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_info("Selected parition %d, header at %lx\n", bflow->part,
-                (ulong)map_to_sysmem(hdr));
-       preamble = (void *)hdr + hdr->keyblock_size;
        log_debug("Kernel preamble at %lx, version major %x, minor %x\n",
                  (ulong)map_to_sysmem(preamble),
                  preamble->header_version_major,
@@ -165,65 +277,125 @@ static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow)
                  (ulong)preamble->bootloader_address,
                  (ulong)preamble->bootloader_size);
 
-       body_offset = hdr->keyblock_size + preamble->preamble_size;
-       body_size = preamble->body_signature.data_size;
-       log_debug("Kernel body at %lx size %lx\n", body_offset, body_size);
-       bflow->size = body_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);
 
-       buf = memalign(SZ_1K, body_size);
+       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 (body_offset < PROBE_SIZE)
+       if (priv->body_offset < PROBE_SIZE)
                return log_msg_ret("san", EFAULT);
 
        /* Read kernel body */
-       num_blks = body_size >> desc->log2blksz;
+       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, body_size,
+                 (ulong)map_to_sysmem(buf), bflow->blk->name, priv->body_size,
                  num_blks);
        ret = blk_read(bflow->blk,
-                      info.start + (body_offset >> desc->log2blksz),
+                      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) + preamble->bootloader_address -
-               preamble->body_load_address;
-
+       base = map_to_sysmem(buf) + priv->bootloader_address -
+               priv->body_load_address;
        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
-       const char *version;
+       bflow->buf = buf;
+       bflow->x86_setup = map_sysmem(setup, 0);
 
-       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);
+       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 ret;
+
+       log_debug("starting, part=%x\n", bflow->part);
+
+       /* Check for kernel partitions */
+       ret = scan_part(bflow->blk, bflow->part, &info, &hdr);
+       if (ret) {
+               log_debug("- scan failed: err=%d\n", ret);
+               return log_msg_ret("scan", ret);
+       }
+
+       priv = malloc(sizeof(struct cros_priv));
+       if (!priv) {
+               free(hdr);
+               return log_msg_ret("buf", -ENOMEM);
+       }
+       bflow->bootmeth_priv = priv;
+
+       log_debug("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 = copy_cmdline(map_sysmem(cmdline, 0), uuid, &bflow->cmdline);
-       if (ret)
-               return log_msg_ret("cmd", ret);
-
+       ret = cros_read_info(bflow, uuid, preamble);
+       preamble = NULL;
+       free(hdr);
+       if (ret) {
+               free(priv->info_buf);
+               free(priv);
+               return log_msg_ret("inf", ret);
+       }
+       bflow->size = priv->body_size;
        bflow->state = BOOTFLOWST_READY;
-       bflow->buf = buf;
-       bflow->x86_setup = map_sysmem(setup, 0);
 
        return 0;
 }
@@ -234,15 +406,41 @@ static int cros_read_file(struct udevice *dev, struct bootflow *bflow,
        return -ENOSYS;
 }
 
+#if CONFIG_IS_ENABLED(BOOTSTD_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 /* BOOTSTD_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_run(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)
@@ -250,6 +448,7 @@ static int cros_bootmeth_bind(struct udevice *dev)
        struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev);
 
        plat->desc = "ChromiumOS boot";
+       plat->flags = BOOTMETHF_ANY_PART;
 
        return 0;
 }
@@ -259,6 +458,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(BOOTSTD_FULL)
+       .read_all       = cros_read_all,
+#endif /* BOOTSTD_FULL */
 };
 
 static const struct udevice_id cros_bootmeth_ids[] = {
This page took 0.035197 seconds and 4 git commands to generate.