* [PATCH 0/2] arm: socfpga: agilex5: add VAB verification for SSBL
@ 2026-10-21 20:33 Michael Tretter
2026-10-21 20:33 ` [PATCH 1/2] arm: socfpga: mailbox_s10: add certificate validation Michael Tretter
2026-10-21 20:33 ` [PATCH 2/2] arm: socfpga: agilex5: add SSBL verification Michael Tretter
0 siblings, 2 replies; 3+ messages in thread
From: Michael Tretter @ 2026-10-21 20:33 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX; +Cc: Michael Tretter
If Vendor Authorized Boot (VAB) is enabled on Agilex 5, the firmware
checks the signature of the first stage loader. If the first stage
loader loads the second stage (SSBL) from flash, the first stage loader
is then responsible for checking the authenticity of the SSBL.
Add support for verifying a certificate that is attached to an SSBL and
rejecting an SSBL with an invalid certificate if VAB is enabled.
Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
Michael Tretter (2):
arm: socfpga: mailbox_s10: add certificate validation
arm: socfpga: agilex5: add SSBL verification
arch/arm/mach-socfpga/agilex5-xload.c | 157 ++++++++++++++++++++++++++++++++++
arch/arm/mach-socfpga/mailbox_s10.c | 53 ++++++++++++
include/mach/socfpga/mailbox_s10.h | 2 +
3 files changed, 212 insertions(+)
---
base-commit: e3bdefdf2484c19e17cf3a8cd7cb29d47917b545
change-id: 20261021-socfpga-agilex5-verify-ssbl-4fb1af9aa267
Best regards,
--
Michael Tretter <m.tretter@pengutronix.de>
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 1/2] arm: socfpga: mailbox_s10: add certificate validation
2026-10-21 20:33 [PATCH 0/2] arm: socfpga: agilex5: add VAB verification for SSBL Michael Tretter
@ 2026-10-21 20:33 ` Michael Tretter
2026-10-21 20:33 ` [PATCH 2/2] arm: socfpga: agilex5: add SSBL verification Michael Tretter
1 sibling, 0 replies; 3+ messages in thread
From: Michael Tretter @ 2026-10-21 20:33 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX; +Cc: Michael Tretter
The SDM firmware is able to verify a certificate in the proprietary
certificate format against the programmed root key.
Add a wrapper function for the MBOX_VAB_SRC_CERT mailbox command to be
able to verify a certificate more easily.
Note that the certificate has to be passed in a slighly different format
than what is produced by the signing tools provided by Altera.
Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
arch/arm/mach-socfpga/mailbox_s10.c | 53 +++++++++++++++++++++++++++++++++++++
include/mach/socfpga/mailbox_s10.h | 2 ++
2 files changed, 55 insertions(+)
diff --git a/arch/arm/mach-socfpga/mailbox_s10.c b/arch/arm/mach-socfpga/mailbox_s10.c
index 1cf92813b5a7..11804969c29f 100644
--- a/arch/arm/mach-socfpga/mailbox_s10.c
+++ b/arch/arm/mach-socfpga/mailbox_s10.c
@@ -287,6 +287,59 @@ int socfpga_mailbox_s10_init(void)
return 0;
}
+/*
+ * The SDM firmware apparently has an off-by-one error when verifying the
+ * certificate. The passed address must be one word before the actual start of
+ * the certificate, otherwise the firmware reports a 0x80 error with response
+ * set to 0x2. If the first word in the buffer isn't 0, the firmware reports a
+ * 0x3ff error code.
+ *
+ * Therefore, data has to start with a zero word and the actual certificate has
+ * to start with a 32 bit offset.
+ *
+ * The downstream U-Boot and TF-A allocate a temporary buffer, and copy the
+ * certificate with a start offset of -1 in the source buffer. The
+ * documentation in U-Boot and the TF-A seems to be wrong.
+ */
+int mbox_vab_src_cert(void *data, size_t size)
+{
+ int ret;
+ u32 *buf = (u32 *)data;
+ u32 buf_len = size / sizeof(*buf);
+ u32 resp_buf;
+ u32 resp_buf_len = 1;
+
+ ret = mbox_send_cmd(MBOX_ID_BAREBOX,
+ MBOX_VAB_SRC_CERT,
+ MBOX_CMD_DIRECT,
+ buf_len, buf, 0,
+ &resp_buf_len, &resp_buf);
+ switch (ret) {
+ case MBOX_RESP_STATOK:
+ ret = 0;
+ break;
+ case MBOX_RESP_NOT_ALLOWED_UNDER_SECURITY_SETTINGS:
+ pr_warn("Authentication bypass\n");
+ ret = 0;
+ break;
+ case MBOX_RESP_AUTH_FAIL:
+ pr_err("Authentication failed\n");
+ break;
+ case MBOX_RESP_UNKNOWN:
+ pr_err("Authentication not supported\n");
+ break;
+ case 0x80:
+ pr_err("Authentication command specific error: 0x%x (0x%x)\n",
+ ret, resp_buf & 0x1ff);
+ break;
+ default:
+ pr_err("VAB_SRC_CERT failed: 0x%x (0x%x)\n", ret, resp_buf);
+ break;
+ }
+
+ return ret;
+}
+
int socfpga_mailbox_s10_qspi_close(void)
{
return mbox_send_cmd(MBOX_ID_BAREBOX, MBOX_QSPI_CLOSE, MBOX_CMD_DIRECT,
diff --git a/include/mach/socfpga/mailbox_s10.h b/include/mach/socfpga/mailbox_s10.h
index bc0fd97ff0fc..abe770b8128f 100644
--- a/include/mach/socfpga/mailbox_s10.h
+++ b/include/mach/socfpga/mailbox_s10.h
@@ -186,6 +186,8 @@ enum ALT_SDM_MBOX_RESP_CODE {
int socfpga_mailbox_s10_init(void);
+int mbox_vab_src_cert(void *data, size_t size);
+
int socfpga_mailbox_s10_qspi_close(void);
int socfpga_mailbox_s10_qspi_open(unsigned long *master_ref_clk);
int socfpga_mailbox_s10_qspi_get_device_info(u32 *resp_buf, u32 resp_buf_len);
--
2.47.3
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 2/2] arm: socfpga: agilex5: add SSBL verification
2026-10-21 20:33 [PATCH 0/2] arm: socfpga: agilex5: add VAB verification for SSBL Michael Tretter
2026-10-21 20:33 ` [PATCH 1/2] arm: socfpga: mailbox_s10: add certificate validation Michael Tretter
@ 2026-10-21 20:33 ` Michael Tretter
1 sibling, 0 replies; 3+ messages in thread
From: Michael Tretter @ 2026-10-21 20:33 UTC (permalink / raw)
To: Sascha Hauer, BAREBOX; +Cc: Michael Tretter
Altera VAB usually uses a certificate that is appended to the SSBL image
using the fcs_prepare [0] tool. The certificate contains a sha384
checksum and is signed using a key chain that ends with the root key
that is written to the hardware.
Assume that the certificate is appended to the SSBL image. Read the
certificate from the same flash partition as the SSBL, pass it to the
SDM firmware for verification and check against the root key, and
finally verify the contained checksum in barebox.
The SDM firmware returns success for the certificate verification if VAB
is disabled. Create a dummy certificate to check if VAB boot is disabled
and accept an unsigned SSBL in this case. If there is a valid
certificate attached, the checksum has to match, even if VAB is
disabled.
The implementation is a bit quirky, because
1. it needs to deal with the broken SDM firmware behavior, which expects
a 32 bit word with value 0 in front of the certificate,
2. needs to walk and parse the (still untrusted) key chain to find the
actual size of the certificate, and
3. I haven't found a better way if VAB is enabled than to check a dummy
certificate.
The VAB code could be moved to a separate module to make it available to
other SoCFPGA platforms or to barebox proper. Since it's only used for
SSBL verification for now and is mingled with the SSBL loading, I kept
it in the xload code.
[0] https://github.com/altera-fpga/libfcs
Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
arch/arm/mach-socfpga/agilex5-xload.c | 157 ++++++++++++++++++++++++++++++++++
1 file changed, 157 insertions(+)
diff --git a/arch/arm/mach-socfpga/agilex5-xload.c b/arch/arm/mach-socfpga/agilex5-xload.c
index 9e55252c04cc..077f67014945 100644
--- a/arch/arm/mach-socfpga/agilex5-xload.c
+++ b/arch/arm/mach-socfpga/agilex5-xload.c
@@ -5,6 +5,11 @@
#include <linux/sizes.h>
#include <mach/socfpga/generic.h>
#include <pbl/bio.h>
+#include <mach/socfpga/mailbox_s10.h>
+#include <mach/socfpga/soc64-system-manager.h>
+
+#include <crypto/pbl-sha.h>
+#include <crypto/sha.h>
static inline u32 barebox_arm_size(const char *head)
{
@@ -52,9 +57,128 @@ static ssize_t agilex5_bl33_read(struct pbl_bio *bio, void *bl33,
return size;
}
+struct vab_certificate_data {
+ u32 magic;
+ u32 flags;
+ u32 reserved0;
+ u32 reserved1;
+ u8 hash[SHA384_DIGEST_SIZE];
+ u32 measurement;
+ u32 reserved2;
+};
+
+struct vab_certificate_int {
+ u32 magic;
+ u32 size;
+ u32 version;
+ u32 type;
+ struct vab_certificate_data d;
+ u32 keychain[0];
+};
+
+/*
+ * Use a wrapper struct that starts with a zero word to be able to pass the
+ * certificate to the SDM firmware as it. Let the code that reads the
+ * certificate deal with it to avoid a copy of the certificate.
+ */
+struct vab_certificate {
+ u32 zero;
+ struct vab_certificate_int cert;
+};
+
+static ssize_t vab_get_cert_size(struct vab_certificate_int *cert,
+ size_t size)
+{
+ struct {
+ u32 magic;
+ u32 size;
+ } *header;
+ ssize_t offset = 0;
+
+ /*
+ * Each element in the key chain has a header with some magic and the
+ * size of the element. The length of the entire key chain is attached
+ * to the end of the key chain. Walk the entire key chain to find the
+ * actual size of the key chain.
+ *
+ * Note that the header is still untrusted!
+ */
+ while (offset < size) {
+ header = ((void *)cert) + offset;
+ if (header->magic == offset)
+ break;
+ offset += header->size;
+ if (offset < 0 || offset > size)
+ return -EINVAL;
+ }
+
+ return offset;
+}
+
+static ssize_t agilex5_vab_read(struct pbl_bio *bio,
+ struct vab_certificate *cert,
+ off_t start, size_t size)
+{
+ ssize_t ret;
+ struct vab_certificate_int *tmp = &(cert->cert);
+ size_t tmp_size = size - sizeof(u32);
+
+ cert->zero = 0;
+ ret = pbl_bio_read(bio, start, tmp, tmp_size);
+ if (ret < 0)
+ return -EINVAL;
+
+ if (tmp->magic != 0x25d04e7f) {
+ pr_err("vab cert: invalid magic 0x%x\n", tmp->magic);
+ return -EINVAL;
+ }
+ if (tmp->size != sizeof(*tmp)) {
+ pr_err("vab cert: invalid size %u", tmp->size);
+ return -EINVAL;
+ }
+
+ ret = vab_get_cert_size(tmp, tmp_size);
+ if (ret <= 0) {
+ pr_err("vab cert: invalid certificate chain\n");
+ return -EINVAL;
+ }
+
+ pr_info("read vab cert to %p from %lx (%zu bytes)\n",
+ tmp, start, ret);
+
+ return ret + sizeof(u32);
+}
+
+static ssize_t agilex5_vab_dummy(struct vab_certificate *cert)
+{
+ struct vab_certificate_int *tmp = &(cert->cert);
+
+ tmp->magic = 0x25d04e7f;
+ tmp->size = sizeof(*tmp);
+ tmp->version = 1;
+ tmp->type = 6;
+
+ return tmp->size;
+}
+
+static int agilex5_vab_verify(const void *data, size_t size,
+ struct vab_certificate *cert)
+{
+ char computed_hash[SHA384_DIGEST_SIZE];
+ char *hash = cert->cert.d.hash;
+
+ pbl_sha384(data, size, computed_hash);
+
+ return memcmp(hash, computed_hash, SHA384_DIGEST_SIZE);
+}
+
int agilex5_xload_bl33(struct pbl_bio *bio, void *bl33)
{
ssize_t size;
+ off_t cert_offset;
+ ssize_t cert_size;
+ struct vab_certificate *cert;
+ int ret;
if (!bio) {
pr_warn("source device for SSBL is not configured\n");
@@ -70,5 +194,38 @@ int agilex5_xload_bl33(struct pbl_bio *bio, void *bl33)
if (size < 0)
return size;
+ /*
+ * The certificate is 8 byte aligned and attached to the SSBL binary.
+ * The certificate must not be larger than the partition and is
+ * usually smaller than 4K.
+ */
+ size = ALIGN(size, 8);
+ cert_offset = barebox_part->nor_offset + size;
+ cert_size = min_t(size_t, SZ_4K, barebox_part->nor_size - size);
+
+ /* Use the SDRAM behind the bl33 for the certificate */
+ cert = bl33 + size;
+ cert_size = agilex5_vab_read(bio, cert, cert_offset, cert_size);
+ if (cert_size < 0) {
+ /*
+ * There is no valid certificate. Generate a dummy certificate
+ * to ask the firmware if authentication bypass is allowed.
+ */
+ cert_size = agilex5_vab_dummy(cert);
+ }
+
+ ret = mbox_vab_src_cert(cert, cert_size);
+ if (ret)
+ return ret;
+
+ ret = agilex5_vab_verify(bl33, size, cert);
+ if (ret) {
+ if (cert_size != sizeof(*cert)) {
+ pr_err("SSBL hash mismatch\n");
+ return ret;
+ }
+ pr_debug("SSBL hash mismatch: Bypass authentication\n");
+ }
+
return 0;
}
--
2.47.3
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-21 20:35 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-21 20:33 [PATCH 0/2] arm: socfpga: agilex5: add VAB verification for SSBL Michael Tretter
2026-10-21 20:33 ` [PATCH 1/2] arm: socfpga: mailbox_s10: add certificate validation Michael Tretter
2026-10-21 20:33 ` [PATCH 2/2] arm: socfpga: agilex5: add SSBL verification Michael Tretter
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox