X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=ioport.c;h=04e360e79a3dfdcaa51f1ae796967d4be9decddd;hb=a162b572e9e5a85c440656fe97d7a8b133257379;hp=78a3b890fbe887d99d124c5abd2f23190ab6d262;hpb=33cf629a3754b58a1e2dbbe01d91d97e712b7c06;p=mirror_qemu.git diff --git a/ioport.c b/ioport.c index 78a3b890fb..04e360e79a 100644 --- a/ioport.c +++ b/ioport.c @@ -25,373 +25,228 @@ * splitted out ioport related stuffs from vl.c. */ -#include "ioport.h" -#include "trace.h" -#include "memory.h" +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/ioport.h" +#include "trace-root.h" +#include "exec/memory.h" +#include "exec/address-spaces.h" -/***********************************************************/ -/* IO Port */ +typedef struct MemoryRegionPortioList { + MemoryRegion mr; + void *portio_opaque; + MemoryRegionPortio ports[]; +} MemoryRegionPortioList; -//#define DEBUG_UNUSED_IOPORT -//#define DEBUG_IOPORT - -#ifdef DEBUG_UNUSED_IOPORT -# define LOG_UNUSED_IOPORT(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__) -#else -# define LOG_UNUSED_IOPORT(fmt, ...) do{ } while (0) -#endif - -#ifdef DEBUG_IOPORT -# define LOG_IOPORT(...) qemu_log_mask(CPU_LOG_IOPORT, ## __VA_ARGS__) -#else -# define LOG_IOPORT(...) do { } while (0) -#endif - -/* XXX: use a two level table to limit memory usage */ - -static void *ioport_opaque[MAX_IOPORTS]; -static IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS]; -static IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS]; -static IOPortDestructor *ioport_destructor_table[MAX_IOPORTS]; - -static IOPortReadFunc default_ioport_readb, default_ioport_readw, default_ioport_readl; -static IOPortWriteFunc default_ioport_writeb, default_ioport_writew, default_ioport_writel; - -static uint32_t ioport_read(int index, uint32_t address) -{ - static IOPortReadFunc * const default_func[3] = { - default_ioport_readb, - default_ioport_readw, - default_ioport_readl - }; - IOPortReadFunc *func = ioport_read_table[index][address]; - if (!func) - func = default_func[index]; - return func(ioport_opaque[address], address); -} - -static void ioport_write(int index, uint32_t address, uint32_t data) -{ - static IOPortWriteFunc * const default_func[3] = { - default_ioport_writeb, - default_ioport_writew, - default_ioport_writel - }; - IOPortWriteFunc *func = ioport_write_table[index][address]; - if (!func) - func = default_func[index]; - func(ioport_opaque[address], address, data); -} - -static uint32_t default_ioport_readb(void *opaque, uint32_t address) -{ - LOG_UNUSED_IOPORT("unused inb: port=0x%04"PRIx32"\n", address); - return 0xff; -} - -static void default_ioport_writeb(void *opaque, uint32_t address, uint32_t data) +static uint64_t unassigned_io_read(void *opaque, hwaddr addr, unsigned size) { - LOG_UNUSED_IOPORT("unused outb: port=0x%04"PRIx32" data=0x%02"PRIx32"\n", - address, data); + return -1ULL; } -/* default is to make two byte accesses */ -static uint32_t default_ioport_readw(void *opaque, uint32_t address) +static void unassigned_io_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) { - uint32_t data; - data = ioport_read(0, address); - address = (address + 1) & IOPORTS_MASK; - data |= ioport_read(0, address) << 8; - return data; } -static void default_ioport_writew(void *opaque, uint32_t address, uint32_t data) -{ - ioport_write(0, address, data & 0xff); - address = (address + 1) & IOPORTS_MASK; - ioport_write(0, address, (data >> 8) & 0xff); -} +const MemoryRegionOps unassigned_io_ops = { + .read = unassigned_io_read, + .write = unassigned_io_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; -static uint32_t default_ioport_readl(void *opaque, uint32_t address) +void cpu_outb(uint32_t addr, uint8_t val) { - LOG_UNUSED_IOPORT("unused inl: port=0x%04"PRIx32"\n", address); - return 0xffffffff; + trace_cpu_out(addr, 'b', val); + address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, + &val, 1); } -static void default_ioport_writel(void *opaque, uint32_t address, uint32_t data) +void cpu_outw(uint32_t addr, uint16_t val) { - LOG_UNUSED_IOPORT("unused outl: port=0x%04"PRIx32" data=0x%02"PRIx32"\n", - address, data); -} + uint8_t buf[2]; -static int ioport_bsize(int size, int *bsize) -{ - if (size == 1) { - *bsize = 0; - } else if (size == 2) { - *bsize = 1; - } else if (size == 4) { - *bsize = 2; - } else { - return -1; - } - return 0; + trace_cpu_out(addr, 'w', val); + stw_p(buf, val); + address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, + buf, 2); } -/* size is the word size in byte */ -int register_ioport_read(pio_addr_t start, int length, int size, - IOPortReadFunc *func, void *opaque) +void cpu_outl(uint32_t addr, uint32_t val) { - int i, bsize; + uint8_t buf[4]; - if (ioport_bsize(size, &bsize)) { - hw_error("register_ioport_read: invalid size"); - return -1; - } - for(i = start; i < start + length; ++i) { - ioport_read_table[bsize][i] = func; - if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque) - hw_error("register_ioport_read: invalid opaque for address 0x%x", - i); - ioport_opaque[i] = opaque; - } - return 0; -} - -/* size is the word size in byte */ -int register_ioport_write(pio_addr_t start, int length, int size, - IOPortWriteFunc *func, void *opaque) -{ - int i, bsize; - - if (ioport_bsize(size, &bsize)) { - hw_error("register_ioport_write: invalid size"); - return -1; - } - for(i = start; i < start + length; ++i) { - ioport_write_table[bsize][i] = func; - if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque) - hw_error("register_ioport_write: invalid opaque for address 0x%x", - i); - ioport_opaque[i] = opaque; - } - return 0; + trace_cpu_out(addr, 'l', val); + stl_p(buf, val); + address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, + buf, 4); } -static uint32_t ioport_readb_thunk(void *opaque, uint32_t addr) +uint8_t cpu_inb(uint32_t addr) { - IORange *ioport = opaque; - uint64_t data; + uint8_t val; - ioport->ops->read(ioport, addr - ioport->base, 1, &data); - return data; + address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, + &val, 1); + trace_cpu_in(addr, 'b', val); + return val; } -static uint32_t ioport_readw_thunk(void *opaque, uint32_t addr) +uint16_t cpu_inw(uint32_t addr) { - IORange *ioport = opaque; - uint64_t data; + uint8_t buf[2]; + uint16_t val; - ioport->ops->read(ioport, addr - ioport->base, 2, &data); - return data; + address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 2); + val = lduw_p(buf); + trace_cpu_in(addr, 'w', val); + return val; } -static uint32_t ioport_readl_thunk(void *opaque, uint32_t addr) +uint32_t cpu_inl(uint32_t addr) { - IORange *ioport = opaque; - uint64_t data; + uint8_t buf[4]; + uint32_t val; - ioport->ops->read(ioport, addr - ioport->base, 4, &data); - return data; + address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 4); + val = ldl_p(buf); + trace_cpu_in(addr, 'l', val); + return val; } -static void ioport_writeb_thunk(void *opaque, uint32_t addr, uint32_t data) +void portio_list_init(PortioList *piolist, + Object *owner, + const MemoryRegionPortio *callbacks, + void *opaque, const char *name) { - IORange *ioport = opaque; - - ioport->ops->write(ioport, addr - ioport->base, 1, data); -} + unsigned n = 0; -static void ioport_writew_thunk(void *opaque, uint32_t addr, uint32_t data) -{ - IORange *ioport = opaque; + while (callbacks[n].size) { + ++n; + } - ioport->ops->write(ioport, addr - ioport->base, 2, data); + piolist->ports = callbacks; + piolist->nr = 0; + piolist->regions = g_new0(MemoryRegion *, n); + piolist->address_space = NULL; + piolist->opaque = opaque; + piolist->owner = owner; + piolist->name = name; + piolist->flush_coalesced_mmio = false; } -static void ioport_writel_thunk(void *opaque, uint32_t addr, uint32_t data) +void portio_list_set_flush_coalesced(PortioList *piolist) { - IORange *ioport = opaque; - - ioport->ops->write(ioport, addr - ioport->base, 4, data); + piolist->flush_coalesced_mmio = true; } -static void iorange_destructor_thunk(void *opaque) +void portio_list_destroy(PortioList *piolist) { - IORange *iorange = opaque; + MemoryRegionPortioList *mrpio; + unsigned i; - if (iorange->ops->destructor) { - iorange->ops->destructor(iorange); + for (i = 0; i < piolist->nr; ++i) { + mrpio = container_of(piolist->regions[i], MemoryRegionPortioList, mr); + object_unparent(OBJECT(&mrpio->mr)); + g_free(mrpio); } + g_free(piolist->regions); } -void ioport_register(IORange *ioport) -{ - register_ioport_read(ioport->base, ioport->len, 1, - ioport_readb_thunk, ioport); - register_ioport_read(ioport->base, ioport->len, 2, - ioport_readw_thunk, ioport); - register_ioport_read(ioport->base, ioport->len, 4, - ioport_readl_thunk, ioport); - register_ioport_write(ioport->base, ioport->len, 1, - ioport_writeb_thunk, ioport); - register_ioport_write(ioport->base, ioport->len, 2, - ioport_writew_thunk, ioport); - register_ioport_write(ioport->base, ioport->len, 4, - ioport_writel_thunk, ioport); - ioport_destructor_table[ioport->base] = iorange_destructor_thunk; -} - -void isa_unassign_ioport(pio_addr_t start, int length) +static const MemoryRegionPortio *find_portio(MemoryRegionPortioList *mrpio, + uint64_t offset, unsigned size, + bool write) { - int i; - - if (ioport_destructor_table[start]) { - ioport_destructor_table[start](ioport_opaque[start]); - ioport_destructor_table[start] = NULL; - } - for(i = start; i < start + length; i++) { - ioport_read_table[0][i] = NULL; - ioport_read_table[1][i] = NULL; - ioport_read_table[2][i] = NULL; + const MemoryRegionPortio *mrp; - ioport_write_table[0][i] = NULL; - ioport_write_table[1][i] = NULL; - ioport_write_table[2][i] = NULL; - - ioport_opaque[i] = NULL; + 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; } -bool isa_is_ioport_assigned(pio_addr_t start) -{ - return (ioport_read_table[0][start] || ioport_write_table[0][start] || - ioport_read_table[1][start] || ioport_write_table[1][start] || - ioport_read_table[2][start] || ioport_write_table[2][start]); -} - -/***********************************************************/ - -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); -} - -void cpu_outw(pio_addr_t addr, uint16_t val) -{ - LOG_IOPORT("outw: %04"FMT_pioaddr" %04"PRIx16"\n", addr, val); - trace_cpu_out(addr, val); - ioport_write(1, addr, val); -} - -void cpu_outl(pio_addr_t addr, uint32_t val) -{ - LOG_IOPORT("outl: %04"FMT_pioaddr" %08"PRIx32"\n", addr, val); - trace_cpu_out(addr, val); - ioport_write(2, addr, val); -} - -uint8_t cpu_inb(pio_addr_t addr) +static uint64_t portio_read(void *opaque, hwaddr addr, unsigned size) { - uint8_t val; - val = ioport_read(0, addr); - trace_cpu_in(addr, val); - LOG_IOPORT("inb : %04"FMT_pioaddr" %02"PRIx8"\n", addr, val); - return val; -} - -uint16_t cpu_inw(pio_addr_t addr) -{ - uint16_t val; - val = ioport_read(1, addr); - trace_cpu_in(addr, val); - LOG_IOPORT("inw : %04"FMT_pioaddr" %04"PRIx16"\n", addr, val); - return val; -} + MemoryRegionPortioList *mrpio = opaque; + const MemoryRegionPortio *mrp = find_portio(mrpio, addr, size, false); + uint64_t data; -uint32_t cpu_inl(pio_addr_t addr) -{ - uint32_t val; - val = ioport_read(2, addr); - trace_cpu_in(addr, val); - LOG_IOPORT("inl : %04"FMT_pioaddr" %08"PRIx32"\n", addr, val); - return val; + 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); + if (mrp) { + data = mrp->read(mrpio->portio_opaque, mrp->base + addr); + if (addr + 1 < mrp->offset + mrp->len) { + data |= mrp->read(mrpio->portio_opaque, mrp->base + addr + 1) << 8; + } else { + data |= 0xff00; + } + } + } + return data; } -void portio_list_init(PortioList *piolist, - const MemoryRegionPortio *callbacks, - void *opaque, const char *name) +static void portio_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) { - unsigned n = 0; + MemoryRegionPortioList *mrpio = opaque; + const MemoryRegionPortio *mrp = find_portio(mrpio, addr, size, true); - while (callbacks[n].size) { - ++n; + if (mrp) { + mrp->write(mrpio->portio_opaque, mrp->base + addr, data); + } else if (size == 2) { + mrp = find_portio(mrpio, addr, 1, true); + if (mrp) { + mrp->write(mrpio->portio_opaque, mrp->base + addr, data & 0xff); + if (addr + 1 < mrp->offset + mrp->len) { + mrp->write(mrpio->portio_opaque, mrp->base + addr + 1, data >> 8); + } + } } - - 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; } -void portio_list_destroy(PortioList *piolist) -{ - g_free(piolist->regions); - g_free(piolist->aliases); -} +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, - UINT64_MAX); - memory_region_init_alias(alias, piolist->name, - region, start + off_low, off_high - off_low); + memory_region_init_io(&mrpio->mr, piolist->owner, &portio_ops, mrpio, + piolist->name, off_high - off_low); + if (piolist->flush_coalesced_mmio) { + memory_region_set_flush_coalesced(&mrpio->mr); + } 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; } @@ -406,7 +261,7 @@ void portio_list_add(PortioList *piolist, /* Handle the first entry specially. */ off_last = off_low = pio_start->offset; - off_high = off_low + pio_start->len; + off_high = off_low + pio_start->len + pio_start->size - 1; count = 1; for (pio = pio_start + 1; pio->size != 0; pio++, count++) { @@ -421,10 +276,10 @@ void portio_list_add(PortioList *piolist, /* ... and start collecting anew. */ pio_start = pio; off_low = off_last; - off_high = off_low + pio->len; + off_high = off_low + pio->len + pio_start->size - 1; count = 0; } else if (off_last + pio->len > off_high) { - off_high = off_last + pio->len; + off_high = off_last + pio->len + pio_start->size - 1; } } @@ -434,19 +289,11 @@ 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); - piolist->regions[i] = NULL; - piolist->aliases[i] = NULL; + mrpio = container_of(piolist->regions[i], MemoryRegionPortioList, mr); + memory_region_del_subregion(piolist->address_space, &mrpio->mr); } }