diff options
Diffstat (limited to 'ioport.c')
-rw-r--r-- | ioport.c | 138 |
1 files changed, 101 insertions, 37 deletions
@@ -28,6 +28,7 @@ #include "exec/ioport.h" #include "trace.h" #include "exec/memory.h" +#include "exec/address-spaces.h" /***********************************************************/ /* IO Port */ @@ -47,6 +48,12 @@ # define LOG_IOPORT(...) do { } while (0) #endif +typedef struct MemoryRegionPortioList { + MemoryRegion mr; + void *portio_opaque; + MemoryRegionPortio ports[]; +} MemoryRegionPortioList; + /* XXX: use a two level table to limit memory usage */ static void *ioport_opaque[MAX_IOPORTS]; @@ -279,27 +286,34 @@ void cpu_outb(pio_addr_t addr, uint8_t val) { LOG_IOPORT("outb: %04"FMT_pioaddr" %02"PRIx8"\n", addr, val); trace_cpu_out(addr, val); - ioport_write(0, addr, val); + address_space_write(&address_space_io, addr, &val, 1); } void cpu_outw(pio_addr_t addr, uint16_t val) { + uint8_t buf[2]; + LOG_IOPORT("outw: %04"FMT_pioaddr" %04"PRIx16"\n", addr, val); trace_cpu_out(addr, val); - ioport_write(1, addr, val); + stw_p(buf, val); + address_space_write(&address_space_io, addr, buf, 2); } void cpu_outl(pio_addr_t addr, uint32_t val) { + uint8_t buf[4]; + LOG_IOPORT("outl: %04"FMT_pioaddr" %08"PRIx32"\n", addr, val); trace_cpu_out(addr, val); - ioport_write(2, addr, val); + stl_p(buf, val); + address_space_write(&address_space_io, addr, buf, 4); } uint8_t cpu_inb(pio_addr_t addr) { uint8_t val; - val = ioport_read(0, addr); + + address_space_read(&address_space_io, addr, &val, 1); trace_cpu_in(addr, val); LOG_IOPORT("inb : %04"FMT_pioaddr" %02"PRIx8"\n", addr, val); return val; @@ -307,8 +321,11 @@ uint8_t cpu_inb(pio_addr_t addr) uint16_t cpu_inw(pio_addr_t addr) { + uint8_t buf[2]; uint16_t val; - val = ioport_read(1, addr); + + address_space_read(&address_space_io, addr, buf, 2); + val = lduw_p(buf); trace_cpu_in(addr, val); LOG_IOPORT("inw : %04"FMT_pioaddr" %04"PRIx16"\n", addr, val); return val; @@ -316,8 +333,11 @@ uint16_t cpu_inw(pio_addr_t addr) uint32_t cpu_inl(pio_addr_t addr) { + uint8_t buf[4]; uint32_t val; - val = ioport_read(2, addr); + + address_space_read(&address_space_io, addr, buf, 4); + val = ldl_p(buf); trace_cpu_in(addr, val); LOG_IOPORT("inl : %04"FMT_pioaddr" %08"PRIx32"\n", addr, val); return val; @@ -336,7 +356,6 @@ void portio_list_init(PortioList *piolist, piolist->ports = callbacks; piolist->nr = 0; piolist->regions = g_new0(MemoryRegion *, n); - piolist->aliases = g_new0(MemoryRegion *, n); piolist->address_space = NULL; piolist->opaque = opaque; piolist->name = name; @@ -345,46 +364,96 @@ void portio_list_init(PortioList *piolist, void portio_list_destroy(PortioList *piolist) { g_free(piolist->regions); - g_free(piolist->aliases); } +static const MemoryRegionPortio *find_portio(MemoryRegionPortioList *mrpio, + uint64_t offset, unsigned size, + bool write) +{ + const MemoryRegionPortio *mrp; + + for (mrp = mrpio->ports; mrp->size; ++mrp) { + if (offset >= mrp->offset && offset < mrp->offset + mrp->len && + size == mrp->size && + (write ? (bool)mrp->write : (bool)mrp->read)) { + return mrp; + } + } + return NULL; +} + +static uint64_t portio_read(void *opaque, hwaddr addr, unsigned size) +{ + MemoryRegionPortioList *mrpio = opaque; + const MemoryRegionPortio *mrp = find_portio(mrpio, addr, size, false); + uint64_t data; + + data = ((uint64_t)1 << (size * 8)) - 1; + if (mrp) { + data = mrp->read(mrpio->portio_opaque, mrp->base + addr); + } else if (size == 2) { + mrp = find_portio(mrpio, addr, 1, false); + assert(mrp); + data = mrp->read(mrpio->portio_opaque, mrp->base + addr) | + (mrp->read(mrpio->portio_opaque, mrp->base + addr + 1) << 8); + } + return data; +} + +static void portio_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + MemoryRegionPortioList *mrpio = opaque; + const MemoryRegionPortio *mrp = find_portio(mrpio, addr, size, true); + + if (mrp) { + mrp->write(mrpio->portio_opaque, mrp->base + addr, data); + } else if (size == 2) { + mrp = find_portio(mrpio, addr, 1, true); + assert(mrp); + mrp->write(mrpio->portio_opaque, mrp->base + addr, data & 0xff); + mrp->write(mrpio->portio_opaque, mrp->base + addr + 1, data >> 8); + } +} + +static const MemoryRegionOps portio_ops = { + .read = portio_read, + .write = portio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.unaligned = true, + .impl.unaligned = true, +}; + 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, *alias; + MemoryRegionPortioList *mrpio; 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)); + mrpio = g_malloc0(sizeof(MemoryRegionPortioList) + + sizeof(MemoryRegionPortio) * (count + 1)); + mrpio->portio_opaque = piolist->opaque; + memcpy(mrpio->ports, pio_init, sizeof(MemoryRegionPortio) * count); + memset(mrpio->ports + 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; + mrpio->ports[i].offset -= off_low; + mrpio->ports[i].base = start + off_low; } - ops = g_new0(MemoryRegionOps, 1); - ops->old_portio = pio; - - region = g_new(MemoryRegion, 1); - alias = g_new(MemoryRegion, 1); /* * Use an alias so that the callback is called with an absolute address, * rather than an offset relative to to start + off_low. */ - memory_region_init_io(region, ops, piolist->opaque, piolist->name, - INT64_MAX); - memory_region_init_alias(alias, piolist->name, - region, start + off_low, off_high - off_low); + memory_region_init_io(&mrpio->mr, &portio_ops, mrpio, piolist->name, + off_high - off_low); memory_region_add_subregion(piolist->address_space, - start + off_low, alias); - piolist->regions[piolist->nr] = region; - piolist->aliases[piolist->nr] = alias; + start + off_low, &mrpio->mr); + piolist->regions[piolist->nr] = &mrpio->mr; ++piolist->nr; } @@ -427,19 +496,14 @@ void portio_list_add(PortioList *piolist, void portio_list_del(PortioList *piolist) { - MemoryRegion *mr, *alias; + MemoryRegionPortioList *mrpio; unsigned i; for (i = 0; i < piolist->nr; ++i) { - mr = piolist->regions[i]; - alias = piolist->aliases[i]; - memory_region_del_subregion(piolist->address_space, alias); - memory_region_destroy(alias); - memory_region_destroy(mr); - g_free((MemoryRegionOps *)mr->ops); - g_free(mr); - g_free(alias); + mrpio = container_of(piolist->regions[i], MemoryRegionPortioList, mr); + memory_region_del_subregion(piolist->address_space, &mrpio->mr); + memory_region_destroy(&mrpio->mr); + g_free(mrpio); piolist->regions[i] = NULL; - piolist->aliases[i] = NULL; } } |