[PATCH v3 2/3] hw/misc/aspeed_sbc: Connect Aspeed OTP memory device to SBC controller

Kane Chen via posted 3 patches 6 months, 3 weeks ago
Maintainers: "Cédric Le Goater" <clg@kaod.org>, Peter Maydell <peter.maydell@linaro.org>, Steven Lee <steven_lee@aspeedtech.com>, Troy Lee <leetroy@gmail.com>, Jamin Lin <jamin_lin@aspeedtech.com>, Andrew Jeffery <andrew@codeconstruct.com.au>, Joel Stanley <joel@jms.id.au>
[PATCH v3 2/3] hw/misc/aspeed_sbc: Connect Aspeed OTP memory device to SBC controller
Posted by Kane Chen via 6 months, 3 weeks ago
From: Kane-Chen-AS <kane_chen@aspeedtech.com>

This patch integrates the `aspeed.otpmem` device with the ASPEED
Secure Boot Controller (SBC). The SBC now accepts an OTP backend via
a QOM link property ("otpmem"), enabling internal access to OTP content
for controller-specific logic.

This connection provides the foundation for future enhancements
involving fuse storage, device configuration, or secure manufacturing
data provisioning.

Signed-off-by: Kane-Chen-AS <kane_chen@aspeedtech.com>
---
 hw/misc/aspeed_sbc.c         | 146 +++++++++++++++++++++++++++++++++++
 include/hw/misc/aspeed_sbc.h |  15 ++++
 2 files changed, 161 insertions(+)

diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c
index e4a6bd1581..f0ce7bbdf0 100644
--- a/hw/misc/aspeed_sbc.c
+++ b/hw/misc/aspeed_sbc.c
@@ -17,7 +17,11 @@
 #include "migration/vmstate.h"
 
 #define R_PROT          (0x000 / 4)
+#define R_CMD           (0x004 / 4)
+#define R_ADDR          (0x010 / 4)
 #define R_STATUS        (0x014 / 4)
+#define R_CAMP1         (0x020 / 4)
+#define R_CAMP2         (0x024 / 4)
 #define R_QSR           (0x040 / 4)
 
 /* R_STATUS */
@@ -57,6 +61,143 @@ static uint64_t aspeed_sbc_read(void *opaque, hwaddr addr, unsigned int size)
     return s->regs[addr];
 }
 
+static void aspeed_sbc_otpmem_read(void *opaque)
+{
+    AspeedSBCState *s = ASPEED_SBC(opaque);
+    uint32_t otp_addr, data, otp_offset;
+    bool is_data = false;
+    Error *local_err = NULL;
+
+    assert(s->otpmem);
+
+    otp_addr = s->regs[R_ADDR];
+    if (otp_addr < OTP_DATA_DWORD_COUNT) {
+        is_data = true;
+    } else if (otp_addr >= OTP_TOTAL_DWORD_COUNT) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Invalid OTP addr 0x%x\n",
+                      __func__, otp_addr);
+        return;
+    }
+    otp_offset = otp_addr << 2;
+
+    s->otpmem->ops->read(s->otpmem, otp_offset, &data, &local_err);
+    if (local_err) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Failed to read data 0x%x, %s\n",
+                      __func__, otp_offset,
+                      error_get_pretty(local_err));
+        error_free(local_err);
+        return;
+    }
+    s->regs[R_CAMP1] = data;
+
+    if (is_data) {
+        s->otpmem->ops->read(s->otpmem, otp_offset + 4, &data, &local_err);
+        if (local_err) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "%s: Failed to read data 0x%x, %s\n",
+                          __func__, otp_offset,
+                          error_get_pretty(local_err));
+            error_free(local_err);
+            return;
+        }
+        s->regs[R_CAMP2] = data;
+    }
+}
+
+static void mr_handler(uint32_t otp_addr, uint32_t data)
+{
+    switch (otp_addr) {
+    case MODE_REGISTER:
+    case MODE_REGISTER_A:
+    case MODE_REGISTER_B:
+        /* HW behavior, do nothing here */
+        break;
+    default:
+    qemu_log_mask(LOG_GUEST_ERROR,
+                  "%s: Unsupported address 0x%x\n",
+                  __func__, otp_addr);
+        return;
+    }
+}
+
+static void aspeed_sbc_otpmem_write(void *opaque)
+{
+    AspeedSBCState *s = ASPEED_SBC(opaque);
+    uint32_t otp_addr, data;
+
+    otp_addr = s->regs[R_ADDR];
+    data = s->regs[R_CAMP1];
+
+    if (otp_addr == 0) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: ignore write program bit request\n",
+                      __func__);
+    } else if (otp_addr >= MODE_REGISTER) {
+        mr_handler(otp_addr, data);
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Unhandled OTP write address 0x%x\n",
+                      __func__, otp_addr);
+    }
+}
+
+static void aspeed_sbc_otpmem_prog(void *opaque)
+{
+    AspeedSBCState *s = ASPEED_SBC(opaque);
+    uint32_t otp_addr, value;
+    Error *local_err = NULL;
+
+    assert(s->otpmem);
+
+    otp_addr = s->regs[R_ADDR];
+    value = s->regs[R_CAMP1];
+    if (otp_addr >= OTP_TOTAL_DWORD_COUNT) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Invalid OTP addr 0x%x\n",
+                      __func__, otp_addr);
+        return;
+    }
+
+    s->otpmem->ops->prog(s->otpmem, otp_addr, value, &local_err);
+    if (local_err) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Failed to program data 0x%x to 0x%x, %s\n",
+                      __func__, value, otp_addr,
+                      error_get_pretty(local_err));
+        error_free(local_err);
+        return;
+    }
+}
+
+static void aspeed_sbc_handle_command(void *opaque, uint32_t cmd)
+{
+    AspeedSBCState *s = ASPEED_SBC(opaque);
+
+    s->regs[R_STATUS] &= ~(OTP_MEM_IDLE | OTP_IDLE);
+
+    switch (cmd) {
+    case READ_CMD:
+        aspeed_sbc_otpmem_read(s);
+        break;
+    case WRITE_CMD:
+        aspeed_sbc_otpmem_write(s);
+        break;
+    case PROG_CMD:
+        aspeed_sbc_otpmem_prog(s);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Unknown command 0x%x\n",
+                      __func__, cmd);
+        break;
+    }
+
+    s->regs[R_STATUS] |= (OTP_MEM_IDLE | OTP_IDLE);
+}
+
+
 static void aspeed_sbc_write(void *opaque, hwaddr addr, uint64_t data,
                               unsigned int size)
 {
@@ -78,6 +219,9 @@ static void aspeed_sbc_write(void *opaque, hwaddr addr, uint64_t data,
                       "%s: write to read only register 0x%" HWADDR_PRIx "\n",
                       __func__, addr << 2);
         return;
+    case R_CMD:
+        aspeed_sbc_handle_command(opaque, data);
+        return;
     default:
         break;
     }
@@ -139,6 +283,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("otpmem", AspeedSBCState, otpmem,
+                     TYPE_ASPEED_OTPMEM, AspeedOTPMemState *),
 };
 
 static void aspeed_sbc_class_init(ObjectClass *klass, void *data)
diff --git a/include/hw/misc/aspeed_sbc.h b/include/hw/misc/aspeed_sbc.h
index 405e6782b9..8ae59d977e 100644
--- a/include/hw/misc/aspeed_sbc.h
+++ b/include/hw/misc/aspeed_sbc.h
@@ -10,6 +10,7 @@
 #define ASPEED_SBC_H
 
 #include "hw/sysbus.h"
+#include "hw/misc/aspeed_otpmem.h"
 
 #define TYPE_ASPEED_SBC "aspeed.sbc"
 #define TYPE_ASPEED_AST2600_SBC TYPE_ASPEED_SBC "-ast2600"
@@ -27,6 +28,18 @@ OBJECT_DECLARE_TYPE(AspeedSBCState, AspeedSBCClass, ASPEED_SBC)
 #define QSR_SHA384                  (0x2 << 10)
 #define QSR_SHA512                  (0x3 << 10)
 
+#define READ_CMD                    (0x23b1e361)
+#define WRITE_CMD                   (0x23b1e362)
+#define PROG_CMD                    (0x23b1e364)
+
+#define OTP_DATA_DWORD_COUNT        (0x800)
+#define OTP_TOTAL_DWORD_COUNT       (0x1000)
+#define OTP_FILE_SIZE               (OTP_TOTAL_DWORD_COUNT * sizeof(uint32_t))
+
+#define MODE_REGISTER               (0x1000)
+#define MODE_REGISTER_A             (0x3000)
+#define MODE_REGISTER_B             (0x5000)
+
 struct AspeedSBCState {
     SysBusDevice parent;
 
@@ -36,6 +49,8 @@ struct AspeedSBCState {
     MemoryRegion iomem;
 
     uint32_t regs[ASPEED_SBC_NR_REGS];
+
+    AspeedOTPMemState *otpmem;
 };
 
 struct AspeedSBCClass {
-- 
2.43.0
Re: [PATCH v3 2/3] hw/misc/aspeed_sbc: Connect Aspeed OTP memory device to SBC controller
Posted by Cédric Le Goater 6 months, 3 weeks ago
On 4/23/25 04:56, Kane Chen wrote:
> From: Kane-Chen-AS <kane_chen@aspeedtech.com>
> 
> This patch integrates the `aspeed.otpmem` device with the ASPEED
> Secure Boot Controller (SBC). The SBC now accepts an OTP backend via
> a QOM link property ("otpmem"), enabling internal access to OTP content
> for controller-specific logic.
> 
> This connection provides the foundation for future enhancements
> involving fuse storage, device configuration, or secure manufacturing
> data provisioning.
> 
> Signed-off-by: Kane-Chen-AS <kane_chen@aspeedtech.com>
> ---
>   hw/misc/aspeed_sbc.c         | 146 +++++++++++++++++++++++++++++++++++
>   include/hw/misc/aspeed_sbc.h |  15 ++++
>   2 files changed, 161 insertions(+)
> 
> diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c
> index e4a6bd1581..f0ce7bbdf0 100644
> --- a/hw/misc/aspeed_sbc.c
> +++ b/hw/misc/aspeed_sbc.c
> @@ -17,7 +17,11 @@
>   #include "migration/vmstate.h"
>   
>   #define R_PROT          (0x000 / 4)
> +#define R_CMD           (0x004 / 4)
> +#define R_ADDR          (0x010 / 4)
>   #define R_STATUS        (0x014 / 4)
> +#define R_CAMP1         (0x020 / 4)
> +#define R_CAMP2         (0x024 / 4)
>   #define R_QSR           (0x040 / 4)
>   
>   /* R_STATUS */
> @@ -57,6 +61,143 @@ static uint64_t aspeed_sbc_read(void *opaque, hwaddr addr, unsigned int size)
>       return s->regs[addr];
>   }
>   
> +static void aspeed_sbc_otpmem_read(void *opaque)

You could improve this prototype. How about :

bool aspeed_sbc_otpmem_read(AspeedSBCState *s, uint32_t otp_addr, Error *errp)

same below.

> +{
> +    AspeedSBCState *s = ASPEED_SBC(opaque);
> +    uint32_t otp_addr, data, otp_offset;
> +    bool is_data = false;
> +    Error *local_err = NULL;
> +
> +    assert(s->otpmem);
> +
> +    otp_addr = s->regs[R_ADDR];
> +    if (otp_addr < OTP_DATA_DWORD_COUNT) {
> +        is_data = true;
> +    } else if (otp_addr >= OTP_TOTAL_DWORD_COUNT) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: Invalid OTP addr 0x%x\n",
> +                      __func__, otp_addr);
> +        return;
> +    }
> +    otp_offset = otp_addr << 2;
> +
> +    s->otpmem->ops->read(s->otpmem, otp_offset, &data, &local_err);
> +    if (local_err) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: Failed to read data 0x%x, %s\n",
> +                      __func__, otp_offset,
> +                      error_get_pretty(local_err));
> +        error_free(local_err);
> +        return;> +    }

Please use an AddressSpace. See aspeed_smc for an example.

> +    s->regs[R_CAMP1] = data;
> +
> +    if (is_data) {
> +        s->otpmem->ops->read(s->otpmem, otp_offset + 4, &data, &local_err);
> +        if (local_err) {
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                          "%s: Failed to read data 0x%x, %s\n",
> +                          __func__, otp_offset,
> +                          error_get_pretty(local_err));
> +            error_free(local_err);
> +            return;
> +        }
> +        s->regs[R_CAMP2] = data;
> +    }
> +}
> +
> +static void mr_handler(uint32_t otp_addr, uint32_t data)

data is unused

> +{
> +    switch (otp_addr) {
> +    case MODE_REGISTER:
> +    case MODE_REGISTER_A:
> +    case MODE_REGISTER_B:
> +        /* HW behavior, do nothing here */
> +        break;
> +    default:
> +    qemu_log_mask(LOG_GUEST_ERROR,

alignment issue.

> +                  "%s: Unsupported address 0x%x\n",
> +                  __func__, otp_addr);
> +        return;
> +    }
> +}
> +
> +static void aspeed_sbc_otpmem_write(void *opaque)
> +{
> +    AspeedSBCState *s = ASPEED_SBC(opaque);
> +    uint32_t otp_addr, data;
> +
> +    otp_addr = s->regs[R_ADDR];
> +    data = s->regs[R_CAMP1];
> +
> +    if (otp_addr == 0) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: ignore write program bit request\n",
> +                      __func__);
> +    } else if (otp_addr >= MODE_REGISTER) {
> +        mr_handler(otp_addr, data);
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: Unhandled OTP write address 0x%x\n",
> +                      __func__, otp_addr);
> +    }
> +}
> +
> +static void aspeed_sbc_otpmem_prog(void *opaque)
> +{
> +    AspeedSBCState *s = ASPEED_SBC(opaque);
> +    uint32_t otp_addr, value;
> +    Error *local_err = NULL;
> +
> +    assert(s->otpmem);
> +
> +    otp_addr = s->regs[R_ADDR];
> +    value = s->regs[R_CAMP1];
> +    if (otp_addr >= OTP_TOTAL_DWORD_COUNT) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: Invalid OTP addr 0x%x\n",
> +                      __func__, otp_addr);
> +        return;
> +    }
> +
> +    s->otpmem->ops->prog(s->otpmem, otp_addr, value, &local_err);
> +    if (local_err) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: Failed to program data 0x%x to 0x%x, %s\n",
> +                      __func__, value, otp_addr,
> +                      error_get_pretty(local_err));
> +        error_free(local_err);
> +        return;
> +    }
> +}
> +
> +static void aspeed_sbc_handle_command(void *opaque, uint32_t cmd)
> +{
> +    AspeedSBCState *s = ASPEED_SBC(opaque);
> +
> +    s->regs[R_STATUS] &= ~(OTP_MEM_IDLE | OTP_IDLE);
> +
> +    switch (cmd) {
> +    case READ_CMD:
> +        aspeed_sbc_otpmem_read(s);
> +        break;
> +    case WRITE_CMD:
> +        aspeed_sbc_otpmem_write(s);
> +        break;
> +    case PROG_CMD:
> +        aspeed_sbc_otpmem_prog(s);
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: Unknown command 0x%x\n",
> +                      __func__, cmd);
> +        break;
> +    }
> +
> +    s->regs[R_STATUS] |= (OTP_MEM_IDLE | OTP_IDLE);
> +}
> +
> +
>   static void aspeed_sbc_write(void *opaque, hwaddr addr, uint64_t data,
>                                 unsigned int size)
>   {
> @@ -78,6 +219,9 @@ static void aspeed_sbc_write(void *opaque, hwaddr addr, uint64_t data,
>                         "%s: write to read only register 0x%" HWADDR_PRIx "\n",
>                         __func__, addr << 2);
>           return;
> +    case R_CMD:
> +        aspeed_sbc_handle_command(opaque, data);
> +        return;
>       default:
>           break;
>       }
> @@ -139,6 +283,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("otpmem", AspeedSBCState, otpmem,
> +                     TYPE_ASPEED_OTPMEM, AspeedOTPMemState *),

hmm, no.

Instead AspeedOTPMemState should be a child object of AspeedSBCState,
only created and realized for ast2600/1030  Soc. This means you will
probably need a class attribute in AspeedSBCClass.



>   };
>   
>   static void aspeed_sbc_class_init(ObjectClass *klass, void *data)
> diff --git a/include/hw/misc/aspeed_sbc.h b/include/hw/misc/aspeed_sbc.h
> index 405e6782b9..8ae59d977e 100644
> --- a/include/hw/misc/aspeed_sbc.h
> +++ b/include/hw/misc/aspeed_sbc.h
> @@ -10,6 +10,7 @@
>   #define ASPEED_SBC_H
>   
>   #include "hw/sysbus.h"
> +#include "hw/misc/aspeed_otpmem.h"
>   
>   #define TYPE_ASPEED_SBC "aspeed.sbc"
>   #define TYPE_ASPEED_AST2600_SBC TYPE_ASPEED_SBC "-ast2600"
> @@ -27,6 +28,18 @@ OBJECT_DECLARE_TYPE(AspeedSBCState, AspeedSBCClass, ASPEED_SBC)
>   #define QSR_SHA384                  (0x2 << 10)
>   #define QSR_SHA512                  (0x3 << 10)
>   
> +#define READ_CMD                    (0x23b1e361)
> +#define WRITE_CMD                   (0x23b1e362)
> +#define PROG_CMD                    (0x23b1e364)
> +
> +#define OTP_DATA_DWORD_COUNT        (0x800)
> +#define OTP_TOTAL_DWORD_COUNT       (0x1000)
> +#define OTP_FILE_SIZE               (OTP_TOTAL_DWORD_COUNT * sizeof(uint32_t))
> +
> +#define MODE_REGISTER               (0x1000)
> +#define MODE_REGISTER_A             (0x3000)
> +#define MODE_REGISTER_B             (0x5000)
> +

These define belong to the implementation : aspeed_sbc.c.


Thanks,

C.


>   struct AspeedSBCState {
>       SysBusDevice parent;
>   
> @@ -36,6 +49,8 @@ struct AspeedSBCState {
>       MemoryRegion iomem;
>   
>       uint32_t regs[ASPEED_SBC_NR_REGS];
> +
> +    AspeedOTPMemState *otpmem;
>   };
>   
>   struct AspeedSBCClass {
RE: [PATCH v3 2/3] hw/misc/aspeed_sbc: Connect Aspeed OTP memory device to SBC controller
Posted by Kane Chen 6 months, 3 weeks ago
Hi Cédric,

> -----Original Message-----
> From: Cédric Le Goater <clg@kaod.org>
> Sent: Monday, April 28, 2025 3:21 PM
> To: Kane Chen <kane_chen@aspeedtech.com>; Peter Maydell
> <peter.maydell@linaro.org>; Steven Lee <steven_lee@aspeedtech.com>; Troy
> Lee <leetroy@gmail.com>; Jamin Lin <jamin_lin@aspeedtech.com>; Andrew
> Jeffery <andrew@codeconstruct.com.au>; Joel Stanley <joel@jms.id.au>; open
> list:ASPEED BMCs <qemu-arm@nongnu.org>; open list:All patches CC here
> <qemu-devel@nongnu.org>
> Cc: Troy Lee <troy_lee@aspeedtech.com>
> Subject: Re: [PATCH v3 2/3] hw/misc/aspeed_sbc: Connect Aspeed OTP
> memory device to SBC controller
> 
> On 4/23/25 04:56, Kane Chen wrote:
> > From: Kane-Chen-AS <kane_chen@aspeedtech.com>
> >
> > This patch integrates the `aspeed.otpmem` device with the ASPEED
> > Secure Boot Controller (SBC). The SBC now accepts an OTP backend via a
> > QOM link property ("otpmem"), enabling internal access to OTP content
> > for controller-specific logic.
> >
> > This connection provides the foundation for future enhancements
> > involving fuse storage, device configuration, or secure manufacturing
> > data provisioning.
> >
> > Signed-off-by: Kane-Chen-AS <kane_chen@aspeedtech.com>
> > ---
> >   hw/misc/aspeed_sbc.c         | 146
> +++++++++++++++++++++++++++++++++++
> >   include/hw/misc/aspeed_sbc.h |  15 ++++
> >   2 files changed, 161 insertions(+)
> >
> > diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c index
> > e4a6bd1581..f0ce7bbdf0 100644
> > --- a/hw/misc/aspeed_sbc.c
> > +++ b/hw/misc/aspeed_sbc.c
> > @@ -17,7 +17,11 @@
> >   #include "migration/vmstate.h"
> >
> >   #define R_PROT          (0x000 / 4)
> > +#define R_CMD           (0x004 / 4)
> > +#define R_ADDR          (0x010 / 4)
> >   #define R_STATUS        (0x014 / 4)
> > +#define R_CAMP1         (0x020 / 4)
> > +#define R_CAMP2         (0x024 / 4)
> >   #define R_QSR           (0x040 / 4)
> >
> >   /* R_STATUS */
> > @@ -57,6 +61,143 @@ static uint64_t aspeed_sbc_read(void *opaque,
> hwaddr addr, unsigned int size)
> >       return s->regs[addr];
> >   }
> >
> > +static void aspeed_sbc_otpmem_read(void *opaque)
> 
> You could improve this prototype. How about :
> 
> bool aspeed_sbc_otpmem_read(AspeedSBCState *s, uint32_t otp_addr, Error
> *errp)
> 
> same below.
Sure. I will update the function prototype in the next patch, as you suggested.
> 
> > +{
> > +    AspeedSBCState *s = ASPEED_SBC(opaque);
> > +    uint32_t otp_addr, data, otp_offset;
> > +    bool is_data = false;
> > +    Error *local_err = NULL;
> > +
> > +    assert(s->otpmem);
> > +
> > +    otp_addr = s->regs[R_ADDR];
> > +    if (otp_addr < OTP_DATA_DWORD_COUNT) {
> > +        is_data = true;
> > +    } else if (otp_addr >= OTP_TOTAL_DWORD_COUNT) {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: Invalid OTP addr 0x%x\n",
> > +                      __func__, otp_addr);
> > +        return;
> > +    }
> > +    otp_offset = otp_addr << 2;
> > +
> > +    s->otpmem->ops->read(s->otpmem, otp_offset, &data, &local_err);
> > +    if (local_err) {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: Failed to read data 0x%x, %s\n",
> > +                      __func__, otp_offset,
> > +                      error_get_pretty(local_err));
> > +        error_free(local_err);
> > +        return;> +    }
> 
> Please use an AddressSpace. See aspeed_smc for an example.
> 
I will rework the code to use an AddressSpace for OTP memory access.
Thanks for pointing me to the aspeed_smc example — it’s very helpful.
> > +    s->regs[R_CAMP1] = data;
> > +
> > +    if (is_data) {
> > +        s->otpmem->ops->read(s->otpmem, otp_offset + 4, &data,
> &local_err);
> > +        if (local_err) {
> > +            qemu_log_mask(LOG_GUEST_ERROR,
> > +                          "%s: Failed to read data 0x%x, %s\n",
> > +                          __func__, otp_offset,
> > +                          error_get_pretty(local_err));
> > +            error_free(local_err);
> > +            return;
> > +        }
> > +        s->regs[R_CAMP2] = data;
> > +    }
> > +}
> > +
> > +static void mr_handler(uint32_t otp_addr, uint32_t data)
> 
> data is unused
I will remove it in the next patch.
> 
> > +{
> > +    switch (otp_addr) {
> > +    case MODE_REGISTER:
> > +    case MODE_REGISTER_A:
> > +    case MODE_REGISTER_B:
> > +        /* HW behavior, do nothing here */
> > +        break;
> > +    default:
> > +    qemu_log_mask(LOG_GUEST_ERROR,
> 
> alignment issue.
> 
I will adjust it in the next patch.
> > +                  "%s: Unsupported address 0x%x\n",
> > +                  __func__, otp_addr);
> > +        return;
> > +    }
> > +}
> > +
> > +static void aspeed_sbc_otpmem_write(void *opaque) {
> > +    AspeedSBCState *s = ASPEED_SBC(opaque);
> > +    uint32_t otp_addr, data;
> > +
> > +    otp_addr = s->regs[R_ADDR];
> > +    data = s->regs[R_CAMP1];
> > +
> > +    if (otp_addr == 0) {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: ignore write program bit request\n",
> > +                      __func__);
> > +    } else if (otp_addr >= MODE_REGISTER) {
> > +        mr_handler(otp_addr, data);
> > +    } else {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: Unhandled OTP write address 0x%x\n",
> > +                      __func__, otp_addr);
> > +    }
> > +}
> > +
> > +static void aspeed_sbc_otpmem_prog(void *opaque) {
> > +    AspeedSBCState *s = ASPEED_SBC(opaque);
> > +    uint32_t otp_addr, value;
> > +    Error *local_err = NULL;
> > +
> > +    assert(s->otpmem);
> > +
> > +    otp_addr = s->regs[R_ADDR];
> > +    value = s->regs[R_CAMP1];
> > +    if (otp_addr >= OTP_TOTAL_DWORD_COUNT) {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: Invalid OTP addr 0x%x\n",
> > +                      __func__, otp_addr);
> > +        return;
> > +    }
> > +
> > +    s->otpmem->ops->prog(s->otpmem, otp_addr, value, &local_err);
> > +    if (local_err) {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: Failed to program data 0x%x to 0x%x,
> %s\n",
> > +                      __func__, value, otp_addr,
> > +                      error_get_pretty(local_err));
> > +        error_free(local_err);
> > +        return;
> > +    }
> > +}
> > +
> > +static void aspeed_sbc_handle_command(void *opaque, uint32_t cmd) {
> > +    AspeedSBCState *s = ASPEED_SBC(opaque);
> > +
> > +    s->regs[R_STATUS] &= ~(OTP_MEM_IDLE | OTP_IDLE);
> > +
> > +    switch (cmd) {
> > +    case READ_CMD:
> > +        aspeed_sbc_otpmem_read(s);
> > +        break;
> > +    case WRITE_CMD:
> > +        aspeed_sbc_otpmem_write(s);
> > +        break;
> > +    case PROG_CMD:
> > +        aspeed_sbc_otpmem_prog(s);
> > +        break;
> > +    default:
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: Unknown command 0x%x\n",
> > +                      __func__, cmd);
> > +        break;
> > +    }
> > +
> > +    s->regs[R_STATUS] |= (OTP_MEM_IDLE | OTP_IDLE); }
> > +
> > +
> >   static void aspeed_sbc_write(void *opaque, hwaddr addr, uint64_t data,
> >                                 unsigned int size)
> >   {
> > @@ -78,6 +219,9 @@ static void aspeed_sbc_write(void *opaque, hwaddr
> addr, uint64_t data,
> >                         "%s: write to read only register 0x%"
> HWADDR_PRIx "\n",
> >                         __func__, addr << 2);
> >           return;
> > +    case R_CMD:
> > +        aspeed_sbc_handle_command(opaque, data);
> > +        return;
> >       default:
> >           break;
> >       }
> > @@ -139,6 +283,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("otpmem", AspeedSBCState, otpmem,
> > +                     TYPE_ASPEED_OTPMEM, AspeedOTPMemState
> *),
> 
> hmm, no.
> 
> Instead AspeedOTPMemState should be a child object of AspeedSBCState, only
> created and realized for ast2600/1030  Soc. This means you will probably
> need a class attribute in AspeedSBCClass.

I will remove the DEFINE_PROP_LINK("otpmem", ...) approach and instead
manage AspeedOTPMemState as a child object of AspeedSBCState.
> 
> 
> 
> >   };
> >
> >   static void aspeed_sbc_class_init(ObjectClass *klass, void *data)
> > diff --git a/include/hw/misc/aspeed_sbc.h
> > b/include/hw/misc/aspeed_sbc.h index 405e6782b9..8ae59d977e 100644
> > --- a/include/hw/misc/aspeed_sbc.h
> > +++ b/include/hw/misc/aspeed_sbc.h
> > @@ -10,6 +10,7 @@
> >   #define ASPEED_SBC_H
> >
> >   #include "hw/sysbus.h"
> > +#include "hw/misc/aspeed_otpmem.h"
> >
> >   #define TYPE_ASPEED_SBC "aspeed.sbc"
> >   #define TYPE_ASPEED_AST2600_SBC TYPE_ASPEED_SBC "-ast2600"
> > @@ -27,6 +28,18 @@ OBJECT_DECLARE_TYPE(AspeedSBCState,
> AspeedSBCClass, ASPEED_SBC)
> >   #define QSR_SHA384                  (0x2 << 10)
> >   #define QSR_SHA512                  (0x3 << 10)
> >
> > +#define READ_CMD                    (0x23b1e361)
> > +#define WRITE_CMD                   (0x23b1e362)
> > +#define PROG_CMD                    (0x23b1e364)
> > +
> > +#define OTP_DATA_DWORD_COUNT        (0x800)
> > +#define OTP_TOTAL_DWORD_COUNT       (0x1000)
> > +#define OTP_FILE_SIZE               (OTP_TOTAL_DWORD_COUNT *
> sizeof(uint32_t))
> > +
> > +#define MODE_REGISTER               (0x1000)
> > +#define MODE_REGISTER_A             (0x3000)
> > +#define MODE_REGISTER_B             (0x5000)
> > +
> 
> These define belong to the implementation : aspeed_sbc.c.
> 
I will move them to aspeed_sbc.c.

> 
> Thanks,
> 
> C.
> 
> 
> >   struct AspeedSBCState {
> >       SysBusDevice parent;
> >
> > @@ -36,6 +49,8 @@ struct AspeedSBCState {
> >       MemoryRegion iomem;
> >
> >       uint32_t regs[ASPEED_SBC_NR_REGS];
> > +
> > +    AspeedOTPMemState *otpmem;
> >   };
> >
> >   struct AspeedSBCClass {

Thanks again for your comments and review.

Best Regards,
Kane