diff --git a/hw/piix_pci.c b/hw/piix_pci.c index 3d79c73fda..6c77e493e4 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -31,6 +31,7 @@ #include "qemu/range.h" #include "xen.h" #include "pam.h" +#include "sysemu/sysemu.h" /* * I440FX chipset data sheet. @@ -46,6 +47,12 @@ typedef struct I440FXState { #define XEN_PIIX_NUM_PIRQS 128ULL #define PIIX_PIRQC 0x60 +/* + * Reset Control Register: PCI-accessible ISA-Compatible Register at address + * 0xcf9, provided by the PCI/ISA bridge (PIIX3 PCI function 0, 8086:7000). + */ +#define RCR_IOPORT 0xcf9 + typedef struct PIIX3State { PCIDevice dev; @@ -67,6 +74,12 @@ typedef struct PIIX3State { /* This member isn't used. Just for save/load compatibility */ int32_t pci_irq_levels_vmstate[PIIX_NUM_PIRQS]; + + /* Reset Control Register contents */ + uint8_t rcr; + + /* IO memory region for Reset Control Register (RCR_IOPORT) */ + MemoryRegion rcr_mem; } PIIX3State; struct PCII440FXState { @@ -442,6 +455,7 @@ static void piix3_reset(void *opaque) pci_conf[0xae] = 0x00; d->pic_levels = 0; + d->rcr = 0; } static int piix3_post_load(void *opaque, int version_id) @@ -462,6 +476,23 @@ static void piix3_pre_save(void *opaque) } } +static bool piix3_rcr_needed(void *opaque) +{ + PIIX3State *piix3 = opaque; + + return (piix3->rcr != 0); +} + +static const VMStateDescription vmstate_piix3_rcr = { + .name = "PIIX3/rcr", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_UINT8(rcr, PIIX3State), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_piix3 = { .name = "PIIX3", .version_id = 3, @@ -469,19 +500,56 @@ static const VMStateDescription vmstate_piix3 = { .minimum_version_id_old = 2, .post_load = piix3_post_load, .pre_save = piix3_pre_save, - .fields = (VMStateField []) { + .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(dev, PIIX3State), VMSTATE_INT32_ARRAY_V(pci_irq_levels_vmstate, PIIX3State, PIIX_NUM_PIRQS, 3), VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection[]) { + { + .vmsd = &vmstate_piix3_rcr, + .needed = piix3_rcr_needed, + }, + { 0 } } }; + +static void rcr_write(void *opaque, hwaddr addr, uint64_t val, unsigned len) +{ + PIIX3State *d = opaque; + + if (val & 4) { + qemu_system_reset_request(); + return; + } + d->rcr = val & 2; /* keep System Reset type only */ +} + +static uint64_t rcr_read(void *opaque, hwaddr addr, unsigned len) +{ + PIIX3State *d = opaque; + + return d->rcr; +} + +static const MemoryRegionOps rcr_ops = { + .read = rcr_read, + .write = rcr_write, + .endianness = DEVICE_LITTLE_ENDIAN +}; + static int piix3_initfn(PCIDevice *dev) { PIIX3State *d = DO_UPCAST(PIIX3State, dev, dev); isa_bus_new(&d->dev.qdev, pci_address_space_io(dev)); + + memory_region_init_io(&d->rcr_mem, &rcr_ops, d, "piix3-reset-control", 1); + memory_region_add_subregion_overlap(pci_address_space_io(dev), RCR_IOPORT, + &d->rcr_mem, 1); + qemu_register_reset(piix3_reset, d); return 0; }