From b254a8359ecdc8ec85cd93e055c6bbeee259df1d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 7 Aug 2024 16:47:24 -0600 Subject: sandbox: Return error code from read/write/seek The existing API for these functions is different from the rest of U-Boot, in that any error code must be obtained from the errno variable on failure. This variable is part of the C library, so accessing it outside of the special 'sandbox' shim-functions is not ideal. Adjust the API to return an error code, to avoid this. Update existing uses to check for any negative value, rather than just -1. Signed-off-by: Simon Glass --- include/os.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/os.h b/include/os.h index 877404a6c13..4371270a1ee 100644 --- a/include/os.h +++ b/include/os.h @@ -29,7 +29,7 @@ int os_printf(const char *format, ...); * @fd: File descriptor as returned by os_open() * @buf: Buffer to place data * @count: Number of bytes to read - * Return: number of bytes read, or -1 on error + * Return: number of bytes read, or -errno on error */ ssize_t os_read(int fd, void *buf, size_t count); @@ -39,7 +39,7 @@ ssize_t os_read(int fd, void *buf, size_t count); * @fd: File descriptor as returned by os_open() * @buf: Buffer containing data to write * @count: Number of bytes to write - * Return: number of bytes written, or -1 on error + * Return: number of bytes written, or -errno on error */ ssize_t os_write(int fd, const void *buf, size_t count); @@ -49,7 +49,7 @@ ssize_t os_write(int fd, const void *buf, size_t count); * @fd: File descriptor as returned by os_open() * @offset: File offset (based on whence) * @whence: Position offset is relative to (see below) - * Return: new file offset + * Return: new file offset, or -errno on error */ off_t os_lseek(int fd, off_t offset, int whence); -- cgit v1.3.1 From 90469da3da0dc6f113dfc582d4db2ac9e9f171ec Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 7 Aug 2024 16:47:27 -0600 Subject: upl: Add support for reading a upl handoff Universal Payload provides a standard way of handing off control between two firmware phases. Add support for reading the handoff information into a structure. Signed-off-by: Simon Glass --- MAINTAINERS | 7 + boot/Kconfig | 19 ++ boot/Makefile | 3 + boot/upl_common.c | 60 +++++ boot/upl_common.h | 24 ++ boot/upl_read.c | 588 ++++++++++++++++++++++++++++++++++++++++++++++ configs/sandbox_defconfig | 1 + include/upl.h | 373 +++++++++++++++++++++++++++++ 8 files changed, 1075 insertions(+) create mode 100644 boot/upl_common.c create mode 100644 boot/upl_common.h create mode 100644 boot/upl_read.c create mode 100644 include/upl.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index ddcb7128db4..04a6f23891c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1709,6 +1709,13 @@ M: Neha Malcom Francis S: Maintained F: drivers/ufs/ +UPL +M: Simon Glass +S: Maintained +T: git https://source.denx.de/u-boot/custodians/u-boot-dm.git +F: boot/upl* +F: include/upl.h + USB M: Marek Vasut S: Maintained diff --git a/boot/Kconfig b/boot/Kconfig index 940389d4882..30106291e6b 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -745,6 +745,25 @@ config BOOTMETH_SCRIPT This provides a way to try out standard boot on an existing boot flow. It is not enabled by default to save space. +config UPL + bool "upl - Universal Payload Specification" + imply UPL_READ + help + Provides support for UPL payloads and handoff information. U-Boot + supports generating and accepting handoff information. The mkimage + tool will eventually support creating payloads. + +if UPL + +config UPL_READ + bool "upl - Support reading a Universal Payload handoff" + help + Provides support for decoding a UPL-format payload into a C structure + which can be used elsewhere in U-Boot. This is just the reading + implementation, useful for trying it out. + +endif # UPL + endif # BOOTSTD config LEGACY_IMAGE_FORMAT diff --git a/boot/Makefile b/boot/Makefile index dff6f99081a..9e4e5a602dc 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -43,6 +43,9 @@ endif obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += fdt_support.o obj-$(CONFIG_$(SPL_TPL_)FDT_SIMPLEFB) += fdt_simplefb.o +obj-$(CONFIG_$(SPL_TPL_)UPL) += upl_common.o +obj-$(CONFIG_$(SPL_TPL_)UPL_READ) += upl_read.o + obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += fdt_region.o obj-$(CONFIG_$(SPL_TPL_)FIT) += image-fit.o diff --git a/boot/upl_common.c b/boot/upl_common.c new file mode 100644 index 00000000000..3924423abd5 --- /dev/null +++ b/boot/upl_common.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * UPL handoff command functions + * + * Copyright 2024 Google LLC + * Written by Simon Glass + */ + +#define LOG_CATEGORY UCLASS_BOOTSTD + +#include +#include + +/* Names of bootmodes */ +const char *const bootmode_names[UPLBM_COUNT] = { + [UPLBM_FULL] = "full", + [UPLBM_MINIMAL] = "minimal", + [UPLBM_FAST] = "fast", + [UPLBM_DIAG] = "diag", + [UPLBM_DEFAULT] = "default", + [UPLBM_S2] = "s2", + [UPLBM_S3] = "s3", + [UPLBM_S4] = "s4", + [UPLBM_S5] = "s5", + [UPLBM_FACTORY] = "factory", + [UPLBM_FLASH] = "flash", + [UPLBM_RECOVERY] = "recovery", +}; + +/* Names of memory usages */ +const char *const usage_names[UPLUS_COUNT] = { + [UPLUS_ACPI_RECLAIM] = "acpi-reclaim", + [UPLUS_ACPI_NVS] = "acpi-nvs", + [UPLUS_BOOT_CODE] = "boot-code", + [UPLUS_BOOT_DATA] = "boot-data", + [UPLUS_RUNTIME_CODE] = "runtime-code", + [UPLUS_RUNTIME_DATA] = "runtime-data", +}; + +/* Names of access types */ +const char *const access_types[UPLUS_COUNT] = { + [UPLAT_MMIO] = "mmio", + [UPLAT_IO] = "io", +}; + +/* Names of graphics formats */ +const char *const graphics_formats[UPLUS_COUNT] = { + [UPLGF_ARGB32] = "a8r8g8b8", + [UPLGF_ABGR32] = "a8b8g8r8", + [UPLGF_ABGR64] = "a16b16g16r16", +}; + +void upl_init(struct upl *upl) +{ + memset(upl, '\0', sizeof(struct upl)); + alist_init_struct(&upl->image, struct upl_image); + alist_init_struct(&upl->mem, struct upl_mem); + alist_init_struct(&upl->memmap, struct upl_memmap); + alist_init_struct(&upl->memres, struct upl_memres); +} diff --git a/boot/upl_common.h b/boot/upl_common.h new file mode 100644 index 00000000000..cc517dc1de9 --- /dev/null +++ b/boot/upl_common.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * UPL handoff command functions + * + * Copyright 2024 Google LLC + * Written by Simon Glass + */ + +#ifndef __UPL_COMMON_H +#define __UPL_COMMON_H + +/* Names of bootmodes */ +extern const char *const bootmode_names[UPLBM_COUNT]; + +/* Names of memory usages */ +extern const char *const usage_names[UPLUS_COUNT]; + +/* Names of access types */ +extern const char *const access_types[UPLUS_COUNT]; + +/* Names of graphics formats */ +extern const char *const graphics_formats[UPLUS_COUNT]; + +#endif /* __UPL_COMMON_H */ diff --git a/boot/upl_read.c b/boot/upl_read.c new file mode 100644 index 00000000000..5063897a132 --- /dev/null +++ b/boot/upl_read.c @@ -0,0 +1,588 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * UPL handoff parsing + * + * Copyright 2024 Google LLC + * Written by Simon Glass + */ + +#define LOG_CATEGORY UCLASS_BOOTSTD + +#include +#include +#include +#include "upl_common.h" + +/** + * read_addr() - Read an address + * + * Reads an address in the correct format, either 32- or 64-bit + * + * @upl: UPL state + * @node: Node to read from + * @prop: Property name to read + * @addr: Place to put the address + * Return: 0 if OK, -ve on error + */ +static int read_addr(const struct upl *upl, ofnode node, const char *prop, + ulong *addrp) +{ + int ret; + + if (upl->addr_cells == 1) { + u32 val; + + ret = ofnode_read_u32(node, prop, &val); + if (!ret) + *addrp = val; + } else { + u64 val; + + ret = ofnode_read_u64(node, prop, &val); + if (!ret) + *addrp = val; + } + + return ret; +} + +/** + * read_size() - Read a size + * + * Reads a size in the correct format, either 32- or 64-bit + * + * @upl: UPL state + * @node: Node to read from + * @prop: Property name to read + * @addr: Place to put the size + * Return: 0 if OK, -ve on error + */ +static int read_size(const struct upl *upl, ofnode node, const char *prop, + ulong *sizep) +{ + int ret; + + if (upl->size_cells == 1) { + u32 val; + + ret = ofnode_read_u32(node, prop, &val); + if (!ret) + *sizep = val; + } else { + u64 val; + + ret = ofnode_read_u64(node, prop, &val); + if (!ret) + *sizep = val; + } + + return ret; +} + +/** + * ofnode_read_bitmask() - Read a bit mask from a string list + * + * @node: Node to read from + * @prop: Property name to read + * @names: Array of names for each bit + * @count: Number of array entries + * @value: Returns resulting bit-mask value on success + * Return: 0 if OK, -EINVAL if a bit number is not defined, -ENOSPC if the + * string is too long for the (internal) buffer, -EINVAL if no such property + */ +static int ofnode_read_bitmask(ofnode node, const char *prop, + const char *const names[], uint count, + uint *valuep) +{ + const char **list; + const char **strp; + uint val; + uint bit; + int ret; + + ret = ofnode_read_string_list(node, prop, &list); + if (ret < 0) + return log_msg_ret("rea", ret); + + val = 0; + for (strp = list; *strp; strp++) { + const char *str = *strp; + bool found = false; + + for (bit = 0; bit < count; bit++) { + if (!strcmp(str, names[bit])) { + found = true; + break; + } + } + if (found) + val |= BIT(bit); + else + log_warning("%s/%s: Invalid value '%s'\n", + ofnode_get_name(node), prop, str); + } + *valuep = val; + + return 0; +} + +/** + * ofnode_read_value() - Read a string value as an int using a lookup + * + * @node: Node to read from + * @prop: Property name to read + * @names: Array of names for each int value + * @count: Number of array entries + * @valuep: Returns int value read + * Return: 0 if OK, -EINVAL if a bit number is not defined, -ENOENT if the + * property does not exist + */ +static int ofnode_read_value(ofnode node, const char *prop, + const char *const names[], uint count, + uint *valuep) +{ + const char *str; + int i; + + str = ofnode_read_string(node, prop); + if (!str) + return log_msg_ret("rd", -ENOENT); + + for (i = 0; i < count; i++) { + if (!strcmp(names[i], str)) { + *valuep = i; + return 0; + } + } + + log_debug("Unnamed value '%s'\n", str); + return log_msg_ret("val", -EINVAL); +} + +static int read_uint(ofnode node, const char *prop, uint *valp) +{ + u32 val; + int ret; + + ret = ofnode_read_u32(node, prop, &val); + if (ret) + return ret; + *valp = val; + + return 0; +} + +/** + * decode_root_props() - Decode root properties from the tree + * + * @upl: UPL state + * @node: Node to decode + * Return 0 if OK, -ve on error + */ +static int decode_root_props(struct upl *upl, ofnode node) +{ + int ret; + + ret = read_uint(node, UPLP_ADDRESS_CELLS, &upl->addr_cells); + if (!ret) + ret = read_uint(node, UPLP_SIZE_CELLS, &upl->size_cells); + if (ret) + return log_msg_ret("cel", ret); + + return 0; +} + +/** + * decode_root_props() - Decode UPL parameters from the tree + * + * @upl: UPL state + * @node: Node to decode + * Return 0 if OK, -ve on error + */ +static int decode_upl_params(struct upl *upl, ofnode options) +{ + ofnode node; + int ret; + + node = ofnode_find_subnode(options, UPLN_UPL_PARAMS); + if (!ofnode_valid(node)) + return log_msg_ret("par", -EINVAL); + log_debug("decoding '%s'\n", ofnode_get_name(node)); + + ret = read_addr(upl, node, UPLP_SMBIOS, &upl->smbios); + if (ret) + return log_msg_ret("smb", ret); + ret = read_addr(upl, node, UPLP_ACPI, &upl->acpi); + if (ret) + return log_msg_ret("acp", ret); + ret = ofnode_read_bitmask(node, UPLP_BOOTMODE, bootmode_names, + UPLBM_COUNT, &upl->bootmode); + if (ret) + return log_msg_ret("boo", ret); + ret = read_uint(node, UPLP_ADDR_WIDTH, &upl->addr_width); + if (ret) + return log_msg_ret("add", ret); + ret = read_uint(node, UPLP_ACPI_NVS_SIZE, &upl->acpi_nvs_size); + if (ret) + return log_msg_ret("nvs", ret); + + return 0; +} + +/** + * decode_upl_images() - Decode /options/upl-image nodes + * + * @node: /options node in which to look for the node + * Return 0 if OK, -ve on error + */ +static int decode_upl_images(struct upl *upl, ofnode options) +{ + ofnode node, images; + int ret; + + images = ofnode_find_subnode(options, UPLN_UPL_IMAGE); + if (!ofnode_valid(images)) + return log_msg_ret("img", -EINVAL); + log_debug("decoding '%s'\n", ofnode_get_name(images)); + + ret = read_addr(upl, images, UPLP_FIT, &upl->fit); + if (!ret) + ret = read_uint(images, UPLP_CONF_OFFSET, &upl->conf_offset); + if (ret) + return log_msg_ret("cnf", ret); + + ofnode_for_each_subnode(node, images) { + struct upl_image img; + + ret = read_addr(upl, node, UPLP_LOAD, &img.load); + if (!ret) + ret = read_size(upl, node, UPLP_SIZE, &img.size); + if (!ret) + ret = read_uint(node, UPLP_OFFSET, &img.offset); + img.description = ofnode_read_string(node, UPLP_DESCRIPTION); + if (!img.description) + return log_msg_ret("sim", ret); + if (!alist_add(&upl->image, img)) + return log_msg_ret("img", -ENOMEM); + } + + return 0; +} + +/** + * decode_addr_size() - Decide a set of addr/size pairs + * + * Each base/size value from the devicetree is written to the region list + * + * @upl: UPL state + * @buf: Bytes to decode + * @size: Number of bytes to decode + * @regions: List of regions to process (struct memregion) + * Returns: number of regions found, if OK, else -ve on error + */ +static int decode_addr_size(const struct upl *upl, const char *buf, int size, + struct alist *regions) +{ + const char *ptr, *end = buf + size; + int i; + + alist_init_struct(regions, struct memregion); + ptr = buf; + for (i = 0; ptr < end; i++) { + struct memregion reg; + + if (upl->addr_cells == 1) + reg.base = fdt32_to_cpu(*(u32 *)ptr); + else + reg.base = fdt64_to_cpu(*(u64 *)ptr); + ptr += upl->addr_cells * sizeof(u32); + + if (upl->size_cells == 1) + reg.size = fdt32_to_cpu(*(u32 *)ptr); + else + reg.size = fdt64_to_cpu(*(u64 *)ptr); + ptr += upl->size_cells * sizeof(u32); + if (ptr > end) + return -ENOSPC; + + if (!alist_add(regions, reg)) + return log_msg_ret("reg", -ENOMEM); + } + + return i; +} + +/** + * node_matches_at() - Check if a node name matches "base@..." + * + * Return: true if the node name matches the base string followed by an @ sign; + * false otherwise + */ +static bool node_matches_at(ofnode node, const char *base) +{ + const char *name = ofnode_get_name(node); + int len = strlen(base); + + return !strncmp(base, name, len) && name[len] == '@'; +} + +/** + * decode_upl_memory_node() - Decode a /memory node from the tree + * + * @upl: UPL state + * @node: Node to decode + * Return 0 if OK, -ve on error + */ +static int decode_upl_memory_node(struct upl *upl, ofnode node) +{ + struct upl_mem mem; + const char *buf; + int size, len; + + buf = ofnode_read_prop(node, UPLP_REG, &size); + if (!buf) { + log_warning("Node '%s': Missing '%s' property\n", + ofnode_get_name(node), UPLP_REG); + return log_msg_ret("reg", -EINVAL); + } + len = decode_addr_size(upl, buf, size, &mem.region); + if (len < 0) + return log_msg_ret("buf", len); + mem.hotpluggable = ofnode_read_bool(node, UPLP_HOTPLUGGABLE); + if (!alist_add(&upl->mem, mem)) + return log_msg_ret("mem", -ENOMEM); + + return 0; +} + +/** + * decode_upl_memmap() - Decode memory-map nodes from the tree + * + * @upl: UPL state + * @root: Parent node containing the /memory-map nodes + * Return 0 if OK, -ve on error + */ +static int decode_upl_memmap(struct upl *upl, ofnode root) +{ + ofnode node; + + ofnode_for_each_subnode(node, root) { + struct upl_memmap memmap; + int size, len, ret; + const char *buf; + + memmap.name = ofnode_get_name(node); + memmap.usage = 0; + + buf = ofnode_read_prop(node, UPLP_REG, &size); + if (!buf) { + log_warning("Node '%s': Missing '%s' property\n", + ofnode_get_name(node), UPLP_REG); + continue; + } + + len = decode_addr_size(upl, buf, size, &memmap.region); + if (len < 0) + return log_msg_ret("buf", len); + ret = ofnode_read_bitmask(node, UPLP_USAGE, usage_names, + UPLUS_COUNT, &memmap.usage); + if (ret && ret != -EINVAL) /* optional property */ + return log_msg_ret("bit", ret); + + if (!alist_add(&upl->memmap, memmap)) + return log_msg_ret("mmp", -ENOMEM); + } + + return 0; +} + +/** + * decode_upl_memres() - Decode reserved-memory nodes from the tree + * + * @upl: UPL state + * @root: Parent node containing the reserved-memory nodes + * Return 0 if OK, -ve on error + */ +static int decode_upl_memres(struct upl *upl, ofnode root) +{ + ofnode node; + + ofnode_for_each_subnode(node, root) { + struct upl_memres memres; + const char *buf; + int size, len; + + log_debug("decoding '%s'\n", ofnode_get_name(node)); + memres.name = ofnode_get_name(node); + + buf = ofnode_read_prop(node, UPLP_REG, &size); + if (!buf) { + log_warning("Node '%s': Missing 'reg' property\n", + ofnode_get_name(node)); + continue; + } + + len = decode_addr_size(upl, buf, size, &memres.region); + if (len < 0) + return log_msg_ret("buf", len); + memres.no_map = ofnode_read_bool(node, UPLP_NO_MAP); + + if (!alist_add(&upl->memres, memres)) + return log_msg_ret("mre", -ENOMEM); + } + + return 0; +} + +/** + * decode_upl_serial() - Decode the serial node + * + * @upl: UPL state + * @root: Parent node contain node + * Return 0 if OK, -ve on error + */ +static int decode_upl_serial(struct upl *upl, ofnode node) +{ + struct upl_serial *ser = &upl->serial; + const char *buf; + int len, size; + int ret; + + ser->compatible = ofnode_read_string(node, UPLP_COMPATIBLE); + if (!ser->compatible) { + log_warning("Node '%s': Missing compatible string\n", + ofnode_get_name(node)); + return log_msg_ret("com", -EINVAL); + } + ret = read_uint(node, UPLP_CLOCK_FREQUENCY, &ser->clock_frequency); + if (!ret) + ret = read_uint(node, UPLP_CURRENT_SPEED, &ser->current_speed); + if (ret) + return log_msg_ret("spe", ret); + + buf = ofnode_read_prop(node, UPLP_REG, &size); + if (!buf) { + log_warning("Node '%s': Missing 'reg' property\n", + ofnode_get_name(node)); + return log_msg_ret("reg", -EINVAL); + } + + len = decode_addr_size(upl, buf, sizeof(buf), &ser->reg); + if (len < 0) + return log_msg_ret("buf", len); + + /* set defaults */ + ser->reg_io_shift = UPLD_REG_IO_SHIFT; + ser->reg_offset = UPLD_REG_OFFSET; + ser->reg_io_width = UPLD_REG_IO_WIDTH; + read_uint(node, UPLP_REG_IO_SHIFT, &ser->reg_io_shift); + read_uint(node, UPLP_REG_OFFSET, &ser->reg_offset); + read_uint(node, UPLP_REG_IO_WIDTH, &ser->reg_io_width); + read_addr(upl, node, UPLP_VIRTUAL_REG, &ser->virtual_reg); + ret = ofnode_read_value(node, UPLP_ACCESS_TYPE, access_types, + ARRAY_SIZE(access_types), &ser->access_type); + if (ret && ret != -ENOENT) + return log_msg_ret("ser", ret); + + return 0; +} + +/** + * decode_upl_graphics() - Decode graphics node + * + * @upl: UPL state + * @root: Node to decode + * Return 0 if OK, -ve on error + */ +static int decode_upl_graphics(struct upl *upl, ofnode node) +{ + struct upl_graphics *gra = &upl->graphics; + const char *buf, *compat; + int len, size; + int ret; + + compat = ofnode_read_string(node, UPLP_COMPATIBLE); + if (!compat) { + log_warning("Node '%s': Missing compatible string\n", + ofnode_get_name(node)); + return log_msg_ret("com", -EINVAL); + } + if (strcmp(UPLC_GRAPHICS, compat)) { + log_warning("Node '%s': Ignoring compatible '%s'\n", + ofnode_get_name(node), compat); + return 0; + } + + buf = ofnode_read_prop(node, UPLP_REG, &size); + if (!buf) { + log_warning("Node '%s': Missing 'reg' property\n", + ofnode_get_name(node)); + return log_msg_ret("reg", -EINVAL); + } + + len = decode_addr_size(upl, buf, sizeof(buf), &gra->reg); + if (len < 0) + return log_msg_ret("buf", len); + + ret = read_uint(node, UPLP_WIDTH, &gra->width); + if (!ret) + ret = read_uint(node, UPLP_HEIGHT, &gra->height); + if (!ret) + ret = read_uint(node, UPLP_STRIDE, &gra->stride); + if (!ret) { + ret = ofnode_read_value(node, UPLP_GRAPHICS_FORMAT, + graphics_formats, + ARRAY_SIZE(graphics_formats), + &gra->format); + } + if (ret) + return log_msg_ret("pro", ret); + + return 0; +} + +int upl_read_handoff(struct upl *upl, oftree tree) +{ + ofnode root, node; + int ret; + + if (!oftree_valid(tree)) + return log_msg_ret("tre", -EINVAL); + + root = oftree_root(tree); + + upl_init(upl); + ret = decode_root_props(upl, root); + if (ret) + return log_msg_ret("roo", ret); + + ofnode_for_each_subnode(node, root) { + const char *name = ofnode_get_name(node); + + log_debug("decoding '%s'\n", name); + if (!strcmp(UPLN_OPTIONS, name)) { + ret = decode_upl_params(upl, node); + if (ret) + return log_msg_ret("opt", ret); + + ret = decode_upl_images(upl, node); + } else if (node_matches_at(node, UPLN_MEMORY)) { + ret = decode_upl_memory_node(upl, node); + } else if (!strcmp(UPLN_MEMORY_MAP, name)) { + ret = decode_upl_memmap(upl, node); + } else if (!strcmp(UPLN_MEMORY_RESERVED, name)) { + ret = decode_upl_memres(upl, node); + } else if (node_matches_at(node, UPLN_SERIAL)) { + ret = decode_upl_serial(upl, node); + } else if (node_matches_at(node, UPLN_GRAPHICS)) { + ret = decode_upl_graphics(upl, node); + } else { + log_debug("Unknown node '%s'\n", name); + ret = 0; + } + if (ret) + return log_msg_ret("err", ret); + } + + return 0; +} diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index dc5fcdbd1c9..484f9e1bf8d 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -16,6 +16,7 @@ CONFIG_FIT_RSASSA_PSS=y CONFIG_FIT_CIPHER=y CONFIG_FIT_VERBOSE=y CONFIG_BOOTMETH_ANDROID=y +CONFIG_UPL=y CONFIG_LEGACY_IMAGE_FORMAT=y CONFIG_MEASURED_BOOT=y CONFIG_BOOTSTAGE=y diff --git a/include/upl.h b/include/upl.h new file mode 100644 index 00000000000..df644c47344 --- /dev/null +++ b/include/upl.h @@ -0,0 +1,373 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * UPL handoff generation + * + * Copyright 2024 Google LLC + * Written by Simon Glass + */ + +#ifndef __UPL_WRITE_H +#define __UPL_WRITE_H + +#ifndef USE_HOSTCC + +#include +#include +#include + +struct unit_test_state; + +#define UPLP_ADDRESS_CELLS "#address-cells" +#define UPLP_SIZE_CELLS "#size-cells" + +#define UPLN_OPTIONS "options" +#define UPLN_UPL_PARAMS "upl-params" +#define UPLP_SMBIOS "smbios" +#define UPLP_ACPI "acpi" +#define UPLP_BOOTMODE "bootmode" +#define UPLP_ADDR_WIDTH "addr-width" +#define UPLP_ACPI_NVS_SIZE "acpi-nvs-size" + +#define UPLPATH_UPL_IMAGE "/options/upl-image" +#define UPLN_UPL_IMAGE "upl-image" +#define UPLN_IMAGE "image" +#define UPLP_FIT "fit" +#define UPLP_CONF_OFFSET "conf-offset" +#define UPLP_LOAD "load" +#define UPLP_SIZE "size" +#define UPLP_OFFSET "offset" +#define UPLP_DESCRIPTION "description" + +#define UPLN_MEMORY "memory" +#define UPLP_HOTPLUGGABLE "hotpluggable" + +#define UPLPATH_MEMORY_MAP "/memory-map" +#define UPLN_MEMORY_MAP "memory-map" +#define UPLP_USAGE "usage" + +#define UPLN_MEMORY_RESERVED "reserved-memory" +#define UPLPATH_MEMORY_RESERVED "/reserved-memory" +#define UPLP_NO_MAP "no-map" + +#define UPLN_SERIAL "serial" +#define UPLP_REG "reg" +#define UPLP_COMPATIBLE "compatible" +#define UPLP_CLOCK_FREQUENCY "clock-frequency" +#define UPLP_CURRENT_SPEED "current-speed" +#define UPLP_REG_IO_SHIFT "reg-io-shift" +#define UPLP_REG_OFFSET "reg-offset" +#define UPLP_REG_IO_WIDTH "reg-io-width" +#define UPLP_VIRTUAL_REG "virtual-reg" +#define UPLP_ACCESS_TYPE "access-type" + +#define UPLN_GRAPHICS "framebuffer" +#define UPLC_GRAPHICS "simple-framebuffer" +#define UPLP_WIDTH "width" +#define UPLP_HEIGHT "height" +#define UPLP_STRIDE "stride" +#define UPLP_GRAPHICS_FORMAT "format" + +/** + * enum upl_boot_mode - Encodes the boot mode + * + * Each is a bit number from the boot_mode mask + */ +enum upl_boot_mode { + UPLBM_FULL, + UPLBM_MINIMAL, + UPLBM_FAST, + UPLBM_DIAG, + UPLBM_DEFAULT, + UPLBM_S2, + UPLBM_S3, + UPLBM_S4, + UPLBM_S5, + UPLBM_FACTORY, + UPLBM_FLASH, + UPLBM_RECOVERY, + + UPLBM_COUNT, +}; + +/** + * struct upl_image - UPL image informaiton + * + * @load: Address image was loaded to + * @size: Size of image in bytes + * @offset: Offset of the image in the FIT (0=none) + * @desc: Description of the iamge (taken from the FIT) + */ +struct upl_image { + ulong load; + ulong size; + uint offset; + const char *description; +}; + +/** + * struct memregion - Information about a region of memory + * + * @base: Base address + * @size: Size in bytes + */ +struct memregion { + ulong base; + ulong size; +}; + +/** + * struct upl_mem - Information about physical-memory layout + * + * TODO: Figure out initial-mapped-area + * + * @region: Memory region list (struct memregion) + * @hotpluggable: true if hotpluggable + */ +struct upl_mem { + struct alist region; + bool hotpluggable; +}; + +/** + * enum upl_usage - Encodes the usage + * + * Each is a bit number from the usage mask + */ +enum upl_usage { + UPLUS_ACPI_RECLAIM, + UPLUS_ACPI_NVS, + UPLUS_BOOT_CODE, + UPLUS_BOOT_DATA, + UPLUS_RUNTIME_CODE, + UPLUS_RUNTIME_DATA, + UPLUS_COUNT +}; + +/** + * struct upl_memmap - Information about logical-memory layout + * + * @name: Node name to use + * @region: Memory region list (struct memregion) + * @usage: Memory-usage mask (enum upl_usage) + */ +struct upl_memmap { + const char *name; + struct alist region; + uint usage; +}; + +/** + * struct upl_memres - Reserved memory + * + * @name: Node name to use + * @region: Reserved memory region list (struct memregion) + * @no_map: true to indicate that a virtual mapping must not be created + */ +struct upl_memres { + const char *name; + struct alist region; + bool no_map; +}; + +enum upl_serial_access_type { + UPLSAT_MMIO, + UPLSAT_IO, +}; + +/* serial defaults */ +enum { + UPLD_REG_IO_SHIFT = 0, + UPLD_REG_OFFSET = 0, + UPLD_REG_IO_WIDTH = 1, +}; + +/** + * enum upl_access_type - Access types + * + * @UPLAT_MMIO: Memory-mapped I/O + * @UPLAT_IO: Separate I/O + */ +enum upl_access_type { + UPLAT_MMIO, + UPLAT_IO, +}; + +/** + * struct upl_serial - Serial console + * + * @compatible: Compatible string (NULL if there is no serial console) + * @clock_frequency: Input clock frequency of UART + * @current_speed: Current baud rate of UART + * @reg: List of base address and size of registers (struct memregion) + * @reg_shift_log2: log2 of distance between each register + * @reg_offset: Offset of registers from the base address + * @reg_width: Register width in bytes + * @virtual_reg: Virtual register access (0 for none) + * @access_type: Register access type to use + */ +struct upl_serial { + const char *compatible; + uint clock_frequency; + uint current_speed; + struct alist reg; + uint reg_io_shift; + uint reg_offset; + uint reg_io_width; + ulong virtual_reg; + enum upl_serial_access_type access_type; +}; + +/** + * enum upl_graphics_format - Graphics formats + * + * @UPLGF_ARGB32: 32bpp format using 0xaarrggbb + * @UPLGF_ABGR32: 32bpp format using 0xaabbggrr + * @UPLGF_ARGB64: 64bpp format using 0xaaaabbbbggggrrrr + */ +enum upl_graphics_format { + UPLGF_ARGB32, + UPLGF_ABGR32, + UPLGF_ABGR64, +}; + +/** + * @reg: List of base address and size of registers (struct memregion) + * @width: Width of display in pixels + * @height: Height of display in pixels + * @stride: Number of bytes from one line to the next + * @format: Pixel format + */ +struct upl_graphics { + struct alist reg; + uint width; + uint height; + uint stride; + enum upl_graphics_format format; +}; + +/* + * Information about the UPL state + * + * @addr_cells: Number of address cells used in the handoff + * @size_cells: Number of size cells used in the handoff + * @bootmode: Boot-mode mask (enum upl_boot_mode) + * @fit: Address of FIT image that was loaded + * @conf_offset: Offset in FIT of the configuration that was selected + * @addr_width: Adress-bus width of machine, e.g. 46 for 46 bits + * @acpi_nvs_size: Size of the ACPI non-volatile-storage area in bytes + * @image: Information about each image (struct upl_image) + * @mem: Information about physical-memory regions (struct upl_mem) + * @nennap: Information about logical-memory regions (struct upl_memmap) + * @nennap: Information about reserved-memory regions (struct upl_memres) + */ +struct upl { + int addr_cells; + int size_cells; + + ulong smbios; + ulong acpi; + uint bootmode; + ulong fit; + uint conf_offset; + uint addr_width; + uint acpi_nvs_size; + + struct alist image; + struct alist mem; + struct alist memmap; + struct alist memres; + struct upl_serial serial; + struct upl_graphics graphics; +}; + +/** + * upl_write_handoff() - Write a Unversal Payload handoff structure + * + * upl: UPL state to write + * @root: root node to write it to + * @skip_existing: Avoid recreating any nodes which already exist in the + * devicetree. For example, if there is a serial node, just leave it alone, + * since don't need to create a new one + * Return: 0 on success, -ve on error + */ +int upl_write_handoff(const struct upl *upl, ofnode root, bool skip_existing); + +/** + * upl_create_handoff_tree() - Write a Unversal Payload handoff structure + * + * upl: UPL state to write + * @treep: Returns a new tree containing the handoff + * Return: 0 on success, -ve on error + */ +int upl_create_handoff_tree(const struct upl *upl, oftree *treep); + +/** + * upl_read_handoff() - Read a Unversal Payload handoff structure + * + * upl: UPL state to read into + * @tree: Devicetree containing the data to read + * Return: 0 on success, -ve on error + */ +int upl_read_handoff(struct upl *upl, oftree tree); +#endif /* USE_HOSTCC */ + +#if CONFIG_IS_ENABLED(UPL) && defined(CONFIG_SPL_BUILD) + +/** + * upl_set_fit_info() - Set up basic info about the FIT + * + * @fit: Address of FIT + * @conf_offset: Configuration node being used + * @entry_addr: Entry address for next phase + */ +void upl_set_fit_info(ulong fit, int conf_offset, ulong entry_addr); + +/** + * upl_set_fit_addr() - Set up the address of the FIT + * + * @fit: Address of FIT + */ +void upl_set_fit_addr(ulong fit); + +#else +static inline void upl_set_fit_addr(ulong fit) {} +static inline void upl_set_fit_info(ulong fit, int conf_offset, + ulong entry_addr) {} +#endif /* UPL && SPL */ + +/** + * _upl_add_image() - Internal function to add a new image to the UPL + * + * @node: Image node offset in FIT + * @load_addr: Address to which images was loaded + * @size: Image size in bytes + * @desc: Description of image + * Return: 0 if OK, -ENOMEM if out of memory + */ +int _upl_add_image(int node, ulong load_addr, ulong size, const char *desc); + +/** + * upl_add_image() - Add a new image to the UPL + * + * @fit: Pointer to FIT + * @node: Image node offset in FIT + * @load_addr: Address to which images was loaded + * @size: Image size in bytes + * Return: 0 if OK, -ENOMEM if out of memory + */ +static inline int upl_add_image(const void *fit, int node, ulong load_addr, + ulong size) +{ + if (CONFIG_IS_ENABLED(UPL) && IS_ENABLED(CONFIG_SPL_BUILD)) { + const char *desc = fdt_getprop(fit, node, FIT_DESC_PROP, NULL); + + return _upl_add_image(node, load_addr, size, desc); + } + + return 0; +} + +/** upl_init() - Set up a UPL struct */ +void upl_init(struct upl *upl); + +#endif /* __UPL_WRITE_H */ -- cgit v1.3.1 From 637be2e53f66034ed7475d5981e4445a538a7639 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 7 Aug 2024 16:47:29 -0600 Subject: upl: Add basic tests Add some unit tests to check that we can write a UPL handoff and read it back. Signed-off-by: Simon Glass --- MAINTAINERS | 1 + include/test/suites.h | 1 + include/upl.h | 9 ++ test/boot/Makefile | 2 + test/boot/upl.c | 368 ++++++++++++++++++++++++++++++++++++++++++++++++++ test/cmd_ut.c | 3 + 6 files changed, 384 insertions(+) create mode 100644 test/boot/upl.c (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index 04a6f23891c..acb92bd3f87 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1715,6 +1715,7 @@ S: Maintained T: git https://source.denx.de/u-boot/custodians/u-boot-dm.git F: boot/upl* F: include/upl.h +F: test/boot/upl.c USB M: Marek Vasut diff --git a/include/test/suites.h b/include/test/suites.h index 365d5f20dfe..2ceef577f7f 100644 --- a/include/test/suites.h +++ b/include/test/suites.h @@ -63,5 +63,6 @@ int do_ut_str(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); int do_ut_time(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); int do_ut_unicode(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); +int do_ut_upl(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]); #endif /* __TEST_SUITES_H__ */ diff --git a/include/upl.h b/include/upl.h index df644c47344..2ec5ef1b5cf 100644 --- a/include/upl.h +++ b/include/upl.h @@ -309,6 +309,15 @@ int upl_create_handoff_tree(const struct upl *upl, oftree *treep); * Return: 0 on success, -ve on error */ int upl_read_handoff(struct upl *upl, oftree tree); + +/** + * upl_get_test_data() - Fill a UPL with some test data + * + * @uts: Test state (can be uninited) + * @upl: Returns test data + * Return: 0 on success, 1 on error + */ +int upl_get_test_data(struct unit_test_state *uts, struct upl *upl); #endif /* USE_HOSTCC */ #if CONFIG_IS_ENABLED(UPL) && defined(CONFIG_SPL_BUILD) diff --git a/test/boot/Makefile b/test/boot/Makefile index 068522cb9e0..8ec5daa7bfe 100644 --- a/test/boot/Makefile +++ b/test/boot/Makefile @@ -13,3 +13,5 @@ ifdef CONFIG_OF_LIVE obj-$(CONFIG_BOOTMETH_VBE_SIMPLE) += vbe_simple.o endif obj-$(CONFIG_BOOTMETH_VBE) += vbe_fixup.o + +obj-$(CONFIG_UPL) += upl.o diff --git a/test/boot/upl.c b/test/boot/upl.c new file mode 100644 index 00000000000..aa3fddcbb75 --- /dev/null +++ b/test/boot/upl.c @@ -0,0 +1,368 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * UPL handoff testing + * + * Copyright 2024 Google LLC + * Written by Simon Glass + */ + +#include +#include +#include +#include +#include +#include +#include +#include "bootstd_common.h" + +/* Declare a new upl test */ +#define UPL_TEST(_name, _flags) UNIT_TEST(_name, _flags, upl_test) + +static int add_region(struct unit_test_state *uts, struct alist *lst, + ulong base, ulong size) +{ + struct memregion region; + + region.base = base; + region.size = size; + ut_assertnonnull(alist_add(lst, region)); + + return 0; +} + +int upl_get_test_data(struct unit_test_state *uts, struct upl *upl) +{ + struct upl_memmap memmap; + struct upl_memres memres; + struct upl_image img; + struct upl_mem mem; + + upl_init(upl); + + upl->addr_cells = 1; + upl->size_cells = 1; + upl->smbios = 0x123; + upl->acpi = 0x456; + upl->bootmode = BIT(UPLBM_DEFAULT) | BIT(UPLBM_S3); + upl->fit = 0x789; + upl->conf_offset = 0x234; + upl->addr_width = 46; + upl->acpi_nvs_size = 0x100; + + /* image[0] */ + img.load = 0x1; + img.size = 0x2; + img.offset = 0x3; + img.description = "U-Boot"; + ut_assertnonnull(alist_add(&upl->image, img)); + + /* image[1] */ + img.load = 0x4; + img.size = 0x5; + img.offset = 0x6; + img.description = "ATF"; + ut_assertnonnull(alist_add(&upl->image, img)); + + /* mem[0] : 3 regions */ + memset(&mem, '\0', sizeof(mem)); + alist_init_struct(&mem.region, struct memregion); + ut_assertok(add_region(uts, &mem.region, 0x10, 0x20)); + ut_assertok(add_region(uts, &mem.region, 0x30, 0x40)); + ut_assertok(add_region(uts, &mem.region, 0x40, 0x50)); + ut_assertnonnull(alist_add(&upl->mem, mem)); + + /* mem[0] : 1 region */ + alist_init_struct(&mem.region, struct memregion); + ut_assertok(add_region(uts, &mem.region, 0x70, 0x80)); + mem.hotpluggable = true; + ut_assertnonnull(alist_add(&upl->mem, mem)); + mem.hotpluggable = false; + + /* memmap[0] : 5 regions */ + alist_init_struct(&memmap.region, struct memregion); + memmap.name = "acpi"; + memmap.usage = BIT(UPLUS_ACPI_RECLAIM); + ut_assertok(add_region(uts, &memmap.region, 0x11, 0x12)); + ut_assertok(add_region(uts, &memmap.region, 0x13, 0x14)); + ut_assertok(add_region(uts, &memmap.region, 0x15, 0x16)); + ut_assertok(add_region(uts, &memmap.region, 0x17, 0x18)); + ut_assertok(add_region(uts, &memmap.region, 0x19, 0x1a)); + ut_assertnonnull(alist_add(&upl->memmap, memmap)); + + /* memmap[1] : 1 region */ + memmap.name = "u-boot"; + memmap.usage = BIT(UPLUS_BOOT_DATA); + alist_init_struct(&memmap.region, struct memregion); + ut_assertok(add_region(uts, &memmap.region, 0x21, 0x22)); + ut_assertnonnull(alist_add(&upl->memmap, memmap)); + + /* memmap[2] : 1 region */ + alist_init_struct(&memmap.region, struct memregion); + memmap.name = "efi"; + memmap.usage = BIT(UPLUS_RUNTIME_CODE); + ut_assertok(add_region(uts, &memmap.region, 0x23, 0x24)); + ut_assertnonnull(alist_add(&upl->memmap, memmap)); + + /* memmap[3]: 2 regions */ + alist_init_struct(&memmap.region, struct memregion); + memmap.name = "empty"; + memmap.usage = 0; + ut_assertok(add_region(uts, &memmap.region, 0x25, 0x26)); + ut_assertok(add_region(uts, &memmap.region, 0x27, 0x28)); + ut_assertnonnull(alist_add(&upl->memmap, memmap)); + + /* memmap[4]: 1 region */ + alist_init_struct(&memmap.region, struct memregion); + memmap.name = "acpi-things"; + memmap.usage = BIT(UPLUS_RUNTIME_CODE) | BIT(UPLUS_ACPI_NVS); + ut_assertok(add_region(uts, &memmap.region, 0x29, 0x2a)); + ut_assertnonnull(alist_add(&upl->memmap, memmap)); + + /* memres[0]: 1 region */ + alist_init_struct(&memres.region, struct memregion); + memset(&memres, '\0', sizeof(memres)); + memres.name = "mmio"; + ut_assertok(add_region(uts, &memres.region, 0x2b, 0x2c)); + ut_assertnonnull(alist_add(&upl->memres, memres)); + + /* memres[1]: 2 regions */ + alist_init_struct(&memres.region, struct memregion); + memres.name = "memory"; + ut_assertok(add_region(uts, &memres.region, 0x2d, 0x2e)); + ut_assertok(add_region(uts, &memres.region, 0x2f, 0x30)); + memres.no_map = true; + ut_assertnonnull(alist_add(&upl->memres, memres)); + + upl->serial.compatible = "ns16550a"; + upl->serial.clock_frequency = 1843200; + upl->serial.current_speed = 115200; + alist_init_struct(&upl->serial.reg, struct memregion); + ut_assertok(add_region(uts, &upl->serial.reg, 0xf1de0000, 0x100)); + upl->serial.reg_io_shift = 2; + upl->serial.reg_offset = 0x40; + upl->serial.reg_io_width = 1; + upl->serial.virtual_reg = 0x20000000; + upl->serial.access_type = UPLSAT_MMIO; + + alist_init_struct(&upl->graphics.reg, struct memregion); + ut_assertok(add_region(uts, &upl->graphics.reg, 0xd0000000, 0x10000000)); + upl->graphics.width = 1280; + upl->graphics.height = 1280; + upl->graphics.stride = upl->graphics.width * 4; + upl->graphics.format = UPLGF_ARGB32; + + return 0; +} + +static int compare_upl_image(struct unit_test_state *uts, + const struct upl_image *base, + const struct upl_image *cmp) +{ + ut_asserteq(base->load, cmp->load); + ut_asserteq(base->size, cmp->size); + ut_asserteq(base->offset, cmp->offset); + ut_asserteq_str(base->description, cmp->description); + + return 0; +} + +static int compare_upl_memregion(struct unit_test_state *uts, + const struct memregion *base, + const struct memregion *cmp) +{ + ut_asserteq(base->base, cmp->base); + ut_asserteq(base->size, cmp->size); + + return 0; +} + +static int compare_upl_mem(struct unit_test_state *uts, + const struct upl_mem *base, + const struct upl_mem *cmp) +{ + int i; + + ut_asserteq(base->region.count, cmp->region.count); + ut_asserteq(base->hotpluggable, cmp->hotpluggable); + for (i = 0; i < base->region.count; i++) { + ut_assertok(compare_upl_memregion(uts, + alist_get(&base->region, i, struct memregion), + alist_get(&cmp->region, i, struct memregion))); + } + + return 0; +} + +static int check_device_name(struct unit_test_state *uts, const char *base, + const char *cmp) +{ + const char *p; + + p = strchr(cmp, '@'); + if (p) { + ut_assertnonnull(p); + ut_asserteq_strn(base, cmp); + ut_asserteq(p - cmp, strlen(base)); + } else { + ut_asserteq_str(base, cmp); + } + + return 0; +} + +static int compare_upl_memmap(struct unit_test_state *uts, + const struct upl_memmap *base, + const struct upl_memmap *cmp) +{ + int i; + + ut_assertok(check_device_name(uts, base->name, cmp->name)); + ut_asserteq(base->region.count, cmp->region.count); + ut_asserteq(base->usage, cmp->usage); + for (i = 0; i < base->region.count; i++) + ut_assertok(compare_upl_memregion(uts, + alist_get(&base->region, i, struct memregion), + alist_get(&cmp->region, i, struct memregion))); + + return 0; +} + +static int compare_upl_memres(struct unit_test_state *uts, + const struct upl_memres *base, + const struct upl_memres *cmp) +{ + int i; + + ut_assertok(check_device_name(uts, base->name, cmp->name)); + ut_asserteq(base->region.count, cmp->region.count); + ut_asserteq(base->no_map, cmp->no_map); + for (i = 0; i < base->region.count; i++) + ut_assertok(compare_upl_memregion(uts, + alist_get(&base->region, i, struct memregion), + alist_get(&cmp->region, i, struct memregion))); + + return 0; +} + +static int compare_upl_serial(struct unit_test_state *uts, + struct upl_serial *base, struct upl_serial *cmp) +{ + int i; + + ut_asserteq_str(base->compatible, cmp->compatible); + ut_asserteq(base->clock_frequency, cmp->clock_frequency); + ut_asserteq(base->current_speed, cmp->current_speed); + for (i = 0; i < base->reg.count; i++) + ut_assertok(compare_upl_memregion(uts, + alist_get(&base->reg, i, struct memregion), + alist_get(&cmp->reg, i, struct memregion))); + ut_asserteq(base->reg_io_shift, cmp->reg_io_shift); + ut_asserteq(base->reg_offset, cmp->reg_offset); + ut_asserteq(base->reg_io_width, cmp->reg_io_width); + ut_asserteq(base->virtual_reg, cmp->virtual_reg); + ut_asserteq(base->access_type, cmp->access_type); + + return 0; +} + +static int compare_upl_graphics(struct unit_test_state *uts, + struct upl_graphics *base, + struct upl_graphics *cmp) +{ + int i; + + for (i = 0; i < base->reg.count; i++) + ut_assertok(compare_upl_memregion(uts, + alist_get(&base->reg, i, struct memregion), + alist_get(&cmp->reg, i, struct memregion))); + ut_asserteq(base->width, cmp->width); + ut_asserteq(base->height, cmp->height); + ut_asserteq(base->stride, cmp->stride); + ut_asserteq(base->format, cmp->format); + + return 0; +} + +static int compare_upl(struct unit_test_state *uts, struct upl *base, + struct upl *cmp) +{ + int i; + + ut_asserteq(base->addr_cells, cmp->addr_cells); + ut_asserteq(base->size_cells, cmp->size_cells); + + ut_asserteq(base->smbios, cmp->smbios); + ut_asserteq(base->acpi, cmp->acpi); + ut_asserteq(base->bootmode, cmp->bootmode); + ut_asserteq(base->fit, cmp->fit); + ut_asserteq(base->conf_offset, cmp->conf_offset); + ut_asserteq(base->addr_width, cmp->addr_width); + ut_asserteq(base->acpi_nvs_size, cmp->acpi_nvs_size); + + ut_asserteq(base->image.count, cmp->image.count); + for (i = 0; i < base->image.count; i++) + ut_assertok(compare_upl_image(uts, + alist_get(&base->image, i, struct upl_image), + alist_get(&cmp->image, i, struct upl_image))); + + ut_asserteq(base->mem.count, cmp->mem.count); + for (i = 0; i < base->mem.count; i++) + ut_assertok(compare_upl_mem(uts, + alist_get(&base->mem, i, struct upl_mem), + alist_get(&cmp->mem, i, struct upl_mem))); + + ut_asserteq(base->memmap.count, cmp->memmap.count); + for (i = 0; i < base->memmap.count; i++) + ut_assertok(compare_upl_memmap(uts, + alist_get(&base->memmap, i, struct upl_memmap), + alist_get(&cmp->memmap, i, struct upl_memmap))); + + ut_asserteq(base->memres.count, cmp->memres.count); + for (i = 0; i < base->memres.count; i++) + ut_assertok(compare_upl_memres(uts, + alist_get(&base->memres, i, struct upl_memres), + alist_get(&cmp->memres, i, struct upl_memres))); + + ut_assertok(compare_upl_serial(uts, &base->serial, &cmp->serial)); + ut_assertok(compare_upl_graphics(uts, &base->graphics, &cmp->graphics)); + + return 0; +} + +/* Basic test of writing and reading UPL handoff */ +static int upl_test_base(struct unit_test_state *uts) +{ + oftree tree, check_tree; + struct upl upl, check; + struct abuf buf; + + if (!CONFIG_IS_ENABLED(OFNODE_MULTI_TREE)) + return -EAGAIN; /* skip test */ + ut_assertok(upl_get_test_data(uts, &upl)); + + ut_assertok(upl_create_handoff_tree(&upl, &tree)); + ut_assertok(oftree_to_fdt(tree, &buf)); + + /* + * strings in check_tree and therefore check are only valid so long as + * buf stays around. As soon as we call abuf_uninit they go away + */ + check_tree = oftree_from_fdt(abuf_data(&buf)); + ut_assert(ofnode_valid(oftree_path(check_tree, "/"))); + + ut_assertok(upl_read_handoff(&check, check_tree)); + ut_assertok(compare_upl(uts, &upl, &check)); + abuf_uninit(&buf); + + return 0; +} +UPL_TEST(upl_test_base, 0); + +int do_ut_upl(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct unit_test *tests = UNIT_TEST_SUITE_START(upl_test); + const int n_ents = UNIT_TEST_SUITE_COUNT(upl_test); + + return cmd_ut_category("cmd_upl", "cmd_upl_", tests, n_ents, argc, + argv); +} diff --git a/test/cmd_ut.c b/test/cmd_ut.c index 4e4aa8f1cb2..38ba89ee33e 100644 --- a/test/cmd_ut.c +++ b/test/cmd_ut.c @@ -133,6 +133,9 @@ static struct cmd_tbl cmd_ut_sub[] = { #ifdef CONFIG_CMD_SEAMA U_BOOT_CMD_MKENT(seama, CONFIG_SYS_MAXARGS, 1, do_ut_seama, "", ""), #endif +#ifdef CONFIG_CMD_UPL + U_BOOT_CMD_MKENT(upl, CONFIG_SYS_MAXARGS, 1, do_ut_upl, "", ""), +#endif }; static int do_ut_all(struct cmd_tbl *cmdtp, int flag, int argc, -- cgit v1.3.1 From 264f4b0b34c027fb913c3eac425ecfa30cef1f10 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 7 Aug 2024 16:47:30 -0600 Subject: upl: Add a command Add a 'upl' command to work with Universal Payload features. For now it only supports reading and writing a handoff structure. Signed-off-by: Simon Glass --- MAINTAINERS | 2 + boot/Kconfig | 1 + cmd/Kconfig | 7 ++ cmd/Makefile | 1 + cmd/upl.c | 118 ++++++++++++++++++++++++ doc/usage/cmd/upl.rst | 186 ++++++++++++++++++++++++++++++++++++++ doc/usage/index.rst | 1 + include/asm-generic/global_data.h | 15 +++ test/boot/upl.c | 40 ++++++++ 9 files changed, 371 insertions(+) create mode 100644 cmd/upl.c create mode 100644 doc/usage/cmd/upl.rst (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index acb92bd3f87..4d17c966053 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1714,6 +1714,8 @@ M: Simon Glass S: Maintained T: git https://source.denx.de/u-boot/custodians/u-boot-dm.git F: boot/upl* +F: cmd/upl.c +F: doc/usage/cmd/upl.rst F: include/upl.h F: test/boot/upl.c diff --git a/boot/Kconfig b/boot/Kconfig index d5f1304490b..e37b08bf391 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -747,6 +747,7 @@ config BOOTMETH_SCRIPT config UPL bool "upl - Universal Payload Specification" + imply CMD_UPL imply UPL_READ imply UPL_WRITE help diff --git a/cmd/Kconfig b/cmd/Kconfig index 978f44eda42..8af136d2bd4 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -388,6 +388,13 @@ config CMD_SEAMA help Support reading NAND Seattle Image (SEAMA) images. +config CMD_UPL + bool "upl - Universal Payload Specification" + help + Provides commands to deal with UPL payloads and handoff information. + U-Boot supports generating and accepting handoff information. The + mkimage tool will eventually support creating payloads. + config CMD_VBE bool "vbe - Verified Boot for Embedded" depends on BOOTMETH_VBE diff --git a/cmd/Makefile b/cmd/Makefile index 87133cc27a8..91227f1249c 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -189,6 +189,7 @@ obj-$(CONFIG_CMD_UBIFS) += ubifs.o obj-$(CONFIG_CMD_UNIVERSE) += universe.o obj-$(CONFIG_CMD_UNLZ4) += unlz4.o obj-$(CONFIG_CMD_UNZIP) += unzip.o +obj-$(CONFIG_CMD_UPL) += upl.o obj-$(CONFIG_CMD_VIRTIO) += virtio.o obj-$(CONFIG_CMD_WDT) += wdt.o obj-$(CONFIG_CMD_LZMADEC) += lzmadec.o diff --git a/cmd/upl.c b/cmd/upl.c new file mode 100644 index 00000000000..c9745886507 --- /dev/null +++ b/cmd/upl.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Commands for UPL handoff generation + * + * Copyright 2024 Google LLC + * Written by Simon Glass + */ + +#define LOG_CATEGORY UCLASS_BOOTSTD + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static int do_upl_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const struct upl *upl = gd_upl(); + + printf("UPL state: %sactive\n", upl ? "" : "in"); + if (!upl) + return 0; + if (argc > 1 && !strcmp("-v", argv[1])) { + int i; + + printf("fit %lx\n", upl->fit); + printf("conf_offset %x\n", upl->conf_offset); + for (i = 0; i < upl->image.count; i++) { + const struct upl_image *img = + alist_get(&upl->image, i, struct upl_image); + + printf("image %d: load %lx size %lx offset %x: %s\n", i, + img->load, img->size, img->offset, + img->description); + } + } + + return 0; +} + +static int do_upl_write(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct upl s_upl, *upl = &s_upl; + struct unit_test_state uts; + struct abuf buf; + oftree tree; + ulong addr; + int ret; + + upl_get_test_data(&uts, upl); + + log_debug("Writing UPL\n"); + ret = upl_create_handoff_tree(upl, &tree); + if (ret) { + log_err("Failed to write (err=%dE)\n", ret); + return CMD_RET_FAILURE; + } + + log_debug("Flattening\n"); + ret = oftree_to_fdt(tree, &buf); + if (ret) { + log_err("Failed to write (err=%dE)\n", ret); + return CMD_RET_FAILURE; + } + addr = map_to_sysmem(abuf_data(&buf)); + printf("UPL handoff written to %lx size %lx\n", addr, abuf_size(&buf)); + if (env_set_hex("upladdr", addr) || + env_set_hex("uplsize", abuf_size(&buf))) { + printf("Cannot set env var\n"); + return CMD_RET_FAILURE; + } + + log_debug("done\n"); + + return 0; +} + +static int do_upl_read(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct upl s_upl, *upl = &s_upl; + oftree tree; + ulong addr; + int ret; + + if (argc < 1) + return CMD_RET_USAGE; + addr = hextoul(argv[1], NULL); + + printf("Reading UPL at %lx\n", addr); + tree = oftree_from_fdt(map_sysmem(addr, 0)); + ret = upl_read_handoff(upl, tree); + if (ret) { + log_err("Failed to read (err=%dE)\n", ret); + return CMD_RET_FAILURE; + } + + return 0; +} + +U_BOOT_LONGHELP(upl, + "info [-v] - Check UPL status\n" + "upl read - Read handoff information\n" + "upl write - Write handoff information"); + +U_BOOT_CMD_WITH_SUBCMDS(upl, "Universal Payload support", upl_help_text, + U_BOOT_SUBCMD_MKENT(info, 2, 1, do_upl_info), + U_BOOT_SUBCMD_MKENT(read, 2, 1, do_upl_read), + U_BOOT_SUBCMD_MKENT(write, 1, 1, do_upl_write)); diff --git a/doc/usage/cmd/upl.rst b/doc/usage/cmd/upl.rst new file mode 100644 index 00000000000..a64063d42d5 --- /dev/null +++ b/doc/usage/cmd/upl.rst @@ -0,0 +1,186 @@ +.. SPDX-License-Identifier: GPL-2.0+: + +upl command +=========== + +Synopsis +-------- + +:: + + upl write + upl read + upl info [-v] + +Description +----------- + +The *upl* command is used to test U-Boot's support for the Universal Payload +Specification (UPL) firmware standard. It allows creation of a fake handoff for +use in testing. + + +upl write +~~~~~~~~~ + +Write a fake UPL handoff structure. The `upladdr` environment variable is set to +the address of this structure and `uplsize` is set to the size. + + +upl read +~~~~~~~~ + +Read a UPL handoff structure into internal state. This allows testing that the +handoff can be obtained. + +upl info +~~~~~~~~ + +Show basic information about usage of UPL: + + UPL state + active or inactive (indicates whether U-Boot booted from UPL or not) + + fit + Address of the FIT which was loaded + + conf_offset 2a4 + FIT offset of the chosen configuration + +For each image the following information is shown: + + Image number + Images are numbered from 0 + + load + Address to which the image was loaded + + size + Size of the loaded image + + offset + FIT offset of the image + + description + Description of the image + + +Example +------- + +This shows checking whether a UPL handoff was read at start-up:: + + => upl info + UPL state: active + +This shows how to use the command to write and display the handoff:: + + => upl write + UPL handoff written to bc8a5e0 size 662 + => print upladdr + upladdr=bc8a5e0 + => print uplsize + uplsize=662 + + > fdt addr ${upladdr} + Working FDT set to bc8a5e0 + => fdt print + / { + #address-cells = <0x00000001>; + #size-cells = <0x00000001>; + options { + upl-params { + smbios = <0x00000123>; + acpi = <0x00000456>; + bootmode = "default", "s3"; + addr-width = <0x0000002e>; + acpi-nvs-size = <0x00000100>; + }; + upl-image { + fit = <0x00000789>; + conf-offset = <0x00000234>; + image-1 { + load = <0x00000001>; + size = <0x00000002>; + offset = <0x00000003>; + description = "U-Boot"; + }; + image-2 { + load = <0x00000004>; + size = <0x00000005>; + offset = <0x00000006>; + description = "ATF"; + }; + }; + }; + memory@0x10 { + reg = <0x00000010 0x00000020 0x00000030 0x00000040 0x00000050 0x00000060>; + }; + memory@0x70 { + reg = <0x00000070 0x00000080>; + hotpluggable; + }; + memory-map { + acpi@0x11 { + reg = <0x00000011 0x00000012 0x00000013 0x00000014 0x00000015 0x00000016 0x00000017 0x00000018 0x00000019 0x0000001a>; + usage = "acpi-reclaim"; + }; + u-boot@0x21 { + reg = <0x00000021 0x00000022>; + usage = "boot-data"; + }; + efi@0x23 { + reg = <0x00000023 0x00000024>; + usage = "runtime-code"; + }; + empty@0x25 { + reg = <0x00000025 0x00000026 0x00000027 0x00000028>; + }; + acpi-things@0x2a { + reg = <0x0000002a 0x00000000>; + usage = "acpi-nvs", "runtime-code"; + }; + }; + reserved-memory { + mmio@0x2b { + reg = <0x0000002b 0x0000002c>; + }; + memory@0x2d { + reg = <0x0000002d 0x0000002e 0x0000002f 0x00000030>; + no-map; + }; + }; + serial@0xf1de0000 { + compatible = "ns16550a"; + clock-frequency = <0x001c2000>; + current-speed = <0x0001c200>; + reg = <0xf1de0000 0x00000100>; + reg-io-shift = <0x00000002>; + reg-offset = <0x00000040>; + virtual-reg = <0x20000000>; + access-type = "mmio"; + }; + framebuffer@0xd0000000 { + compatible = "simple-framebuffer"; + reg = <0xd0000000 0x10000000>; + width = <0x00000500>; + height = <0x00000500>; + stride = <0x00001400>; + format = "a8r8g8b8"; + }; + }; + => + +This showing reading the handoff into internal state:: + + => upl read bc8a5e0 + Reading UPL at bc8a5e0 + => + +This shows getting basic information about UPL: + + => upl info -v + UPL state: active + fit 1264000 + conf_offset 2a4 + image 0: load 200000 size 105f5c8 offset a4: U-Boot 2024.07-00770-g739ee12e8358 for sandbox board diff --git a/doc/usage/index.rst b/doc/usage/index.rst index 1f6518b77c4..4246c06d97d 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -114,6 +114,7 @@ Shell commands cmd/tftpput cmd/trace cmd/true + cmd/upl cmd/ums cmd/unbind cmd/ut diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index 27aa75e7036..94d054ee54e 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -30,6 +30,7 @@ struct acpi_ctx; struct driver_rt; +struct upl; typedef struct global_data gd_t; @@ -491,6 +492,12 @@ struct global_data { * @dmtag_list: List of DM tags */ struct list_head dmtag_list; +#if CONFIG_IS_ENABLED(UPL) + /** + * @upl: Universal Payload-handoff information + */ + struct upl *upl; +#endif }; #ifndef DO_DEPS_ONLY static_assert(sizeof(struct global_data) == GD_SIZE); @@ -590,6 +597,14 @@ static_assert(sizeof(struct global_data) == GD_SIZE); #define gd_malloc_ptr() 0L #endif +#if CONFIG_IS_ENABLED(UPL) +#define gd_upl() gd->upl +#define gd_set_upl(_val) gd->upl = (_val) +#else +#define gd_upl() NULL +#define gd_set_upl(val) +#endif + /** * enum gd_flags - global data flags * diff --git a/test/boot/upl.c b/test/boot/upl.c index aa3fddcbb75..056ae54831b 100644 --- a/test/boot/upl.c +++ b/test/boot/upl.c @@ -358,6 +358,46 @@ static int upl_test_base(struct unit_test_state *uts) } UPL_TEST(upl_test_base, 0); +/* Test 'upl info' command */ +static int upl_test_info(struct unit_test_state *uts) +{ + gd_set_upl(NULL); + ut_assertok(run_command("upl info", 0)); + ut_assert_nextline("UPL state: inactive"); + ut_assert_console_end(); + + gd_set_upl((struct upl *)uts); /* set it to any non-zero value */ + ut_assertok(run_command("upl info", 0)); + ut_assert_nextline("UPL state: active"); + ut_assert_console_end(); + gd_set_upl(NULL); + + return 0; +} +UPL_TEST(upl_test_info, UT_TESTF_CONSOLE_REC); + +/* Test 'upl read' and 'upl_write' commands */ +static int upl_test_read_write(struct unit_test_state *uts) +{ + ulong addr; + + if (!CONFIG_IS_ENABLED(OFNODE_MULTI_TREE)) + return -EAGAIN; /* skip test */ + ut_assertok(run_command("upl write", 0)); + + addr = env_get_hex("upladdr", 0); + ut_assert_nextline("UPL handoff written to %lx size %lx", addr, + env_get_hex("uplsize", 0)); + ut_assert_console_end(); + + ut_assertok(run_command("upl read ${upladdr}", 0)); + ut_assert_nextline("Reading UPL at %lx", addr); + ut_assert_console_end(); + + return 0; +} +UPL_TEST(upl_test_read_write, UT_TESTF_CONSOLE_REC); + int do_ut_upl(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { struct unit_test *tests = UNIT_TEST_SUITE_START(upl_test); -- cgit v1.3.1 From fefb53492f729626a515b67b1acca941ad07e974 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 7 Aug 2024 16:47:31 -0600 Subject: upl: Add support for Universal Payload in SPL Add the basic code to create a handoff structure in SPL, so it can be passed to the next phase. For now this is not plumbed in. Signed-off-by: Simon Glass --- MAINTAINERS | 1 + boot/Kconfig | 32 ++++++++++ common/spl/Makefile | 2 + common/spl/spl_upl.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/spl.h | 16 +++++ 5 files changed, 223 insertions(+) create mode 100644 common/spl/spl_upl.c (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index 4d17c966053..43d430c603a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1715,6 +1715,7 @@ S: Maintained T: git https://source.denx.de/u-boot/custodians/u-boot-dm.git F: boot/upl* F: cmd/upl.c +F: common/spl/spl_upl.c F: doc/usage/cmd/upl.rst F: include/upl.h F: test/boot/upl.c diff --git a/boot/Kconfig b/boot/Kconfig index e37b08bf391..60a4cddf5ec 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -750,6 +750,7 @@ config UPL imply CMD_UPL imply UPL_READ imply UPL_WRITE + imply SPL_UPL if SPL help Provides support for UPL payloads and handoff information. U-Boot supports generating and accepting handoff information. The mkimage @@ -766,11 +767,42 @@ config UPL_READ config UPL_WRITE bool "upl - Support writing a Universal Payload handoff" + help + Provides support for encoding a UPL-format payload from a C structure + so it can be passed to another program. This is just the writing + implementation, useful for trying it out. See SPL_UPL_OUT + for how to tell U-Boot SPL to actually write it before jumping to + the next phase. + +if SPL + +config SPL_UPL + bool "Write a UPL handoff in SPL" + imply SPL_UPL_OUT + help + This tells SPL to write a UPL handoff and pass it to the next phase + (e.g. to U-Boot or another program which SPL loads and runs). THis + provides information to help that program run correctly and + efficiently on the machine. + +config SPL_UPL_WRITE + bool # upl - Support writing a Universal Payload handoff in SPL + select SPL_BLOBLIST help Provides support for encoding a UPL-format payload from a C structure so it can be passed to another program. This is just the writing implementation, useful for trying it out. +config SPL_UPL_OUT + bool "upl - Support writing a Universal Payload handoff in SPL" + select SPL_UPL_WRITE + help + Provides support for encoding a UPL-format payload and passing it to + the next firmware phase. This allows U-Boot SPL to function as + Platform Init in the meaning of the specification. + +endif # SPL + endif # UPL endif # BOOTSTD diff --git a/common/spl/Makefile b/common/spl/Makefile index 4809f9c3ec1..137b18428bd 100644 --- a/common/spl/Makefile +++ b/common/spl/Makefile @@ -37,3 +37,5 @@ obj-$(CONFIG_$(SPL_TPL_)SPI_LOAD) += spl_spi.o obj-$(CONFIG_$(SPL_TPL_)RAM_SUPPORT) += spl_ram.o obj-$(CONFIG_$(SPL_TPL_)USB_SDP_SUPPORT) += spl_sdp.o endif + +obj-$(CONFIG_$(SPL_TPL_)UPL) += spl_upl.o diff --git a/common/spl/spl_upl.c b/common/spl/spl_upl.c new file mode 100644 index 00000000000..067d437150f --- /dev/null +++ b/common/spl/spl_upl.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * UPL handoff parsing + * + * Copyright 2024 Google LLC + * Written by Simon Glass + */ + +#define LOG_CATEGORY UCLASS_BOOTSTD + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct upl s_upl; + +void upl_set_fit_addr(ulong fit) +{ + struct upl *upl = &s_upl; + + upl->fit = fit; +} + +void upl_set_fit_info(ulong fit, int conf_offset, ulong entry_addr) +{ + struct upl *upl = &s_upl; + + upl->fit = fit; + upl->conf_offset = conf_offset; + log_debug("upl: add fit %lx conf %x\n", fit, conf_offset); +} + +int _upl_add_image(int node, ulong load_addr, ulong size, const char *desc) +{ + struct upl *upl = &s_upl; + struct upl_image img; + + img.load = load_addr; + img.size = size; + img.offset = node; + img.description = desc; + if (!alist_add(&upl->image, img)) + return -ENOMEM; + log_debug("upl: add image %s at %lx size %lx\n", desc, load_addr, size); + + return 0; +} + +static int write_serial(struct upl_serial *ser) +{ + struct udevice *dev = gd->cur_serial_dev; + struct serial_device_info info; + struct memregion region; + int ret; + + if (!dev) + return log_msg_ret("ser", -ENOENT); + ret = serial_getinfo(dev, &info); + if (ret) + return log_msg_ret("inf", ret); + + ser->compatible = ofnode_read_string(dev_ofnode(dev), "compatible"); + ser->clock_frequency = info.clock; + ser->current_speed = gd->baudrate; + region.base = info.addr; + region.size = info.size; + alist_init_struct(&ser->reg, struct memregion); + if (!alist_add(&ser->reg, region)) + return -ENOMEM; + ser->reg_io_shift = info.reg_shift; + ser->reg_offset = info.reg_offset; + ser->reg_io_width = info.reg_width; + ser->virtual_reg = 0; + ser->access_type = info.addr_space; + + return 0; +} + +static int write_graphics(struct upl_graphics *gra) +{ + struct video_uc_plat *plat; + struct video_priv *priv; + struct memregion region; + struct udevice *dev; + + alist_init_struct(&gra->reg, struct memregion); + uclass_find_first_device(UCLASS_VIDEO, &dev); + if (!dev || !device_active(dev)) + return log_msg_ret("vid", -ENOENT); + + plat = dev_get_uclass_plat(dev); + region.base = plat->base; + region.size = plat->size; + if (!alist_add(&gra->reg, region)) + return log_msg_ret("reg", -ENOMEM); + + priv = dev_get_uclass_priv(dev); + gra->width = priv->xsize; + gra->height = priv->ysize; + gra->stride = priv->line_length; /* private field */ + switch (priv->format) { + case VIDEO_RGBA8888: + case VIDEO_X8R8G8B8: + gra->format = UPLGF_ARGB32; + break; + case VIDEO_X8B8G8R8: + gra->format = UPLGF_ABGR32; + break; + case VIDEO_X2R10G10B10: + log_debug("device '%s': VIDEO_X2R10G10B10 not supported\n", + dev->name); + return log_msg_ret("for", -EPROTO); + case VIDEO_UNKNOWN: + log_debug("device '%s': Unknown video format\n", dev->name); + return log_msg_ret("for", -EPROTO); + } + + return 0; +} + +int spl_write_upl_handoff(struct spl_image_info *spl_image) +{ + struct upl *upl = &s_upl; + struct abuf buf; + ofnode root; + void *ptr; + int ret; + + log_debug("UPL: Writing handoff - image_count=%d\n", upl->image.count); + upl->addr_cells = IS_ENABLED(CONFIG_PHYS_64BIT) ? 2 : 1; + upl->size_cells = IS_ENABLED(CONFIG_PHYS_64BIT) ? 2 : 1; + upl->bootmode = UPLBM_DEFAULT; + ret = write_serial(&upl->serial); + if (ret) + return log_msg_ret("ser", ret); + ret = write_graphics(&upl->graphics); + if (ret && ret != -ENOENT) + return log_msg_ret("gra", ret); + + root = ofnode_root(); + ret = upl_write_handoff(upl, root, true); + if (ret) + return log_msg_ret("wr", ret); + + ret = oftree_to_fdt(oftree_default(), &buf); + if (ret) + return log_msg_ret("fdt", ret); + log_debug("FDT size %zx\n", abuf_size(&buf)); + + ptr = bloblist_add(BLOBLISTT_CONTROL_FDT, abuf_size(&buf), 0); + if (!ptr) + return log_msg_ret("blo", -ENOENT); + memcpy(ptr, abuf_data(&buf), abuf_size(&buf)); + + return 0; +} + +void spl_upl_init(void) +{ + upl_init(&s_upl); +} diff --git a/include/spl.h b/include/spl.h index 1eebea3f981..f92089b69ea 100644 --- a/include/spl.h +++ b/include/spl.h @@ -1073,4 +1073,20 @@ static inline bool spl_decompression_enabled(void) { return IS_ENABLED(CONFIG_SPL_GZIP) || IS_ENABLED(CONFIG_SPL_LZMA); } + +/** + * spl_write_upl_handoff() - Write a Universal Payload hand-off structure + * + * @spl_image: Information about the image being booted + * Return: 0 if OK, -ve on error + */ +int spl_write_upl_handoff(struct spl_image_info *spl_image); + +/** + * spl_upl_init() - Get UPL ready for information to be added + * + * This must be called before upl_add_image(), etc. + */ +void spl_upl_init(void); + #endif -- cgit v1.3.1 From ec2186acbc4c7cf3b921ad6e822f6988d1e134b3 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 7 Aug 2024 16:47:33 -0600 Subject: spl: Plumb in the Universal Payload handoff Specify the FIT and include information about each loaded image, as required by the UPL handoff. Write the UPL handoff into the bloblist before jumping to the next phase. Control this using a runtime flag to avoid conflicting with other handoff mechanisms. Signed-off-by: Simon Glass --- boot/image-fit.c | 3 +++ common/spl/spl.c | 8 ++++++++ common/spl/spl_fit.c | 8 ++++++++ include/asm-generic/global_data.h | 4 ++++ 4 files changed, 23 insertions(+) (limited to 'include') diff --git a/boot/image-fit.c b/boot/image-fit.c index 9253f81fff5..7d56f0b5e6e 100644 --- a/boot/image-fit.c +++ b/boot/image-fit.c @@ -36,6 +36,7 @@ DECLARE_GLOBAL_DATA_PTR; #include #include #include +#include #include /*****************************************************************************/ @@ -2294,6 +2295,8 @@ int fit_image_load(struct bootm_headers *images, ulong addr, bootstage_mark(bootstage_id + BOOTSTAGE_SUB_LOAD); + upl_add_image(fit, noffset, load, len); + *datap = load; *lenp = len; if (fit_unamep) diff --git a/common/spl/spl.c b/common/spl/spl.c index 7794ddccade..d6a364de6ee 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -810,6 +810,14 @@ void board_init_r(gd_t *dummy1, ulong dummy2) printf(SPL_TPL_PROMPT "SPL hand-off write failed (err=%d)\n", ret); } + if (CONFIG_IS_ENABLED(UPL_OUT) && (gd->flags & GD_FLG_UPL)) { + ret = spl_write_upl_handoff(&spl_image); + if (ret) { + printf(SPL_TPL_PROMPT + "UPL hand-off write failed (err=%d)\n", ret); + hang(); + } + } if (CONFIG_IS_ENABLED(BLOBLIST)) { ret = bloblist_finish(); if (ret) diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c index 527a5691ac6..1ad5a69d807 100644 --- a/common/spl/spl_fit.c +++ b/common/spl/spl_fit.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -336,6 +337,8 @@ static int load_simple_fit(struct spl_load_info *info, ulong fit_offset, image_info->entry_point = FDT_ERROR; } + upl_add_image(fit, node, load_addr, length); + return 0; } @@ -847,6 +850,8 @@ int spl_load_simple_fit(struct spl_image_info *spl_image, spl_image->entry_point = spl_image->load_addr; spl_image->flags |= SPL_FIT_FOUND; + upl_set_fit_info(map_to_sysmem(ctx.fit), ctx.conf_node, + spl_image->entry_point); return 0; } @@ -943,5 +948,8 @@ int spl_load_fit_image(struct spl_image_info *spl_image, } spl_image->flags |= SPL_FIT_FOUND; + upl_set_fit_info(map_to_sysmem(header), conf_noffset, + spl_image->entry_point); + return 0; } diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index 94d054ee54e..19c66e1fe5d 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -716,6 +716,10 @@ enum gd_flags { * @GD_FLG_HUSH_MODERN_PARSER: Use hush 2021 parser. */ GD_FLG_HUSH_MODERN_PARSER = 0x2000000, + /** + * @GD_FLG_UPL: Read/write a Universal Payload (UPL) handoff + */ + GD_FLG_UPL = 0x4000000, }; #endif /* __ASSEMBLY__ */ -- cgit v1.3.1