qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

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 
     35 typedef struct MemoryRegionPortioList {
     36     MemoryRegion mr;
     37     void *portio_opaque;
     38     MemoryRegionPortio ports[];
     39 } MemoryRegionPortioList;
     40 
     41 static uint64_t unassigned_io_read(void *opaque, hwaddr addr, unsigned size)
     42 {
     43     return -1ULL;
     44 }
     45 
     46 static void unassigned_io_write(void *opaque, hwaddr addr, uint64_t val,
     47                                 unsigned size)
     48 {
     49 }
     50 
     51 const MemoryRegionOps unassigned_io_ops = {
     52     .read = unassigned_io_read,
     53     .write = unassigned_io_write,
     54     .endianness = DEVICE_NATIVE_ENDIAN,
     55 };
     56 
     57 void 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 
     64 void 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 
     74 void 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 
     84 uint8_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 
     94 uint16_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 
    105 uint32_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 
    116 void 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 
    137 void portio_list_set_flush_coalesced(PortioList *piolist)
    138 {
    139     piolist->flush_coalesced_mmio = true;
    140 }
    141 
    142 void 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 
    155 static 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 
    171 static 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 
    194 static 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 
    213 static 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 
    221 static 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 
    253 void 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 
    290 void 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 }