Signed-off-by: Hervé Poussineau <hpous...@reactos.org> --- hw/isa-bus.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- hw/isa.h | 2 +- 2 files changed, 125 insertions(+), 4 deletions(-)
diff --git a/hw/isa-bus.c b/hw/isa-bus.c index 86b0bbd..bcf7cd4 100644 --- a/hw/isa-bus.c +++ b/hw/isa-bus.c @@ -104,19 +104,140 @@ void isa_register_ioport(ISADevice *dev, MemoryRegion *io, uint16_t start) isa_init_ioport(dev, start); } +typedef struct PortioState { + const char *name; /* debug purposes */ + uint16_t start; + uint16_t offset; + const MemoryRegionPortio *pio_start; + void *opaque; +} PortioState; + +static const MemoryRegionPortio *portio_find(const MemoryRegionPortio *mrp, + uint64_t offset, + unsigned int width, bool write, + bool smaller) +{ + for (; mrp->size; ++mrp) { + if (offset >= mrp->offset && offset < mrp->offset + mrp->len + && (width == mrp->size || (smaller && width < mrp->size)) + && (write ? (bool)mrp->write : (bool)mrp->read)) { + return mrp; + } + } + return NULL; +} + +static uint64_t portio_read(void *opaque, hwaddr addr, unsigned int size) +{ + const PortioState *s = opaque; + const MemoryRegionPortio *mrp; + + addr += s->offset; + mrp = portio_find(s->pio_start, addr, size, false, false); + if (mrp) { + return mrp->read(s->opaque, s->start + addr); + } else if (size == 2) { + uint64_t data; + mrp = portio_find(s->pio_start, addr, 1, false, false); + assert(mrp); + data = mrp->read(s->opaque, s->start + addr) | + (mrp->read(s->opaque, s->start + addr + 1) << 8); + return data; + } + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid read from 0x%x size=%d", + s->name, s->start + (int)addr, size); + return -1U; +} + +static void portio_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + const PortioState *s = opaque; + const MemoryRegionPortio *mrp; + + addr += s->offset; + mrp = portio_find(s->pio_start, addr, size, true, false); + if (mrp) { + mrp->write(s->opaque, s->start + addr, data); + return; + } else if (size == 2) { + mrp = portio_find(s->pio_start, addr, 1, true, false); + assert(mrp); + mrp->write(s->opaque, s->start + addr, data & 0xff); + mrp->write(s->opaque, s->start + addr + 1, data >> 8); + return; + } + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid write to 0x%x size=%d", + s->name, s->start + (int)addr, size); +} + +static bool portio_accepts(void *opaque, hwaddr addr, unsigned int size, + bool is_write) +{ + const PortioState *s = opaque; + const MemoryRegionPortio *mrp; + + addr += s->offset; + mrp = portio_find(s->pio_start, addr, size, is_write, true); + return (mrp != NULL); +} + +const MemoryRegionOps portio_ops = { + .read = portio_read, + .write = portio_write, + .valid.accepts = portio_accepts, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void isa_register_portio_list_1(ISADevice *dev, uint16_t start, + uint16_t offset, uint16_t end, + const MemoryRegionPortio *pio_start, + void *opaque, const char *name) +{ + MemoryRegion *mr = g_new(MemoryRegion, 1); + PortioState *s = g_new(PortioState, 1); + + s->name = name; + s->start = start; + s->offset = offset; + s->pio_start = pio_start; + s->opaque = opaque; + memory_region_init_io(mr, &portio_ops, s, name, end - offset); + memory_region_add_subregion(isa_address_space_io(dev), + start + offset, mr); +} + void isa_register_portio_list(ISADevice *dev, uint16_t start, const MemoryRegionPortio *pio_start, void *opaque, const char *name) { - PortioList *piolist = g_new(PortioList, 1); + const MemoryRegionPortio *pio, *first; + uint16_t end; /* START is how we should treat DEV, regardless of the actual contents of the portio array. This is how the old code actually handled e.g. the FDC device. */ isa_init_ioport(dev, start); - portio_list_init(piolist, pio_start, opaque, name); - portio_list_add(piolist, isabus->address_space_io, start); + assert(pio_start->size); + + first = pio_start; + end = 0; + for (pio = pio_start; pio->size; pio++) { + assert(pio->offset >= first->offset); + if (pio->offset > first->offset + first->len) { + isa_register_portio_list_1(dev, start, first->offset, end, + pio_start, opaque, name); + first = pio; + end = 0; + } + if (pio->offset + pio->len > end) { + end = pio->offset + pio->len; + } + } + + isa_register_portio_list_1(dev, start, first->offset, end, + pio_start, opaque, name); } static int isa_qdev_init(DeviceState *qdev) diff --git a/hw/isa.h b/hw/isa.h index 62e89d3..c3b01ea 100644 --- a/hw/isa.h +++ b/hw/isa.h @@ -73,7 +73,7 @@ void isa_register_ioport(ISADevice *dev, MemoryRegion *io, uint16_t start); * @dev: the ISADevice against which these are registered; may be NULL. * @start: the base I/O port against which the portio->offset is applied. * @portio: the ports, sorted by offset. - * @opaque: passed into the old_portio callbacks. + * @opaque: passed into the portio callbacks. * @name: passed into memory_region_init_io. */ void isa_register_portio_list(ISADevice *dev, uint16_t start, -- 1.7.10.4