@@ -103,6 +103,85 @@ void isa_register_ioport(ISADevice *dev, MemoryRegion *io, uint16_t start)
}
}
+static void isa_register_portio_1(ISADevice *dev,
+ const MemoryRegionPortio *pio_init,
+ unsigned count, unsigned start,
+ unsigned off_low, unsigned off_high,
+ void *opaque, const char *name)
+{
+ MemoryRegionPortio *pio;
+ MemoryRegionOps *ops;
+ MemoryRegion *region;
+ unsigned i;
+
+ if (off_low == 0 && pio_init[count].size == 0) {
+ /* Special case simple adjustments. */
+ pio = (MemoryRegionPortio *) pio_init;
+ } else {
+ /* Copy the sub-list and null-terminate it. */
+ pio = g_new(MemoryRegionPortio, count + 1);
+ memcpy(pio, pio_init, sizeof(MemoryRegionPortio) * count);
+ memset(pio + count, 0, sizeof(MemoryRegionPortio));
+
+ /* Adjust the offsets to all be zero-based for the region. */
+ for (i = 0; i < count; ++i) {
+ pio[i].offset -= off_low;
+ }
+ }
+
+ ops = g_new0(MemoryRegionOps, 1);
+ ops->old_portio = pio;
+
+ region = g_new(MemoryRegion, 1);
+ memory_region_init_io(region, ops, opaque, name, off_high - off_low);
+ memory_region_set_offset(region, start + off_low);
+ memory_region_add_subregion(isabus->address_space_io,
+ start + off_low, region);
+}
+
+void isa_register_portio_list(ISADevice *dev, uint16_t start,
+ const MemoryRegionPortio *pio_start,
+ void *opaque, const char *name)
+{
+ const MemoryRegionPortio *pio;
+ unsigned int off_low, off_high, off_last, count;
+
+ /* 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. */
+ if (dev) {
+ isa_init_ioport(dev, start);
+ }
+
+ /* Handle the first entry specially. */
+ off_last = off_low = pio_start->offset;
+ off_high = off_low + pio_start->len;
+ count = 1;
+
+ for (pio = pio_start + 1; pio->size != 0; pio++, count++) {
+ /* All entries must be sorted by offset. */
+ assert(pio->offset >= off_last);
+ off_last = pio->offset;
+
+ /* If we see a hole, break the region. */
+ if (off_last > off_high) {
+ isa_register_portio_1(dev, pio_start, count, start, off_low,
+ off_high, opaque, name);
+ /* ... and start collecting anew. */
+ pio_start = pio;
+ off_low = off_last;
+ off_high = off_low + pio->len;
+ count = 0;
+ } else if (off_last + pio->len > off_high) {
+ off_high = off_last + pio->len;
+ }
+ }
+
+ /* There will always be an open sub-list. */
+ isa_register_portio_1(dev, pio_start, count, start, off_low,
+ off_high, opaque, name);
+}
+
static int isa_qdev_init(DeviceState *qdev, DeviceInfo *base)
{
ISADevice *dev = DO_UPCAST(ISADevice, qdev, qdev);
@@ -28,7 +28,6 @@ ISABus *isa_bus_new(DeviceState *dev, MemoryRegion *address_space_io);
void isa_bus_irqs(qemu_irq *irqs);
qemu_irq isa_get_irq(int isairq);
void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq);
-void isa_register_ioport(ISADevice *dev, MemoryRegion *io, uint16_t start);
void isa_init_ioport(ISADevice *dev, uint16_t ioport);
void isa_init_ioport_range(ISADevice *dev, uint16_t start, uint16_t length);
void isa_qdev_register(ISADeviceInfo *info);
@@ -37,6 +36,36 @@ ISADevice *isa_create(const char *name);
ISADevice *isa_try_create(const char *name);
ISADevice *isa_create_simple(const char *name);
+/**
+ * isa_register_ioport: Install an I/O port region on the ISA bus.
+ *
+ * Register an I/O port region via memory_region_add_subregion
+ * inside the ISA I/O address space.
+ *
+ * @dev: the ISADevice against which these are registered; may be NULL.
+ * @io: the #MemoryRegion being registered.
+ * @start: the base I/O port.
+ */
+void isa_register_ioport(ISADevice *dev, MemoryRegion *io, uint16_t start);
+
+/**
+ * isa_register_portio_list: Initialize a set of ISA io ports
+ *
+ * Several ISA devices have many dis-joint I/O ports. Worse, these I/O
+ * ports can be interleaved with I/O ports from other devices. This
+ * function makes it easy to create multiple MemoryRegions for a single
+ * device and use the legacy portio routines.
+ *
+ * @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.
+ * @name: passed into memory_region_init_io.
+ */
+void isa_register_portio_list(ISADevice *dev, uint16_t start,
+ const MemoryRegionPortio *portio,
+ void *opaque, const char *name);
+
extern target_phys_addr_t isa_mem_base;
void isa_mmio_setup(MemoryRegion *mr, target_phys_addr_t size);
@@ -396,12 +396,12 @@ static void memory_region_iorange_read(IORange *iorange,
*data = ((uint64_t)1 << (width * 8)) - 1;
if (mrp) {
- *data = mrp->read(mr->opaque, offset);
+ *data = mrp->read(mr->opaque, offset + mr->offset);
}
return;
}
*data = 0;
- access_with_adjusted_size(offset, data, width,
+ access_with_adjusted_size(offset + mr->offset, data, width,
mr->ops->impl.min_access_size,
mr->ops->impl.max_access_size,
memory_region_read_accessor, mr);
@@ -418,11 +418,11 @@ static void memory_region_iorange_write(IORange *iorange,
const MemoryRegionPortio *mrp = find_portio(mr, offset, width, true);
if (mrp) {
- mrp->write(mr->opaque, offset, data);
+ mrp->write(mr->opaque, offset + mr->offset, data);
}
return;
}
- access_with_adjusted_size(offset, &data, width,
+ access_with_adjusted_size(offset + mr->offset, &data, width,
mr->ops->impl.min_access_size,
mr->ops->impl.max_access_size,
memory_region_write_accessor, mr);
Signed-off-by: Richard Henderson <rth@twiddle.net> --- hw/isa-bus.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/isa.h | 31 ++++++++++++++++++++++- memory.c | 8 +++--- 3 files changed, 113 insertions(+), 5 deletions(-)