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