ioport.c 13.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*
 * QEMU System Emulator
 *
 * Copyright (c) 2003-2008 Fabrice Bellard
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
/*
 * splitted out ioport related stuffs from vl.c.
 */

#include "ioport.h"
P
Prerna Saxena 已提交
29
#include "trace.h"
A
Avi Kivity 已提交
30
#include "memory.h"
31 32 33 34 35 36 37

/***********************************************************/
/* IO Port */

//#define DEBUG_UNUSED_IOPORT
//#define DEBUG_IOPORT

38 39 40 41 42 43
#ifdef DEBUG_UNUSED_IOPORT
#  define LOG_UNUSED_IOPORT(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
#else
#  define LOG_UNUSED_IOPORT(fmt, ...) do{ } while (0)
#endif

44 45 46 47 48 49 50 51 52 53 54
#ifdef DEBUG_IOPORT
#  define LOG_IOPORT(...) qemu_log_mask(CPU_LOG_IOPORT, ## __VA_ARGS__)
#else
#  define LOG_IOPORT(...) do { } while (0)
#endif

/* XXX: use a two level table to limit memory usage */

static void *ioport_opaque[MAX_IOPORTS];
static IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS];
static IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS];
55
static IOPortDestructor *ioport_destructor_table[MAX_IOPORTS];
56 57 58 59 60 61

static IOPortReadFunc default_ioport_readb, default_ioport_readw, default_ioport_readl;
static IOPortWriteFunc default_ioport_writeb, default_ioport_writew, default_ioport_writel;

static uint32_t ioport_read(int index, uint32_t address)
{
B
Blue Swirl 已提交
62
    static IOPortReadFunc * const default_func[3] = {
63 64 65 66 67 68 69 70 71 72 73 74
        default_ioport_readb,
        default_ioport_readw,
        default_ioport_readl
    };
    IOPortReadFunc *func = ioport_read_table[index][address];
    if (!func)
        func = default_func[index];
    return func(ioport_opaque[address], address);
}

static void ioport_write(int index, uint32_t address, uint32_t data)
{
B
Blue Swirl 已提交
75
    static IOPortWriteFunc * const default_func[3] = {
76 77 78 79 80 81 82 83 84 85 86 87
        default_ioport_writeb,
        default_ioport_writew,
        default_ioport_writel
    };
    IOPortWriteFunc *func = ioport_write_table[index][address];
    if (!func)
        func = default_func[index];
    func(ioport_opaque[address], address, data);
}

static uint32_t default_ioport_readb(void *opaque, uint32_t address)
{
88
    LOG_UNUSED_IOPORT("unused inb: port=0x%04"PRIx32"\n", address);
89 90 91 92 93
    return 0xff;
}

static void default_ioport_writeb(void *opaque, uint32_t address, uint32_t data)
{
94 95
    LOG_UNUSED_IOPORT("unused outb: port=0x%04"PRIx32" data=0x%02"PRIx32"\n",
                      address, data);
96 97 98 99 100 101 102
}

/* default is to make two byte accesses */
static uint32_t default_ioport_readw(void *opaque, uint32_t address)
{
    uint32_t data;
    data = ioport_read(0, address);
103
    address = (address + 1) & IOPORTS_MASK;
104 105 106 107 108 109 110
    data |= ioport_read(0, address) << 8;
    return data;
}

static void default_ioport_writew(void *opaque, uint32_t address, uint32_t data)
{
    ioport_write(0, address, data & 0xff);
111
    address = (address + 1) & IOPORTS_MASK;
112 113 114 115 116
    ioport_write(0, address, (data >> 8) & 0xff);
}

static uint32_t default_ioport_readl(void *opaque, uint32_t address)
{
117
    LOG_UNUSED_IOPORT("unused inl: port=0x%04"PRIx32"\n", address);
118 119 120 121 122
    return 0xffffffff;
}

static void default_ioport_writel(void *opaque, uint32_t address, uint32_t data)
{
123 124
    LOG_UNUSED_IOPORT("unused outl: port=0x%04"PRIx32" data=0x%02"PRIx32"\n",
                      address, data);
125 126
}

127 128 129 130 131 132 133 134 135 136 137 138 139 140
static int ioport_bsize(int size, int *bsize)
{
    if (size == 1) {
        *bsize = 0;
    } else if (size == 2) {
        *bsize = 1;
    } else if (size == 4) {
        *bsize = 2;
    } else {
        return -1;
    }
    return 0;
}

141
/* size is the word size in byte */
A
Anthony Liguori 已提交
142
int register_ioport_read(pio_addr_t start, int length, int size,
143 144 145 146
                         IOPortReadFunc *func, void *opaque)
{
    int i, bsize;

147
    if (ioport_bsize(size, &bsize)) {
148 149 150
        hw_error("register_ioport_read: invalid size");
        return -1;
    }
151
    for(i = start; i < start + length; ++i) {
152 153
        ioport_read_table[bsize][i] = func;
        if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque)
A
Andreas Färber 已提交
154 155
            hw_error("register_ioport_read: invalid opaque for address 0x%x",
                     i);
156 157 158 159 160 161
        ioport_opaque[i] = opaque;
    }
    return 0;
}

/* size is the word size in byte */
A
Anthony Liguori 已提交
162
int register_ioport_write(pio_addr_t start, int length, int size,
163 164 165 166
                          IOPortWriteFunc *func, void *opaque)
{
    int i, bsize;

167
    if (ioport_bsize(size, &bsize)) {
168 169 170
        hw_error("register_ioport_write: invalid size");
        return -1;
    }
171
    for(i = start; i < start + length; ++i) {
172 173
        ioport_write_table[bsize][i] = func;
        if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque)
A
Andreas Färber 已提交
174 175
            hw_error("register_ioport_write: invalid opaque for address 0x%x",
                     i);
176 177 178 179 180
        ioport_opaque[i] = opaque;
    }
    return 0;
}

A
Avi Kivity 已提交
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
static uint32_t ioport_readb_thunk(void *opaque, uint32_t addr)
{
    IORange *ioport = opaque;
    uint64_t data;

    ioport->ops->read(ioport, addr - ioport->base, 1, &data);
    return data;
}

static uint32_t ioport_readw_thunk(void *opaque, uint32_t addr)
{
    IORange *ioport = opaque;
    uint64_t data;

    ioport->ops->read(ioport, addr - ioport->base, 2, &data);
    return data;
}

static uint32_t ioport_readl_thunk(void *opaque, uint32_t addr)
{
    IORange *ioport = opaque;
    uint64_t data;

    ioport->ops->read(ioport, addr - ioport->base, 4, &data);
    return data;
}

static void ioport_writeb_thunk(void *opaque, uint32_t addr, uint32_t data)
{
    IORange *ioport = opaque;

    ioport->ops->write(ioport, addr - ioport->base, 1, data);
}

static void ioport_writew_thunk(void *opaque, uint32_t addr, uint32_t data)
{
    IORange *ioport = opaque;

    ioport->ops->write(ioport, addr - ioport->base, 2, data);
}

static void ioport_writel_thunk(void *opaque, uint32_t addr, uint32_t data)
{
    IORange *ioport = opaque;

    ioport->ops->write(ioport, addr - ioport->base, 4, data);
}

229 230 231 232 233 234 235 236 237
static void iorange_destructor_thunk(void *opaque)
{
    IORange *iorange = opaque;

    if (iorange->ops->destructor) {
        iorange->ops->destructor(iorange);
    }
}

A
Avi Kivity 已提交
238 239 240 241 242 243 244 245 246 247 248 249 250 251
void ioport_register(IORange *ioport)
{
    register_ioport_read(ioport->base, ioport->len, 1,
                         ioport_readb_thunk, ioport);
    register_ioport_read(ioport->base, ioport->len, 2,
                         ioport_readw_thunk, ioport);
    register_ioport_read(ioport->base, ioport->len, 4,
                         ioport_readl_thunk, ioport);
    register_ioport_write(ioport->base, ioport->len, 1,
                          ioport_writeb_thunk, ioport);
    register_ioport_write(ioport->base, ioport->len, 2,
                          ioport_writew_thunk, ioport);
    register_ioport_write(ioport->base, ioport->len, 4,
                          ioport_writel_thunk, ioport);
252
    ioport_destructor_table[ioport->base] = iorange_destructor_thunk;
A
Avi Kivity 已提交
253 254
}

A
Anthony Liguori 已提交
255
void isa_unassign_ioport(pio_addr_t start, int length)
256 257 258
{
    int i;

259 260 261 262
    if (ioport_destructor_table[start]) {
        ioport_destructor_table[start](ioport_opaque[start]);
        ioport_destructor_table[start] = NULL;
    }
263
    for(i = start; i < start + length; i++) {
264 265 266
        ioport_read_table[0][i] = NULL;
        ioport_read_table[1][i] = NULL;
        ioport_read_table[2][i] = NULL;
267

268 269 270
        ioport_write_table[0][i] = NULL;
        ioport_write_table[1][i] = NULL;
        ioport_write_table[2][i] = NULL;
271 272 273 274 275

        ioport_opaque[i] = NULL;
    }
}

276 277 278 279 280 281 282
bool isa_is_ioport_assigned(pio_addr_t start)
{
    return (ioport_read_table[0][start] || ioport_write_table[0][start] ||
	    ioport_read_table[1][start] || ioport_write_table[1][start] ||
	    ioport_read_table[2][start] || ioport_write_table[2][start]);
}

283 284
/***********************************************************/

A
Anthony Liguori 已提交
285
void cpu_outb(pio_addr_t addr, uint8_t val)
286
{
287
    LOG_IOPORT("outb: %04"FMT_pioaddr" %02"PRIx8"\n", addr, val);
P
Prerna Saxena 已提交
288
    trace_cpu_out(addr, val);
289 290 291
    ioport_write(0, addr, val);
}

A
Anthony Liguori 已提交
292
void cpu_outw(pio_addr_t addr, uint16_t val)
293
{
294
    LOG_IOPORT("outw: %04"FMT_pioaddr" %04"PRIx16"\n", addr, val);
P
Prerna Saxena 已提交
295
    trace_cpu_out(addr, val);
296 297 298
    ioport_write(1, addr, val);
}

A
Anthony Liguori 已提交
299
void cpu_outl(pio_addr_t addr, uint32_t val)
300
{
301
    LOG_IOPORT("outl: %04"FMT_pioaddr" %08"PRIx32"\n", addr, val);
P
Prerna Saxena 已提交
302
    trace_cpu_out(addr, val);
303 304 305
    ioport_write(2, addr, val);
}

A
Anthony Liguori 已提交
306
uint8_t cpu_inb(pio_addr_t addr)
307
{
308
    uint8_t val;
309
    val = ioport_read(0, addr);
P
Prerna Saxena 已提交
310
    trace_cpu_in(addr, val);
311
    LOG_IOPORT("inb : %04"FMT_pioaddr" %02"PRIx8"\n", addr, val);
312 313 314
    return val;
}

A
Anthony Liguori 已提交
315
uint16_t cpu_inw(pio_addr_t addr)
316
{
317
    uint16_t val;
318
    val = ioport_read(1, addr);
P
Prerna Saxena 已提交
319
    trace_cpu_in(addr, val);
320
    LOG_IOPORT("inw : %04"FMT_pioaddr" %04"PRIx16"\n", addr, val);
321 322 323
    return val;
}

A
Anthony Liguori 已提交
324
uint32_t cpu_inl(pio_addr_t addr)
325
{
326
    uint32_t val;
327
    val = ioport_read(2, addr);
P
Prerna Saxena 已提交
328
    trace_cpu_in(addr, val);
329
    LOG_IOPORT("inl : %04"FMT_pioaddr" %08"PRIx32"\n", addr, val);
330 331
    return val;
}
A
Avi Kivity 已提交
332 333 334 335 336 337 338 339 340 341 342 343 344 345

void portio_list_init(PortioList *piolist,
                      const MemoryRegionPortio *callbacks,
                      void *opaque, const char *name)
{
    unsigned n = 0;

    while (callbacks[n].size) {
        ++n;
    }

    piolist->ports = callbacks;
    piolist->nr = 0;
    piolist->regions = g_new0(MemoryRegion *, n);
346
    piolist->aliases = g_new0(MemoryRegion *, n);
A
Avi Kivity 已提交
347 348 349 350 351 352 353 354
    piolist->address_space = NULL;
    piolist->opaque = opaque;
    piolist->name = name;
}

void portio_list_destroy(PortioList *piolist)
{
    g_free(piolist->regions);
355
    g_free(piolist->aliases);
A
Avi Kivity 已提交
356 357 358 359 360 361 362 363 364
}

static void portio_list_add_1(PortioList *piolist,
                              const MemoryRegionPortio *pio_init,
                              unsigned count, unsigned start,
                              unsigned off_low, unsigned off_high)
{
    MemoryRegionPortio *pio;
    MemoryRegionOps *ops;
365
    MemoryRegion *region, *alias;
A
Avi Kivity 已提交
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
    unsigned i;

    /* Copy the sub-list and null-terminate it.  */
    pio = g_new(MemoryRegionPortio, count + 1);
    memcpy(pio, pio_init, sizeof(MemoryRegionPortio) * count);
    memset(pio + count, 0, sizeof(MemoryRegionPortio));

    /* Adjust the offsets to all be zero-based for the region.  */
    for (i = 0; i < count; ++i) {
        pio[i].offset -= off_low;
    }

    ops = g_new0(MemoryRegionOps, 1);
    ops->old_portio = pio;

    region = g_new(MemoryRegion, 1);
382 383 384 385 386
    alias = g_new(MemoryRegion, 1);
    /*
     * Use an alias so that the callback is called with an absolute address,
     * rather than an offset relative to to start + off_low.
     */
A
Avi Kivity 已提交
387
    memory_region_init_io(region, ops, piolist->opaque, piolist->name,
B
Blue Swirl 已提交
388
                          INT64_MAX);
389 390
    memory_region_init_alias(alias, piolist->name,
                             region, start + off_low, off_high - off_low);
A
Avi Kivity 已提交
391
    memory_region_add_subregion(piolist->address_space,
392 393 394 395
                                start + off_low, alias);
    piolist->regions[piolist->nr] = region;
    piolist->aliases[piolist->nr] = alias;
    ++piolist->nr;
A
Avi Kivity 已提交
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
}

void portio_list_add(PortioList *piolist,
                     MemoryRegion *address_space,
                     uint32_t start)
{
    const MemoryRegionPortio *pio, *pio_start = piolist->ports;
    unsigned int off_low, off_high, off_last, count;

    piolist->address_space = address_space;

    /* Handle the first entry specially.  */
    off_last = off_low = pio_start->offset;
    off_high = off_low + pio_start->len;
    count = 1;

    for (pio = pio_start + 1; pio->size != 0; pio++, count++) {
        /* All entries must be sorted by offset.  */
        assert(pio->offset >= off_last);
        off_last = pio->offset;

        /* If we see a hole, break the region.  */
        if (off_last > off_high) {
            portio_list_add_1(piolist, pio_start, count, start, off_low,
                              off_high);
            /* ... and start collecting anew.  */
            pio_start = pio;
            off_low = off_last;
            off_high = off_low + pio->len;
            count = 0;
        } else if (off_last + pio->len > off_high) {
            off_high = off_last + pio->len;
        }
    }

    /* There will always be an open sub-list.  */
    portio_list_add_1(piolist, pio_start, count, start, off_low, off_high);
}

void portio_list_del(PortioList *piolist)
{
437
    MemoryRegion *mr, *alias;
A
Avi Kivity 已提交
438 439 440 441
    unsigned i;

    for (i = 0; i < piolist->nr; ++i) {
        mr = piolist->regions[i];
442 443 444
        alias = piolist->aliases[i];
        memory_region_del_subregion(piolist->address_space, alias);
        memory_region_destroy(alias);
A
Avi Kivity 已提交
445 446 447
        memory_region_destroy(mr);
        g_free((MemoryRegionOps *)mr->ops);
        g_free(mr);
448
        g_free(alias);
A
Avi Kivity 已提交
449
        piolist->regions[i] = NULL;
450
        piolist->aliases[i] = NULL;
A
Avi Kivity 已提交
451 452
    }
}