Merge branch '2023-08-29-integrate-efi-capsule-update-better-in-to-u-boot-buildflow' into next

To quote the author:
This patchset aims to bring two capsule related tasks under the U-Boot
build flow.

The first task is related to generation of capsules. The capsules can be
generated as part of U-Boot build, and this is being achieved through
binman, by adding a capsule entry type. The capsules can be generated by
specifying the capsule parameters as properties under the capsule entry
node.

The other task is the embedding of the public key into the platform's
DTB. The public key is in the form of an EFI Signature List(ESL) file
and is used for capsule authentication. This is being achieved by adding
the signature node containing the capsule public key in the platform's
DTB.

Corresponding changes have also been made to the test setup of the EFI
capsule update feature. The ESL public key file was embedded into the
sandbox platform's test.dtb as part of the test setup, post U-Boot
build. This is now no longer needed as the embedding of the ESL happens
as part of the build.

Secondly, the capsules needed for testing the EFI capsule update feature
were being generated through the invocation of the mkeficapsule tool.
This setup has also been changed to introduce generation of these
capsules through binman.

The document has been updated to reflect the above changes.
This commit is contained in:
Tom Rini
2023-08-29 16:58:42 -04:00
36 changed files with 1175 additions and 224 deletions

View File

@@ -275,7 +275,7 @@ stages:
TEST_PY_BD: "sandbox"
BUILD_ENV: "FTRACE=1 NO_LTO=1"
TEST_PY_TEST_SPEC: "trace"
OVERRIDE: "-a CONFIG_TRACE=y -a CONFIG_TRACE_EARLY=y -a CONFIG_TRACE_EARLY_SIZE=0x01000000"
OVERRIDE: "-a CONFIG_TRACE=y -a CONFIG_TRACE_EARLY=y -a CONFIG_TRACE_EARLY_SIZE=0x01000000 -a CONFIG_TRACE_BUFFER_SIZE=0x02000000"
coreboot:
TEST_PY_BD: "coreboot"
TEST_PY_ID: "--id qemu"

View File

@@ -315,7 +315,7 @@ sandbox trace_test.py:
TEST_PY_BD: "sandbox"
BUILD_ENV: "FTRACE=1 NO_LTO=1"
TEST_PY_TEST_SPEC: "trace"
OVERRIDE: "-a CONFIG_TRACE=y -a CONFIG_TRACE_EARLY=y -a CONFIG_TRACE_EARLY_SIZE=0x01000000"
OVERRIDE: "-a CONFIG_TRACE=y -a CONFIG_TRACE_EARLY=y -a CONFIG_TRACE_EARLY_SIZE=0x01000000 -a CONFIG_TRACE_BUFFER_SIZE=0x02000000"
<<: *buildman_and_testpy_dfn
evb-ast2500 test.py:

View File

@@ -354,4 +354,4 @@
&r1en_pins
&r1oen_pins
>;
};
};

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCmPw1pGd2xNW0p
lesRXkkek3uwUB06Nt61tnZvpMkBKt4IokqGWz1tZls+Z2CqvwOfcsPZ27cPRYSu
xRnM3YdL4MG6SePV7i/YSNw3rq8CP8zLGtCbNIfsfsNfPQEtPBpw6+7pMJKhjqpV
2U2UQzZEiX4qlnhLpyv2JNJag27yf0feLdJi7HnJ9xdvcXpA1DSGm4y+DDhgYeI8
DEteEu6s0TYQfnOZSQOeJi+1/Qz0S594uFJB37MyGh/mB15ILb8gva4nA3ayHOBK
0dd+HSiUCGYrLYO7aj+nfzQj9N1qTlzCnC1603bMczU5pkwcODg6xP0Sn11J6RYy
y0c0qzJLAgMBAAECggEABDY2MLoew3IkBltrParAWAUUcFLi95jw92q6BkOHEJg8
2qia1yCitPUtPodMLmOKF5x4EdgXg5sv2O8MGbWP1VtUKXGh3QJcnRnNmsZ1hXJC
RBcrei2aVLsqf0V2Mg3+GuG8PW3vLWHyZ/Sd6afeuXEYm2Bzrw9J5rfd3dBVKm7f
HBvIyy1ATO/2cbUaEaCLOyhxLhssTI2TIK5SjlsjFLxiQXEi6RyGfBxUCriKZykS
krMdvYh7Tf0uYcv0STmQ5s5Rd+RhRIGCVAdsNBxxJjgBAgqqa/B+kWbcc6o2D41n
yWjErUaBBx3t0A7oT4K4DSTYwMNDVY3fhdd+szsocQKBgQDjnm8LG4UO6OQDm6iX
0vTQTItoAz5TU6GEjHTCfVEqiupD4LKfHhSXwp2hRyzxXO5oNTU9MQCzYd7Npes0
oVk4Tjo3YDacNPgxqKjODu/Q+tkTH15ydzGr674+YXHfCA1uT5GKOiiF0H1FZgMa
Dk0s+3uWX34vbL4QCu97bUhBewKBgQC6+Z0J9sClgWvvjkglJN3XhRnAacp+WgX7
bkpgSboXIIsqeqhd1WCLeV7L1pcZgifYBMPojf5LTBqBedL1q3RuqiqQWD/bSIYN
Oc9KCdTjksS8Zo+w+s5zDObDhW9y13H2mKwDqilYBrT4fiA62wPMf1SjEF+RSC6K
ZrQzHO1xcQKBgAILsXnLFIYOx8XUh05eAf9BQNt9c/jxvnjffkklMS6Nsw9LHK/b
aFn40MvbROcia64aFFFpeFUkYwk8HYIKlS+xXEqVHciHnVds6Z94eOVK69qFJKco
tRSTeNE8tPZJLz23j1pLrYOOXSHbidmZGU53MCQo1Yx9kLO6NW7Ji6WzAoGBALP4
lEoE80Xbn3NEdvkZ1VcfzLvCmKCqMlvjuz+Xd8HPF2VaDznSq01VFAQMmAB7obJy
U8hC9OSxakn6Yy8JS9dBgBrUdxKxaibM4FQZxosOuMPHzMPDhniDkJPemnnmGtIL
/nbAkW8jdYpCjO9Z5PwwC92xYuvKmNGrLgSM8ZhhAoGAfgSZTpASXubM18E3ecfw
5z333wf9qEQgZj7i9MzByFZudyHUhv/FPW1ocUJf36Wu1dfofZg3noSL6oakrm2v
dFDo4PoyCStuF0w9SSzpIld01ZG0t7XqphY0DmshCXIXsqr7Vb4WrbBI7KX+b3Um
BzmROfaSud97NjQ/RA26OZk=
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCwBfaV0P1jRzS6
13U1T+4VbuMVsxFXhwHJY5z5Fx6v+cWBf3K1ruK+7cEnW55ZHXvNE2JCkjMvISKm
hI/DLJWIPnAus8tFdU/R2u5oJbKI+b6GbuamO/CG9HsXZ58lOC6r2ckjixxovsA9
SFshccdIv2YrwiVsWeyFpH+rB3/+cFbrgdWpaUc1367GkU/ZCnSRDBvVvzRRI1a4
y2NogFqbZHXHENpzWNJ3TTXhf9dwM5HFGkmX7SA43Dtazae6CB4EaUKzLYWj3+ae
AQbdvBrupKZQz1PUKn7X6+BGaLujHthvibYppNegPvqbJ1xBbv59CQK+lRULwC05
NYw5+sIxAgMBAAECggEAHn8h/knjpMAw/BAZP//VrYP1Nwy7u/Dpl9U43JUrXWzG
Uc3dd2nR4id6GBIRCLqJePnbQ9JlqMwyXyxHZhbC34SF1imTVbjh9+dY99VULdQr
NMphDrsCzLbt3pu24HFv8Jk+dniDFwi5cMSo+U3nq4xxrLIp3rBjwLHD5sNZYyEU
9xZnj7ziTn5X8da8iRxNpyzz2kQeVemJ0ahr/IkX718bkakSFMesGkln06vH7rAs
069SeqOPrFEbWYXI5iMktLugl3JZpzasRE48j0M42PuProgvT7jb8B35ZF7kn0jT
MqTIHglsJRWcSY0fAb2lHSAvd2vLLVunxr9PDWZvGQKBgQDVzVTuvo1CrVrQLy+B
tpy2k5mjR3qxAOcoWTnKcMErLe8imWWaxukODenP4XqQIX4Sl+X3BXxOqun0Klap
FEsI7TWSHf0eULFtFj0SCgqfRR+V/nblP05eO2nFXgr5YdNa1bWf/aMHplBo4q9e
bbAr4InUB7IGWL2cWjhOhWuJbQKBgQDSw81cBM+vGPUYH/wlxlTVgZCo2Dg2NHjt
LUBqvOZNr21j2F+w8t1vKmqwhkqpc5HIi3pHjEA5gZLTRtmf4GQyo973I6MGn4bS
eayOd6/+FkAi9DUD+WaF7yctJqeevav6KF2UCiz78OtCAU5Y9jFFJpuOANIztI7m
t7ZCUpMFVQKBgFnAsP7oj3SGQbFTnaXeeztKCx04TJExx9hwXIpXe0AdMF5d9wFa
r0tvG9Bg34rSBJLZoXhpnR2JMl2FyIuCMV219t84J6IqTdF1nH2OKZdi9TeKc28Z
fFSirGxmZkT6hDeFr5FScLYtY2QkhWomseY5hKK1+E4hwrd4SFruN46hAoGBAJgh
nzTBgEtqH1enlrCJhSiLmihV0dVGcNb559pjuXTvoG0GfKPT2gPowRPkCzZe5ia0
jrHgSWd44MtCA8nEBW8MG9+VyJH6Si3Yh7ZaLB2iX+8bCL1yow8f/c44bZtGW0F5
K3q1EZ1VW+rL2IqcQhog8P1CGHgb514f0x3yTo71AoGACGdb+Nb6lg8OSJPUcuuH
xsWk6RhkJl9bldTleS+QT3R9zO3FvbTwnCCYJboh5Cq/jVmiA7T+fcVAyEJNHSdm
hxbHdScuiJdNWL9+FczOkylnKH3VEdG3RS5lGdyi6r+miTMs3h8WfzGp4JINysjg
PUFskK36qGjASfkRUn0hizQ=
-----END PRIVATE KEY-----

Binary file not shown.

View File

@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDDzCCAfegAwIBAgIUWw3vHYnrjoHUXytxSm2eYWzbYVAwDQYJKoZIhvcNAQEL
BQAwFjEUMBIGA1UEAwwLVEVTVF9TSUdORVIwIBcNMjMwODA0MTgwODEyWhgPMzAw
MzEwMDYxODA4MTJaMBYxFDASBgNVBAMMC1RFU1RfU0lHTkVSMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEApj8NaRndsTVtKZXrEV5JHpN7sFAdOjbetbZ2
b6TJASreCKJKhls9bWZbPmdgqr8Dn3LD2du3D0WErsUZzN2HS+DBuknj1e4v2Ejc
N66vAj/MyxrQmzSH7H7DXz0BLTwacOvu6TCSoY6qVdlNlEM2RIl+KpZ4S6cr9iTS
WoNu8n9H3i3SYux5yfcXb3F6QNQ0hpuMvgw4YGHiPAxLXhLurNE2EH5zmUkDniYv
tf0M9EufeLhSQd+zMhof5gdeSC2/IL2uJwN2shzgStHXfh0olAhmKy2Du2o/p380
I/Tdak5cwpwtetN2zHM1OaZMHDg4OsT9Ep9dSekWMstHNKsySwIDAQABo1MwUTAd
BgNVHQ4EFgQUm9b8SnF811nweXSfGisfpzUHGwgwHwYDVR0jBBgwFoAUm9b8SnF8
11nweXSfGisfpzUHGwgwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
AQEAaOZFOcQzF1MRekcBmIZMaHSWYxOUrVLzBNSNhFD8muYiUAAufrkyTUq0Mmat
w5hAnJ34VGpU1wxQlr/uwH7wpZZnGuj10rAp3tqES0g24AeH1bC9wmRs+rD6dcZR
YmZq6FxtV7Cv3pQX7lhDYbcBj2za3YT6I1+yczskAHR6KYYuJzKJ7XRVCL7ZlYRX
pUMZBQq2eAVWlW/c5iDT3KoGZUD9Of71F7qyUAqMMYafeDxguDz7gKstoXVCklQ+
I4C7JKmRbrRvMgXx6O1clGhAsRZ0nNAtzi7XT5tD27qFwIPgwv48RWgsmPtzE03S
YGQ5WhYMdHOOjWmcV6MDkCpiSA==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDDzCCAfegAwIBAgIUUzrWhMi7oPFshQP6eFlccqf7exswDQYJKoZIhvcNAQEL
BQAwFjEUMBIGA1UEAwwLVEVTVF9TSUdORVIwIBcNMjMwODA0MTgwNzQyWhgPMzAw
MzEwMDYxODA3NDJaMBYxFDASBgNVBAMMC1RFU1RfU0lHTkVSMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsAX2ldD9Y0c0utd1NU/uFW7jFbMRV4cByWOc
+Rcer/nFgX9yta7ivu3BJ1ueWR17zRNiQpIzLyEipoSPwyyViD5wLrPLRXVP0dru
aCWyiPm+hm7mpjvwhvR7F2efJTguq9nJI4scaL7APUhbIXHHSL9mK8IlbFnshaR/
qwd//nBW64HVqWlHNd+uxpFP2Qp0kQwb1b80USNWuMtjaIBam2R1xxDac1jSd001
4X/XcDORxRpJl+0gONw7Ws2nuggeBGlCsy2Fo9/mngEG3bwa7qSmUM9T1Cp+1+vg
Rmi7ox7Yb4m2KaTXoD76mydcQW7+fQkCvpUVC8AtOTWMOfrCMQIDAQABo1MwUTAd
BgNVHQ4EFgQUHvG7Xchqzwdggky+oyzlpNem8UowHwYDVR0jBBgwFoAUHvG7Xchq
zwdggky+oyzlpNem8UowDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
AQEAUn1ncSqeXbQAHNrVOFldLwu70hNlMxf2z4EfH2M7vJgrpwkRuIFw7PXNITBh
CImd/ghm5NGFysrK7BwdHkFvUXZV3rE93BhcLC9leWfky33kW9olIzpE14i5FfBn
ABmaokPhOrzAneGzU35sZHNotlqOrzgpKVkpOWrykhYZ5Qjk8Sz0xvzuG8TJc20s
2og+W8Rm2u/xI9xPxtFbq9vUjvFS35o1pm+vkzpgNdo4YS1PG37BW/aopsooLSk7
9Rxv5vzNXtQqeZ5qBdKbAVh3OsgqwigTmXVvOX3xpy9r9qiimhaISxCt83RZ7wQW
I19t9pXyxAi6u7MRhJZlAeH/3w==
-----END CERTIFICATE-----

View File

@@ -340,6 +340,7 @@ CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y
CONFIG_EFI_CAPSULE_ON_DISK=y
CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y
CONFIG_EFI_CAPSULE_AUTHENTICATE=y
CONFIG_EFI_CAPSULE_ESL_FILE="board/sandbox/capsule_pub_esl_good.esl"
CONFIG_EFI_SECURE_BOOT=y
CONFIG_TEST_FDTDEC=y
CONFIG_UNIT_TEST=y

View File

@@ -226,6 +226,7 @@ CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y
CONFIG_EFI_CAPSULE_ON_DISK=y
CONFIG_EFI_CAPSULE_FIRMWARE_FIT=y
CONFIG_EFI_CAPSULE_AUTHENTICATE=y
CONFIG_EFI_CAPSULE_ESL_FILE="board/sandbox/capsule_pub_esl_good.esl"
CONFIG_UNIT_TEST=y
CONFIG_UT_TIME=y
CONFIG_UT_DM=y

View File

@@ -236,6 +236,8 @@ CONFIG_TPM=y
CONFIG_LZ4=y
CONFIG_ZSTD=y
CONFIG_ERRNO_STR=y
CONFIG_EFI_CAPSULE_ON_DISK=y
CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y
CONFIG_UNIT_TEST=y
CONFIG_SPL_UNIT_TEST=y
CONFIG_UT_TIME=y

View File

@@ -244,6 +244,8 @@ CONFIG_LZ4=y
CONFIG_ZSTD=y
CONFIG_ERRNO_STR=y
CONFIG_SPL_HEXDUMP=y
CONFIG_EFI_CAPSULE_ON_DISK=y
CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y
CONFIG_UNIT_TEST=y
CONFIG_SPL_UNIT_TEST=y
CONFIG_UT_TIME=y

View File

@@ -255,6 +255,8 @@ CONFIG_LZ4=y
CONFIG_ZSTD=y
# CONFIG_VPL_LZMA is not set
CONFIG_ERRNO_STR=y
CONFIG_EFI_CAPSULE_ON_DISK=y
CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y
CONFIG_UNIT_TEST=y
CONFIG_SPL_UNIT_TEST=y
CONFIG_UT_TIME=y

View File

@@ -318,6 +318,9 @@ Run the following command
--guid <image GUID> \
<capsule_file_name>
Capsule with firmware version
*****************************
The UEFI specification does not define the firmware versioning mechanism.
EDK II reference implementation inserts the FMP Payload Header right before
the payload. It coutains the fw_version and lowest supported version,
@@ -345,6 +348,43 @@ add --fw-version option in mkeficapsule tool.
If the --fw-version option is not set, FMP Payload Header is not inserted
and fw_version is set as 0.
Capsule Generation through binman
*********************************
Support has also been added to generate capsules during U-Boot build
through binman. This requires the platform's DTB to be populated with
the capsule entry nodes for binman. The capsules then can be generated
by specifying the capsule parameters as properties in the capsule
entry node.
Check the test/py/tests/test_efi_capsule/capsule_gen_binman.dts file
as reference for how a typical binman node for capsule generation
looks like. For generating capsules as part of the platform's build, a
capsule node would then have to be included into the platform's
devicetree.
A typical binman node for generating a capsule would look like::
capsule {
filename = "u-boot.capsule";
efi-capsule {
image-index = <0x1>;
image-guid = "09d7cf52-0720-4710-91d1-08469b7fe9c8";
u-boot {
};
};
};
In the above example, a capsule file named u-boot.capsule will be
generated with u-boot.bin as it's input payload. The capsule
generation parameters like image-index and image-guid are being
specified as properties. Similarly, other properties like the private
and public key certificate can be specified for generating signed
capsules. Refer :ref:`etype_efi_capsule` for documentation about the
efi-capsule binman entry type, which describes all the properties that
can be specified.
Performing the update
*********************
@@ -522,20 +562,11 @@ and used by the steps highlighted below.
...
}
You can do step-4 manually with
.. code-block:: console
$ dtc -@ -I dts -O dtb -o signature.dtbo signature.dts
$ fdtoverlay -i orig.dtb -o new.dtb -v signature.dtbo
where signature.dts looks like::
&{/} {
signature {
capsule-key = /incbin/("CRT.esl");
};
};
You can perform step-4 through the Kconfig symbol
CONFIG_EFI_CAPSULE_ESL_FILE. This symbol points to the esl file
generated in step-2. Once the symbol has been populated with the path
to the esl file, it will automatically get embedded into the
platform's dtb as part of U-Boot build.
Anti-rollback Protection
************************

View File

@@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2023, Linaro Limited
*/
#if !defined(_SANDBOX_EFI_CAPSULE_H_)
#define _SANDBOX_EFI_CAPSULE_H_
#define SANDBOX_UBOOT_IMAGE_GUID "09d7cf52-0720-4710-91d1-08469b7fe9c8"
#define SANDBOX_UBOOT_ENV_IMAGE_GUID "5a7021f5-fef2-48b4-aaba-832e777418c0"
#define SANDBOX_FIT_IMAGE_GUID "3673b45d-6a7c-46f3-9e60-adabb03f7937"
#define SANDBOX_INCORRECT_GUID "058b7d83-50d5-4c47-a195-60d86ad341c4"
#define UBOOT_FIT_IMAGE "u-boot_bin_env.itb"
#define CAPSULE_PRIV_KEY "capsule_priv_key_good.key"
#define CAPSULE_PUB_KEY "capsule_pub_key_good.crt"
#define CAPSULE_INVAL_KEY "capsule_priv_key_bad.key"
#define CAPSULE_INVAL_PUB_KEY "capsule_pub_key_bad.crt"
#endif /* _SANDBOX_EFI_CAPSULE_H_ */

View File

@@ -272,6 +272,14 @@ config EFI_CAPSULE_MAX
Select the max capsule index value used for capsule report
variables. This value is used to create CapsuleMax variable.
config EFI_CAPSULE_ESL_FILE
string "Path to the EFI Signature List File"
depends on EFI_CAPSULE_AUTHENTICATE
help
Provides the path to the EFI Signature List file which will
be embedded in the platform's device tree and used for
capsule authentication at the time of capsule update.
config EFI_DEVICE_PATH_TO_TEXT
bool "Device path to text protocol"
default y

View File

@@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0+
/**
* Devicetree file with the public key EFI Signature List(ESL)
* node. This file is used to generate the dtsi file to be
* included into the DTB.
*/
/ {
signature {
capsule-key = /incbin/("ESL_BIN_FILE");
};
};

View File

@@ -179,10 +179,13 @@ ifdef DEVICE_TREE_DEBUG
u_boot_dtsi_options_debug = $(warning $(u_boot_dtsi_options_raw))
endif
# We use the first match
u_boot_dtsi = $(strip $(u_boot_dtsi_options_debug) \
# We use the first match to be included
dtsi_include_list = $(strip $(u_boot_dtsi_options_debug) \
$(notdir $(firstword $(u_boot_dtsi_options))))
# The CONFIG_DEVICE_TREE_INCLUDES also need to be included
dtsi_include_list += $(CONFIG_DEVICE_TREE_INCLUDES)
# Modified for U-Boot
dtc_cpp_flags = -Wp,-MD,$(depfile).pre.tmp -nostdinc \
$(UBOOTINCLUDE) \
@@ -320,8 +323,8 @@ quiet_cmd_dtc = DTC $@
# Bring in any U-Boot-specific include at the end of the file
# And finally any custom .dtsi fragments specified with CONFIG_DEVICE_TREE_INCLUDES
cmd_dtc = mkdir -p $(dir ${dtc-tmp}) ; \
(cat $<; $(if $(u_boot_dtsi),echo '$(pound)include "$(u_boot_dtsi)"')) > $(pre-tmp); \
$(foreach f,$(subst $(quote),,$(CONFIG_DEVICE_TREE_INCLUDES)), \
(cat $< > $(pre-tmp)); \
$(foreach f,$(subst $(quote),,$(dtsi_include_list)), \
echo '$(pound)include "$(f)"' >> $(pre-tmp);) \
$(HOSTCC) -E $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $(pre-tmp) ; \
$(DTC) -O dtb -o $@ -b 0 \
@@ -331,7 +334,24 @@ cmd_dtc = mkdir -p $(dir ${dtc-tmp}) ; \
; \
sed "s:$(pre-tmp):$(<):" $(depfile).pre.tmp $(depfile).dtc.tmp > $(depfile)
$(obj)/%.dtb: $(src)/%.dts $(DTC) FORCE
quiet_cmd_capsule_esl_gen = CAPSULE_ESL_GEN $@
cmd_capsule_esl_gen = \
$(shell sed "s:ESL_BIN_FILE:$(capsule_esl_path):" $(capsule_esl_input_file) > $@)
$(obj)/.capsule_esl.dtsi: FORCE
$(call cmd_capsule_esl_gen)
capsule_esl_input_file=$(srctree)/lib/efi_loader/capsule_esl.dtsi.in
capsule_esl_dtsi = .capsule_esl.dtsi
capsule_esl_path=$(abspath $(srctree)/$(subst $(quote),,$(CONFIG_EFI_CAPSULE_ESL_FILE)))
ifdef CONFIG_EFI_CAPSULE_AUTHENTICATE
dtsi_include_list += $(capsule_esl_dtsi)
endif
dtsi_include_list_deps = $(addprefix $(obj)/,$(subst $(quote),,$(dtsi_include_list)))
$(obj)/%.dtb: $(src)/%.dts $(DTC) $(dtsi_include_list_deps) FORCE
$(call if_changed_dep,dtc)
pre-tmp = $(subst $(comma),_,$(dot-target).pre.tmp)

View File

@@ -0,0 +1,321 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Devicetree for capsule generation through binman
*/
/dts-v1/;
#include <sandbox_efi_capsule.h>
/ {
binman: binman {
multiple-images;
};
};
&binman {
itb {
filename = UBOOT_FIT_IMAGE;
fit {
description = "Automatic U-Boot environment update";
#address-cells = <2>;
images {
u-boot-bin {
description = "U-Boot binary on SPI Flash";
compression = "none";
type = "firmware";
arch = "sandbox";
load = <0>;
text {
text = "u-boot:New";
};
hash-1 {
algo = "sha1";
};
};
u-boot-env {
description = "U-Boot environment on SPI Flash";
compression = "none";
type = "firmware";
arch = "sandbox";
load = <0>;
text {
text = "u-boot-env:New";
};
hash-1 {
algo = "sha1";
};
};
};
};
};
capsule1 {
filename = "Test01";
efi-capsule {
image-index = <0x1>;
image-guid = SANDBOX_UBOOT_IMAGE_GUID;
text {
text = "u-boot:New";
};
};
};
capsule2 {
filename = "Test02";
efi-capsule {
image-index = <0x2>;
image-guid = SANDBOX_UBOOT_ENV_IMAGE_GUID;
text {
text = "u-boot-env:New";
};
};
};
capsule3 {
filename = "Test03";
efi-capsule {
image-index = <0x1>;
image-guid = SANDBOX_INCORRECT_GUID;
text {
text = "u-boot:New";
};
};
};
capsule4 {
filename = "Test04";
efi-capsule {
image-index = <0x1>;
image-guid = SANDBOX_FIT_IMAGE_GUID;
blob {
filename = UBOOT_FIT_IMAGE;
};
};
};
capsule5 {
filename = "Test05";
efi-capsule {
image-index = <0x1>;
image-guid = SANDBOX_INCORRECT_GUID;
blob {
filename = UBOOT_FIT_IMAGE;
};
};
};
capsule6 {
filename = "Test101";
efi-capsule {
image-index = <0x1>;
fw-version = <0x5>;
image-guid = SANDBOX_UBOOT_IMAGE_GUID;
text {
text = "u-boot:New";
};
};
};
capsule7 {
filename = "Test102";
efi-capsule {
image-index = <0x2>;
fw-version = <0xa>;
image-guid = SANDBOX_UBOOT_ENV_IMAGE_GUID;
text {
text = "u-boot-env:New";
};
};
};
capsule8 {
filename = "Test103";
efi-capsule {
image-index = <0x1>;
fw-version = <0x2>;
image-guid = SANDBOX_UBOOT_IMAGE_GUID;
text {
text = "u-boot:New";
};
};
};
capsule9 {
filename = "Test104";
efi-capsule {
image-index = <0x1>;
fw-version = <0x5>;
image-guid = SANDBOX_FIT_IMAGE_GUID;
blob {
filename = UBOOT_FIT_IMAGE;
};
};
};
capsule10 {
filename = "Test105";
efi-capsule {
image-index = <0x1>;
fw-version = <0x2>;
image-guid = SANDBOX_FIT_IMAGE_GUID;
blob {
filename = UBOOT_FIT_IMAGE;
};
};
};
capsule11 {
filename = "Test11";
efi-capsule {
image-index = <0x1>;
image-guid = SANDBOX_UBOOT_IMAGE_GUID;
private-key = CAPSULE_PRIV_KEY;
public-key-cert = CAPSULE_PUB_KEY;
monotonic-count = <0x1>;
text {
text = "u-boot:New";
};
};
};
capsule12 {
filename = "Test12";
efi-capsule {
image-index = <0x1>;
image-guid = SANDBOX_UBOOT_IMAGE_GUID;
private-key = CAPSULE_INVAL_KEY;
public-key-cert = CAPSULE_INVAL_PUB_KEY;
monotonic-count = <0x1>;
text {
text = "u-boot:New";
};
};
};
capsule13 {
filename = "Test13";
efi-capsule {
image-index = <0x1>;
image-guid = SANDBOX_FIT_IMAGE_GUID;
private-key = CAPSULE_PRIV_KEY;
public-key-cert = CAPSULE_PUB_KEY;
monotonic-count = <0x1>;
blob {
filename = UBOOT_FIT_IMAGE;
};
};
};
capsule14 {
filename = "Test14";
efi-capsule {
image-index = <0x1>;
image-guid = SANDBOX_FIT_IMAGE_GUID;
private-key = CAPSULE_INVAL_KEY;
public-key-cert = CAPSULE_INVAL_PUB_KEY;
monotonic-count = <0x1>;
blob {
filename = UBOOT_FIT_IMAGE;
};
};
};
capsule15 {
filename = "Test111";
efi-capsule {
image-index = <0x1>;
fw-version = <0x5>;
image-guid = SANDBOX_UBOOT_IMAGE_GUID;
private-key = CAPSULE_PRIV_KEY;
public-key-cert = CAPSULE_PUB_KEY;
monotonic-count = <0x1>;
text {
text = "u-boot:New";
};
};
};
capsule16 {
filename = "Test112";
efi-capsule {
image-index = <0x2>;
fw-version = <0xa>;
image-guid = SANDBOX_UBOOT_ENV_IMAGE_GUID;
private-key = CAPSULE_PRIV_KEY;
public-key-cert = CAPSULE_PUB_KEY;
monotonic-count = <0x1>;
text {
text = "u-boot-env:New";
};
};
};
capsule17 {
filename = "Test113";
efi-capsule {
image-index = <0x1>;
fw-version = <0x2>;
image-guid = SANDBOX_UBOOT_IMAGE_GUID;
private-key = CAPSULE_PRIV_KEY;
public-key-cert = CAPSULE_PUB_KEY;
monotonic-count = <0x1>;
text {
text = "u-boot:New";
};
};
};
capsule18 {
filename = "Test114";
efi-capsule {
image-index = <0x1>;
fw-version = <0x5>;
image-guid = SANDBOX_FIT_IMAGE_GUID;
private-key = CAPSULE_PRIV_KEY;
public-key-cert = CAPSULE_PUB_KEY;
monotonic-count = <0x1>;
blob {
filename = UBOOT_FIT_IMAGE;
};
};
};
capsule19 {
filename = "Test115";
efi-capsule {
image-index = <0x1>;
fw-version = <0x2>;
image-guid = SANDBOX_FIT_IMAGE_GUID;
private-key = CAPSULE_PRIV_KEY;
public-key-cert = CAPSULE_PUB_KEY;
monotonic-count = <0x1>;
blob {
filename = UBOOT_FIT_IMAGE;
};
};
};
};

View File

@@ -4,6 +4,8 @@
"""Fixture for UEFI capsule test."""
import os
from subprocess import call, check_call, CalledProcessError
import pytest
from capsule_defs import CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR, EFITOOLS_PATH
@@ -34,39 +36,30 @@ def efi_capsule_data(request, u_boot_config):
capsule_auth_enabled = u_boot_config.buildconfig.get(
'config_efi_capsule_authenticate')
key_dir = u_boot_config.source_dir + '/board/sandbox'
if capsule_auth_enabled:
# Create private key (SIGNER.key) and certificate (SIGNER.crt)
check_call('cd %s; '
'openssl req -x509 -sha256 -newkey rsa:2048 '
'-subj /CN=TEST_SIGNER/ -keyout SIGNER.key '
'-out SIGNER.crt -nodes -days 365'
% data_dir, shell=True)
check_call('cd %s; %scert-to-efi-sig-list SIGNER.crt SIGNER.esl'
% (data_dir, EFITOOLS_PATH), shell=True)
# Get the keys from the board directory
check_call('cp %s/capsule_priv_key_good.key %s/SIGNER.key'
% (key_dir, data_dir), shell=True)
check_call('cp %s/capsule_pub_key_good.crt %s/SIGNER.crt'
% (key_dir, data_dir), shell=True)
check_call('cp %s/capsule_pub_esl_good.esl %s/SIGNER.esl'
% (key_dir, data_dir), shell=True)
# Update dtb adding capsule certificate
check_call('cd %s; '
'cp %s/test/py/tests/test_efi_capsule/signature.dts .'
% (data_dir, u_boot_config.source_dir), shell=True)
check_call('cd %s; '
'dtc -@ -I dts -O dtb -o signature.dtbo signature.dts; '
'fdtoverlay -i %s/arch/sandbox/dts/test.dtb '
'-o test_sig.dtb signature.dtbo'
% (data_dir, u_boot_config.build_dir), shell=True)
# Create *malicious* private key (SIGNER2.key) and certificate
# (SIGNER2.crt)
check_call('cd %s; '
'openssl req -x509 -sha256 -newkey rsa:2048 '
'-subj /CN=TEST_SIGNER/ -keyout SIGNER2.key '
'-out SIGNER2.crt -nodes -days 365'
% data_dir, shell=True)
check_call('cp %s/capsule_priv_key_bad.key %s/SIGNER2.key'
% (key_dir, data_dir), shell=True)
check_call('cp %s/capsule_pub_key_bad.crt %s/SIGNER2.crt'
% (key_dir, data_dir), shell=True)
# Update dtb to add the version information
check_call('cd %s; '
'cp %s/test/py/tests/test_efi_capsule/version.dts .'
% (data_dir, u_boot_config.source_dir), shell=True)
if capsule_auth_enabled:
check_call('cd %s; '
'cp %s/arch/sandbox/dts/test.dtb test_sig.dtb'
% (data_dir, u_boot_config.build_dir), shell=True)
check_call('cd %s; '
'dtc -@ -I dts -O dtb -o version.dtbo version.dts; '
'fdtoverlay -i test_sig.dtb '
@@ -79,132 +72,20 @@ def efi_capsule_data(request, u_boot_config):
'-o test_ver.dtb version.dtbo'
% (data_dir, u_boot_config.build_dir), shell=True)
# Create capsule files
# two regions: one for u-boot.bin and the other for u-boot.env
check_call('cd %s; echo -n u-boot:Old > u-boot.bin.old; echo -n u-boot:New > u-boot.bin.new; echo -n u-boot-env:Old > u-boot.env.old; echo -n u-boot-env:New > u-boot.env.new' % data_dir,
shell=True)
check_call('sed -e \"s?BINFILE1?u-boot.bin.new?\" -e \"s?BINFILE2?u-boot.env.new?\" %s/test/py/tests/test_efi_capsule/uboot_bin_env.its > %s/uboot_bin_env.its' %
(u_boot_config.source_dir, data_dir),
shell=True)
check_call('cd %s; %s/tools/mkimage -f uboot_bin_env.its uboot_bin_env.itb' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot.bin.new Test01' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 2 --guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot.env.new Test02' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 058B7D83-50D5-4C47-A195-60D86AD341C4 u-boot.bin.new Test03' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 uboot_bin_env.itb Test04' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --guid 058B7D83-50D5-4C47-A195-60D86AD341C4 uboot_bin_env.itb Test05' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 5 '
'--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot.bin.new Test101' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 2 --fw-version 10 '
'--guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 u-boot.env.new Test102' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 2 '
'--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 u-boot.bin.new Test103' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 5 '
'--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 uboot_bin_env.itb Test104' %
(data_dir, u_boot_config.build_dir),
shell=True)
check_call('cd %s; %s/tools/mkeficapsule --index 1 --fw-version 2 '
'--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 uboot_bin_env.itb Test105' %
(data_dir, u_boot_config.build_dir),
shell=True)
if capsule_auth_enabled:
# raw firmware signed with proper key
check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--private-key SIGNER.key --certificate SIGNER.crt '
'--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 '
'u-boot.bin.new Test11'
% (data_dir, u_boot_config.build_dir),
shell=True)
# raw firmware signed with *mal* key
check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--private-key SIGNER2.key '
'--certificate SIGNER2.crt '
'--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 '
'u-boot.bin.new Test12'
% (data_dir, u_boot_config.build_dir),
shell=True)
# FIT firmware signed with proper key
check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--private-key SIGNER.key --certificate SIGNER.crt '
'--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 '
'uboot_bin_env.itb Test13'
% (data_dir, u_boot_config.build_dir),
shell=True)
# FIT firmware signed with *mal* key
check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--private-key SIGNER2.key '
'--certificate SIGNER2.crt '
'--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 '
'uboot_bin_env.itb Test14'
% (data_dir, u_boot_config.build_dir),
shell=True)
# raw firmware signed with proper key with version information
check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--fw-version 5 '
'--private-key SIGNER.key --certificate SIGNER.crt '
'--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 '
'u-boot.bin.new Test111'
% (data_dir, u_boot_config.build_dir),
shell=True)
# raw firmware signed with proper key with version information
check_call('cd %s; '
'%s/tools/mkeficapsule --index 2 --monotonic-count 1 '
'--fw-version 10 '
'--private-key SIGNER.key --certificate SIGNER.crt '
'--guid 5A7021F5-FEF2-48B4-AABA-832E777418C0 '
'u-boot.env.new Test112'
% (data_dir, u_boot_config.build_dir),
shell=True)
# raw firmware signed with proper key with lower version information
check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--fw-version 2 '
'--private-key SIGNER.key --certificate SIGNER.crt '
'--guid 09D7CF52-0720-4710-91D1-08469B7FE9C8 '
'u-boot.bin.new Test113'
% (data_dir, u_boot_config.build_dir),
shell=True)
# FIT firmware signed with proper key with version information
check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--fw-version 5 '
'--private-key SIGNER.key --certificate SIGNER.crt '
'--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 '
'uboot_bin_env.itb Test114'
% (data_dir, u_boot_config.build_dir),
shell=True)
# FIT firmware signed with proper key with lower version information
check_call('cd %s; '
'%s/tools/mkeficapsule --index 1 --monotonic-count 1 '
'--fw-version 2 '
'--private-key SIGNER.key --certificate SIGNER.crt '
'--guid 3673B45D-6A7C-46F3-9E60-ADABB03F7937 '
'uboot_bin_env.itb Test115'
% (data_dir, u_boot_config.build_dir),
shell=True)
pythonpath = os.environ.get('PYTHONPATH', '')
os.environ['PYTHONPATH'] = pythonpath + ':' + '%s/scripts/dtc/pylibfdt' % u_boot_config.build_dir
check_call('cd %s; '
'cc -E -I %s/include -x assembler-with-cpp -o capsule_gen_tmp.dts %s/test/py/tests/test_efi_capsule/capsule_gen_binman.dts; '
'dtc -I dts -O dtb capsule_gen_tmp.dts -o capsule_binman.dtb;'
% (data_dir, u_boot_config.source_dir, u_boot_config.source_dir), shell=True)
check_call('cd %s; '
'./tools/binman/binman --toolpath %s/tools build -u -d %s/capsule_binman.dtb -O %s -m --allow-missing -I %s -I ./board/sandbox -I ./arch/sandbox/dts'
% (u_boot_config.source_dir, u_boot_config.build_dir, data_dir, data_dir, data_dir), shell=True)
os.environ['PYTHONPATH'] = pythonpath
# Create a disk image with EFI system partition
check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' %

View File

@@ -1,10 +0,0 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/plugin/;
&{/} {
signature {
capsule-key = /incbin/("SIGNER.esl");
};
};

View File

@@ -1,36 +0,0 @@
/*
* Automatic software update for U-Boot
* Make sure the flashing addresses ('load' prop) is correct for your board!
*/
/dts-v1/;
/ {
description = "Automatic U-Boot environment update";
#address-cells = <2>;
images {
u-boot-bin {
description = "U-Boot binary on SPI Flash";
data = /incbin/("BINFILE1");
compression = "none";
type = "firmware";
arch = "sandbox";
load = <0>;
hash-1 {
algo = "sha1";
};
};
u-boot-env {
description = "U-Boot environment on SPI Flash";
data = /incbin/("BINFILE2");
compression = "none";
type = "firmware";
arch = "sandbox";
load = <0>;
hash-1 {
algo = "sha1";
};
};
};
};

View File

@@ -61,7 +61,7 @@ def collect_trace(cons):
# Read out the trace data
addr = 0x02000000
size = 0x01000000
size = 0x02000000
out = cons.run_command(f'trace calls {addr:x} {size:x}')
print(out)
fname = os.path.join(TMPDIR, 'trace')

View File

@@ -328,7 +328,7 @@ class Bintool:
return result.stdout
@classmethod
def build_from_git(cls, git_repo, make_target, bintool_path, flags=None):
def build_from_git(cls, git_repo, make_targets, bintool_path, flags=None):
"""Build a bintool from a git repo
This clones the repo in a temporary directory, builds it with 'make',
@@ -336,7 +336,8 @@ class Bintool:
Args:
git_repo (str): URL of git repo
make_target (str): Target to pass to 'make' to build the tool
make_targets (list of str): List of targets to pass to 'make' to build
the tool
bintool_path (str): Relative path of the tool in the repo, after
build is complete
flags (list of str): Flags or variables to pass to make, or None
@@ -350,12 +351,14 @@ class Bintool:
tmpdir = tempfile.mkdtemp(prefix='binmanf.')
print(f"- clone git repo '{git_repo}' to '{tmpdir}'")
tools.run('git', 'clone', '--depth', '1', git_repo, tmpdir)
print(f"- build target '{make_target}'")
cmd = ['make', '-C', tmpdir, '-j', f'{multiprocessing.cpu_count()}',
make_target]
if flags:
cmd += flags
tools.run(*cmd)
for target in make_targets:
print(f"- build target '{target}'")
cmd = ['make', '-C', tmpdir, '-j', f'{multiprocessing.cpu_count()}',
target]
if flags:
cmd += flags
tools.run(*cmd)
fname = os.path.join(tmpdir, bintool_path)
if not os.path.exists(fname):
print(f"- File '{fname}' was not produced")

View File

@@ -0,0 +1,101 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright 2023 Linaro Limited
#
"""Bintool implementation for mkeficapsule tool
mkeficapsule is a tool used for generating EFI capsules.
The following are the commandline options to be provided
to the tool
Usage: mkeficapsule [options] <image blob> <output file>
Options:
-g, --guid <guid string> guid for image blob type
-i, --index <index> update image index
-I, --instance <instance> update hardware instance
-v, --fw-version <version> firmware version
-p, --private-key <privkey file> private key file
-c, --certificate <cert file> signer's certificate file
-m, --monotonic-count <count> monotonic count
-d, --dump_sig dump signature (*.p7)
-A, --fw-accept firmware accept capsule, requires GUID, no image blob
-R, --fw-revert firmware revert capsule, takes no GUID, no image blob
-o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff
-h, --help print a help message
"""
from binman import bintool
class Bintoolmkeficapsule(bintool.Bintool):
"""Handles the 'mkeficapsule' tool
This bintool is used for generating the EFI capsules. The
capsule generation parameters can either be specified through
commandline, or through a config file.
"""
def __init__(self, name):
super().__init__(name, 'mkeficapsule tool for generating capsules')
def generate_capsule(self, image_index, image_guid, hardware_instance,
payload, output_fname, priv_key, pub_key,
monotonic_count=0, version=0, oemflags=0):
"""Generate a capsule through commandline-provided parameters
Args:
image_index (int): Unique number for identifying payload image
image_guid (str): GUID used for identifying the image
hardware_instance (int): Optional unique hardware instance of
a device in the system. 0 if not being used
payload (str): Path to the input payload image
output_fname (str): Path to the output capsule file
priv_key (str): Path to the private key
pub_key(str): Path to the public key
monotonic_count (int): Count used when signing an image
version (int): Image version (Optional)
oemflags (int): Optional 16 bit OEM flags
Returns:
str: Tool output
"""
args = [
f'--index={image_index}',
f'--guid={image_guid}',
f'--instance={hardware_instance}'
]
if version:
args += [f'--fw-version={version}']
if oemflags:
args += [f'--capoemflag={oemflags}']
if priv_key and pub_key:
args += [
f'--monotonic-count={monotonic_count}',
f'--private-key={priv_key}',
f'--certificate={pub_key}'
]
args += [
payload,
output_fname
]
return self.run_cmd(*args)
def fetch(self, method):
"""Fetch handler for mkeficapsule
This builds the tool from source
Returns:
tuple:
str: Filename of fetched file to copy to a suitable directory
str: Name of temp directory to remove, or None
"""
if method != bintool.FETCH_BUILD:
return None
cmd = ['tools-only_defconfig', 'tools']
result = self.build_from_git(
'https://source.denx.de/u-boot/u-boot.git',
cmd,
'tools/mkeficapsule')
return result

View File

@@ -468,6 +468,70 @@ updating the EC on startup via software sync.
.. _etype_efi_capsule:
Entry: capsule: Entry for generating EFI Capsule files
------------------------------------------------------
The parameters needed for generation of the capsules can be provided
as properties in the entry.
Properties / Entry arguments:
- image-index: Unique number for identifying corresponding
payload image. Number between 1 and descriptor count, i.e.
the total number of firmware images that can be updated. Mandatory
property.
- image-guid: Image GUID which will be used for identifying the
updatable image on the board. Mandatory property.
- hardware-instance: Optional number for identifying unique
hardware instance of a device in the system. Default value of 0
for images where value is not to be used.
- fw-version: Value of image version that can be put on the capsule
through the Firmware Management Protocol(FMP) header.
- monotonic-count: Count used when signing an image.
- private-key: Path to PEM formatted .key private key file. Mandatory
property for generating signed capsules.
- public-key-cert: Path to PEM formatted .crt public key certificate
file. Mandatory property for generating signed capsules.
- oem-flags - OEM flags to be passed through capsule header.
Since this is a subclass of Entry_section, all properties of the parent
class also apply here. Except for the properties stated as mandatory, the
rest of the properties are optional.
For more details on the description of the capsule format, and the capsule
update functionality, refer Section 8.5 and Chapter 23 in the `UEFI
specification`_.
The capsule parameters like image index and image GUID are passed as
properties in the entry. The payload to be used in the capsule is to be
provided as a subnode of the capsule entry.
A typical capsule entry node would then look something like this::
capsule {
type = "efi-capsule";
image-index = <0x1>;
/* Image GUID for testing capsule update */
image-guid = SANDBOX_UBOOT_IMAGE_GUID;
hardware-instance = <0x0>;
private-key = "path/to/the/private/key";
public-key-cert = "path/to/the/public-key-cert";
oem-flags = <0x8000>;
u-boot {
};
};
In the above example, the capsule payload is the U-Boot image. The
capsule entry would read the contents of the payload and put them
into the capsule. Any external file can also be specified as the
payload using the blob-ext subnode.
.. _`UEFI specification`: https://uefi.org/sites/default/files/resources/UEFI_Spec_2_10_Aug29.pdf
.. _etype_encrypted:
Entry: encrypted: Externally built encrypted binary blob

View File

@@ -0,0 +1,143 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2023 Linaro Limited
#
# Entry-type module for producing a EFI capsule
#
import os
from binman.entry import Entry
from binman.etype.section import Entry_section
from dtoc import fdt_util
from u_boot_pylib import tools
class Entry_efi_capsule(Entry_section):
"""Generate EFI capsules
The parameters needed for generation of the capsules can
be provided as properties in the entry.
Properties / Entry arguments:
- image-index: Unique number for identifying corresponding
payload image. Number between 1 and descriptor count, i.e.
the total number of firmware images that can be updated. Mandatory
property.
- image-guid: Image GUID which will be used for identifying the
updatable image on the board. Mandatory property.
- hardware-instance: Optional number for identifying unique
hardware instance of a device in the system. Default value of 0
for images where value is not to be used.
- fw-version: Value of image version that can be put on the capsule
through the Firmware Management Protocol(FMP) header.
- monotonic-count: Count used when signing an image.
- private-key: Path to PEM formatted .key private key file. Mandatory
property for generating signed capsules.
- public-key-cert: Path to PEM formatted .crt public key certificate
file. Mandatory property for generating signed capsules.
- oem-flags - OEM flags to be passed through capsule header.
Since this is a subclass of Entry_section, all properties of the parent
class also apply here. Except for the properties stated as mandatory, the
rest of the properties are optional.
For more details on the description of the capsule format, and the capsule
update functionality, refer Section 8.5 and Chapter 23 in the `UEFI
specification`_.
The capsule parameters like image index and image GUID are passed as
properties in the entry. The payload to be used in the capsule is to be
provided as a subnode of the capsule entry.
A typical capsule entry node would then look something like this
capsule {
type = "efi-capsule";
image-index = <0x1>;
/* Image GUID for testing capsule update */
image-guid = SANDBOX_UBOOT_IMAGE_GUID;
hardware-instance = <0x0>;
private-key = "path/to/the/private/key";
public-key-cert = "path/to/the/public-key-cert";
oem-flags = <0x8000>;
u-boot {
};
};
In the above example, the capsule payload is the U-Boot image. The
capsule entry would read the contents of the payload and put them
into the capsule. Any external file can also be specified as the
payload using the blob-ext subnode.
.. _`UEFI specification`: https://uefi.org/sites/default/files/resources/UEFI_Spec_2_10_Aug29.pdf
"""
def __init__(self, section, etype, node):
super().__init__(section, etype, node)
self.required_props = ['image-index', 'image-guid']
self.image_index = 0
self.image_guid = ''
self.hardware_instance = 0
self.monotonic_count = 0
self.fw_version = 0
self.oem_flags = 0
self.private_key = ''
self.public_key_cert = ''
self.auth = 0
def ReadNode(self):
super().ReadNode()
self.image_index = fdt_util.GetInt(self._node, 'image-index')
self.image_guid = fdt_util.GetString(self._node, 'image-guid')
self.fw_version = fdt_util.GetInt(self._node, 'fw-version')
self.hardware_instance = fdt_util.GetInt(self._node, 'hardware-instance')
self.monotonic_count = fdt_util.GetInt(self._node, 'monotonic-count')
self.oem_flags = fdt_util.GetInt(self._node, 'oem-flags')
self.private_key = fdt_util.GetString(self._node, 'private-key')
self.public_key_cert = fdt_util.GetString(self._node, 'public-key-cert')
if ((self.private_key and not self.public_key_cert) or (self.public_key_cert and not self.private_key)):
self.Raise('Both private key and public key certificate need to be provided')
elif not (self.private_key and self.public_key_cert):
self.auth = 0
else:
self.auth = 1
def BuildSectionData(self, required):
def get_binman_test_guid(type_str):
TYPE_TO_GUID = {
'binman-test' : '09d7cf52-0720-4710-91d1-08469b7fe9c8'
}
return TYPE_TO_GUID[type_str]
private_key = ''
public_key_cert = ''
if self.auth:
if not os.path.isabs(self.private_key):
private_key = tools.get_input_filename(self.private_key)
if not os.path.isabs(self.public_key_cert):
public_key_cert = tools.get_input_filename(self.public_key_cert)
data, payload, uniq = self.collect_contents_to_file(
self._entries.values(), 'capsule_in')
outfile = self._filename if self._filename else 'capsule.%s' % uniq
capsule_fname = tools.get_output_filename(outfile)
guid = self.image_guid
if self.image_guid == "binman-test":
guid = get_binman_test_guid('binman-test')
ret = self.mkeficapsule.generate_capsule(self.image_index,
guid,
self.hardware_instance,
payload,
capsule_fname,
private_key,
public_key_cert,
self.monotonic_count,
self.fw_version,
self.oem_flags)
if ret is not None:
os.remove(payload)
return tools.read_file(capsule_fname)
def AddBintools(self, btools):
self.mkeficapsule = self.AddBintool(btools, 'mkeficapsule')

View File

@@ -48,6 +48,7 @@ U_BOOT_VPL_DATA = b'vpl76543210fedcbazywxyz_'
BLOB_DATA = b'89'
ME_DATA = b'0abcd'
VGA_DATA = b'vga'
EFI_CAPSULE_DATA = b'efi'
U_BOOT_DTB_DATA = b'udtb'
U_BOOT_SPL_DTB_DATA = b'spldtb'
U_BOOT_TPL_DTB_DATA = b'tpldtb'
@@ -119,6 +120,11 @@ COMP_BINTOOLS = ['bzip2', 'gzip', 'lz4', 'lzma_alone', 'lzop', 'xz', 'zstd']
TEE_ADDR = 0x5678
# Firmware Management Protocol(FMP) GUID
FW_MGMT_GUID = 'edd5cb6d2de8444cbda17194199ad92a'
# Image GUID specified in the DTS
CAPSULE_IMAGE_GUID = '52cfd7092007104791d108469b7fe9c8'
class TestFunctional(unittest.TestCase):
"""Functional tests for binman
@@ -215,6 +221,7 @@ class TestFunctional(unittest.TestCase):
TestFunctional._MakeInputFile('scp.bin', SCP_DATA)
TestFunctional._MakeInputFile('rockchip-tpl.bin', ROCKCHIP_TPL_DATA)
TestFunctional._MakeInputFile('ti_unsecure.bin', TI_UNSECURE_DATA)
TestFunctional._MakeInputFile('capsule_input.bin', EFI_CAPSULE_DATA)
# Add a few .dtb files for testing
TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR,
@@ -7216,5 +7223,116 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
self.assertRegex(err,
"Image 'image'.*missing bintools.*: bootgen")
def _CheckCapsule(self, data, signed_capsule=False, version_check=False,
capoemflags=False):
fmp_signature = "4d535331" # 'M', 'S', 'S', '1'
fmp_size = "10"
fmp_fw_version = "02"
oemflag = "0080"
payload_data = EFI_CAPSULE_DATA
# TODO - Currently, these offsets for capsule fields are hardcoded.
# There are plans to add support to the mkeficapsule tool to dump
# the capsule contents which can then be used for capsule
# verification.
# Firmware Management Protocol(FMP) GUID - offset(0 - 32)
self.assertEqual(FW_MGMT_GUID, data.hex()[:32])
# Image GUID - offset(96 - 128)
self.assertEqual(CAPSULE_IMAGE_GUID, data.hex()[96:128])
if capoemflags:
# OEM Flags - offset(40 - 44)
self.assertEqual(oemflag, data.hex()[40:44])
if signed_capsule and version_check:
# FMP header signature - offset(4770 - 4778)
self.assertEqual(fmp_signature, data.hex()[4770:4778])
# FMP header size - offset(4778 - 4780)
self.assertEqual(fmp_size, data.hex()[4778:4780])
# firmware version - offset(4786 - 4788)
self.assertEqual(fmp_fw_version, data.hex()[4786:4788])
# payload offset signed capsule(4802 - 4808)
self.assertEqual(payload_data.hex(), data.hex()[4802:4808])
elif signed_capsule:
# payload offset signed capsule(4770 - 4776)
self.assertEqual(payload_data.hex(), data.hex()[4770:4776])
elif version_check:
# FMP header signature - offset(184 - 192)
self.assertEqual(fmp_signature, data.hex()[184:192])
# FMP header size - offset(192 - 194)
self.assertEqual(fmp_size, data.hex()[192:194])
# firmware version - offset(200 - 202)
self.assertEqual(fmp_fw_version, data.hex()[200:202])
# payload offset for non-signed capsule with version header(216 - 222)
self.assertEqual(payload_data.hex(), data.hex()[216:222])
else:
# payload offset for non-signed capsule with no version header(184 - 190)
self.assertEqual(payload_data.hex(), data.hex()[184:190])
def testCapsuleGen(self):
"""Test generation of EFI capsule"""
data = self._DoReadFile('311_capsule.dts')
self._CheckCapsule(data)
def testSignedCapsuleGen(self):
"""Test generation of EFI capsule"""
data = tools.read_file(self.TestFile("key.key"))
self._MakeInputFile("key.key", data)
data = tools.read_file(self.TestFile("key.pem"))
self._MakeInputFile("key.crt", data)
data = self._DoReadFile('312_capsule_signed.dts')
self._CheckCapsule(data, signed_capsule=True)
def testCapsuleGenVersionSupport(self):
"""Test generation of EFI capsule with version support"""
data = self._DoReadFile('313_capsule_version.dts')
self._CheckCapsule(data, version_check=True)
def testCapsuleGenSignedVer(self):
"""Test generation of signed EFI capsule with version information"""
data = tools.read_file(self.TestFile("key.key"))
self._MakeInputFile("key.key", data)
data = tools.read_file(self.TestFile("key.pem"))
self._MakeInputFile("key.crt", data)
data = self._DoReadFile('314_capsule_signed_ver.dts')
self._CheckCapsule(data, signed_capsule=True, version_check=True)
def testCapsuleGenCapOemFlags(self):
"""Test generation of EFI capsule with OEM Flags set"""
data = self._DoReadFile('315_capsule_oemflags.dts')
self._CheckCapsule(data, capoemflags=True)
def testCapsuleGenKeyMissing(self):
"""Test that binman errors out on missing key"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('316_capsule_missing_key.dts')
self.assertIn("Both private key and public key certificate need to be provided",
str(e.exception))
def testCapsuleGenIndexMissing(self):
"""Test that binman errors out on missing image index"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('317_capsule_missing_index.dts')
self.assertIn("entry is missing properties: image-index",
str(e.exception))
def testCapsuleGenGuidMissing(self):
"""Test that binman errors out on missing image GUID"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('318_capsule_missing_guid.dts')
self.assertIn("entry is missing properties: image-guid",
str(e.exception))
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
efi-capsule {
image-index = <0x1>;
/* Image GUID for testing capsule update */
image-guid = "binman-test";
hardware-instance = <0x0>;
blob {
filename = "capsule_input.bin";
};
};
};
};

View File

@@ -0,0 +1,23 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
efi-capsule {
image-index = <0x1>;
/* Image GUID for testing capsule update */
image-guid = "binman-test";
hardware-instance = <0x0>;
private-key = "key.key";
public-key-cert = "key.crt";
blob {
filename = "capsule_input.bin";
};
};
};
};

View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
efi-capsule {
image-index = <0x1>;
fw-version = <0x2>;
/* Image GUID for testing capsule update */
image-guid = "binman-test";
hardware-instance = <0x0>;
blob {
filename = "capsule_input.bin";
};
};
};
};

View File

@@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
efi-capsule {
image-index = <0x1>;
fw-version = <0x2>;
/* Image GUID for testing capsule update */
image-guid = "binman-test";
hardware-instance = <0x0>;
private-key = "key.key";
public-key-cert = "key.crt";
blob {
filename = "capsule_input.bin";
};
};
};
};

View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
efi-capsule {
image-index = <0x1>;
/* Image GUID for testing capsule update */
image-guid = "binman-test";
hardware-instance = <0x0>;
oem-flags = <0x8000>;
blob {
filename = "capsule_input.bin";
};
};
};
};

View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
efi-capsule {
image-index = <0x1>;
/* Image GUID for testing capsule update */
image-guid = "binman-test";
hardware-instance = <0x0>;
private-key = "tools/binman/test/key.key";
blob {
filename = "capsule_input.bin";
};
};
};
};

View File

@@ -0,0 +1,20 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
efi-capsule {
/* Image GUID for testing capsule update */
image-guid = "binman-test";
hardware-instance = <0x0>;
blob {
filename = "capsule_input.bin";
};
};
};
};

View File

@@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
efi-capsule {
image-index = <0x1>;
hardware-instance = <0x0>;
blob {
filename = "capsule_input.bin";
};
};
};
};