qemu/ioport.c
<<
>>
Prefs
   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 "trace-root.h"
  32#include "exec/memory.h"
  33#include "exec/address-spaces.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}
 300