All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jamin Lin <jamin_lin@aspeedtech.com>
To: "Daniel P. Berrangé" <berrange@redhat.com>,
	"Cédric Le Goater" <clg@kaod.org>,
	"Peter Maydell" <peter.maydell@linaro.org>,
	"Steven Lee" <steven_lee@aspeedtech.com>,
	"Troy Lee" <leetroy@gmail.com>,
	"Kane Chen" <kane_chen@aspeedtech.com>,
	"Andrew Jeffery" <andrew@codeconstruct.com.au>,
	"Joel Stanley" <joel@jms.id.au>, "Eric Blake" <eblake@redhat.com>,
	"Markus Armbruster" <armbru@redhat.com>,
	"Fabiano Rosas" <farosas@suse.de>,
	"Laurent Vivier" <lvivier@redhat.com>,
	"Paolo Bonzini" <pbonzini@redhat.com>,
	"open list:All patches CC here" <qemu-devel@nongnu.org>,
	"open list:ASPEED BMCs" <qemu-arm@nongnu.org>
Cc: Jamin Lin <jamin_lin@aspeedtech.com>, Troy Lee <troy_lee@aspeedtech.com>
Subject: [PATCH v4 8/9] hw/misc/aspeed_sbc: Support the ECDSA verify command
Date: Tue, 1 Sep 2026 08:52:48 +0000	[thread overview]
Message-ID: <20260901085238.995968-9-jamin_lin@aspeedtech.com> (raw)
In-Reply-To: <20260901085238.995968-1-jamin_lin@aspeedtech.com>

The AST10x0 secure boot controller register block also hosts an ECDSA
engine. Emulate its secp384r1 "verify" command: on a trigger write to
the command register, read the public key, signature and SHA-384 digest
that the firmware staged in the SEC SRAM and defer the verification to the
crypto akcipher backend, reporting the result through the status
register.

The model reads its operands from the SEC SRAM through a dedicated
address space, using a 'sram' link. The SEC SRAM is mapped at offset 0
of that address space, so the engine addresses each operand directly
with its SRAM-relative offset.

Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com>
---
 hw/misc/aspeed_sbc.c | 141 +++++++++++++++++++++++++++++++++++++++++++
 hw/misc/trace-events |   2 +
 2 files changed, 143 insertions(+)

diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c
index f10f7ac578..5c193d9086 100644
--- a/hw/misc/aspeed_sbc.c
+++ b/hw/misc/aspeed_sbc.c
@@ -10,11 +10,13 @@
 
 #include "qemu/osdep.h"
 #include "qemu/log.h"
+#include "qemu/cutils.h"
 #include "qemu/error-report.h"
 #include "hw/core/qdev-properties.h"
 #include "hw/misc/aspeed_sbc.h"
 #include "qapi/error.h"
 #include "migration/vmstate.h"
+#include "crypto/akcipher.h"
 #include "trace.h"
 
 #define R_PROT          (0x000 / 4)
@@ -24,8 +26,22 @@
 #define R_CAMP1         (0x020 / 4)
 #define R_CAMP2         (0x024 / 4)
 #define R_QSR           (0x040 / 4)
+#define R_SEC_TRIGGER   (0x0bc / 4)
+
+/*
+ * SEC SRAM layout for a secp384r1 ECDSA verify operation. All operands are
+ * 48-byte big-endian values.
+ */
+#define ECDSA_SRAM_QX   0x2080
+#define ECDSA_SRAM_QY   0x20c0
+#define ECDSA_SRAM_R    0x21c0
+#define ECDSA_SRAM_S    0x2200
+#define ECDSA_SRAM_M    0x2240
+#define ECDSA_P384_COORD_LEN    48
 
 /* R_STATUS */
+#define ECDSA_VERIFY_PASS       BIT(21)
+#define ECDSA_VERIFY_DONE       BIT(20)
 #define ABR_EN                  BIT(14) /* Mirrors SCU510[11] */
 #define ABR_IMAGE_SOURCE        BIT(13)
 #define SPI_ABR_IMAGE_SOURCE    BIT(12)
@@ -42,6 +58,10 @@
 #define OTP_MEM_IDLE            BIT(1)
 #define OTP_COMPARE_STATUS      BIT(0)
 
+/* R_SEC_TRIGGER */
+#define ECDSA_CMD_TRIGGER BIT(1)
+#define RSA_CMD_TRIGGER   BIT(0)
+
 /* QSR */
 #define QSR_RSA_MASK           (0x3 << 12)
 #define QSR_HASH_MASK          (0x3 << 10)
@@ -220,10 +240,111 @@ static void aspeed_sbc_handle_command(void *opaque, uint32_t cmd)
     s->regs[R_STATUS] |= (OTP_MEM_IDLE | OTP_IDLE);
 }
 
+static void sbc_ecdsa_hexdump(const char *desc, const char *buf, size_t size)
+{
+    g_autoptr(GString) str = g_string_sized_new(64);
+    size_t len;
+    size_t i;
+
+    for (i = 0; i < size; i += len) {
+        len = MIN(16, size - i);
+        g_string_truncate(str, 0);
+        qemu_hexdump_line(str, buf + i, len, 1, 4);
+        trace_aspeed_sbc_ecdsa_hexdump(desc, i, str->str);
+    }
+}
+
+/*
+ * The hardware only supports ECDSA secp384r1 (NIST P-384). The firmware has
+ * already staged the public key, signature and digest in the SEC SRAM; read
+ * them out and defer the actual verification to the crypto backend.
+ */
+static bool aspeed_sbc_ecdsa_verify(AspeedSBCState *s)
+{
+    QCryptoAkCipherOptions opts = {
+        .alg = QCRYPTO_AK_CIPHER_ALGO_ECDSA,
+        .u.ecdsa.curve_id = QCRYPTO_CURVE_ID_SECP384R1,
+    };
+    g_autoptr(QCryptoAkCipher) akcipher = NULL;
+    uint8_t pubkey[ECDSA_P384_COORD_LEN * 2];
+    uint8_t sig[ECDSA_P384_COORD_LEN * 2];
+    uint8_t dgst[ECDSA_P384_COORD_LEN];
+    Error *err = NULL;
+
+    if (!qcrypto_akcipher_supports(&opts)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: ECDSA secp384r1 is not supported by the crypto "
+                      "backend\n", __func__);
+        return false;
+    }
+
+    if (address_space_read(&s->sram_as, ECDSA_SRAM_QX,
+                           MEMTXATTRS_UNSPECIFIED, pubkey,
+                           ECDSA_P384_COORD_LEN) != MEMTX_OK) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: failed to read ECDSA QX from SEC SRAM\n", __func__);
+        return false;
+    }
+    if (address_space_read(&s->sram_as, ECDSA_SRAM_QY,
+                           MEMTXATTRS_UNSPECIFIED,
+                           pubkey + ECDSA_P384_COORD_LEN,
+                           ECDSA_P384_COORD_LEN) != MEMTX_OK) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: failed to read ECDSA QY from SEC SRAM\n", __func__);
+        return false;
+    }
+    if (address_space_read(&s->sram_as, ECDSA_SRAM_R,
+                           MEMTXATTRS_UNSPECIFIED, sig,
+                           ECDSA_P384_COORD_LEN) != MEMTX_OK) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: failed to read ECDSA R from SEC SRAM\n", __func__);
+        return false;
+    }
+    if (address_space_read(&s->sram_as, ECDSA_SRAM_S,
+                           MEMTXATTRS_UNSPECIFIED, sig + ECDSA_P384_COORD_LEN,
+                           ECDSA_P384_COORD_LEN) != MEMTX_OK) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: failed to read ECDSA S from SEC SRAM\n", __func__);
+        return false;
+    }
+    if (address_space_read(&s->sram_as, ECDSA_SRAM_M,
+                           MEMTXATTRS_UNSPECIFIED, dgst,
+                           ECDSA_P384_COORD_LEN) != MEMTX_OK) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: failed to read ECDSA M from SEC SRAM\n",
+                      __func__);
+        return false;
+    }
+
+    if (trace_event_get_state_backends(TRACE_ASPEED_SBC_ECDSA_HEXDUMP)) {
+        sbc_ecdsa_hexdump("pubkey", (char *)pubkey, sizeof(pubkey));
+        sbc_ecdsa_hexdump("signature", (char *)sig, sizeof(sig));
+        sbc_ecdsa_hexdump("digest", (char *)dgst, sizeof(dgst));
+    }
+
+    akcipher = qcrypto_akcipher_new(&opts, QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC,
+                                    pubkey, sizeof(pubkey), &err);
+    if (!akcipher) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: %s\n", __func__,
+                      error_get_pretty(err));
+        error_free(err);
+        return false;
+    }
+
+    if (qcrypto_akcipher_verify(akcipher, sig, sizeof(sig),
+                                dgst, sizeof(dgst), &err) != 0) {
+        error_free(err);
+        return false;
+    }
+
+    return true;
+}
+
 static void aspeed_sbc_write(void *opaque, hwaddr addr, uint64_t data,
                               unsigned int size)
 {
     AspeedSBCState *s = ASPEED_SBC(opaque);
+    AspeedSBCClass *sc = ASPEED_SBC_GET_CLASS(s);
 
     addr >>= 2;
 
@@ -244,6 +365,26 @@ static void aspeed_sbc_write(void *opaque, hwaddr addr, uint64_t data,
     case R_CMD:
         aspeed_sbc_handle_command(opaque, data);
         return;
+    case R_SEC_TRIGGER:
+        if (data & RSA_CMD_TRIGGER) {
+            qemu_log_mask(LOG_UNIMP,
+                          "%s: RSA is not supported\n", __func__);
+        }
+        if (data & ECDSA_CMD_TRIGGER) {
+            if (!sc->has_ecdsa) {
+                qemu_log_mask(LOG_GUEST_ERROR,
+                              "%s: ECDSA is not supported\n", __func__);
+                return;
+            }
+            s->regs[R_STATUS] &= ~(ECDSA_VERIFY_DONE | ECDSA_VERIFY_PASS);
+            if (aspeed_sbc_ecdsa_verify(s)) {
+                s->regs[R_STATUS] |= ECDSA_VERIFY_PASS;
+            }
+            s->regs[R_STATUS] |= ECDSA_VERIFY_DONE;
+            trace_aspeed_sbc_ecdsa_verify(
+                (s->regs[R_STATUS] & ECDSA_VERIFY_PASS) ? "pass" : "fail");
+        }
+        return;
     default:
         break;
     }
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index bbec0d2178..735d11d447 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -95,6 +95,8 @@ aspeed_sbc_ignore_cmd(uint32_t cmd) "Ignoring command 0x%" PRIx32
 aspeed_sbc_handle_cmd(uint32_t cmd, uint32_t addr, bool ret) "Handling command 0x%" PRIx32 " for OTP addr 0x%" PRIx32 " Result: %d"
 aspeed_sbc_otp_read(uint32_t addr, uint32_t value) "OTP Memory read: addr 0x%" PRIx32 " value 0x%" PRIx32
 aspeed_sbc_otp_prog(uint32_t addr, uint32_t value) "OTP Memory write: addr 0x%" PRIx32 " value 0x%" PRIx32
+aspeed_sbc_ecdsa_verify(const char *result) "ECDSA verify done: %s"
+aspeed_sbc_ecdsa_hexdump(const char *desc, uint32_t offset, const char *s) "%s: 0x%08x: %s"
 
 # aspeed_scu.c
 aspeed_scu_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32
-- 
2.53.0


  parent reply	other threads:[~2026-09-01  8:54 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-01  8:52 [PATCH v4 0/9] Add ECDSA akcipher support and the ASPEED SBC ECDSA engine for AST10x0 Jamin Lin
2026-09-01  8:52 ` [PATCH v4 1/9] qapi/crypto: Add ECDSA algorithm and curve id Jamin Lin
2026-09-01  8:52 ` [PATCH v4 2/9] crypto/akcipher: Support ECDSA sign/verify with gcrypt Jamin Lin
2026-09-01  8:52 ` [PATCH v4 3/9] crypto/akcipher: Support ECDSA sign/verify with nettle Jamin Lin
2026-09-01  8:52 ` [PATCH v4 4/9] tests/crypto: Add ECDSA sign/verify tests Jamin Lin
2026-09-01  8:52 ` [PATCH v4 5/9] hw/arm/aspeed_ast10x0: Remove obsolete unimplemented SBC mapping Jamin Lin
2026-09-01  8:52 ` [PATCH v4 6/9] hw/misc/aspeed_sbc: Increase register space to 0x1000 Jamin Lin
2026-09-01 12:28   ` Cédric Le Goater
2026-09-01  8:52 ` [PATCH v4 7/9] hw/arm/aspeed_ast10x0: Wire SEC SRAM to the SBC model Jamin Lin
2026-09-01 12:29   ` Cédric Le Goater
2026-09-01  8:52 ` Jamin Lin [this message]
2026-09-01 12:32   ` [PATCH v4 8/9] hw/misc/aspeed_sbc: Support the ECDSA verify command Cédric Le Goater
2026-09-01  8:52 ` [PATCH v4 9/9] tests/qtest: Add ASPEED SBC ECDSA engine test Jamin Lin
2026-09-01 12:32   ` Cédric Le Goater
2026-09-02  6:10 ` [PATCH v4 0/9] Add ECDSA akcipher support and the ASPEED SBC ECDSA engine for AST10x0 Cédric Le Goater
2026-09-02  6:10 ` Cédric Le Goater

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=20260901085238.995968-9-jamin_lin@aspeedtech.com \
    --to=jamin_lin@aspeedtech.com \
    --cc=andrew@codeconstruct.com.au \
    --cc=armbru@redhat.com \
    --cc=berrange@redhat.com \
    --cc=clg@kaod.org \
    --cc=eblake@redhat.com \
    --cc=farosas@suse.de \
    --cc=joel@jms.id.au \
    --cc=kane_chen@aspeedtech.com \
    --cc=leetroy@gmail.com \
    --cc=lvivier@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-arm@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --cc=steven_lee@aspeedtech.com \
    --cc=troy_lee@aspeedtech.com \
    /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.