Patchwork [08/10] isa: use memory regions instead of portio_list_* functions

login
register
mail settings
Submitter Hervé Poussineau
Date Jan. 4, 2013, 9:29 p.m.
Message ID <1357334986-13941-9-git-send-email-hpoussin@reactos.org>
Download mbox | patch
Permalink /patch/209546/
State New
Headers show

Comments

Hervé Poussineau - Jan. 4, 2013, 9:29 p.m.
Signed-off-by: Hervé Poussineau <hpoussin@reactos.org>
---
 hw/isa-bus.c |  127 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 hw/isa.h     |    2 +-
 2 files changed, 125 insertions(+), 4 deletions(-)
Andreas Färber - Jan. 12, 2013, 7:21 p.m.
Am 04.01.2013 22:29, schrieb Hervé Poussineau:
> Signed-off-by: Hervé Poussineau <hpoussin@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);

I note that patch 10/10 shows that memory.c does it similarly, assuming
Little Endian for size == 2 and ignoring assembled size == 4.

> +    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);

Ditto.

> +}
> +
> +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 = {

static const?

> +    .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,

Otherwise looks okay to me, but I'd appreciate another pair of eyes.

AFAIU this moves the current implementation from ioport.c (09/10) and
memory.c (10/10) into isa-bus.c, to remove MemoryRegionOps::old_portio
in 10/10. But ISA keeps using MemoryRegionPortio unlike memory.c itself.

I wonder if it were not a cleaner course of action to remove
isa_register_portio_list() completely by converting callers to use
isa_register_ioport() with a MemoryRegion owned by the caller?
Remaining MemoryRegionPortio lists after this series below.

Regards,
Andreas


dma.c:
/* IOport from page_base */
static const MemoryRegionPortio page_portio_list[] = {
    { 0x01, 3, 1, .write = write_page, .read = read_page, },
    { 0x07, 1, 1, .write = write_page, .read = read_page, },
    PORTIO_END_OF_LIST(),
};

/* IOport from pageh_base */
static const MemoryRegionPortio pageh_portio_list[] = {
    { 0x01, 3, 1, .write = write_pageh, .read = read_pageh, },
    { 0x07, 3, 1, .write = write_pageh, .read = read_pageh, },
    PORTIO_END_OF_LIST(),
};

fdc.c:
static const MemoryRegionPortio fdc_portio_list[] = {
    { 1, 5, 1, .read = fdctrl_read, .write = fdctrl_write },
    { 7, 1, 1, .read = fdctrl_read, .write = fdctrl_write },
    PORTIO_END_OF_LIST(),
};

gus.c:
static const MemoryRegionPortio gus_portio_list1[] = {
    {0x000,  1, 1, .write = gus_writeb },
    {0x000,  1, 2, .write = gus_writew },
    {0x006, 10, 1, .read = gus_readb, .write = gus_writeb },
    {0x006, 10, 2, .read = gus_readw, .write = gus_writew },
    {0x100,  8, 1, .read = gus_readb, .write = gus_writeb },
    {0x100,  8, 2, .read = gus_readw, .write = gus_writew },
    PORTIO_END_OF_LIST (),
};

static const MemoryRegionPortio gus_portio_list2[] = {
    {0, 1, 1, .read = gus_readb },
    {0, 1, 2, .read = gus_readw },
    PORTIO_END_OF_LIST (),
};

ide/core.c:
static const MemoryRegionPortio ide_portio_list[] = {
    { 0, 8, 1, .read = ide_ioport_read, .write = ide_ioport_write },
    { 0, 2, 2, .read = ide_data_readw, .write = ide_data_writew },
    { 0, 4, 4, .read = ide_data_readl, .write = ide_data_writel },
    PORTIO_END_OF_LIST(),
};

static const MemoryRegionPortio ide_portio2_list[] = {
    { 0, 1, 1, .read = ide_status_read, .write = ide_cmd_write },
    PORTIO_END_OF_LIST(),
};

parallel.c:
static const MemoryRegionPortio isa_parallel_portio_hw_list[] = {
    { 0, 8, 1,
      .read = parallel_ioport_read_hw,
      .write = parallel_ioport_write_hw },
    { 4, 1, 2,
      .read = parallel_ioport_eppdata_read_hw2,
      .write = parallel_ioport_eppdata_write_hw2 },
    { 4, 1, 4,
      .read = parallel_ioport_eppdata_read_hw4,
      .write = parallel_ioport_eppdata_write_hw4 },
    { 0x400, 8, 1,
      .read = parallel_ioport_ecp_read,
      .write = parallel_ioport_ecp_write },
    PORTIO_END_OF_LIST(),
};

static const MemoryRegionPortio isa_parallel_portio_sw_list[] = {
    { 0, 8, 1,
      .read = parallel_ioport_read_sw,
      .write = parallel_ioport_write_sw },
    PORTIO_END_OF_LIST(),
};

qxl.c:
static const MemoryRegionPortio qxl_vga_portio_list[] = {
    { 0x04,  2, 1, .read  = vga_ioport_read,
                   .write = qxl_vga_ioport_write }, /* 3b4 */
    { 0x0a,  1, 1, .read  = vga_ioport_read,
                   .write = qxl_vga_ioport_write }, /* 3ba */
    { 0x10, 16, 1, .read  = vga_ioport_read,
                   .write = qxl_vga_ioport_write }, /* 3c0 */
    { 0x24,  2, 1, .read  = vga_ioport_read,
                   .write = qxl_vga_ioport_write }, /* 3d4 */
    { 0x2a,  1, 1, .read  = vga_ioport_read,
                   .write = qxl_vga_ioport_write }, /* 3da */
    PORTIO_END_OF_LIST(),
};

sb16.c:
static const MemoryRegionPortio sb16_ioport_list[] = {
    {  4, 1, 1, .write = mixer_write_indexb },
    {  4, 1, 2, .write = mixer_write_indexw },
    {  5, 1, 1, .read = mixer_read, .write = mixer_write_datab },
    {  6, 1, 1, .read = dsp_read, .write = dsp_write },
    { 10, 1, 1, .read = dsp_read },
    { 12, 1, 1, .write = dsp_write },
    { 12, 4, 1, .read = dsp_read },
    PORTIO_END_OF_LIST (),
};

vga.c:
static const MemoryRegionPortio vga_portio_list[] = {
    { 0x04,  2, 1, .read = vga_ioport_read, .write = vga_ioport_write },
/* 3b4 */
    { 0x0a,  1, 1, .read = vga_ioport_read, .write = vga_ioport_write },
/* 3ba */
    { 0x10, 16, 1, .read = vga_ioport_read, .write = vga_ioport_write },
/* 3c0 */
    { 0x24,  2, 1, .read = vga_ioport_read, .write = vga_ioport_write },
/* 3d4 */
    { 0x2a,  1, 1, .read = vga_ioport_read, .write = vga_ioport_write },
/* 3da */
    PORTIO_END_OF_LIST(),
};

static const MemoryRegionPortio vbe_portio_list[] = {
    { 0, 1, 2, .read = vbe_ioport_read_index, .write =
vbe_ioport_write_index },
# ifdef TARGET_I386
    { 1, 1, 2, .read = vbe_ioport_read_data, .write =
vbe_ioport_write_data },
# endif
    { 2, 1, 2, .read = vbe_ioport_read_data, .write =
vbe_ioport_write_data },
    PORTIO_END_OF_LIST(),
};

Patch

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,