All of lore.kernel.org
 help / color / mirror / Atom feed
From: Wadim Mueller <wafgo01@gmail.com>
To: qemu-devel@nongnu.org
Cc: qemu-arm@nongnu.org, "Peter Maydell" <peter.maydell@linaro.org>,
	"Philippe Mathieu-Daudé" <philmd@mailo.com>,
	"Bin Meng" <bmeng.cn@gmail.com>,
	"Paolo Bonzini" <pbonzini@redhat.com>,
	"Fabiano Rosas" <farosas@suse.de>,
	"Wadim Mueller" <wafgo01@gmail.com>
Subject: [RFC PATCH v2 11/14] hw/arm: add TI K3 combined boot image parser
Date: Thu, 20 Aug 2026 14:48:11 +0200	[thread overview]
Message-ID: <20260820124824.618671-12-wafgo01@gmail.com> (raw)
In-Reply-To: <20260820124824.618671-1-wafgo01@gmail.com>

The K3 boot ROM consumes a "combined boot image" (tiboot3.bin): an X.509
certificate carrying a TI-specific boot extension that describes the
individual components (SYSFW, board config blobs, the R5 SPL), followed by
the component data.

Add a standalone parser for that container. It is deliberately free of any
device or machine state so it can be unit tested; the loader, which acts
on the result, comes with the SoC model.

Signed-off-by: Wadim Mueller <wafgo01@gmail.com>
---
 hw/arm/k3-bootrom-parse.c   | 250 ++++++++++++++++++++++++++++++++++++
 hw/arm/meson.build          |   1 +
 include/hw/arm/k3-bootrom.h |  43 +++++++
 3 files changed, 294 insertions(+)
 create mode 100644 hw/arm/k3-bootrom-parse.c
 create mode 100644 include/hw/arm/k3-bootrom.h

diff --git a/hw/arm/k3-bootrom-parse.c b/hw/arm/k3-bootrom-parse.c
new file mode 100644
index 0000000000..e3e428b6b1
--- /dev/null
+++ b/hw/arm/k3-bootrom-parse.c
@@ -0,0 +1,250 @@
+/*
+ * TI K3 boot-ROM emulation: X.509 combined boot image parser
+ *
+ * Parses the DER wrapper and ext_boot_info extension
+ * (OID 1.3.6.1.4.1.294.1.9) for payload type, destination and size.
+ * No signature verification, since QEMU models a GP device.
+ *
+ * Copyright (c) 2026 CMBLU Energy AG
+ * Author: Wadim Mueller <wafgo01@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/arm/k3-bootrom.h"
+
+typedef struct DerSlice {
+    const uint8_t *p;
+    const uint8_t *end;
+} DerSlice;
+
+static bool der_read_tlv(DerSlice *s, uint8_t *tag, DerSlice *content,
+                         Error **errp)
+{
+    uint64_t len;
+
+    if (s->end - s->p < 2) {
+        error_setg(errp, "k3-bootrom: truncated DER structure");
+        return false;
+    }
+    *tag = *s->p++;
+    len = *s->p++;
+    if (len & 0x80) {
+        unsigned n = len & 0x7f;
+
+        if (n == 0 || n > 4 || (size_t)(s->end - s->p) < n) {
+            error_setg(errp, "k3-bootrom: bad DER length encoding");
+            return false;
+        }
+        len = 0;
+        while (n--) {
+            len = (len << 8) | *s->p++;
+        }
+    }
+    if ((uint64_t)(s->end - s->p) < len) {
+        error_setg(errp, "k3-bootrom: DER length exceeds buffer");
+        return false;
+    }
+    content->p = s->p;
+    content->end = s->p + len;
+    s->p += len;
+    return true;
+}
+
+static bool der_read_uint(DerSlice *s, uint64_t *out, Error **errp)
+{
+    DerSlice c;
+    uint8_t tag;
+    uint64_t v = 0;
+
+    if (!der_read_tlv(s, &tag, &c, errp)) {
+        return false;
+    }
+    if (tag != 0x02) {
+        error_setg(errp, "k3-bootrom: expected INTEGER, got tag 0x%02x",
+                   tag);
+        return false;
+    }
+    if (c.p == c.end) {
+        error_setg(errp, "k3-bootrom: empty INTEGER");
+        return false;
+    }
+    if (c.end - c.p > 9 || (c.end - c.p == 9 && c.p[0] != 0)) {
+        error_setg(errp, "k3-bootrom: INTEGER too large");
+        return false;
+    }
+    for (const uint8_t *q = c.p; q < c.end; q++) {
+        v = (v << 8) | *q;
+    }
+    *out = v;
+    return true;
+}
+
+static bool der_read_u32(DerSlice *s, uint32_t *out, Error **errp)
+{
+    uint64_t v;
+
+    if (!der_read_uint(s, &v, errp)) {
+        return false;
+    }
+    if (v > UINT32_MAX) {
+        error_setg(errp, "k3-bootrom: integer field %" PRIu64
+                   " exceeds 32 bits", v);
+        return false;
+    }
+    *out = v;
+    return true;
+}
+
+/* Big-endian OCTET STRING (<= 8 bytes), as uint64. */
+static bool der_read_addr(DerSlice *s, uint64_t *out, Error **errp)
+{
+    DerSlice c;
+    uint8_t tag;
+    uint64_t v = 0;
+
+    if (!der_read_tlv(s, &tag, &c, errp)) {
+        return false;
+    }
+    if (tag != 0x04 || c.end - c.p > 8) {
+        error_setg(errp, "k3-bootrom: bad destAddr field (tag 0x%02x)",
+                   tag);
+        return false;
+    }
+    for (const uint8_t *q = c.p; q < c.end; q++) {
+        v = (v << 8) | *q;
+    }
+    *out = v;
+    return true;
+}
+
+/* DER TLV for TI ext_boot_info OID 1.3.6.1.4.1.294.1.9. */
+static const uint8_t k3_ext_boot_oid[] = {
+    0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x26, 0x01, 0x09
+};
+
+static const uint8_t *find_bytes(const uint8_t *hay, size_t hay_len,
+                                 const uint8_t *needle, size_t needle_len)
+{
+    if (hay_len < needle_len) {
+        return NULL;
+    }
+    for (size_t i = 0; i + needle_len <= hay_len; i++) {
+        if (memcmp(hay + i, needle, needle_len) == 0) {
+            return hay + i;
+        }
+    }
+    return NULL;
+}
+
+bool k3_bootrom_parse(const uint8_t *buf, size_t len, K3BootImage *out,
+                      Error **errp)
+{
+    DerSlice top = { buf, buf + len };
+    DerSlice cert, rest, octets, info;
+    const uint8_t *oid;
+    uint8_t tag;
+    uint64_t v;
+    uint64_t payload_off;
+
+    memset(out, 0, sizeof(*out));
+
+    if (!der_read_tlv(&top, &tag, &cert, errp)) {
+        return false;
+    }
+    if (tag != 0x30) {
+        error_setg(errp,
+                   "k3-bootrom: not an X.509 boot image (tag 0x%02x)", tag);
+        return false;
+    }
+    out->cert_len = cert.end - buf;
+
+    oid = find_bytes(cert.p, cert.end - cert.p, k3_ext_boot_oid,
+                     sizeof(k3_ext_boot_oid));
+    if (!oid) {
+        error_setg(errp, "k3-bootrom: ext_boot_info extension "
+                   "(OID 1.3.6.1.4.1.294.1.9) not found");
+        return false;
+    }
+    rest.p = oid + sizeof(k3_ext_boot_oid);
+    rest.end = cert.end;
+
+    /* Optional BOOLEAN 'critical', between OID and extnValue. */
+    if (rest.p < rest.end && rest.p[0] == 0x01) {
+        DerSlice skip;
+
+        if (!der_read_tlv(&rest, &tag, &skip, errp)) {
+            return false;
+        }
+    }
+    if (!der_read_tlv(&rest, &tag, &octets, errp)) {
+        return false;
+    }
+    if (tag != 0x04) {
+        error_setg(errp, "k3-bootrom: extension value is not an "
+                   "OCTET STRING (tag 0x%02x)", tag);
+        return false;
+    }
+    if (!der_read_tlv(&octets, &tag, &info, errp)) {
+        return false;
+    }
+    if (tag != 0x30) {
+        error_setg(errp, "k3-bootrom: ext_boot_info is not a SEQUENCE");
+        return false;
+    }
+
+    if (!der_read_uint(&info, &out->ext_img_size, errp)) {
+        return false;
+    }
+    if (!der_read_uint(&info, &v, errp)) {
+        return false;
+    }
+    if (v == 0 || v > K3_BOOTROM_MAX_COMPS) {
+        error_setg(errp, "k3-bootrom: unsupported component count %"
+                   PRIu64, v);
+        return false;
+    }
+    out->num_comps = v;
+
+    payload_off = out->cert_len;
+    for (uint32_t i = 0; i < out->num_comps; i++) {
+        K3BootComponent *c = &out->comps[i];
+        DerSlice comp;
+
+        if (!der_read_tlv(&info, &tag, &comp, errp)) {
+            return false;
+        }
+        if (tag != 0x30) {
+            error_setg(errp, "k3-bootrom: component %u is not a SEQUENCE",
+                       i);
+            return false;
+        }
+        if (!der_read_u32(&comp, &c->comp_type, errp)) {
+            return false;
+        }
+        if (!der_read_u32(&comp, &c->boot_core, errp)) {
+            return false;
+        }
+        if (!der_read_u32(&comp, &c->comp_opts, errp)) {
+            return false;
+        }
+        if (!der_read_addr(&comp, &c->dest_addr, errp)) {
+            return false;
+        }
+        if (!der_read_u32(&comp, &c->comp_size, errp)) {
+            return false;
+        }
+        /* shaType / shaValue are not needed for the loading. */
+        /* payload_off is bounded by len below, so it fits into size_t. */
+        c->payload_offset = payload_off;
+        payload_off += c->comp_size;
+        if (payload_off > len) {
+            error_setg(errp, "k3-bootrom: image truncated (components "
+                       "need %" PRIu64 " bytes, file has %zu)",
+                       payload_off, len);
+            return false;
+        }
+    }
+    return true;
+}
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index 8ee5307a91..22691afdd8 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -109,6 +109,7 @@ arm_common_ss.add(when: 'CONFIG_STRONGARM', if_true: files('strongarm.c'))
 arm_common_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c'))
 arm_common_ss.add(when: 'CONFIG_VERSATILE', if_true: files('versatilepb.c'))
 arm_common_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c'))
+arm_common_ss.add(when: 'CONFIG_TI_AM64X', if_true: files('k3-bootrom-parse.c'))
 
 arm_common_ss.add(when: ['CONFIG_AXIADO_SOC', 'TARGET_AARCH64'], if_true: files(
   'ax3000-soc.c'))
diff --git a/include/hw/arm/k3-bootrom.h b/include/hw/arm/k3-bootrom.h
new file mode 100644
index 0000000000..cf60efc047
--- /dev/null
+++ b/include/hw/arm/k3-bootrom.h
@@ -0,0 +1,43 @@
+/*
+ * TI K3 boot-ROM (RBL) emulation - X.509 combined image loading
+ *
+ * Copyright (c) 2026 CMBLU Energy AG
+ * Author: Wadim Mueller <wafgo01@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef HW_ARM_K3_BOOTROM_H
+#define HW_ARM_K3_BOOTROM_H
+
+#include "qapi/error.h"
+
+#define K3_BOOTROM_MAX_COMPS 8
+
+/* comp_type values from TI combined-image certificate. */
+#define K3_COMP_TYPE_SBL        1
+#define K3_COMP_TYPE_SYSFW      2
+#define K3_COMP_TYPE_SYSFW_DATA 18
+
+typedef struct K3BootComponent {
+    uint32_t comp_type;
+    uint32_t boot_core;
+    uint32_t comp_opts;
+    uint64_t dest_addr;
+    uint32_t comp_size;
+    size_t payload_offset;
+} K3BootComponent;
+
+typedef struct K3BootImage {
+    uint32_t num_comps;
+    uint64_t ext_img_size;
+    size_t cert_len;
+    K3BootComponent comps[K3_BOOTROM_MAX_COMPS];
+} K3BootImage;
+
+bool k3_bootrom_parse(const uint8_t *buf, size_t len, K3BootImage *out,
+                      Error **errp);
+
+typedef struct TIAM64xState TIAM64xState;
+void k3_bootrom_load(TIAM64xState *soc, const char *filename, Error **errp);
+
+#endif
-- 
2.43.0



  parent reply	other threads:[~2026-08-20 12:50 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-20 12:48 [RFC PATCH v2 00/14] hw/arm: add TI AM64x SoC and am64-virt machine Wadim Mueller
2026-08-20 12:48 ` [RFC PATCH v2 01/14] hw/i2c/omap_i2c: add a dedicated CONFIG_OMAP_I2C symbol Wadim Mueller
2026-08-20 12:48 ` [RFC PATCH v2 02/14] hw/i2c/omap_i2c: implement soft reset and NACK reporting Wadim Mueller
2026-08-20 12:48 ` [RFC PATCH v2 03/14] hw/sd/sdhci: complete non-interrupt ADMA descriptor chains in one pass Wadim Mueller
2026-08-20 12:48 ` [RFC PATCH v2 04/14] hw/char: add TI AM64x UART model Wadim Mueller
2026-08-20 12:48 ` [RFC PATCH v2 05/14] hw/timer: add TI K3 DMTimer model Wadim Mueller
2026-08-20 12:48 ` [RFC PATCH v2 06/14] hw/misc: add TI K3 CTRL_MMR, GTC, DDRSS, SDHCI PHY and TRNG models Wadim Mueller
2026-08-20 12:48 ` [RFC PATCH v2 07/14] hw/misc: add TI RAT (region address translation) model Wadim Mueller
2026-08-20 12:48 ` [RFC PATCH v2 08/14] hw/misc: add TI mailbox (IPC) model Wadim Mueller
2026-08-20 12:48 ` [RFC PATCH v2 09/14] hw/misc: add TI K3 secure proxy model Wadim Mueller
2026-08-20 12:48 ` [RFC PATCH v2 10/14] hw/misc: add TI DMSC (TI-SCI system controller) model Wadim Mueller
2026-08-20 12:48 ` Wadim Mueller [this message]
2026-08-20 12:48 ` [RFC PATCH v2 12/14] hw/arm: add TI AM64x SoC model Wadim Mueller
2026-08-20 12:48 ` [RFC PATCH v2 13/14] hw/arm: add the am64-virt machine Wadim Mueller
2026-08-20 12:48 ` [RFC PATCH v2 14/14] tests: add AM64x unit, qtest and functional tests Wadim Mueller

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260820124824.618671-12-wafgo01@gmail.com \
    --to=wafgo01@gmail.com \
    --cc=bmeng.cn@gmail.com \
    --cc=farosas@suse.de \
    --cc=pbonzini@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=philmd@mailo.com \
    --cc=qemu-arm@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.