Hi Cédric,

> -----Original Message-----
> From: Cédric Le Goater <c...@kaod.org>
> Sent: Monday, April 28, 2025 3:21 PM
> To: Kane Chen <kane_c...@aspeedtech.com>; Peter Maydell
> <peter.mayd...@linaro.org>; Steven Lee <steven_...@aspeedtech.com>; Troy
> Lee <leet...@gmail.com>; Jamin Lin <jamin_...@aspeedtech.com>; Andrew
> Jeffery <and...@codeconstruct.com.au>; Joel Stanley <j...@jms.id.au>; open
> list:ASPEED BMCs <qemu-...@nongnu.org>; open list:All patches CC here
> <qemu-devel@nongnu.org>
> Cc: Troy Lee <troy_...@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_c...@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_c...@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

Reply via email to