From 3f2546b2ef55b661fd8dd69682b38992225e86f6 Mon Sep 17 00:00:00 2001 From: fishsoupisgood Date: Mon, 29 Apr 2019 01:17:54 +0100 Subject: Initial import of qemu-2.4.1 --- ioport.c | 311 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100644 ioport.c (limited to 'ioport.c') diff --git a/ioport.c b/ioport.c new file mode 100644 index 00000000..e39093ed --- /dev/null +++ b/ioport.c @@ -0,0 +1,311 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/* + * splitted out ioport related stuffs from vl.c. + */ + +#include "exec/ioport.h" +#include "trace.h" +#include "exec/memory.h" +#include "exec/address-spaces.h" + +//#define DEBUG_IOPORT + +#ifdef DEBUG_IOPORT +# define LOG_IOPORT(...) qemu_log_mask(CPU_LOG_IOPORT, ## __VA_ARGS__) +#else +# define LOG_IOPORT(...) do { } while (0) +#endif + +typedef struct MemoryRegionPortioList { + MemoryRegion mr; + void *portio_opaque; + MemoryRegionPortio ports[]; +} MemoryRegionPortioList; + +static uint64_t unassigned_io_read(void *opaque, hwaddr addr, unsigned size) +{ + return -1ULL; +} + +static void unassigned_io_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ +} + +const MemoryRegionOps unassigned_io_ops = { + .read = unassigned_io_read, + .write = unassigned_io_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +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); + address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, + &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); + stw_p(buf, val); + address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, + 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); + stl_p(buf, val); + address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, + buf, 4); +} + +uint8_t cpu_inb(pio_addr_t addr) +{ + uint8_t val; + + address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, + &val, 1); + 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) +{ + uint8_t buf[2]; + uint16_t val; + + address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 2); + val = lduw_p(buf); + trace_cpu_in(addr, val); + LOG_IOPORT("inw : %04"FMT_pioaddr" %04"PRIx16"\n", addr, val); + return val; +} + +uint32_t cpu_inl(pio_addr_t addr) +{ + uint8_t buf[4]; + uint32_t val; + + address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 4); + val = ldl_p(buf); + trace_cpu_in(addr, val); + LOG_IOPORT("inl : %04"FMT_pioaddr" %08"PRIx32"\n", addr, val); + return val; +} + +void portio_list_init(PortioList *piolist, + Object *owner, + 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->owner = owner; + piolist->name = name; + piolist->flush_coalesced_mmio = false; +} + +void portio_list_set_flush_coalesced(PortioList *piolist) +{ + piolist->flush_coalesced_mmio = true; +} + +void portio_list_destroy(PortioList *piolist) +{ + MemoryRegionPortioList *mrpio; + unsigned i; + + 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); +} + +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); + 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; +} + +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); + 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); + } + } + } +} + +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) +{ + MemoryRegionPortioList *mrpio; + unsigned i; + + /* Copy the sub-list and null-terminate it. */ + 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) { + mrpio->ports[i].offset -= off_low; + mrpio->ports[i].base = start + 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, &mrpio->mr); + piolist->regions[piolist->nr] = &mrpio->mr; + ++piolist->nr; +} + +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 + pio_start->size - 1; + 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 + pio_start->size - 1; + count = 0; + } else if (off_last + pio->len > off_high) { + off_high = off_last + pio->len + pio_start->size - 1; + } + } + + /* 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) +{ + MemoryRegionPortioList *mrpio; + unsigned i; + + for (i = 0; i < piolist->nr; ++i) { + mrpio = container_of(piolist->regions[i], MemoryRegionPortioList, mr); + memory_region_del_subregion(piolist->address_space, &mrpio->mr); + } +} -- cgit v1.2.3