On Thu, 15 Oct 2026 at 19:11, Marcelo Manzo <[email protected]> wrote:
>
> From: Sergey Kambalin <[email protected]>
>
> Replace the stub MMIO read/write handlers with real dispatch over
> the register offsets from the previous patches: SYS revision
> control, INTRL0/1 status/mask registers, UMAC command/MAC-address/
> MDIO-command registers, and TDMA/RDMA ring configuration, wired up
> to the NIC's realize()/reset() and qdev NIC properties
> (DEFINE_NIC_PROPERTIES) so the device gets a MAC address and peer
> netdev from the command line.
>
> Part of Sergey Kambalin's original Raspberry Pi 4B PCIe/GENET
> networking series (patchwork series 829638, posted to qemu-devel in
> 2024, never merged). Carried forward and completed by Marcelo Manzo:
> adapted to the current Property/class_init/Resettable APIs, with no
> functional change to the register dispatch logic itself.
>
> Signed-off-by: Sergey Kambalin <[email protected]>
> Signed-off-by: Marcelo Manzo <[email protected]>



>  static uint64_t bcm2838_genet_read(void *opaque, hwaddr offset, unsigned 
> size)
>  {
>      uint64_t value = ~0;
> +    BCM2838GenetState *s = opaque;
>
> -    qemu_log_mask(
> -        LOG_GUEST_ERROR,
> -        "%s: out-of-range access, %u bytes @ offset 0x%04" PRIx64 "\n",
> -        __func__, size, offset);
> +    if (offset + size < sizeof(s->regs)) {
> +        memcpy(&value, (uint8_t *)&s->regs + offset, size);

This has endianness problems, because not everything in s->regs
is a host uint32_t. It works for the uint32_t fields because we
force that only 4-byte accesses are valid and 'value' here is
in host order, but in e.g. BCM2838GenetRegsHfb the flt[] array
is uint8_t. (Plus it looks on the face of it like it will handle
an arbitrary offset/size but it won't work on big-endian hosts
for anything except 4/4.) Similarly for the write memcpy.

This kind of thing is why devices tend to either
(a) have a uint32_t regs[] array, and use "s->regs[offset >> 2] = value"
as their default "set the register", or
(b) have structs where the fields are named after the registers, and
have switch(offset) statements which handle each register case
separately with "s->myfield = value", and don't try to have a default
case that handles most registers.


> +    } else {
> +        qemu_log_mask(
> +            LOG_GUEST_ERROR,
> +            "%s: out-of-range access, %u bytes @ offset 0x%04" PRIx64 "\n",
> +            __func__, size, offset);
> +    }
>
>      trace_bcm2838_genet_read(size, offset, value);
>      return value;
>  }
>
>  static void bcm2838_genet_write(void *opaque, hwaddr offset, uint64_t value,
> -                                unsigned size) {
> -    qemu_log_mask(
> -        LOG_GUEST_ERROR,
> -        "%s: out-of-range access, %u bytes @ offset 0x%04" PRIx64 "\n",
> -        __func__, size, offset);
> +                                unsigned size)
> +{
> +    BCM2838GenetState *s = opaque;
> +    MACAddr *mac = &s->nic_conf.macaddr;
> +    NetClientState *ncs = qemu_get_queue(s->nic);
> +
> +    trace_bcm2838_genet_write(size, offset, value);
> +
> +    if (offset + size < sizeof(s->regs)) {
> +        switch (offset) {
> +        case BCM2838_GENET_INTRL0_SET:
> +            s->regs.intrl0.stat |= value;
> +            break;
> +        case BCM2838_GENET_INTRL0_CLEAR:
> +            s->regs.intrl0.stat &= ~value;
> +            break;
> +        case BCM2838_GENET_INTRL0_MASK_SET:
> +            s->regs.intrl0.mask_status |= value;
> +            break;
> +        case BCM2838_GENET_INTRL0_MASK_CLEAR:
> +            s->regs.intrl0.mask_status &= ~value;
> +            break;
> +        case BCM2838_GENET_INTRL1_SET:
> +            s->regs.intrl1.stat |= value;
> +            break;
> +        case BCM2838_GENET_INTRL1_CLEAR:
> +            s->regs.intrl1.stat &= ~value;
> +            break;
> +        case BCM2838_GENET_INTRL1_MASK_SET:
> +            s->regs.intrl1.mask_status |= value;
> +            break;
> +        case BCM2838_GENET_INTRL1_MASK_CLEAR:
> +            s->regs.intrl1.mask_status &= ~value;
> +            break;
> +        case BCM2838_GENET_UMAC_CMD:
> +            /* Complete SW reset as soon as it has been requested */
> +            if (FIELD_EX32(value, GENET_UMAC_CMD, SW_RESET) == 1) {
> +                device_cold_reset(DEVICE(s));
> +                value = FIELD_DP32(value, GENET_UMAC_CMD, SW_RESET, 0);
> +            }
> +            break;
> +        /*
> +         * TODO: before changing MAC address we'd better inform QEMU
> +         * network subsystem about freeing previously used one, but
> +         * qemu_macaddr_set_free function isn't accessible for us (marked
> +         * as static in net/net.c), see also https://lists.nongnu.org/
> +         * archive/html/qemu-devel/2022-07/msg02123.html
> +         */
> +        case BCM2838_GENET_UMAC_MAC0:
> +            mac->a[0] = FIELD_EX32(value, GENET_UMAC_MAC_0, ADDR_0);
> +            mac->a[1] = FIELD_EX32(value, GENET_UMAC_MAC_0, ADDR_1);
> +            mac->a[2] = FIELD_EX32(value, GENET_UMAC_MAC_0, ADDR_2);
> +            mac->a[3] = FIELD_EX32(value, GENET_UMAC_MAC_0, ADDR_3);
> +            qemu_macaddr_default_if_unset(mac);

No other network device uses this function like this. This is the
function for "in your realize method, if the user didn't specify
a MAC address via config, let the net code pick one for them".

Maybe (as per the linked email thread) the APIs here could be
improved, but a new device model isn't the place to start doing
something that no other device is doing. The standard thing seems
to be that we call qemu_format_nic_info_str() with the new MAC and
call that good enough.

If we want to fix bugs in the MAC address APIs we should do that
as a separate patchset that proposes its change and implements it
in the existing device models.

> +            qemu_format_nic_info_str(ncs, mac->a);
> +            trace_bcm2838_genet_mac_address(ncs->info_str);
> +            break;
> +        case BCM2838_GENET_UMAC_MAC1:
> +            mac->a[4] = FIELD_EX32(value, GENET_UMAC_MAC_1, ADDR_4);
> +            mac->a[5] = FIELD_EX32(value, GENET_UMAC_MAC_1, ADDR_5);
> +            qemu_macaddr_default_if_unset(mac);
> +            qemu_format_nic_info_str(ncs, mac->a);
> +            trace_bcm2838_genet_mac_address(ncs->info_str);
> +            break;

thanks
-- PMM

Reply via email to