Platforms following EBBR / Arm SystemReady IR keep the devicetree on a firmware-owned partition, updated independently of the operating system, rather than shipping it in the OS image or on the EFI System Partition. U-Boot has no generic way to source the devicetree from such a partition, so vendors carry out-of-tree machinery for it.
Add a bootstd helper, firmware_fdt_load(), that assembles the OS devicetree from a FIT manifest carried on that partition: the FIT images hold the base DTB and its overlays, and each FIT configuration names one bootable combination through its 'fdt' property. The partition is described in the control devicetree: the bootstd node carries a 'firmware-fdt-source' phandle to a source node, a child of the media device owning the partition, which identifies it by GPT type UUID and/or name. The 'boot_dtb' environment variable may pin an A/B partition number. The error semantics are fail closed: -ENOENT strictly means "no source configured" (the only case where callers may fall back to their normal devicetree); once a source is configured, any assembly failure is fatal, and downstream -ENOENT codes (missing manifest, missing FIT configuration) are remapped so a missing or bad firmware devicetree is never silently replaced by an unverified one. Manifest images carrying a 'load' property are rejected so a misbuilt manifest cannot overwrite arbitrary memory. Signed-off-by: Carlo Caione <[email protected]> --- MAINTAINERS | 3 + boot/Kconfig | 23 ++ boot/Makefile | 1 + boot/firmware_fdt.c | 382 ++++++++++++++++++++++++++++++ doc/develop/bootstd/firmware_fdt.rst | 94 ++++++++ doc/develop/bootstd/index.rst | 1 + doc/device-tree-bindings/firmware-fdt.txt | 149 ++++++++++++ include/firmware_fdt.h | 78 ++++++ 8 files changed, 731 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 571af196465..00078f93a95 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1002,8 +1002,10 @@ F: boot/bootdev*.c F: boot/bootflow.c F: boot/bootmeth*.c F: boot/bootstd.c +F: boot/firmware_fdt.c F: cmd/bootdev.c F: cmd/bootflow.c +F: doc/device-tree-bindings/firmware-fdt.txt F: doc/develop/bootstd/ F: doc/usage/bootdev.rst F: doc/usage/bootflow.rst @@ -1013,6 +1015,7 @@ F: include/bootdev.h F: include/bootflow.h F: include/bootmeth.h F: include/bootstd.h +F: include/firmware_fdt.h F: net/eth_bootdevice.c F: test/boot/ diff --git a/boot/Kconfig b/boot/Kconfig index ae6f09a6ede..a93cc3e0c3c 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -638,6 +638,29 @@ config BOOTMETH_EFILOADER This provides a way to try out standard boot on an existing boot flow. +config BOOTSTD_FIRMWARE_FDT + bool "Source the devicetree from a firmware-owned partition" + depends on BOOTSTD && BLK && FIT + select EFI_PARTITION + select PARTITION_TYPE_GUID + select OF_LIBFDT + select OF_LIBFDT_OVERLAY + help + Source the devicetree from a firmware-owned partition rather than + from the EFI System Partition or U-Boot's built-in control + devicetree. The partition carries a FIT manifest whose images hold + the base DTB and its overlays, and whose configurations name the + bootable combinations. The assembled devicetree is handed to the OS, + so it can be updated as part of the firmware, independently of the + operating system. + + For secure boot, sign the manifest configurations and enable + FIT_SIGNATURE with a required key in the control devicetree; the + standard verified-boot policy then rejects unsigned manifests. + + This is intended for platforms following EBBR / Arm SystemReady IR. + Say N unless you are booting such a platform. + config BOOTMETH_EFI_BOOTMGR bool "Bootdev support for EFI boot manager" depends on EFI_BOOTMGR diff --git a/boot/Makefile b/boot/Makefile index 7fb56e7ef37..8e6f0dfc8d5 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_$(PHASE_)BOOTSTD) += bootstd-uclass.o obj-$(CONFIG_$(PHASE_)BOOTSTD_MENU) += bootflow_menu.o obj-$(CONFIG_$(PHASE_)BOOTSTD_PROG) += prog_boot.o +obj-$(CONFIG_$(PHASE_)BOOTSTD_FIRMWARE_FDT) += firmware_fdt.o obj-$(CONFIG_$(PHASE_)BOOTMETH_EXTLINUX) += bootmeth_extlinux.o obj-$(CONFIG_$(PHASE_)BOOTMETH_EXTLINUX_PXE) += bootmeth_pxe.o diff --git a/boot/firmware_fdt.c b/boot/firmware_fdt.c new file mode 100644 index 00000000000..952299f1e1b --- /dev/null +++ b/boot/firmware_fdt.c @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Firmware-owned devicetree (FDT) source. + * + * Some platforms (EBBR / Arm SystemReady IR) keep the devicetree on a + * firmware-owned partition rather than in the OS image or the EFI System + * Partition. The devicetree is carried by a FIT manifest on that partition: + * the FIT images hold the base DTB and its overlays, and each FIT + * configuration names one bootable combination through its 'fdt' property. + * + * The partition is described by the control devicetree: the bootstd node + * carries a 'firmware-fdt-source' phandle to a source node, a child of the + * media device that owns the partition, e.g.: + * + * bootstd { + * compatible = "u-boot,boot-std"; + * firmware-fdt-source = <&fw_fdt>; + * }; + * &mmc0 { + * fw_fdt: firmware-fdt { + * compatible = "u-boot,firmware-fdt-block"; + * partition-type-uuid = "...."; (GPT type UUID) + * partition-name = "firmware"; (optional fallback) + * filename = "fdt.itb"; (optional, the default) + * }; + * }; + * + * At most two runtime values come from the environment: 'boot_dtb' may pin + * an A/B firmware partition and 'fw_fdt_config' may select a FIT + * configuration. Both only choose among combinations the firmware author + * shipped (and, in secure mode, signed). + */ + +#define LOG_CATEGORY UCLASS_BOOTSTD + +#include <blk.h> +#include <dm.h> +#include <env.h> +#include <firmware_fdt.h> +#include <fs.h> +#include <image.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <part.h> +#include <vsprintf.h> +#include <dm/ofnode.h> +#include <linux/libfdt.h> +#include <linux/sizes.h> +#include <linux/string.h> + +/* The manifest lives in a filesystem on a GPT partition of a block device */ +#define FW_FDT_COMPAT_BLOCK "u-boot,firmware-fdt-block" + +/* Default manifest filename on the firmware partition */ +#define FW_FDT_FILENAME "fdt.itb" + +/* Sanity cap on the manifest size */ +#define FW_FDT_MAX_SIZE SZ_4M + +/** + * fw_fdt_get_source() - find the configured firmware-FDT source node + * + * Reads the 'firmware-fdt-source' phandle from the bootstd node. + * + * @srcp: returns the source ofnode on success + * Return: 0 on success, -ENOENT if no source is configured, -EINVAL if the + * property is present but its phandle does not resolve + */ +static int fw_fdt_get_source(ofnode *srcp) +{ + ofnode bootstd, src; + const void *prop; + + bootstd = ofnode_by_compatible(ofnode_null(), "u-boot,boot-std"); + if (!ofnode_valid(bootstd)) + return -ENOENT; + + prop = ofnode_get_property(bootstd, "firmware-fdt-source", NULL); + if (!prop) + return -ENOENT; + + src = ofnode_parse_phandle(bootstd, "firmware-fdt-source", 0); + if (!ofnode_valid(src)) + return log_msg_ret("phandle", -EINVAL); + + *srcp = src; + + return 0; +} + +static int fw_fdt_get_checked_source(ofnode *srcp) +{ + int ret; + + ret = fw_fdt_get_source(srcp); + if (ret) + return ret; + + if (!ofnode_device_is_compatible(*srcp, FW_FDT_COMPAT_BLOCK)) + return log_msg_ret("compat", -EOPNOTSUPP); + + return 0; +} + +/** + * fw_fdt_get_blk() - resolve the block device that owns the source node + * + * The source node is a child of its media device (e.g. &mmc0); resolve that + * parent to a udevice. The lookup also probes it, which the EFI boot-manager + * path relies on. + * + * @src: the firmware-FDT source node + * @descp: returns the block descriptor on success + * Return: 0 on success, negative on error + */ +static int fw_fdt_get_blk(ofnode src, struct blk_desc **descp) +{ + struct udevice *media, *blk; + ofnode parent; + int ret; + + parent = ofnode_get_parent(src); + if (!ofnode_valid(parent)) + return log_msg_ret("par", -EINVAL); + + /* a source is configured: remap -ENOENT to -ENODEV to fail closed */ + ret = device_get_global_by_ofnode(parent, &media); + if (ret) + return log_msg_ret("media", ret == -ENOENT ? -ENODEV : ret); + + ret = blk_get_from_parent(media, &blk); + if (ret) + return log_msg_ret("blk", ret == -ENOENT ? -ENODEV : ret); + + *descp = dev_get_uclass_plat(blk); + + return 0; +} + +/** + * fw_fdt_find_part() - find the firmware partition on @desc + * + * 'boot_dtb', if set, pins an explicit partition number. Otherwise the + * first partition matching every configured selector is used: when both + * @type_uuid and @name are configured, both must match, so a misprovisioned + * disk fails instead of silently selecting whichever same-type (e.g. A/B) + * partition comes first. At least one selector must be configured. + * + * @desc: block device to scan + * @type_uuid: GPT type UUID to match, or NULL + * @name: partition name to match, or NULL + * Return: partition number (>= 1), -EINVAL if 'boot_dtb' is set to an + * invalid partition number or no selector is configured, or -ENODEV if no + * partition matched + */ +static int fw_fdt_find_part(struct blk_desc *desc, const char *type_uuid, + const char *name) +{ + const char *sel; + struct disk_partition info; + bool want_type = type_uuid && *type_uuid; + bool want_name = name && *name; + int p; + + sel = env_get("boot_dtb"); + if (sel && *sel) { + char *end; + ulong pin; + + pin = dectoul(sel, &end); + if (*end || !pin || pin > MAX_SEARCH_PARTITIONS) + return log_msg_ret("pin", -EINVAL); + + if (part_get_info(desc, pin, &info)) + return log_msg_ret("pin", -ENODEV); + + return pin; + } + + if (!want_type && !want_name) + return log_msg_ret("sel", -EINVAL); + + for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) { + bool type_match, name_match; + + if (part_get_info(desc, p, &info)) + continue; + + /* UUID text is case-insensitive (RFC 4122) */ + type_match = !want_type || + !strncasecmp(disk_partition_type_guid(&info), type_uuid, + UUID_STR_LEN); + name_match = !want_name || + !strcmp((const char *)info.name, name); + + if (type_match && name_match) + return p; + } + + return -ENODEV; +} + +/** + * fw_fdt_read_fit() - read the FIT manifest into an allocated buffer + * + * @desc: block device holding the firmware partition + * @part: partition number + * @fname: manifest filename + * @bufp: returns the allocated buffer holding the manifest + * @sizep: returns the manifest size in bytes + * Return: 0 on success, negative on error + */ +static int fw_fdt_read_fit(struct blk_desc *desc, int part, const char *fname, + void **bufp, ulong *sizep) +{ + loff_t size; + int ret; + + ret = fs_set_blk_dev_with_part(desc, part); + if (ret) + return log_msg_ret("fs", -EIO); + + ret = fs_size(fname, &size); + if (ret) + return log_msg_ret("size", -EIO); + + if (!size || size > FW_FDT_MAX_SIZE) + return log_msg_ret("big", -E2BIG); + + /* fs_size() consumed the mount */ + ret = fs_set_blk_dev_with_part(desc, part); + if (ret) + return log_msg_ret("fs2", -EIO); + + ret = fs_read_alloc(fname, size, 0, bufp); + if (ret) + return log_msg_ret("read", ret); + + *sizep = size; + + return 0; +} + +/** + * fw_fdt_check_images() - reject manifests that are not self-contained + * + * @fit: the FIT manifest + * Return: 0 if every image is embedded and has no load address, -EINVAL + * otherwise + */ +static int fw_fdt_check_images(const void *fit) +{ + int images, node; + + images = fdt_path_offset(fit, FIT_IMAGES_PATH); + if (images < 0) + return log_msg_ret("img", -EINVAL); + + fdt_for_each_subnode(node, fit, images) { + if (fdt_getprop(fit, node, FIT_LOAD_PROP, NULL)) + return log_msg_ret("load", -EINVAL); + + if (fdt_getprop(fit, node, FIT_DATA_OFFSET_PROP, NULL) || + fdt_getprop(fit, node, FIT_DATA_POSITION_PROP, NULL)) + return log_msg_ret("ext", -EINVAL); + } + + return 0; +} + +/** + * fw_fdt_assemble() - load the manifest and assemble the devicetree + * + * @out: returns the assembled devicetree and its backing buffers + * @src: the (validated) firmware-FDT source node + * Return: 0 on success, negative on error + */ +static int fw_fdt_assemble(struct firmware_fdt *out, ofnode src) +{ + struct bootm_headers images; + const char *type_uuid, *part_name, *fname, *conf; + struct blk_desc *desc; + ulong data, len; + void *fdt; + int part, ret; + + memset(&images, '\0', sizeof(images)); + images.verify = 1; + + fname = ofnode_read_string(src, "filename"); + if (!fname) + fname = FW_FDT_FILENAME; + + type_uuid = ofnode_read_string(src, "partition-type-uuid"); + part_name = ofnode_read_string(src, "partition-name"); + + ret = fw_fdt_get_blk(src, &desc); + if (ret) + return ret; + + part = fw_fdt_find_part(desc, type_uuid, part_name); + if (part < 0) + return log_msg_ret("part", part); + + ret = fw_fdt_read_fit(desc, part, fname, &out->fit, &out->fit_size); + if (ret) + return ret; + + ret = fit_check_format(out->fit, out->fit_size); + if (ret) + return log_msg_ret("fit", -EINVAL); + + ret = fw_fdt_check_images(out->fit); + if (ret) + return ret; + + conf = env_get("fw_fdt_config"); + + ret = boot_get_fdt_fit(&images, map_to_sysmem(out->fit), NULL, &conf, + IH_ARCH_DEFAULT, &data, &len); + if (ret < 0) + return log_msg_ret("conf", ret); + + fdt = map_sysmem(data, len); + + ret = fdt_check_full(fdt, len); + if (ret) + return log_msg_ret("chk", -EINVAL); + + out->fdt = fdt; + out->size = len; + out->name = fname; + + return 0; +} + +int firmware_fdt_load(struct firmware_fdt *out) +{ + ofnode src; + int ret; + + memset(out, '\0', sizeof(*out)); + + ret = fw_fdt_get_checked_source(&src); + if (ret) + return ret; + + ret = fw_fdt_assemble(out, src); + if (ret) { + firmware_fdt_free(out); + + /* + * Callers treat -ENOENT as "no source configured" and fall + * back to their normal devicetree. A source IS configured + * here, so remap any downstream -ENOENT (missing manifest, + * missing FIT configuration, ...) to -ENODEV to keep the + * failure fatal (fail closed). + */ + if (ret == -ENOENT) + ret = -ENODEV; + } + + return ret; +} + +void firmware_fdt_free(struct firmware_fdt *fw) +{ + u8 *fit = fw->fit; + + /* + * The assembled devicetree either points into the manifest buffer or + * is a separate allocation (made when applying overlays, or when + * aligning the devicetree); free it only in the latter case. + */ + if (fw->fdt && + ((u8 *)fw->fdt < fit || (u8 *)fw->fdt >= fit + fw->fit_size)) + free(fw->fdt); + + free(fw->fit); + memset(fw, '\0', sizeof(*fw)); +} diff --git a/doc/develop/bootstd/firmware_fdt.rst b/doc/develop/bootstd/firmware_fdt.rst new file mode 100644 index 00000000000..d849e66b5db --- /dev/null +++ b/doc/develop/bootstd/firmware_fdt.rst @@ -0,0 +1,94 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Firmware-owned devicetree +========================= + +Some platforms following EBBR / Arm SystemReady IR treat the devicetree as +part of the firmware: it lives on a firmware-owned partition and is updated +independently of the operating system, instead of being shipped in the OS +image or on the EFI System Partition. U-Boot must read that devicetree, +assemble it (base plus overlays) and hand it to the OS. + +The :c:func:`firmware_fdt_load` helper (``CONFIG_BOOTSTD_FIRMWARE_FDT``) +provides this. It is consumed by both EFI launch paths so the firmware-owned +devicetree is installed regardless of how the EFI application is started: + + - the per-device EFI bootmeth (``bootmeth_efi``), and + - the EFI boot manager (``efi_bootmgr_run()``). + +In each case the assembled devicetree is installed into the EFI configuration +table via :c:func:`efi_install_fdt`, exactly like any other source, so the OS +cannot tell where it came from. + +This is a generic replacement for vendor-specific firmware-devicetree +commands. + +The manifest +------------ + +The firmware partition carries a FIT manifest (by default ``fdt.itb``). Its +images hold the base DTB and any overlays, and each of its configurations +names one bootable combination through the standard ``fdt`` property:: + + configurations { + default = "conf-panel"; + conf-panel { + fdt = "fdt-base", "fdt-panel"; + }; + }; + +The helper selects a configuration, verifies it, loads the base devicetree +and applies the listed overlays in order (via :c:func:`boot_get_fdt_fit`, the +same code path ``bootm`` uses). The manifest describes and carries the +devicetree as one artefact, updated atomically with it. + +Images in the manifest must be self-contained flat devicetrees: images that +carry a ``load`` address and manifests using external data are rejected. With +``FIT_SIGNATURE`` enabled, node and configuration names must not contain +``@``. + +Configuration +------------- + +The manifest location is described in the control devicetree (see +``doc/device-tree-bindings/firmware-fdt.txt``): the ``bootstd`` node carries +a ``firmware-fdt-source`` phandle to a ``u-boot,firmware-fdt-block`` node, +which is a child of the media device that owns the partition and identifies +it by GPT type UUID and/or name, with an optional ``filename`` for the +manifest path. + +Two optional environment variables select among what the manifest ships: +``boot_dtb`` pins a partition number (A/B firmware partitions) and +``fw_fdt_config`` names the FIT configuration to use (defaulting to the +manifest's ``default`` configuration). + +If no source is configured, the helper returns ``-ENOENT`` and the caller +falls back to its normal devicetree source (ESP / built-in control FDT). If a +source is configured but cannot be assembled, the error is fatal for that EFI +launch path; this prevents a bad or unauthenticated firmware devicetree from +being silently replaced by another devicetree source. + +A firmware-owned devicetree is the complete, authoritative devicetree: no +other devicetree source is layered on top of it. In particular, +extension-board overlays (``extension_scan()``) are intentionally not +applied, since modifying the assembled (and, in secure mode, signed) +devicetree would defeat the authenticated-combination model. Boards using +extension boards should ship each supported combination as a manifest +configuration and select it with ``fw_fdt_config``. + +Secure boot +----------- + +Signing a FIT configuration authenticates the whole combination: the base, +the overlay set and its ordering are the signed unit, and a tampered selector +can only pick among combinations the firmware author pre-signed. Sign the +manifest and inject the public key into U-Boot's control devicetree as +usual:: + + mkimage -f fdt.its -k keys -K u-boot.dtb -r fdt.itb + +With ``CONFIG_FIT_SIGNATURE`` enabled and a required key in the control +devicetree, verification is enforced by the standard verified-boot policy: +an unsigned or tampered manifest is rejected and, because a configured source +never falls back, the boot fails closed rather than booting an unverified +devicetree. diff --git a/doc/develop/bootstd/index.rst b/doc/develop/bootstd/index.rst index ec74fc2fb9d..a7cd4c47261 100644 --- a/doc/develop/bootstd/index.rst +++ b/doc/develop/bootstd/index.rst @@ -14,4 +14,5 @@ Standard Boot cros rauc script + firmware_fdt sandbox diff --git a/doc/device-tree-bindings/firmware-fdt.txt b/doc/device-tree-bindings/firmware-fdt.txt new file mode 100644 index 00000000000..9b7cf2aff59 --- /dev/null +++ b/doc/device-tree-bindings/firmware-fdt.txt @@ -0,0 +1,149 @@ +U-Boot firmware-owned devicetree source (firmware-fdt) +====================================================== + +Some platforms (EBBR / Arm SystemReady IR) keep the devicetree on a +firmware-owned partition, updated independently of the operating system, +rather than shipping it in the OS image or the EFI System Partition. The +partition carries a FIT manifest: its images hold the base DTB and any +overlays, and each of its configurations names one bootable combination +through the standard 'fdt' property. U-Boot selects a configuration, +verifies it per the usual verified-boot policy, assembles the devicetree +(base plus overlays, in order) and installs it via the EFI configuration +table. + +This binding describes where that manifest lives. The source is referenced +from the bootstd node and described by a node that is a child of the media +device (UCLASS_MMC, ...) that owns the partition. + + +bootstd node +------------ + +Optional property: + +firmware-fdt-source: + phandle to the firmware-fdt source node to use + + +firmware-fdt source node +------------------------ + +Required properties: + +compatible: + "u-boot,firmware-fdt-block" - the manifest lives in a filesystem on a + GPT partition of a block device (the parent media device) + +The partition is selected by the 'boot_dtb' environment variable, if set, +which pins a partition number (for A/B firmware partitions). Otherwise the +first partition matching every configured selector is used: when both +'partition-type-uuid' and 'partition-name' are present, both must match (so +a misprovisioned disk fails closed instead of silently selecting whichever +same-type partition comes first). + +At least one of 'partition-type-uuid' or 'partition-name' must be present. + +Optional properties: + +partition-type-uuid: + GPT partition type UUID (string, case-insensitive) identifying the + firmware partition. When A/B firmware partitions share a type UUID, + 'partition-name' disambiguates between them. + +partition-name: + GPT partition name (string) identifying the firmware partition. Used to + disambiguate, or as a fallback when 'partition-type-uuid' is absent. + +filename: + Path of the FIT manifest on the partition (default: "fdt.itb"). + + +Environment +----------- + +Two optional environment variables select among what the manifest ships: + + boot_dtb pin a specific partition number (A/B firmware partitions) + fw_fdt_config name of the FIT configuration to use; when unset the + manifest's default configuration (or the best compatible + match, with FIT_BEST_MATCH) is used + +Both values only choose among combinations the firmware author shipped; with +signed configurations a tampered value cannot select an unsigned combination. + + +The manifest +------------ + +The manifest is a standard FIT image. Every image must be a flat devicetree +('type = "flat_dt"') without a load address (images carrying a 'load' +property are rejected), self-contained (no external data). For secure boot, +sign the configurations and enable FIT_SIGNATURE with a required key in the +control devicetree. Example source (.its): + + /dts-v1/; + / { + description = "Firmware-owned devicetree"; + #address-cells = <1>; + + images { + fdt-base { + description = "base board devicetree"; + data = /incbin/("board.dtb"); + type = "flat_dt"; + arch = "arm64"; + compression = "none"; + hash-1 { algo = "sha256"; }; + }; + fdt-panel { + description = "panel overlay"; + data = /incbin/("panel.dtbo"); + type = "flat_dt"; + arch = "arm64"; + compression = "none"; + hash-1 { algo = "sha256"; }; + }; + }; + + configurations { + default = "conf-panel"; + conf-panel { + fdt = "fdt-base", "fdt-panel"; + signature-1 { + algo = "sha256,rsa2048"; + key-name-hint = "fw"; + sign-images = "fdt"; + }; + }; + conf-base { + fdt = "fdt-base"; + signature-1 { + algo = "sha256,rsa2048"; + key-name-hint = "fw"; + sign-images = "fdt"; + }; + }; + }; + }; + +Note: with FIT_SIGNATURE enabled, node and configuration names must not +contain the '@' character. + + +Example +------- + + bootstd { + compatible = "u-boot,boot-std"; + + firmware-fdt-source = <&fw_fdt>; + }; + + &mmc0 { + fw_fdt: firmware-fdt { + compatible = "u-boot,firmware-fdt-block"; + partition-type-uuid = + "384e979b-eb76-435a-a3a6-1a071dbad91d"; + partition-name = "firmware"; + }; + }; diff --git a/include/firmware_fdt.h b/include/firmware_fdt.h new file mode 100644 index 00000000000..269e6e741f2 --- /dev/null +++ b/include/firmware_fdt.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Firmware-owned devicetree (FDT) source for EFI boot. + * + * Some platforms (EBBR / Arm SystemReady IR) keep the devicetree on a + * firmware-owned partition rather than in the OS image or the EFI System + * Partition. This provides a single helper that locates that partition, + * assembles the devicetree from the FIT manifest it carries, and returns the + * result so the caller can install it via the EFI configuration table + * (efi_install_fdt()). + * + * The same helper is intended to be consumed by both the per-device EFI + * bootmeth and the EFI boot manager, so firmware-owned DT provenance is + * honoured regardless of which path launches the EFI application. + */ + +#ifndef __FIRMWARE_FDT_H +#define __FIRMWARE_FDT_H + +#include <linux/errno.h> +#include <linux/types.h> + +/** + * struct firmware_fdt - an assembled, firmware-owned devicetree + * + * @fdt: pointer to the assembled devicetree in memory + * @size: size of the assembled devicetree, in bytes + * @name: manifest filename it was assembled from (for diagnostics) + * @fit: internal: buffer holding the FIT manifest + * @fit_size: internal: size of the FIT manifest, in bytes + * + * All memory is owned by the helper: release it with firmware_fdt_free() + * once the devicetree has been consumed (installed or copied). + */ +struct firmware_fdt { + void *fdt; + ulong size; + const char *name; + void *fit; + ulong fit_size; +}; + +#if CONFIG_IS_ENABLED(BOOTSTD_FIRMWARE_FDT) +/** + * firmware_fdt_load() - assemble the devicetree from a firmware partition + * + * Assemble the devicetree (the base DTB with its overlays applied, as + * described by the FIT manifest on the configured firmware partition) and + * return it in @out, ready to hand to the OS. + * + * @out: returns the assembled devicetree on success + * Return: 0 on success; -ENOENT if no source is configured (the caller may + * fall back to its normal devicetree); another negative errno if a + * configured source fails to assemble (the caller must fail, never + * fall back) + */ +int firmware_fdt_load(struct firmware_fdt *out); + +/** + * firmware_fdt_free() - release the memory behind an assembled devicetree + * + * Safe to call on a zeroed or already-freed @fw. + * + * @fw: the assembled devicetree to release + */ +void firmware_fdt_free(struct firmware_fdt *fw); +#else +static inline int firmware_fdt_load(struct firmware_fdt *out) +{ + return -ENOSYS; +} + +static inline void firmware_fdt_free(struct firmware_fdt *fw) +{ +} +#endif + +#endif /* __FIRMWARE_FDT_H */ -- 2.55.0

