On Fri, Sep 04, 2026 at 11:57:31PM +0800, Bin Meng wrote:
> U-Boot requests the PolarFire SoC device serial number during board
> late initialization. The IOSCB previously rejected every request, so

This is kinda crazy actually, we probably should change u-boot to not do
this since the reason for acquiring the serial number is populating the
mac address.
I guess it just never actually ever is problematic in hardware, but it
does seem a bit mad to reject boot over.

> board setup could not complete.
> 
> Model the System Services control registers and byte-addressable
> mailbox. Obtain the 128-bit serial number from an optional device
> property, using a deterministic value when it is not configured.
> Return an explicit failure for unsupported services and reset the new
> runtime state with the rest of the device.
> 
> Signed-off-by: Bin Meng <[email protected]>
> ---
> 
> (no changes since v1)
> 
>  include/hw/misc/mchp_pfsoc_ioscb.h |   6 ++
>  hw/misc/mchp_pfsoc_ioscb.c         | 141 +++++++++++++++++++++++++----
>  2 files changed, 130 insertions(+), 17 deletions(-)
> 
> diff --git a/include/hw/misc/mchp_pfsoc_ioscb.h 
> b/include/hw/misc/mchp_pfsoc_ioscb.h
> index 9687ea25b1..fd31427304 100644
> --- a/include/hw/misc/mchp_pfsoc_ioscb.h
> +++ b/include/hw/misc/mchp_pfsoc_ioscb.h
> @@ -25,6 +25,8 @@
>  
>  #include "hw/core/sysbus.h"
>  
> +#define MCHP_PFSOC_IOSCB_MAILBOX_SIZE 0x1000
> +
>  typedef struct MchpPfSoCIoscbState {
>      SysBusDevice parent;
>      MemoryRegion container;
> @@ -47,6 +49,10 @@ typedef struct MchpPfSoCIoscbState {
>      MemoryRegion cfm_sgmii;
>      MemoryRegion bc_sgmii;
>      MemoryRegion io_calib_sgmii;
> +    uint32_t services_cr;
> +    uint32_t services_sr;
> +    uint8_t mailbox_data[MCHP_PFSOC_IOSCB_MAILBOX_SIZE];
> +    char *serial_number;
>      qemu_irq irq;
>  } MchpPfSoCIoscbState;
>  
> diff --git a/hw/misc/mchp_pfsoc_ioscb.c b/hw/misc/mchp_pfsoc_ioscb.c
> index 09b702e520..7c82b55986 100644
> --- a/hw/misc/mchp_pfsoc_ioscb.c
> +++ b/hw/misc/mchp_pfsoc_ioscb.c
> @@ -25,6 +25,7 @@
>  #include "qemu/log.h"
>  #include "qapi/error.h"
>  #include "hw/core/irq.h"
> +#include "hw/core/qdev-properties.h"
>  #include "hw/core/sysbus.h"
>  #include "hw/misc/mchp_pfsoc_ioscb.h"
>  
> @@ -37,6 +38,9 @@
>  #define IOSCB_CCC_REG_SIZE          0x2000000
>  #define IOSCB_CTRL_REG_SIZE         0x800
>  #define IOSCB_QSPIXIP_REG_SIZE      0x200
> +#define IOSCB_SERIAL_NUMBER_SIZE    16U
> +#define IOSCB_DEFAULT_SERIAL_NUMBER "0123456789abcdef"
> +#define IOSCB_PROP_SERIAL_NUMBER    "serial-number"
>  
>  
>  /*
> @@ -186,33 +190,71 @@ static const MemoryRegionOps 
> mchp_pfsoc_io_calib_ddr_ops = {
>      .endianness = DEVICE_LITTLE_ENDIAN,
>  };
>  
> -#define SERVICES_CR             0x50
> -#define SERVICES_SR             0x54
> -#define SERVICES_STATUS_SHIFT   16
> +#define SERVICES_CR                         0x50
> +#define SERVICES_CR_REQUEST                 BIT(0)
> +#define SERVICES_CR_COMMAND_SHIFT           16
> +#define SERVICES_CR_COMMAND_WIDTH           8

This I think should actually be 7.

> +#define SERVICES_CR_COMMAND_MASK            \
> +        MAKE_64BIT_MASK(SERVICES_CR_COMMAND_SHIFT, SERVICES_CR_COMMAND_WIDTH)
> +#define SERVICES_CR_MASK                    \
> +        (SERVICES_CR_REQUEST | SERVICES_CR_COMMAND_MASK)
> +#define SERVICES_SR                         0x54
> +#define SERVICES_SR_STATUS_SHIFT            16
> +#define SERVICES_COMMAND_SERIAL_NUMBER      0
> +#define SERVICES_STATUS_SUCCESS             0
> +#define SERVICES_STATUS_FAILED              1
> +#define SERVICES_MAILBOX_RESPONSE_OFFSET    0
> +
> +static void services_cr_write(MchpPfSoCIoscbState *s, uint32_t value)
> +{
> +    uint32_t command;
> +    uint32_t status = SERVICES_STATUS_FAILED;
> +
> +    if (device_is_in_reset(DEVICE(s)) ||
> +        !(value & SERVICES_CR_REQUEST)) {
> +        return;
> +    }
> +
> +    /*
> +     * System services complete synchronously in this model, so clear the
> +     * request bit before exposing the response to the guest.
> +     */
> +    s->services_cr &= ~SERVICES_CR_REQUEST;
> +
> +    command = (value & SERVICES_CR_COMMAND_MASK) >>
> +              SERVICES_CR_COMMAND_SHIFT;
> +    if (command == SERVICES_COMMAND_SERIAL_NUMBER) {
> +        /*
> +         * The serial-number service returns a 128-bit response starting at
> +         * the beginning of the mailbox.

That's not actually strictly true, bits 15:7 of the command actually
determine the location in the mailbox. I'm not actually aware of any users
of this feature, Linux and U-Boot both leave this at 0, so it doesn't really
matter for functionality, but the comment is wrong.
See section 3 of the document in the comment you're removing below for
how this works.

> +         */
> +        memset(&s->mailbox_data[SERVICES_MAILBOX_RESPONSE_OFFSET], 0,
> +               IOSCB_SERIAL_NUMBER_SIZE);
> +        memcpy(&s->mailbox_data[SERVICES_MAILBOX_RESPONSE_OFFSET],
> +               s->serial_number, strlen(s->serial_number));
> +        status = SERVICES_STATUS_SUCCESS;
> +    }
> +
> +    s->services_sr = status << SERVICES_SR_STATUS_SHIFT;
> +    qemu_irq_raise(s->irq);
> +}
>  
>  static uint64_t mchp_pfsoc_ctrl_read(void *opaque, hwaddr offset,
>                                       unsigned size)
>  {
> -    uint32_t val = 0;
> +    MchpPfSoCIoscbState *s = opaque;
>  
>      switch (offset) {
> +    case SERVICES_CR:
> +        return s->services_cr;
>      case SERVICES_SR:
> -        /*
> -         * Although some services have no error codes, most do. All services
> -         * that do implement errors, begin their error codes at 1. Treat all
> -         * service requests as failures & return 1.
> -         * See the "PolarFire® FPGA and PolarFire SoC FPGA System Services"
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

> -         * user guide for more information on service error codes.
> -         */
> -        val = 1u << SERVICES_STATUS_SHIFT;
> -        break;
> +        return s->services_sr;
>      default:
>          qemu_log_mask(LOG_UNIMP, "%s: unimplemented device read "
>                        "(size %d, offset 0x%" HWADDR_PRIx ")\n",
>                        __func__, size, offset);
> +        return 0;
>      }
> -
> -    return val;
>  }
>  
>  static void mchp_pfsoc_ctrl_write(void *opaque, hwaddr offset,
> @@ -222,13 +264,17 @@ static void mchp_pfsoc_ctrl_write(void *opaque, hwaddr 
> offset,
>  
>      switch (offset) {
>      case SERVICES_CR:
> -        qemu_irq_raise(s->irq);
> +        s->services_cr = value & SERVICES_CR_MASK;
> +        services_cr_write(s, value);
> +        break;
> +    case SERVICES_SR:
>          break;
>      default:
>          qemu_log_mask(LOG_UNIMP, "%s: unimplemented device write "
>                        "(size %d, value 0x%" PRIx64
>                        ", offset 0x%" HWADDR_PRIx ")\n",
>                        __func__, size, value, offset);
> +        break;
>      }
>  }
>  
> @@ -236,6 +282,55 @@ static const MemoryRegionOps mchp_pfsoc_ctrl_ops = {
>      .read = mchp_pfsoc_ctrl_read,
>      .write = mchp_pfsoc_ctrl_write,
>      .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = sizeof(uint32_t),

I'm not au fait enough with qemu to know, but does this mean that
accessing these registers with an 8 or 16 bit accessor will fail
somehow?
If so, that's not how the hardware behaves, and below...

> +        .max_access_size = sizeof(uint32_t),
> +    },
> +};
> +
> +/*
> + * The System Controller uses the mailbox as a byte-addressable shared buffer
> + * for service command payloads and responses.
> + */
> +static uint64_t mchp_pfsoc_mailbox_read(void *opaque, hwaddr offset,
> +                                        unsigned size)
> +{
> +    MchpPfSoCIoscbState *s = opaque;
> +
> +    return ldn_le_p(&s->mailbox_data[offset], size);
> +}
> +
> +static void mchp_pfsoc_mailbox_write(void *opaque, hwaddr offset,
> +                                     uint64_t value, unsigned size)
> +{
> +    MchpPfSoCIoscbState *s = opaque;
> +
> +    stn_le_p(&s->mailbox_data[offset], size, value);
> +}
> +
> +static const MemoryRegionOps mchp_pfsoc_mailbox_ops = {
> +    .read = mchp_pfsoc_mailbox_read,
> +    .write = mchp_pfsoc_mailbox_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = sizeof(uint8_t),

...you have the more expected 8.

> +        .max_access_size = sizeof(uint32_t),
> +    },
> +};
> +
> +static void mchp_pfsoc_ioscb_reset(DeviceState *dev)
> +{
> +    MchpPfSoCIoscbState *s = MCHP_PFSOC_IOSCB(dev);
> +
> +    s->services_cr = 0;
> +    s->services_sr = 0;
> +    memset(s->mailbox_data, 0, sizeof(s->mailbox_data));
> +    qemu_irq_lower(s->irq);
> +}
> +
> +static const Property mchp_pfsoc_ioscb_properties[] = {
> +    DEFINE_PROP_STRING(IOSCB_PROP_SERIAL_NUMBER,
> +                       MchpPfSoCIoscbState, serial_number),
>  };
>  
>  static void mchp_pfsoc_ioscb_realize(DeviceState *dev, Error **errp)
> @@ -243,6 +338,16 @@ static void mchp_pfsoc_ioscb_realize(DeviceState *dev, 
> Error **errp)
>      MchpPfSoCIoscbState *s = MCHP_PFSOC_IOSCB(dev);
>      SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
>  
> +    /* Use a deterministic identity when no serial number is configured */
> +    if (!s->serial_number) {
> +        s->serial_number = g_strdup(IOSCB_DEFAULT_SERIAL_NUMBER);
> +    }
> +    if (strlen(s->serial_number) > IOSCB_SERIAL_NUMBER_SIZE) {
> +        error_setg(errp, "The serial number can't be longer than %u bytes",
> +                   IOSCB_SERIAL_NUMBER_SIZE);
> +        return;
> +    }
> +
>      memory_region_init(&s->container, OBJECT(s),
>                         "mchp.pfsoc.ioscb", IOSCB_WHOLE_REG_SIZE);
>      sysbus_init_mmio(sbd, &s->container);
> @@ -265,7 +370,7 @@ static void mchp_pfsoc_ioscb_realize(DeviceState *dev, 
> Error **errp)
>                            "mchp.pfsoc.ioscb.qspixip", 
> IOSCB_QSPIXIP_REG_SIZE);
>      memory_region_add_subregion(&s->container, IOSCB_QSPIXIP_BASE, 
> &s->qspixip);
>  
> -    memory_region_init_io(&s->mailbox, OBJECT(s), &mchp_pfsoc_dummy_ops, s,
> +    memory_region_init_io(&s->mailbox, OBJECT(s), &mchp_pfsoc_mailbox_ops, s,
>                            "mchp.pfsoc.ioscb.mailbox", IOSCB_SUBMOD_REG_SIZE);
>      memory_region_add_subregion(&s->container, IOSCB_MAILBOX_BASE, 
> &s->mailbox);
>  
> @@ -343,6 +448,8 @@ static void mchp_pfsoc_ioscb_class_init(ObjectClass 
> *klass, const void *data)
>  
>      dc->desc = "Microchip PolarFire SoC IOSCB modules";
>      dc->realize = mchp_pfsoc_ioscb_realize;
> +    device_class_set_legacy_reset(dc, mchp_pfsoc_ioscb_reset);
> +    device_class_set_props(dc, mchp_pfsoc_ioscb_properties);
>  }
>  
>  static const TypeInfo mchp_pfsoc_ioscb_info = {
> -- 
> 2.53.0
> 
> 

Attachment: signature.asc
Description: PGP signature

Reply via email to