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:
parent
ebf47c24b0
commit
6bf9fd43cf
|
@ -82,7 +82,7 @@ common-obj-$(CONFIG_WIN32) += os-win32.o
|
||||||
common-obj-$(CONFIG_POSIX) += os-posix.o
|
common-obj-$(CONFIG_POSIX) += os-posix.o
|
||||||
|
|
||||||
common-obj-y += tcg-runtime.o host-utils.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_PTIMER) += ptimer.o
|
||||||
common-obj-$(CONFIG_MAX7310) += max7310.o
|
common-obj-$(CONFIG_MAX7310) += max7310.o
|
||||||
common-obj-$(CONFIG_WM8750) += wm8750.o
|
common-obj-$(CONFIG_WM8750) += wm8750.o
|
||||||
|
|
|
@ -183,7 +183,7 @@ endif #CONFIG_BSD_USER
|
||||||
# System emulator target
|
# System emulator target
|
||||||
ifdef CONFIG_SOFTMMU
|
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.
|
# virtio has to be here due to weird dependency between PCI and virtio-net.
|
||||||
# need to fix this properly
|
# need to fix this properly
|
||||||
obj-$(CONFIG_NO_PCI) += pci-stub.o
|
obj-$(CONFIG_NO_PCI) += pci-stub.o
|
||||||
|
|
108
ioport.c
108
ioport.c
|
@ -27,6 +27,7 @@
|
||||||
|
|
||||||
#include "ioport.h"
|
#include "ioport.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
#include "memory.h"
|
||||||
|
|
||||||
/***********************************************************/
|
/***********************************************************/
|
||||||
/* IO Port */
|
/* 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);
|
LOG_IOPORT("inl : %04"FMT_pioaddr" %08"PRIx32"\n", addr, val);
|
||||||
return 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
21
ioport.h
21
ioport.h
|
@ -52,4 +52,25 @@ uint8_t cpu_inb(pio_addr_t addr);
|
||||||
uint16_t cpu_inw(pio_addr_t addr);
|
uint16_t cpu_inw(pio_addr_t addr);
|
||||||
uint32_t cpu_inl(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 */
|
#endif /* IOPORT_H */
|
||||||
|
|
8
memory.c
8
memory.c
|
@ -403,12 +403,12 @@ static void memory_region_iorange_read(IORange *iorange,
|
||||||
|
|
||||||
*data = ((uint64_t)1 << (width * 8)) - 1;
|
*data = ((uint64_t)1 << (width * 8)) - 1;
|
||||||
if (mrp) {
|
if (mrp) {
|
||||||
*data = mrp->read(mr->opaque, offset);
|
*data = mrp->read(mr->opaque, offset + mr->offset);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
*data = 0;
|
*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.min_access_size,
|
||||||
mr->ops->impl.max_access_size,
|
mr->ops->impl.max_access_size,
|
||||||
memory_region_read_accessor, mr);
|
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);
|
const MemoryRegionPortio *mrp = find_portio(mr, offset, width, true);
|
||||||
|
|
||||||
if (mrp) {
|
if (mrp) {
|
||||||
mrp->write(mr->opaque, offset, data);
|
mrp->write(mr->opaque, offset + mr->offset, data);
|
||||||
}
|
}
|
||||||
return;
|
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.min_access_size,
|
||||||
mr->ops->impl.max_access_size,
|
mr->ops->impl.max_access_size,
|
||||||
memory_region_write_accessor, mr);
|
memory_region_write_accessor, mr);
|
||||||
|
|
Loading…
Reference in a new issue