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 "qemu-common.h"
  30#include "cpu.h"
  31#include "exec/ioport.h"
  32#include "trace-root.h"
  33#include "exec/memory.h"
  34#include "exec/address-spaces.h"
  35
  36typedef struct MemoryRegionPortioList {
  37    MemoryRegion mr;
  38    void *portio_opaque;
  39    MemoryRegionPortio ports[];
  40} MemoryRegionPortioList;
  41
  42static uint64_t unassigned_io_read(void *opaque, hwaddr addr, unsigned size)
  43{
  44    return -1ULL;
  45}
  46
  47static void unassigned_io_write(void *opaque, hwaddr addr, uint64_t val,
  48                                unsigned size)
  49{
  50}
  51
  52const MemoryRegionOps unassigned_io_ops = {
  53    .read = unassigned_io_read,
  54    .write = unassigned_io_write,
  55    .endianness = DEVICE_NATIVE_ENDIAN,
  56};
  57
  58void cpu_outb(uint32_t addr, uint8_t val)
  59{
  60    trace_cpu_out(addr, 'b', val);
  61    address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED,
  62                        &val, 1);
  63}
  64
  65void cpu_outw(uint32_t addr, uint16_t val)
  66{
  67    uint8_t buf[2];
  68
  69    trace_cpu_out(addr, 'w', val);
  70    stw_p(buf, val);
  71    address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED,
  72                        buf, 2);
  73}
  74
  75void cpu_outl(uint32_t addr, uint32_t val)
  76{
  77    uint8_t buf[4];
  78
  79    trace_cpu_out(addr, 'l', val);
  80    stl_p(buf, val);
  81    address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED,
  82                        buf, 4);
  83}
  84
  85uint8_t cpu_inb(uint32_t addr)
  86{
  87    uint8_t val;
  88
  89    address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED,
  90                       &val, 1);
  91    trace_cpu_in(addr, 'b', val);
  92    return val;
  93}
  94
  95uint16_t cpu_inw(uint32_t addr)
  96{
  97    uint8_t buf[2];
  98    uint16_t val;
  99
 100    address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 2);
 101    val = lduw_p(buf);
 102    trace_cpu_in(addr, 'w', val);
 103    return val;
 104}
 105
 106uint32_t cpu_inl(uint32_t addr)
 107{
 108    uint8_t buf[4];
 109    uint32_t val;
 110
 111    address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 4);
 112    val = ldl_p(buf);
 113    trace_cpu_in(addr, 'l', val);
 114    return val;
 115}
 116
 117void portio_list_init(PortioList *piolist,
 118                      Object *owner,
 119                      const MemoryRegionPortio *callbacks,
 120                      void *opaque, const char *name)
 121{
 122    unsigned n = 0;
 123
 124    while (callbacks[n].size) {
 125        ++n;
 126    }
 127
 128    piolist->ports = callbacks;
 129    piolist->nr = 0;
 130    piolist->regions = g_new0(MemoryRegion *, n);
 131    piolist->address_space = NULL;
 132    piolist->opaque = opaque;
 133    piolist->owner = owner;
 134    piolist->name = name;
 135    piolist->flush_coalesced_mmio = false;
 136}
 137
 138void portio_list_set_flush_coalesced(PortioList *piolist)
 139{
 140    piolist->flush_coalesced_mmio = true;
 141}
 142
 143void portio_list_destroy(PortioList *piolist)
 144{
 145    MemoryRegionPortioList *mrpio;
 146    unsigned i;
 147
 148    for (i = 0; i < piolist->nr; ++i) {
 149        mrpio = container_of(piolist->regions[i], MemoryRegionPortioList, mr);
 150        object_unparent(OBJECT(&mrpio->mr));
 151        g_free(mrpio);
 152    }
 153    g_free(piolist->regions);
 154}
 155
 156static const MemoryRegionPortio *find_portio(MemoryRegionPortioList *mrpio,
 157                                             uint64_t offset, unsigned size,
 158                                             bool write)
 159{
 160    const MemoryRegionPortio *mrp;
 161
 162    for (mrp = mrpio->ports; mrp->size; ++mrp) {
 163        if (offset >= mrp->offset && offset < mrp->offset + mrp->len &&
 164            size == mrp->size &&
 165            (write ? (bool)mrp->write : (bool)mrp->read)) {
 166            return mrp;
 167        }
 168    }
 169    return NULL;
 170}
 171
 172static uint64_t portio_read(void *opaque, hwaddr addr, unsigned size)
 173{
 174    MemoryRegionPortioList *mrpio = opaque;
 175    const MemoryRegionPortio *mrp = find_portio(mrpio, addr, size, false);
 176    uint64_t data;
 177
 178    data = ((uint64_t)1 << (size * 8)) - 1;
 179    if (mrp) {
 180        data = mrp->read(mrpio->portio_opaque, mrp->base + addr);
 181    } else if (size == 2) {
 182        mrp = find_portio(mrpio, addr, 1, false);
 183        if (mrp) {
 184            data = mrp->read(mrpio->portio_opaque, mrp->base + addr);
 185            if (addr + 1 < mrp->offset + mrp->len) {
 186                data |= mrp->read(mrpio->portio_opaque, mrp->base + addr + 1) << 8;
 187            } else {
 188                data |= 0xff00;
 189            }
 190        }
 191    }
 192    return data;
 193}
 194
 195static void portio_write(void *opaque, hwaddr addr, uint64_t data,
 196                         unsigned size)
 197{
 198    MemoryRegionPortioList *mrpio = opaque;
 199    const MemoryRegionPortio *mrp = find_portio(mrpio, addr, size, true);
 200
 201    if (mrp) {
 202        mrp->write(mrpio->portio_opaque, mrp->base + addr, data);
 203    } else if (size == 2) {
 204        mrp = find_portio(mrpio, addr, 1, true);
 205        if (mrp) {
 206            mrp->write(mrpio->portio_opaque, mrp->base + addr, data & 0xff);
 207            if (addr + 1 < mrp->offset + mrp->len) {
 208                mrp->write(mrpio->portio_opaque, mrp->base + addr + 1, data >> 8);
 209            }
 210        }
 211    }
 212}
 213
 214static const MemoryRegionOps portio_ops = {
 215    .read = portio_read,
 216    .write = portio_write,
 217    .endianness = DEVICE_LITTLE_ENDIAN,
 218    .valid.unaligned = true,
 219    .impl.unaligned = true,
 220};
 221
 222static void portio_list_add_1(PortioList *piolist,
 223                              const MemoryRegionPortio *pio_init,
 224                              unsigned count, unsigned start,
 225                              unsigned off_low, unsigned off_high)
 226{
 227    MemoryRegionPortioList *mrpio;
 228    unsigned i;
 229
 230    /* Copy the sub-list and null-terminate it.  */
 231    mrpio = g_malloc0(sizeof(MemoryRegionPortioList) +
 232                      sizeof(MemoryRegionPortio) * (count + 1));
 233    mrpio->portio_opaque = piolist->opaque;
 234    memcpy(mrpio->ports, pio_init, sizeof(MemoryRegionPortio) * count);
 235    memset(mrpio->ports + count, 0, sizeof(MemoryRegionPortio));
 236
 237    /* Adjust the offsets to all be zero-based for the region.  */
 238    for (i = 0; i < count; ++i) {
 239        mrpio->ports[i].offset -= off_low;
 240        mrpio->ports[i].base = start + off_low;
 241    }
 242
 243    memory_region_init_io(&mrpio->mr, piolist->owner, &portio_ops, mrpio,
 244                          piolist->name, off_high - off_low);
 245    if (piolist->flush_coalesced_mmio) {
 246        memory_region_set_flush_coalesced(&mrpio->mr);
 247    }
 248    memory_region_add_subregion(piolist->address_space,
 249                                start + off_low, &mrpio->mr);
 250    piolist->regions[piolist->nr] = &mrpio->mr;
 251    ++piolist->nr;
 252}
 253
 254void portio_list_add(PortioList *piolist,
 255                     MemoryRegion *address_space,
 256                     uint32_t start)
 257{
 258    const MemoryRegionPortio *pio, *pio_start = piolist->ports;
 259    unsigned int off_low, off_high, off_last, count;
 260
 261    piolist->address_space = address_space;
 262
 263    /* Handle the first entry specially.  */
 264    off_last = off_low = pio_start->offset;
 265    off_high = off_low + pio_start->len + pio_start->size - 1;
 266    count = 1;
 267
 268    for (pio = pio_start + 1; pio->size != 0; pio++, count++) {
 269        /* All entries must be sorted by offset.  */
 270        assert(pio->offset >= off_last);
 271        off_last = pio->offset;
 272
 273        /* If we see a hole, break the region.  */
 274        if (off_last > off_high) {
 275            portio_list_add_1(piolist, pio_start, count, start, off_low,
 276                              off_high);
 277            /* ... and start collecting anew.  */
 278            pio_start = pio;
 279            off_low = off_last;
 280            off_high = off_low + pio->len + pio_start->size - 1;
 281            count = 0;
 282        } else if (off_last + pio->len > off_high) {
 283            off_high = off_last + pio->len + pio_start->size - 1;
 284        }
 285    }
 286
 287    /* There will always be an open sub-list.  */
 288    portio_list_add_1(piolist, pio_start, count, start, off_low, off_high);
 289}
 290
 291void portio_list_del(PortioList *piolist)
 292{
 293    MemoryRegionPortioList *mrpio;
 294    unsigned i;
 295
 296    for (i = 0; i < piolist->nr; ++i) {
 297        mrpio = container_of(piolist->regions[i], MemoryRegionPortioList, mr);
 298        memory_region_del_subregion(piolist->address_space, &mrpio->mr);
 299    }
 300}
 301