diff options
Diffstat (limited to 'hw/misc/aspeed_sbc.c')
| -rw-r--r-- | hw/misc/aspeed_sbc.c | 158 |
1 files changed, 155 insertions, 3 deletions
diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c index 1dfcf14e5b..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; } @@ -306,16 +447,24 @@ static void aspeed_sbc_realize(DeviceState *dev, Error **errp) } } + if (sc->has_ecdsa) { + if (!s->sram) { + error_setg(errp, TYPE_ASPEED_SBC ": 'sram' link not set"); + return; + } + address_space_init(&s->sram_as, s->sram, TYPE_ASPEED_SBC ".sram"); + } + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_sbc_ops, s, - TYPE_ASPEED_SBC, 0x1000); + TYPE_ASPEED_SBC, ASPEED_SBC_NR_REGS << 2); sysbus_init_mmio(sbd, &s->iomem); } static const VMStateDescription vmstate_aspeed_sbc = { .name = TYPE_ASPEED_SBC, - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedSBCState, ASPEED_SBC_NR_REGS), VMSTATE_END_OF_LIST(), @@ -325,6 +474,8 @@ static const VMStateDescription vmstate_aspeed_sbc = { static const Property aspeed_sbc_properties[] = { DEFINE_PROP_BOOL("emmc-abr", AspeedSBCState, emmc_abr, 0), DEFINE_PROP_UINT32("signing-settings", AspeedSBCState, signing_settings, 0), + DEFINE_PROP_LINK("sram", AspeedSBCState, sram, + TYPE_MEMORY_REGION, MemoryRegion *), }; static void aspeed_sbc_class_init(ObjectClass *klass, const void *data) @@ -355,6 +506,7 @@ static void aspeed_ast10x0_sbc_class_init(ObjectClass *klass, const void *data) dc->desc = "AST10X0 Secure Boot Controller"; sc->has_otp = true; + sc->has_ecdsa = true; } static const TypeInfo aspeed_sbc_types[] = { |
