ioport.c (9168B)
1/* 2 * QEMU System Emulator 3 * 4 * Copyright (c) 2003-2008 Fabrice Bellard 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24/* 25 * splitted out ioport related stuffs from vl.c. 26 */ 27 28#include "qemu/osdep.h" 29#include "cpu.h" 30#include "exec/ioport.h" 31#include "exec/memory.h" 32#include "exec/address-spaces.h" 33#include "trace.h" 34 35typedef struct MemoryRegionPortioList { 36 MemoryRegion mr; 37 void *portio_opaque; 38 MemoryRegionPortio ports[]; 39} MemoryRegionPortioList; 40 41static uint64_t unassigned_io_read(void *opaque, hwaddr addr, unsigned size) 42{ 43 return -1ULL; 44} 45 46static void unassigned_io_write(void *opaque, hwaddr addr, uint64_t val, 47 unsigned size) 48{ 49} 50 51const MemoryRegionOps unassigned_io_ops = { 52 .read = unassigned_io_read, 53 .write = unassigned_io_write, 54 .endianness = DEVICE_NATIVE_ENDIAN, 55}; 56 57void cpu_outb(uint32_t addr, uint8_t val) 58{ 59 trace_cpu_out(addr, 'b', val); 60 address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, 61 &val, 1); 62} 63 64void cpu_outw(uint32_t addr, uint16_t val) 65{ 66 uint8_t buf[2]; 67 68 trace_cpu_out(addr, 'w', val); 69 stw_p(buf, val); 70 address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, 71 buf, 2); 72} 73 74void cpu_outl(uint32_t addr, uint32_t val) 75{ 76 uint8_t buf[4]; 77 78 trace_cpu_out(addr, 'l', val); 79 stl_p(buf, val); 80 address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, 81 buf, 4); 82} 83 84uint8_t cpu_inb(uint32_t addr) 85{ 86 uint8_t val; 87 88 address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, 89 &val, 1); 90 trace_cpu_in(addr, 'b', val); 91 return val; 92} 93 94uint16_t cpu_inw(uint32_t addr) 95{ 96 uint8_t buf[2]; 97 uint16_t val; 98 99 address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 2); 100 val = lduw_p(buf); 101 trace_cpu_in(addr, 'w', val); 102 return val; 103} 104 105uint32_t cpu_inl(uint32_t addr) 106{ 107 uint8_t buf[4]; 108 uint32_t val; 109 110 address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 4); 111 val = ldl_p(buf); 112 trace_cpu_in(addr, 'l', val); 113 return val; 114} 115 116void portio_list_init(PortioList *piolist, 117 Object *owner, 118 const MemoryRegionPortio *callbacks, 119 void *opaque, const char *name) 120{ 121 unsigned n = 0; 122 123 while (callbacks[n].size) { 124 ++n; 125 } 126 127 piolist->ports = callbacks; 128 piolist->nr = 0; 129 piolist->regions = g_new0(MemoryRegion *, n); 130 piolist->address_space = NULL; 131 piolist->opaque = opaque; 132 piolist->owner = owner; 133 piolist->name = name; 134 piolist->flush_coalesced_mmio = false; 135} 136 137void portio_list_set_flush_coalesced(PortioList *piolist) 138{ 139 piolist->flush_coalesced_mmio = true; 140} 141 142void portio_list_destroy(PortioList *piolist) 143{ 144 MemoryRegionPortioList *mrpio; 145 unsigned i; 146 147 for (i = 0; i < piolist->nr; ++i) { 148 mrpio = container_of(piolist->regions[i], MemoryRegionPortioList, mr); 149 object_unparent(OBJECT(&mrpio->mr)); 150 g_free(mrpio); 151 } 152 g_free(piolist->regions); 153} 154 155static const MemoryRegionPortio *find_portio(MemoryRegionPortioList *mrpio, 156 uint64_t offset, unsigned size, 157 bool write) 158{ 159 const MemoryRegionPortio *mrp; 160 161 for (mrp = mrpio->ports; mrp->size; ++mrp) { 162 if (offset >= mrp->offset && offset < mrp->offset + mrp->len && 163 size == mrp->size && 164 (write ? (bool)mrp->write : (bool)mrp->read)) { 165 return mrp; 166 } 167 } 168 return NULL; 169} 170 171static uint64_t portio_read(void *opaque, hwaddr addr, unsigned size) 172{ 173 MemoryRegionPortioList *mrpio = opaque; 174 const MemoryRegionPortio *mrp = find_portio(mrpio, addr, size, false); 175 uint64_t data; 176 177 data = ((uint64_t)1 << (size * 8)) - 1; 178 if (mrp) { 179 data = mrp->read(mrpio->portio_opaque, mrp->base + addr); 180 } else if (size == 2) { 181 mrp = find_portio(mrpio, addr, 1, false); 182 if (mrp) { 183 data = mrp->read(mrpio->portio_opaque, mrp->base + addr); 184 if (addr + 1 < mrp->offset + mrp->len) { 185 data |= mrp->read(mrpio->portio_opaque, mrp->base + addr + 1) << 8; 186 } else { 187 data |= 0xff00; 188 } 189 } 190 } 191 return data; 192} 193 194static void portio_write(void *opaque, hwaddr addr, uint64_t data, 195 unsigned size) 196{ 197 MemoryRegionPortioList *mrpio = opaque; 198 const MemoryRegionPortio *mrp = find_portio(mrpio, addr, size, true); 199 200 if (mrp) { 201 mrp->write(mrpio->portio_opaque, mrp->base + addr, data); 202 } else if (size == 2) { 203 mrp = find_portio(mrpio, addr, 1, true); 204 if (mrp) { 205 mrp->write(mrpio->portio_opaque, mrp->base + addr, data & 0xff); 206 if (addr + 1 < mrp->offset + mrp->len) { 207 mrp->write(mrpio->portio_opaque, mrp->base + addr + 1, data >> 8); 208 } 209 } 210 } 211} 212 213static const MemoryRegionOps portio_ops = { 214 .read = portio_read, 215 .write = portio_write, 216 .endianness = DEVICE_LITTLE_ENDIAN, 217 .valid.unaligned = true, 218 .impl.unaligned = true, 219}; 220 221static void portio_list_add_1(PortioList *piolist, 222 const MemoryRegionPortio *pio_init, 223 unsigned count, unsigned start, 224 unsigned off_low, unsigned off_high) 225{ 226 MemoryRegionPortioList *mrpio; 227 unsigned i; 228 229 /* Copy the sub-list and null-terminate it. */ 230 mrpio = g_malloc0(sizeof(MemoryRegionPortioList) + 231 sizeof(MemoryRegionPortio) * (count + 1)); 232 mrpio->portio_opaque = piolist->opaque; 233 memcpy(mrpio->ports, pio_init, sizeof(MemoryRegionPortio) * count); 234 memset(mrpio->ports + count, 0, sizeof(MemoryRegionPortio)); 235 236 /* Adjust the offsets to all be zero-based for the region. */ 237 for (i = 0; i < count; ++i) { 238 mrpio->ports[i].offset -= off_low; 239 mrpio->ports[i].base = start + off_low; 240 } 241 242 memory_region_init_io(&mrpio->mr, piolist->owner, &portio_ops, mrpio, 243 piolist->name, off_high - off_low); 244 if (piolist->flush_coalesced_mmio) { 245 memory_region_set_flush_coalesced(&mrpio->mr); 246 } 247 memory_region_add_subregion(piolist->address_space, 248 start + off_low, &mrpio->mr); 249 piolist->regions[piolist->nr] = &mrpio->mr; 250 ++piolist->nr; 251} 252 253void portio_list_add(PortioList *piolist, 254 MemoryRegion *address_space, 255 uint32_t start) 256{ 257 const MemoryRegionPortio *pio, *pio_start = piolist->ports; 258 unsigned int off_low, off_high, off_last, count; 259 260 piolist->address_space = address_space; 261 262 /* Handle the first entry specially. */ 263 off_last = off_low = pio_start->offset; 264 off_high = off_low + pio_start->len + pio_start->size - 1; 265 count = 1; 266 267 for (pio = pio_start + 1; pio->size != 0; pio++, count++) { 268 /* All entries must be sorted by offset. */ 269 assert(pio->offset >= off_last); 270 off_last = pio->offset; 271 272 /* If we see a hole, break the region. */ 273 if (off_last > off_high) { 274 portio_list_add_1(piolist, pio_start, count, start, off_low, 275 off_high); 276 /* ... and start collecting anew. */ 277 pio_start = pio; 278 off_low = off_last; 279 off_high = off_low + pio->len + pio_start->size - 1; 280 count = 0; 281 } else if (off_last + pio->len > off_high) { 282 off_high = off_last + pio->len + pio_start->size - 1; 283 } 284 } 285 286 /* There will always be an open sub-list. */ 287 portio_list_add_1(piolist, pio_start, count, start, off_low, off_high); 288} 289 290void portio_list_del(PortioList *piolist) 291{ 292 MemoryRegionPortioList *mrpio; 293 unsigned i; 294 295 for (i = 0; i < piolist->nr; ++i) { 296 mrpio = container_of(piolist->regions[i], MemoryRegionPortioList, mr); 297 memory_region_del_subregion(piolist->address_space, &mrpio->mr); 298 } 299}