Message ID | 20180115205848.26027-1-mark.cave-ayland@ilande.co.uk |
---|---|
State | New |
Headers | show |
Series | sun4u: implement power device | expand |
CC'ing PCI maintainers. Hi Mark, On 01/15/2018 05:58 PM, Mark Cave-Ayland wrote: > This inbuilt device contains a single 4-byte register, of which bit 24 is used > to power down the machine on a real Ultra 5. > > The power device exists at offset 0x724000 on a real machine, but due to the > current configuration of the BARs in QEMU it must be located lower in PCI IO > space. Is is some issue in pci_bar_address()? > > For the moment we place the power device at offset 0x7240 as a reminder of its > original location and raise the base PCI IO address from 0x4000 to 0x8000. If we can't fix it, I rather prefer a #define with a comment in the code, IMHO this is safer and easier to remember than looking in git history. > > Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> > --- > hw/sparc64/sun4u.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 63 insertions(+), 1 deletion(-) > > diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c > index ec45ec2801..26ab6e9a9c 100644 > --- a/hw/sparc64/sun4u.c > +++ b/hw/sparc64/sun4u.c > @@ -205,6 +205,59 @@ typedef struct ResetData { > uint64_t prom_addr; > } ResetData; > > +#define TYPE_SUN4U_POWER "power" > +#define SUN4U_POWER(obj) OBJECT_CHECK(PowerDevice, (obj), TYPE_SUN4U_POWER) > + > +typedef struct PowerDevice { > + SysBusDevice parent_obj; > + > + MemoryRegion power_mmio; > +} PowerDevice; > + > +/* Power */ > +static void power_mem_write(void *opaque, hwaddr addr, > + uint64_t val, unsigned size) > +{ > + /* According to a real Ultra 5, bit 24 controls the power */ > + if (val & 0x1000000) { > + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); > + } > +} > + > +static const MemoryRegionOps power_mem_ops = { > + .write = power_mem_write, > + .endianness = DEVICE_NATIVE_ENDIAN, > + .valid = { > + .min_access_size = 4, > + .max_access_size = 4, > + }, > +}; > + > +static void power_realize(DeviceState *dev, Error **errp) > +{ > + PowerDevice *d = SUN4U_POWER(dev); > + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); > + > + memory_region_init_io(&d->power_mmio, OBJECT(dev), &power_mem_ops, d, > + "power", sizeof(uint32_t)); > + > + sysbus_init_mmio(sbd, &d->power_mmio); > +} > + > +static void power_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + > + dc->realize = power_realize; > +} > + > +static const TypeInfo power_info = { > + .name = TYPE_SUN4U_POWER, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_size = sizeof(PowerDevice), > + .class_init = power_class_init, > +}; > + > static void ebus_isa_irq_handler(void *opaque, int n, int level) > { > EbusState *s = EBUS(opaque); > @@ -221,6 +274,7 @@ static void ebus_isa_irq_handler(void *opaque, int n, int level) > static void ebus_realize(PCIDevice *pci_dev, Error **errp) > { > EbusState *s = EBUS(pci_dev); > + SysBusDevice *sbd; > DeviceState *dev; > qemu_irq *isa_irq; > DriveInfo *fd[MAX_FD]; > @@ -270,6 +324,13 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) > qdev_prop_set_uint32(dev, "dma", -1); > qdev_init_nofail(dev); > > + /* Power */ > + dev = qdev_create(NULL, TYPE_SUN4U_POWER); > + qdev_init_nofail(dev); > + sbd = SYS_BUS_DEVICE(dev); > + memory_region_add_subregion(pci_address_space_io(pci_dev), 0x7240, > + sysbus_mmio_get_region(sbd, 0)); > + > /* PCI */ > pci_dev->config[0x04] = 0x06; // command = bus master, pci mem > pci_dev->config[0x05] = 0x00; > @@ -282,7 +343,7 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) > 0, 0x1000000); > pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar0); > memory_region_init_alias(&s->bar1, OBJECT(s), "bar1", get_system_io(), > - 0, 0x4000); > + 0, 0x8000); What about using 2 MR, bar1_lo/bar1_hi, registering bar1_lo at offset @0 and bar1_hi at @0x724000? > pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->bar1); > } > > @@ -693,6 +754,7 @@ static const TypeInfo sun4v_type = { > > static void sun4u_register_types(void) > { > + type_register_static(&power_info); > type_register_static(&ebus_info); > type_register_static(&prom_info); > type_register_static(&ram_info); >
Hi Philippe, On 16/01/2018 2:54, Philippe Mathieu-Daudé wrote: > CC'ing PCI maintainers. > > Hi Mark, > > On 01/15/2018 05:58 PM, Mark Cave-Ayland wrote: >> This inbuilt device contains a single 4-byte register, of which bit 24 is used >> to power down the machine on a real Ultra 5. >> >> The power device exists at offset 0x724000 on a real machine, but due to the >> current configuration of the BARs in QEMU it must be located lower in PCI IO >> space. > Is is some issue in pci_bar_address()? > The QEMU IO layout: /* * QEMU I/O address space usage: * 0000 - 0fff legacy isa, pci config, pci root bus, ... * 1000 - 9fff free * a000 - afff hotplug (cpu, pci via acpi, i440fx/piix only) * b000 - bfff power management (PORT_ACPI_PM_BASE) * [ qemu 1.4+ implements pci config registers * properly so guests can place the registers * where they want, on older versions its fixed ] * c000 - ffff free, traditionally used for pci io */ As you can see we don't have IO address space over ffff. Thanks, Marcel >> >> For the moment we place the power device at offset 0x7240 as a reminder of its >> original location and raise the base PCI IO address from 0x4000 to 0x8000. > > If we can't fix it, I rather prefer a #define with a comment in the > code, IMHO this is safer and easier to remember than looking in git history. > >> >> Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> >> --- >> hw/sparc64/sun4u.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++- >> 1 file changed, 63 insertions(+), 1 deletion(-) >> >> diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c >> index ec45ec2801..26ab6e9a9c 100644 >> --- a/hw/sparc64/sun4u.c >> +++ b/hw/sparc64/sun4u.c >> @@ -205,6 +205,59 @@ typedef struct ResetData { >> uint64_t prom_addr; >> } ResetData; >> >> +#define TYPE_SUN4U_POWER "power" >> +#define SUN4U_POWER(obj) OBJECT_CHECK(PowerDevice, (obj), TYPE_SUN4U_POWER) >> + >> +typedef struct PowerDevice { >> + SysBusDevice parent_obj; >> + >> + MemoryRegion power_mmio; >> +} PowerDevice; >> + >> +/* Power */ >> +static void power_mem_write(void *opaque, hwaddr addr, >> + uint64_t val, unsigned size) >> +{ >> + /* According to a real Ultra 5, bit 24 controls the power */ >> + if (val & 0x1000000) { >> + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); >> + } >> +} >> + >> +static const MemoryRegionOps power_mem_ops = { >> + .write = power_mem_write, >> + .endianness = DEVICE_NATIVE_ENDIAN, >> + .valid = { >> + .min_access_size = 4, >> + .max_access_size = 4, >> + }, >> +}; >> + >> +static void power_realize(DeviceState *dev, Error **errp) >> +{ >> + PowerDevice *d = SUN4U_POWER(dev); >> + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); >> + >> + memory_region_init_io(&d->power_mmio, OBJECT(dev), &power_mem_ops, d, >> + "power", sizeof(uint32_t)); >> + >> + sysbus_init_mmio(sbd, &d->power_mmio); >> +} >> + >> +static void power_class_init(ObjectClass *klass, void *data) >> +{ >> + DeviceClass *dc = DEVICE_CLASS(klass); >> + >> + dc->realize = power_realize; >> +} >> + >> +static const TypeInfo power_info = { >> + .name = TYPE_SUN4U_POWER, >> + .parent = TYPE_SYS_BUS_DEVICE, >> + .instance_size = sizeof(PowerDevice), >> + .class_init = power_class_init, >> +}; >> + >> static void ebus_isa_irq_handler(void *opaque, int n, int level) >> { >> EbusState *s = EBUS(opaque); >> @@ -221,6 +274,7 @@ static void ebus_isa_irq_handler(void *opaque, int n, int level) >> static void ebus_realize(PCIDevice *pci_dev, Error **errp) >> { >> EbusState *s = EBUS(pci_dev); >> + SysBusDevice *sbd; >> DeviceState *dev; >> qemu_irq *isa_irq; >> DriveInfo *fd[MAX_FD]; >> @@ -270,6 +324,13 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) >> qdev_prop_set_uint32(dev, "dma", -1); >> qdev_init_nofail(dev); >> >> + /* Power */ >> + dev = qdev_create(NULL, TYPE_SUN4U_POWER); >> + qdev_init_nofail(dev); >> + sbd = SYS_BUS_DEVICE(dev); >> + memory_region_add_subregion(pci_address_space_io(pci_dev), 0x7240, >> + sysbus_mmio_get_region(sbd, 0)); >> + >> /* PCI */ >> pci_dev->config[0x04] = 0x06; // command = bus master, pci mem >> pci_dev->config[0x05] = 0x00; >> @@ -282,7 +343,7 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) >> 0, 0x1000000); >> pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar0); >> memory_region_init_alias(&s->bar1, OBJECT(s), "bar1", get_system_io(), >> - 0, 0x4000); >> + 0, 0x8000); > > What about using 2 MR, bar1_lo/bar1_hi, registering bar1_lo at offset @0 > and bar1_hi at @0x724000? > >> pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->bar1); >> } >> >> @@ -693,6 +754,7 @@ static const TypeInfo sun4v_type = { >> >> static void sun4u_register_types(void) >> { >> + type_register_static(&power_info); >> type_register_static(&ebus_info); >> type_register_static(&prom_info); >> type_register_static(&ram_info); >> >
On 16/01/18 00:54, Philippe Mathieu-Daudé wrote: > CC'ing PCI maintainers. > > Hi Mark, > > On 01/15/2018 05:58 PM, Mark Cave-Ayland wrote: >> This inbuilt device contains a single 4-byte register, of which bit 24 is used >> to power down the machine on a real Ultra 5. >> >> The power device exists at offset 0x724000 on a real machine, but due to the >> current configuration of the BARs in QEMU it must be located lower in PCI IO >> space. > Is is some issue in pci_bar_address()? Hi Philippe, It's not an issue with QEMU at all, more the way in which the on-board devices are mapped. From memory there are 3 different issues with the current setup: - OpenBIOS uses PCI IO space accesses for all its drivers In a real Ultra 5 the on-board devices are behind a memory BAR but everything in OpenBIOS is hard-coded to use IO accesses. Changing that will be fairly hard. - Some OSs use the ebus BAR to access the onboard devices, others use the direct physical accesses This means that the ebus BAR has to be located at address zero (even though its in slot 1) for both accesses to work. The first part of PCI IO space containing the ebus is then aliased back onto its physical address to make everything consistent. - Linux sabre fix-up bug Potentially this could be fixed by increasing the base physical address of the simba IO space, however Linux has a fixup routine for this particular PCI host bridge and this has a bug which means that it presumes the lower word of the address is 0 (like a real Ultra 5) rather than reading it from the DT. Therefore whilst moving the base of PCI IO space within the physical address space should work, due to this bug the PCI addresses are converted incorrectly from physical addresses causing a panic. >> For the moment we place the power device at offset 0x7240 as a reminder of its >> original location and raise the base PCI IO address from 0x4000 to 0x8000. > > If we can't fix it, I rather prefer a #define with a comment in the > code, IMHO this is safer and easier to remember than looking in git history. Well TBH just about all of the current devices for sun4u are either incorrect or located at the wrong address, and could possibly change later if required for Solaris compatibility. So while things are still quite fluid, I'm more inclined to leave them as hard-coded offsets (similarly in OpenBIOS) until things become more clear. ATB, Mark.
On 16/01/18 14:23, Marcel Apfelbaum wrote: > Hi Philippe, > > On 16/01/2018 2:54, Philippe Mathieu-Daudé wrote: >> CC'ing PCI maintainers. >> >> Hi Mark, >> >> On 01/15/2018 05:58 PM, Mark Cave-Ayland wrote: >>> This inbuilt device contains a single 4-byte register, of which bit >>> 24 is used >>> to power down the machine on a real Ultra 5. >>> >>> The power device exists at offset 0x724000 on a real machine, but due >>> to the >>> current configuration of the BARs in QEMU it must be located lower in >>> PCI IO >>> space. >> Is is some issue in pci_bar_address()? >> > > The QEMU IO layout: > > /* > * QEMU I/O address space usage: > * 0000 - 0fff legacy isa, pci config, pci root bus, ... > * 1000 - 9fff free > * a000 - afff hotplug (cpu, pci via acpi, i440fx/piix only) > * b000 - bfff power management (PORT_ACPI_PM_BASE) > * [ qemu 1.4+ implements pci config registers > * properly so guests can place the registers > * where they want, on older versions its fixed ] > * c000 - ffff free, traditionally used for pci io > */ > > As you can see we don't have IO address space over ffff. Well that's not actually quite true - we use a separate ebus address space for the onboard devices (and that does have 32-bit PCI IO accesses enabled), but the issue here is one of the sun4u PCI host/onboard device configuration rather than anything to do with QEMU. ATB, Mark.
On 16/01/2018 22:05, Mark Cave-Ayland wrote: > On 16/01/18 14:23, Marcel Apfelbaum wrote: > >> Hi Philippe, >> >> On 16/01/2018 2:54, Philippe Mathieu-Daudé wrote: >>> CC'ing PCI maintainers. >>> >>> Hi Mark, >>> >>> On 01/15/2018 05:58 PM, Mark Cave-Ayland wrote: >>>> This inbuilt device contains a single 4-byte register, of which bit 24 is used >>>> to power down the machine on a real Ultra 5. >>>> >>>> The power device exists at offset 0x724000 on a real machine, but due to the >>>> current configuration of the BARs in QEMU it must be located lower in PCI IO >>>> space. >>> Is is some issue in pci_bar_address()? >>> >> >> The QEMU IO layout: >> >> /* >> * QEMU I/O address space usage: >> * 0000 - 0fff legacy isa, pci config, pci root bus, ... >> * 1000 - 9fff free >> * a000 - afff hotplug (cpu, pci via acpi, i440fx/piix only) >> * b000 - bfff power management (PORT_ACPI_PM_BASE) >> * [ qemu 1.4+ implements pci config registers >> * properly so guests can place the registers >> * where they want, on older versions its fixed ] >> * c000 - ffff free, traditionally used for pci io >> */ >> >> As you can see we don't have IO address space over ffff. > > Well that's not actually quite true - we use a separate ebus address space for the onboard devices (and that does have > 32-bit PCI IO accesses enabled), but the issue here is one of the sun4u PCI host/onboard device configuration rather > than anything to do with QEMU. > Got it, thanks (the above is probably true only for x86 machines) Marcel > > ATB, > > Mark.
Hi Mark, On Mon, Jan 15, 2018 at 9:58 PM, Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> wrote: > This inbuilt device contains a single 4-byte register, of which bit 24 is used > to power down the machine on a real Ultra 5. > > The power device exists at offset 0x724000 on a real machine, but due to the > current configuration of the BARs in QEMU it must be located lower in PCI IO > space. > > For the moment we place the power device at offset 0x7240 as a reminder of its > original location and raise the base PCI IO address from 0x4000 to 0x8000. I think it's ok to have it there for now. The guests should get the info from the device tree. > Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Reviewed-by: Artyom Tarasenko <atar4qemu@gmail.com> > --- > hw/sparc64/sun4u.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 63 insertions(+), 1 deletion(-) > > diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c > index ec45ec2801..26ab6e9a9c 100644 > --- a/hw/sparc64/sun4u.c > +++ b/hw/sparc64/sun4u.c > @@ -205,6 +205,59 @@ typedef struct ResetData { > uint64_t prom_addr; > } ResetData; > > +#define TYPE_SUN4U_POWER "power" > +#define SUN4U_POWER(obj) OBJECT_CHECK(PowerDevice, (obj), TYPE_SUN4U_POWER) > + > +typedef struct PowerDevice { > + SysBusDevice parent_obj; > + > + MemoryRegion power_mmio; > +} PowerDevice; > + > +/* Power */ > +static void power_mem_write(void *opaque, hwaddr addr, > + uint64_t val, unsigned size) > +{ > + /* According to a real Ultra 5, bit 24 controls the power */ > + if (val & 0x1000000) { > + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); > + } > +} > + > +static const MemoryRegionOps power_mem_ops = { > + .write = power_mem_write, > + .endianness = DEVICE_NATIVE_ENDIAN, > + .valid = { > + .min_access_size = 4, > + .max_access_size = 4, > + }, > +}; > + > +static void power_realize(DeviceState *dev, Error **errp) > +{ > + PowerDevice *d = SUN4U_POWER(dev); > + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); > + > + memory_region_init_io(&d->power_mmio, OBJECT(dev), &power_mem_ops, d, > + "power", sizeof(uint32_t)); > + > + sysbus_init_mmio(sbd, &d->power_mmio); > +} > + > +static void power_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + > + dc->realize = power_realize; > +} > + > +static const TypeInfo power_info = { > + .name = TYPE_SUN4U_POWER, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_size = sizeof(PowerDevice), > + .class_init = power_class_init, > +}; > + > static void ebus_isa_irq_handler(void *opaque, int n, int level) > { > EbusState *s = EBUS(opaque); > @@ -221,6 +274,7 @@ static void ebus_isa_irq_handler(void *opaque, int n, int level) > static void ebus_realize(PCIDevice *pci_dev, Error **errp) > { > EbusState *s = EBUS(pci_dev); > + SysBusDevice *sbd; > DeviceState *dev; > qemu_irq *isa_irq; > DriveInfo *fd[MAX_FD]; > @@ -270,6 +324,13 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) > qdev_prop_set_uint32(dev, "dma", -1); > qdev_init_nofail(dev); > > + /* Power */ > + dev = qdev_create(NULL, TYPE_SUN4U_POWER); > + qdev_init_nofail(dev); > + sbd = SYS_BUS_DEVICE(dev); > + memory_region_add_subregion(pci_address_space_io(pci_dev), 0x7240, > + sysbus_mmio_get_region(sbd, 0)); > + > /* PCI */ > pci_dev->config[0x04] = 0x06; // command = bus master, pci mem > pci_dev->config[0x05] = 0x00; > @@ -282,7 +343,7 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) > 0, 0x1000000); > pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar0); > memory_region_init_alias(&s->bar1, OBJECT(s), "bar1", get_system_io(), > - 0, 0x4000); > + 0, 0x8000); > pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->bar1); > } > > @@ -693,6 +754,7 @@ static const TypeInfo sun4v_type = { > > static void sun4u_register_types(void) > { > + type_register_static(&power_info); > type_register_static(&ebus_info); > type_register_static(&prom_info); > type_register_static(&ram_info); > -- > 2.11.0 >
On 20/01/18 20:10, Artyom Tarasenko wrote: > Hi Mark, > > > On Mon, Jan 15, 2018 at 9:58 PM, Mark Cave-Ayland > <mark.cave-ayland@ilande.co.uk> wrote: >> This inbuilt device contains a single 4-byte register, of which bit 24 is used >> to power down the machine on a real Ultra 5. >> >> The power device exists at offset 0x724000 on a real machine, but due to the >> current configuration of the BARs in QEMU it must be located lower in PCI IO >> space. >> >> For the moment we place the power device at offset 0x7240 as a reminder of its >> original location and raise the base PCI IO address from 0x4000 to 0x8000. > > I think it's ok to have it there for now. The guests should get the > info from the device tree. Yes indeed. For reference you can see the corresponding OpenBIOS patchset at https://mail.coreboot.org/pipermail/openbios/2018-January/010160.html. ATB, Mark. >> Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> > > Reviewed-by: Artyom Tarasenko <atar4qemu@gmail.com> > >> --- >> hw/sparc64/sun4u.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++- >> 1 file changed, 63 insertions(+), 1 deletion(-) >> >> diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c >> index ec45ec2801..26ab6e9a9c 100644 >> --- a/hw/sparc64/sun4u.c >> +++ b/hw/sparc64/sun4u.c >> @@ -205,6 +205,59 @@ typedef struct ResetData { >> uint64_t prom_addr; >> } ResetData; >> >> +#define TYPE_SUN4U_POWER "power" >> +#define SUN4U_POWER(obj) OBJECT_CHECK(PowerDevice, (obj), TYPE_SUN4U_POWER) >> + >> +typedef struct PowerDevice { >> + SysBusDevice parent_obj; >> + >> + MemoryRegion power_mmio; >> +} PowerDevice; >> + >> +/* Power */ >> +static void power_mem_write(void *opaque, hwaddr addr, >> + uint64_t val, unsigned size) >> +{ >> + /* According to a real Ultra 5, bit 24 controls the power */ >> + if (val & 0x1000000) { >> + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); >> + } >> +} >> + >> +static const MemoryRegionOps power_mem_ops = { >> + .write = power_mem_write, >> + .endianness = DEVICE_NATIVE_ENDIAN, >> + .valid = { >> + .min_access_size = 4, >> + .max_access_size = 4, >> + }, >> +}; >> + >> +static void power_realize(DeviceState *dev, Error **errp) >> +{ >> + PowerDevice *d = SUN4U_POWER(dev); >> + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); >> + >> + memory_region_init_io(&d->power_mmio, OBJECT(dev), &power_mem_ops, d, >> + "power", sizeof(uint32_t)); >> + >> + sysbus_init_mmio(sbd, &d->power_mmio); >> +} >> + >> +static void power_class_init(ObjectClass *klass, void *data) >> +{ >> + DeviceClass *dc = DEVICE_CLASS(klass); >> + >> + dc->realize = power_realize; >> +} >> + >> +static const TypeInfo power_info = { >> + .name = TYPE_SUN4U_POWER, >> + .parent = TYPE_SYS_BUS_DEVICE, >> + .instance_size = sizeof(PowerDevice), >> + .class_init = power_class_init, >> +}; >> + >> static void ebus_isa_irq_handler(void *opaque, int n, int level) >> { >> EbusState *s = EBUS(opaque); >> @@ -221,6 +274,7 @@ static void ebus_isa_irq_handler(void *opaque, int n, int level) >> static void ebus_realize(PCIDevice *pci_dev, Error **errp) >> { >> EbusState *s = EBUS(pci_dev); >> + SysBusDevice *sbd; >> DeviceState *dev; >> qemu_irq *isa_irq; >> DriveInfo *fd[MAX_FD]; >> @@ -270,6 +324,13 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) >> qdev_prop_set_uint32(dev, "dma", -1); >> qdev_init_nofail(dev); >> >> + /* Power */ >> + dev = qdev_create(NULL, TYPE_SUN4U_POWER); >> + qdev_init_nofail(dev); >> + sbd = SYS_BUS_DEVICE(dev); >> + memory_region_add_subregion(pci_address_space_io(pci_dev), 0x7240, >> + sysbus_mmio_get_region(sbd, 0)); >> + >> /* PCI */ >> pci_dev->config[0x04] = 0x06; // command = bus master, pci mem >> pci_dev->config[0x05] = 0x00; >> @@ -282,7 +343,7 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) >> 0, 0x1000000); >> pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar0); >> memory_region_init_alias(&s->bar1, OBJECT(s), "bar1", get_system_io(), >> - 0, 0x4000); >> + 0, 0x8000); >> pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->bar1); >> } >> >> @@ -693,6 +754,7 @@ static const TypeInfo sun4v_type = { >> >> static void sun4u_register_types(void) >> { >> + type_register_static(&power_info); >> type_register_static(&ebus_info); >> type_register_static(&prom_info); >> type_register_static(&ram_info); >> -- >> 2.11.0 >> > > >
diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index ec45ec2801..26ab6e9a9c 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -205,6 +205,59 @@ typedef struct ResetData { uint64_t prom_addr; } ResetData; +#define TYPE_SUN4U_POWER "power" +#define SUN4U_POWER(obj) OBJECT_CHECK(PowerDevice, (obj), TYPE_SUN4U_POWER) + +typedef struct PowerDevice { + SysBusDevice parent_obj; + + MemoryRegion power_mmio; +} PowerDevice; + +/* Power */ +static void power_mem_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + /* According to a real Ultra 5, bit 24 controls the power */ + if (val & 0x1000000) { + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + } +} + +static const MemoryRegionOps power_mem_ops = { + .write = power_mem_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void power_realize(DeviceState *dev, Error **errp) +{ + PowerDevice *d = SUN4U_POWER(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&d->power_mmio, OBJECT(dev), &power_mem_ops, d, + "power", sizeof(uint32_t)); + + sysbus_init_mmio(sbd, &d->power_mmio); +} + +static void power_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = power_realize; +} + +static const TypeInfo power_info = { + .name = TYPE_SUN4U_POWER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PowerDevice), + .class_init = power_class_init, +}; + static void ebus_isa_irq_handler(void *opaque, int n, int level) { EbusState *s = EBUS(opaque); @@ -221,6 +274,7 @@ static void ebus_isa_irq_handler(void *opaque, int n, int level) static void ebus_realize(PCIDevice *pci_dev, Error **errp) { EbusState *s = EBUS(pci_dev); + SysBusDevice *sbd; DeviceState *dev; qemu_irq *isa_irq; DriveInfo *fd[MAX_FD]; @@ -270,6 +324,13 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) qdev_prop_set_uint32(dev, "dma", -1); qdev_init_nofail(dev); + /* Power */ + dev = qdev_create(NULL, TYPE_SUN4U_POWER); + qdev_init_nofail(dev); + sbd = SYS_BUS_DEVICE(dev); + memory_region_add_subregion(pci_address_space_io(pci_dev), 0x7240, + sysbus_mmio_get_region(sbd, 0)); + /* PCI */ pci_dev->config[0x04] = 0x06; // command = bus master, pci mem pci_dev->config[0x05] = 0x00; @@ -282,7 +343,7 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) 0, 0x1000000); pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar0); memory_region_init_alias(&s->bar1, OBJECT(s), "bar1", get_system_io(), - 0, 0x4000); + 0, 0x8000); pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->bar1); } @@ -693,6 +754,7 @@ static const TypeInfo sun4v_type = { static void sun4u_register_types(void) { + type_register_static(&power_info); type_register_static(&ebus_info); type_register_static(&prom_info); type_register_static(&ram_info);
This inbuilt device contains a single 4-byte register, of which bit 24 is used to power down the machine on a real Ultra 5. The power device exists at offset 0x724000 on a real machine, but due to the current configuration of the BARs in QEMU it must be located lower in PCI IO space. For the moment we place the power device at offset 0x7240 as a reminder of its original location and raise the base PCI IO address from 0x4000 to 0x8000. Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> --- hw/sparc64/sun4u.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-)