On Sun Feb 23, 2025 at 3:52 AM AEST, BALATON Zoltan wrote: > The board has a battery backed NVRAM where U-Boot environment is > stored which is also accessed by AmigaOS and e.g. C:NVGetVar command > crashes without it having at least a valid checksum. > > Signed-off-by: BALATON Zoltan <bala...@eik.bme.hu> > --- > hw/ppc/amigaone.c | 116 ++++++++++++++++++++++++++++++++++++++++++++-- > 1 file changed, 113 insertions(+), 3 deletions(-) > > diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c > index 4290d58613..5273543460 100644 > --- a/hw/ppc/amigaone.c > +++ b/hw/ppc/amigaone.c > @@ -21,10 +21,13 @@ > #include "hw/ide/pci.h" > #include "hw/i2c/smbus_eeprom.h" > #include "hw/ppc/ppc.h" > +#include "system/block-backend.h" > #include "system/qtest.h" > #include "system/reset.h" > #include "kvm_ppc.h" > > +#include <zlib.h> /* for crc32 */ > + > #define BUS_FREQ_HZ 100000000 > > /* > @@ -46,6 +49,103 @@ static const char dummy_fw[] = { > 0x4e, 0x80, 0x00, 0x20, /* blr */ > }; > > +#define NVRAM_ADDR 0xfd0e0000 > +#define NVRAM_SIZE (4 * KiB) > + > +#define TYPE_A1_NVRAM "a1-nvram" > +OBJECT_DECLARE_SIMPLE_TYPE(A1NVRAMState, A1_NVRAM) > + > +struct A1NVRAMState { > + SysBusDevice parent_obj; > + > + MemoryRegion mr; > + BlockBackend *blk; > +}; > + > +/* read callback not used because of romd mode, only here just in case */
Better make it g_assert_not_reached() then. > +static uint64_t nvram_read(void *opaque, hwaddr addr, unsigned int size) > +{ > + A1NVRAMState *s = opaque; > + uint8_t *p = memory_region_get_ram_ptr(&s->mr); > + > + return p[addr]; > +} > + > +static void nvram_write(void *opaque, hwaddr addr, uint64_t val, > + unsigned int size) > +{ > + A1NVRAMState *s = opaque; > + uint8_t *p = memory_region_get_ram_ptr(&s->mr); > + > + p[addr] = val; > + if (s->blk) { > + blk_pwrite(s->blk, addr, 1, &val, 0); > + } > +} > + > +static const MemoryRegionOps nvram_ops = { > + .read = nvram_read, > + .write = nvram_write, > + .endianness = DEVICE_BIG_ENDIAN, > + .impl = { > + .min_access_size = 1, > + .max_access_size = 1, > + }, > +}; > + > +static void nvram_realize(DeviceState *dev, Error **errp) > +{ > + A1NVRAMState *s = A1_NVRAM(dev); > + void *p; > + uint32_t *c; > + > + memory_region_init_rom_device(&s->mr, NULL, &nvram_ops, s, "nvram", > + NVRAM_SIZE, &error_fatal); > + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr); > + c = p = memory_region_get_ram_ptr(&s->mr); > + if (s->blk) { > + if (blk_getlength(s->blk) != NVRAM_SIZE) { > + error_setg(errp, "NVRAM backing file size must be %ld bytes", > + NVRAM_SIZE); > + return; > + } > + blk_set_perm(s->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE, > + BLK_PERM_ALL, &error_fatal); > + if (blk_pread(s->blk, 0, NVRAM_SIZE, p, 0) < 0) { > + error_setg(errp, "Cannot read NVRAM contents from backing file"); > + return; > + } > + } > + if (*c == 0) { > + *c = cpu_to_be32(crc32(0, p + 4, NVRAM_SIZE - 4)); > + if (s->blk) { > + blk_pwrite(s->blk, 0, 4, p, 0); > + } > + } > +} So, no need for a reset because it's persistent? > + > +static const Property nvram_properties[] = { > + DEFINE_PROP_DRIVE("drive", A1NVRAMState, blk), > +}; > + > +static void nvram_class_init(ObjectClass *oc, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(oc); > + > + dc->realize = nvram_realize; > + device_class_set_props(dc, nvram_properties); > +} > + > +static const TypeInfo nvram_types[] = { > + { > + .name = TYPE_A1_NVRAM, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_size = sizeof(A1NVRAMState), > + .class_init = nvram_class_init, > + }, > +}; > +DEFINE_TYPES(nvram_types) > + > static void amigaone_cpu_reset(void *opaque) > { > PowerPCCPU *cpu = opaque; > @@ -72,7 +172,7 @@ static void amigaone_init(MachineState *machine) > DeviceState *dev; > I2CBus *i2c_bus; > uint8_t *spd_data; > - int i; > + DriveInfo *di; > > /* init CPU */ > cpu = POWERPC_CPU(cpu_create(machine->cpu_type)); > @@ -97,6 +197,16 @@ static void amigaone_init(MachineState *machine) > memory_region_add_subregion(get_system_memory(), 0x40000000, mr); > } > > + /* nvram */ > + dev = qdev_new(TYPE_A1_NVRAM); > + di = drive_get(IF_MTD, 0, 0); > + if (di) { > + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(di)); > + } > + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); > + memory_region_add_subregion(get_system_memory(), NVRAM_ADDR, > + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), > 0)); > + > /* allocate and load firmware */ > rom = g_new(MemoryRegion, 1); > memory_region_init_rom(rom, NULL, "rom", PROM_SIZE, &error_fatal); > @@ -136,7 +246,7 @@ static void amigaone_init(MachineState *machine) > pci_mem = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); > mr = g_new(MemoryRegion, 1); > memory_region_init_alias(mr, OBJECT(dev), "pci-mem-low", pci_mem, > - 0, 0x1000000); > + 0, 0xe0000); > memory_region_add_subregion(get_system_memory(), 0xfd000000, mr); Better make these addresses #defines at the top of the file with the NVRAM_ADDR? Thanks, Nick > mr = g_new(MemoryRegion, 1); > memory_region_init_alias(mr, OBJECT(dev), "pci-mem-high", pci_mem, > @@ -153,7 +263,7 @@ static void amigaone_init(MachineState *machine) > qdev_connect_gpio_out_named(DEVICE(via), "intr", 0, > qdev_get_gpio_in(DEVICE(cpu), > PPC6xx_INPUT_INT)); > - for (i = 0; i < PCI_NUM_PINS; i++) { > + for (int i = 0; i < PCI_NUM_PINS; i++) { > qdev_connect_gpio_out(dev, i, qdev_get_gpio_in_named(DEVICE(via), > "pirq", i)); > }