summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/sandbox/dts/sandbox-boot.sh2
-rw-r--r--arch/sandbox/dts/sandbox-inner.sh4
-rw-r--r--arch/sandbox/dts/sandbox-outer.sh4
-rw-r--r--arch/sandbox/dts/sandbox_scripts.dtsi24
-rw-r--r--boot/Kconfig13
-rw-r--r--boot/image-fit.c24
-rw-r--r--configs/sandbox_defconfig2
-rw-r--r--doc/develop/devicetree/control.rst58
-rw-r--r--test/py/tests/test_source.py31
9 files changed, 156 insertions, 6 deletions
diff --git a/arch/sandbox/dts/sandbox-boot.sh b/arch/sandbox/dts/sandbox-boot.sh
new file mode 100644
index 00000000000..4f7fa661151
--- /dev/null
+++ b/arch/sandbox/dts/sandbox-boot.sh
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+echo "* default script"
diff --git a/arch/sandbox/dts/sandbox-inner.sh b/arch/sandbox/dts/sandbox-inner.sh
new file mode 100644
index 00000000000..b8fc8f7484b
--- /dev/null
+++ b/arch/sandbox/dts/sandbox-inner.sh
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+
+# Some comment.
+echo "* inner"
diff --git a/arch/sandbox/dts/sandbox-outer.sh b/arch/sandbox/dts/sandbox-outer.sh
new file mode 100644
index 00000000000..40294085433
--- /dev/null
+++ b/arch/sandbox/dts/sandbox-outer.sh
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+echo "* outer 1"
+source ${fdtcontroladdr}:inner
+echo "* outer 2"
diff --git a/arch/sandbox/dts/sandbox_scripts.dtsi b/arch/sandbox/dts/sandbox_scripts.dtsi
new file mode 100644
index 00000000000..c800ec39e87
--- /dev/null
+++ b/arch/sandbox/dts/sandbox_scripts.dtsi
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/ {
+ images {
+ default = "boot";
+ boot {
+ description = "Test boot script";
+ data = /incbin/("sandbox-boot.sh");
+ type = "script";
+ compression = "none";
+ };
+ outer {
+ description = "Script testing recursion";
+ data = /incbin/("sandbox-outer.sh");
+ type = "script";
+ compression = "none";
+ };
+ inner {
+ description = "Another test script";
+ data = /incbin/("sandbox-inner.sh");
+ type = "script";
+ compression = "none";
+ };
+ };
+};
diff --git a/boot/Kconfig b/boot/Kconfig
index e1114aea843..e6927d60b7b 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -103,6 +103,19 @@ config FIT_FULL_CHECK
of bugs or omissions in the code. This includes a bad structure,
multiple root nodes and the like.
+config CONTROL_DTB_AS_FIT
+ bool "Allow U-Boot's control DTB to act as FIT image"
+ help
+ Enable this to exempt U-Boot's control DTB from the sanity
+ checks done to ensure FIT images are valid. This can for
+ example be used to embed whole scripts in the control DTB,
+ that can then be invoked using 'source ${fdtcontroladdr}'.
+ In a secure boot setup, this is safe, as the control DTB is
+ necessarily covered by any mechanism verifying U-Boot and
+ can therefore be trusted. This only affects the case where
+ the image being checked is gd->fdt_blob. See
+ doc/develop/devicetree/control.rst for details.
+
config FIT_SIGNATURE
bool "Enable signature verification of FIT uImages"
depends on DM
diff --git a/boot/image-fit.c b/boot/image-fit.c
index 1cf8e7be957..773eb1857c5 100644
--- a/boot/image-fit.c
+++ b/boot/image-fit.c
@@ -1658,6 +1658,16 @@ static int fdt_check_no_at(const void *fit, int parent)
return 0;
}
+static int fit_check_images_node(const void *fit)
+{
+ if (fdt_path_offset(fit, FIT_IMAGES_PATH) < 0) {
+ log_debug("Wrong FIT format: no images parent node\n");
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
int fit_check_format(const void *fit, ulong size)
{
int ret;
@@ -1670,6 +1680,13 @@ int fit_check_format(const void *fit, ulong size)
return -ENOEXEC;
}
+ /*
+ * For the control DTB to act as a FIT image, we only require
+ * an /images node.
+ */
+ if (CONFIG_IS_ENABLED(CONTROL_DTB_AS_FIT) && fit == gd_fdt_blob())
+ return fit_check_images_node(fit);
+
if (CONFIG_IS_ENABLED(FIT_FULL_CHECK)) {
/*
* If we are not given the size, make do with calculating it.
@@ -1718,12 +1735,7 @@ int fit_check_format(const void *fit, ulong size)
}
/* mandatory subimages parent '/images' node */
- if (fdt_path_offset(fit, FIT_IMAGES_PATH) < 0) {
- log_debug("Wrong FIT format: no images parent node\n");
- return -ENOENT;
- }
-
- return 0;
+ return fit_check_images_node(fit);
}
int fit_conf_find_compat(const void *fit, const void *fdt)
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index ff4a6eb285a..512dbd6a0c6 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -20,6 +20,7 @@ CONFIG_EFI_CAPSULE_AUTHENTICATE=y
CONFIG_EFI_CAPSULE_CRT_FILE="board/sandbox/capsule_pub_key_good.crt"
CONFIG_BUTTON_CMD=y
CONFIG_FIT=y
+CONFIG_CONTROL_DTB_AS_FIT=y
CONFIG_FIT_SIGNATURE=y
CONFIG_FIT_CIPHER=y
CONFIG_FIT_VERITY=y
@@ -153,6 +154,7 @@ CONFIG_CMD_STACKPROTECTOR_TEST=y
CONFIG_CMD_SPAWN=y
CONFIG_MAC_PARTITION=y
CONFIG_OF_LIVE=y
+CONFIG_DEVICE_TREE_INCLUDES="sandbox_scripts.dtsi"
CONFIG_ENV_IS_NOWHERE=y
CONFIG_ENV_IS_IN_FAT=y
CONFIG_ENV_IS_IN_EXT4=y
diff --git a/doc/develop/devicetree/control.rst b/doc/develop/devicetree/control.rst
index 634114af59a..7d6117d5c4b 100644
--- a/doc/develop/devicetree/control.rst
+++ b/doc/develop/devicetree/control.rst
@@ -232,6 +232,64 @@ outside the U-Boot repository. You can use `DEVICE_TREE_INCLUDES` Kconfig
option to specify a list of .dtsi files that will also be included when
building .dtb files.
+Scripts embedded in control DTB
+-------------------------------
+
+The `DEVICE_TREE_INCLUDES` option can also be used to make the control
+DTB serve double duty as a FIT image. By including a `scripts.dtsi`
+file containing something like::
+
+ / {
+ images {
+ default = "boot";
+ boot {
+ description = "Bootscript";
+ data = /incbin/("boot.sh");
+ type = "script";
+ compression = "none";
+ };
+ factory-reset {
+ description = "Script for performing factory reset";
+ data = /incbin/("factory-reset.sh");
+ type = "script";
+ compression = "none";
+ };
+ };
+ };
+
+one can call those scripts using the `source` command in the U-Boot shell::
+
+ source ${fdtcontroladdr}:boot
+
+or just ``source ${fdtcontroladdr}`` for invoking the default.
+
+Since one does not need to separately build a "real" FIT image
+containing those scripts, this simplifies both the build process and
+the boot logic, as the latter does not need to first load the FIT
+image from storage.
+
+Another advantage is that when the bootloader and boot script must be
+updated together, it is easier to achieve a guaranteed atomic update
+when the boot script is embedded inside the U-Boot binary, instead of
+stored separately.
+
+For the above to work, one must enable the `CONTROL_DTB_AS_FIT` config
+option, which will (when the address passed to the `source` command is
+the address of U-Boot's control DTB) elide certain sanity checks that
+are normally done: With the above `.dtsi` snippet, the control DTB
+does not quite become a "real" FIT image - it lacks `timestamp` and
+`description` properties, but more importantly, FIT images cannot
+contain nodes with `@` in their names (unit addresses) anywhere, and
+the control DTB obviously does have such nodes.
+
+This is not a security problem, as the control DTB is necessarily
+trusted. In any secure boot setup where the bootloader is verified,
+that mechanism must also include verification of the control DTB. So
+in fact, since the scripts embedded this way are then also
+automatically verified, it simplifies implementation of secure
+boot. When using a separate FIT image, one must build it with
+appropriate signatures, just as when building a FIT image containing a
+kernel/dtb/initramfs.
Devicetree bindings schema checks
---------------------------------
diff --git a/test/py/tests/test_source.py b/test/py/tests/test_source.py
index 970d8c79869..29ab804f81b 100644
--- a/test/py/tests/test_source.py
+++ b/test/py/tests/test_source.py
@@ -34,3 +34,34 @@ def test_source(ubman):
ubman.run_command('fdt rm /images default')
assert 'Fail' in ubman.run_command('source || echo Fail')
assert 'Fail' in ubman.run_command('source \\# || echo Fail')
+
[email protected]('cmd_echo')
[email protected]('cmd_source')
[email protected]('control_dtb_as_fit')
+def test_source_control_dtb(ubman):
+ output = ubman.run_command('source ${fdtcontroladdr}')
+ assert '* default script' in output
+
+ output = ubman.run_command('source ${fdtcontroladdr}:boot')
+ assert '* default script' in output
+
+ output = ubman.run_command('source ${fdtcontroladdr}:outer')
+ assert '* outer 1' in output
+ assert '* inner' in output
+ assert '* outer 2' in output
+
+ output = ubman.run_command('source ${fdtcontroladdr}:inner')
+ assert '* outer' not in output
+ assert '* inner' in output
+
+ assert 'Fail' in ubman.run_command('source ${fdtcontroladdr}:no-such-script || echo Fail')
+
[email protected]('cmd_echo')
[email protected]('cmd_source')
[email protected]('control_dtb_as_fit')
+def test_source_reject_control_dtb(ubman):
+ assert 'Fail' in ubman.run_command('source ${fdtcontroladdr} || echo Fail')
+ assert 'Fail' in ubman.run_command('source ${fdtcontroladdr}:boot || echo Fail')