Introduce PortioList

Add a type and methods for manipulating a list of disjoint I/O ports,
used in some older hardware devices.

Based on original patch by Richard Henderson.

Signed-off-by: Richard Henderson <rth@twiddle.net>
Signed-off-by: Avi Kivity <avi@redhat.com>
This commit is contained in:
Avi Kivity 2011-09-26 14:52:26 +03:00
parent ebf47c24b0
commit 6bf9fd43cf
5 changed files with 135 additions and 6 deletions

View File

@ -82,7 +82,7 @@ common-obj-$(CONFIG_WIN32) += os-win32.o
common-obj-$(CONFIG_POSIX) += os-posix.o
common-obj-y += tcg-runtime.o host-utils.o
common-obj-y += irq.o ioport.o input.o
common-obj-y += irq.o input.o
common-obj-$(CONFIG_PTIMER) += ptimer.o
common-obj-$(CONFIG_MAX7310) += max7310.o
common-obj-$(CONFIG_WM8750) += wm8750.o

View File

@ -183,7 +183,7 @@ endif #CONFIG_BSD_USER
# System emulator target
ifdef CONFIG_SOFTMMU
obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o
obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o ioport.o
# virtio has to be here due to weird dependency between PCI and virtio-net.
# need to fix this properly
obj-$(CONFIG_NO_PCI) += pci-stub.o

108
ioport.c
View File

@ -27,6 +27,7 @@
#include "ioport.h"
#include "trace.h"
#include "memory.h"
/***********************************************************/
/* IO Port */
@ -313,3 +314,110 @@ uint32_t cpu_inl(pio_addr_t addr)
LOG_IOPORT("inl : %04"FMT_pioaddr" %08"PRIx32"\n", addr, val);
return val;
}
void portio_list_init(PortioList *piolist,
const MemoryRegionPortio *callbacks,
void *opaque, const char *name)
{
unsigned n = 0;
while (callbacks[n].size) {
++n;
}
piolist->ports = callbacks;
piolist->nr = 0;
piolist->regions = g_new0(MemoryRegion *, n);
piolist->address_space = NULL;
piolist->opaque = opaque;
piolist->name = name;
}
void portio_list_destroy(PortioList *piolist)
{
g_free(piolist->regions);
}
static void portio_list_add_1(PortioList *piolist,
const MemoryRegionPortio *pio_init,
unsigned count, unsigned start,
unsigned off_low, unsigned off_high)
{
MemoryRegionPortio *pio;
MemoryRegionOps *ops;
MemoryRegion *region;
unsigned i;
/* 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, piolist->opaque, piolist->name,
off_high - off_low);
memory_region_set_offset(region, start + off_low);
memory_region_add_subregion(piolist->address_space,
start + off_low, region);
piolist->regions[piolist->nr++] = region;
}
void portio_list_add(PortioList *piolist,
MemoryRegion *address_space,
uint32_t start)
{
const MemoryRegionPortio *pio, *pio_start = piolist->ports;
unsigned int off_low, off_high, off_last, count;
piolist->address_space = address_space;
/* 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) {
portio_list_add_1(piolist, pio_start, count, start, off_low,
off_high);
/* ... 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. */
portio_list_add_1(piolist, pio_start, count, start, off_low, off_high);
}
void portio_list_del(PortioList *piolist)
{
MemoryRegion *mr;
unsigned i;
for (i = 0; i < piolist->nr; ++i) {
mr = piolist->regions[i];
memory_region_del_subregion(piolist->address_space, mr);
memory_region_destroy(mr);
g_free((MemoryRegionOps *)mr->ops);
g_free(mr);
piolist->regions[i] = NULL;
}
}

View File

@ -52,4 +52,25 @@ uint8_t cpu_inb(pio_addr_t addr);
uint16_t cpu_inw(pio_addr_t addr);
uint32_t cpu_inl(pio_addr_t addr);
struct MemoryRegion;
struct MemoryRegionPortio;
typedef struct PortioList {
const struct MemoryRegionPortio *ports;
struct MemoryRegion *address_space;
unsigned nr;
struct MemoryRegion **regions;
void *opaque;
const char *name;
} PortioList;
void portio_list_init(PortioList *piolist,
const struct MemoryRegionPortio *callbacks,
void *opaque, const char *name);
void portio_list_destroy(PortioList *piolist);
void portio_list_add(PortioList *piolist,
struct MemoryRegion *address_space,
uint32_t addr);
void portio_list_del(PortioList *piolist);
#endif /* IOPORT_H */

View File

@ -403,12 +403,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);
@ -425,11 +425,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);