Commit aa821a30f7 for qemu.org
commit aa821a30f7539c11a8cbbf57036beeb8baa8b9ef
Author: Kane Chen <kane_chen@aspeedtech.com>
Date: Fri Sep 18 06:53:50 2026 +0000
hw/misc/aspeed_sbc: Derive ABR state from OTP config straps
The eMMC ABR enable state is currently controlled by the "emmc-abr"
machine property instead of the OTP configuration.
Add helpers to read the OTP configuration and resolve strap values.
Use the ABR strap value to update ABR_EN when R_STATUS is read.
Remove the "emmc-abr" property as the ABR state is now derived from
OTP.
Signed-off-by: Kane-Chen-AS <kane_chen@aspeedtech.com>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20260918065347.3713868-3-kane_chen@aspeedtech.com
Signed-off-by: Cédric Le Goater <clg@redhat.com>
diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c
index 3402cef5c3..6cec9ad3da 100644
--- a/hw/misc/aspeed_sbc.c
+++ b/hw/misc/aspeed_sbc.c
@@ -71,6 +71,9 @@
#define SBC_OTP_CMD_WRITE 0x23b1e362
#define SBC_OTP_CMD_PROG 0x23b1e364
+/* OTP strap bits */
+#define OTP_STRAP_ABR_EN 0x2b
+
/* Voltage mode */
#define MODE_REGISTER (0x1000)
#define MODE_REGISTER_A (0x3000)
@@ -89,7 +92,21 @@ static uint64_t aspeed_sbc_read(void *opaque, hwaddr addr, unsigned int size)
return 0;
}
- return s->regs[addr];
+ switch (addr) {
+ case R_STATUS: {
+ uint32_t val = s->regs[R_STATUS];
+
+ if (aspeed_otp_read_strap(&s->otp, OTP_STRAP_ABR_EN)) {
+ val |= ABR_EN;
+ } else {
+ val &= ~ABR_EN;
+ }
+
+ return val;
+ }
+ default:
+ return s->regs[addr];
+ }
}
static bool aspeed_otp_read(AspeedSBCState *s, uint32_t otp_addr,
@@ -388,13 +405,12 @@ static void aspeed_sbc_reset_hold(Object *obj, ResetType type)
memset(s->regs, 0, sizeof(s->regs));
- /* Set secure boot enabled with RSA4096_SHA256 and enable eMMC ABR */
+ /*
+ * ABR_EN is derived from OTP on every read, see aspeed_sbc_read().
+ * Set secure boot enabled with RSA4096_SHA256.
+ */
s->regs[R_STATUS] = OTP_IDLE | OTP_MEM_IDLE;
- if (s->emmc_abr) {
- s->regs[R_STATUS] &= ABR_EN;
- }
-
if (s->signing_settings) {
s->regs[R_STATUS] &= SECURE_BOOT_EN;
}
@@ -452,7 +468,6 @@ 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 *),
diff --git a/hw/nvram/aspeed_otp.c b/hw/nvram/aspeed_otp.c
index 81304bda3a..127da15642 100644
--- a/hw/nvram/aspeed_otp.c
+++ b/hw/nvram/aspeed_otp.c
@@ -106,6 +106,69 @@ static void aspeed_otp_write(void *opaque, hwaddr otp_offset,
trace_aspeed_otp_prog(otp_offset, val, value);
}
+/*
+ * Each OTP configuration setting contains 32 bits of data.
+ * Configuration words are grouped in banks of 8, with banks spaced
+ * 0x200 dwords apart and words within a bank spaced 2 dwords apart:
+ *
+ * offset(n) = OTP_CFG0 + (n / 8) * 0x200 + (n % 8) * 2
+ *
+ * Returns 0 if the OTP read fails.
+ */
+uint32_t aspeed_otp_read_config(AspeedOTPState *s, unsigned int cfg_word)
+{
+ uint32_t otp_addr = OTP_CFG0 + (cfg_word / 8) * 0x200 + (cfg_word % 8) * 2;
+ uint32_t value = 0;
+
+ if (address_space_read(&s->as, otp_addr << 2, MEMTXATTRS_UNSPECIFIED,
+ &value, sizeof(value)) != MEMTX_OK) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Failed to read OTP config word %u\n",
+ __func__, cfg_word);
+ return 0;
+ }
+
+ return value;
+}
+
+/*
+ * OTP straps are a 64-bit value split across two 32-bit config words,
+ * each of which is repeated OTP_STRAP_COPY_NUM times for reliability.
+ * The two words interleave starting at OTP_STRAP_START_INDEX:
+ *
+ * cfg_word: 16 17 18 19 20 21 22 23 24 25 26 27
+ * +----+----+----+----+----+----+----+----+----+----+----+----+
+ * | L0 | H0 | L1 | H1 | L2 | H2 | L3 | H3 | L4 | H4 | L5 | H5 |
+ * +----+----+----+----+----+----+----+----+----+----+----+----+
+ *
+ * Lx = copy #x of bit 0-31, Hx = copy #x of bit 32-63
+ * (x = 0 .. OTP_STRAP_COPY_NUM - 1)
+ *
+ * To resolve strap bit `n` (0 <= n < OTP_STRAP_BIT_NUM):
+ * half = n / 32 -- 0 selects the L* words, 1 selects the H* words
+ * bit_pos = n % 32 -- bit position within the word
+ * word(i) = OTP_STRAP_START_INDEX + half + i * (OTP_STRAP_BIT_NUM / 32)
+ *
+ * The effective value of bit `n` is the XOR of bit `bit_pos` across all
+ * OTP_STRAP_COPY_NUM copies of word(i).
+ */
+bool aspeed_otp_read_strap(AspeedOTPState *s, unsigned int bit)
+{
+ uint32_t cfg_word = OTP_STRAP_START_INDEX + bit / 32;
+ uint32_t bit_pos = bit % 32;
+ bool enable = false;
+ int i;
+
+ assert(bit < OTP_STRAP_BIT_NUM);
+
+ for (i = 0; i < OTP_STRAP_COPY_NUM; i++) {
+ enable ^= (aspeed_otp_read_config(s, cfg_word) >> bit_pos) & 0x1;
+ cfg_word += OTP_STRAP_BIT_NUM / 32;
+ }
+
+ return enable;
+}
+
static bool aspeed_otp_init_storage(AspeedOTPState *s, Error **errp)
{
uint32_t *p;
diff --git a/include/hw/misc/aspeed_sbc.h b/include/hw/misc/aspeed_sbc.h
index 756c612356..b210ffb8aa 100644
--- a/include/hw/misc/aspeed_sbc.h
+++ b/include/hw/misc/aspeed_sbc.h
@@ -32,7 +32,6 @@ OBJECT_DECLARE_TYPE(AspeedSBCState, AspeedSBCClass, ASPEED_SBC)
struct AspeedSBCState {
SysBusDevice parent;
- bool emmc_abr;
uint32_t signing_settings;
MemoryRegion iomem;
diff --git a/include/hw/nvram/aspeed_otp.h b/include/hw/nvram/aspeed_otp.h
index 2b6c2eaaa3..f91af12ea9 100644
--- a/include/hw/nvram/aspeed_otp.h
+++ b/include/hw/nvram/aspeed_otp.h
@@ -24,11 +24,27 @@ OBJECT_DECLARE_SIMPLE_TYPE(AspeedOTPState, ASPEED_OTP)
*
* - [0, OTP_DATA_DWORD_COUNT]: the data region. Each address contains
* 64 bits of data.
- * - [OTP_DATA_DWORD_COUNT, OTP_MEMORY_SIZE / 4]: the configuration
- * region. Each address contains 32 bits of data.
+ * - [OTP_DATA_DWORD_COUNT(OTP_CFG0), OTP_MEMORY_SIZE / 4]: the
+ * configuration region. Each address contains 32 bits of data.
*/
#define OTP_DATA_DWORD_COUNT (0x800)
+/* Start of the OTP configuration/strap region. */
+#define OTP_CFG0 (0x800)
+
+/*
+ * OTP straps are a 64-bit value packed as two 32-bit halves starting at
+ * config word OTP_STRAP_START_INDEX (OTPCFG16 and OTPCFG17). Each strap
+ * bit is stored redundantly in OTP_STRAP_COPY_NUM config words, spaced
+ * (OTP_STRAP_BIT_NUM / 32) words apart -- i.e. one word per 32-bit half,
+ * so the two halves interleave; the effective bit value is the XOR of
+ * all copies, matching how the real hardware and the ast-otp reference
+ * tool resolve straps.
+ */
+#define OTP_STRAP_START_INDEX 16
+#define OTP_STRAP_BIT_NUM 64
+#define OTP_STRAP_COPY_NUM 6
+
typedef struct AspeedOTPState {
DeviceState parent_obj;
@@ -43,4 +59,7 @@ typedef struct AspeedOTPState {
uint8_t *storage;
} AspeedOTPState;
+uint32_t aspeed_otp_read_config(AspeedOTPState *s, unsigned int cfg_word);
+bool aspeed_otp_read_strap(AspeedOTPState *s, unsigned int bit);
+
#endif /* ASPEED_OTP_H */