ioport.c 13 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
/*
 * 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.
 */

28
#include "exec/ioport.h"
Prerna Saxena's avatar
Prerna Saxena committed
29
#include "trace.h"
30
#include "exec/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)
{
Blue Swirl's avatar
Blue Swirl committed
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)
{
Blue Swirl's avatar
Blue Swirl committed
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 */
142
143
static int register_ioport_read(pio_addr_t start, int length, int size,
                                IOPortReadFunc *func, void *opaque)
144
145
146
{
    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)
Andreas Färber's avatar
Andreas Färber committed
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 */
162
163
static int register_ioport_write(pio_addr_t start, int length, int size,
                                 IOPortWriteFunc *func, void *opaque)
164
165
166
{
    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)
Andreas Färber's avatar
Andreas Färber committed
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;
}

Avi Kivity's avatar
Avi Kivity committed
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);
    }
}

Avi Kivity's avatar
Avi Kivity committed
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;
Avi Kivity's avatar
Avi Kivity committed
253
254
}

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
276
277

        ioport_opaque[i] = NULL;
    }
}

/***********************************************************/

278
void cpu_outb(pio_addr_t addr, uint8_t val)
279
{
280
    LOG_IOPORT("outb: %04"FMT_pioaddr" %02"PRIx8"\n", addr, val);
Prerna Saxena's avatar
Prerna Saxena committed
281
    trace_cpu_out(addr, val);
282
283
284
    ioport_write(0, addr, val);
}

285
void cpu_outw(pio_addr_t addr, uint16_t val)
286
{
287
    LOG_IOPORT("outw: %04"FMT_pioaddr" %04"PRIx16"\n", addr, val);
Prerna Saxena's avatar
Prerna Saxena committed
288
    trace_cpu_out(addr, val);
289
290
291
    ioport_write(1, addr, val);
}

292
void cpu_outl(pio_addr_t addr, uint32_t val)
293
{
294
    LOG_IOPORT("outl: %04"FMT_pioaddr" %08"PRIx32"\n", addr, val);
Prerna Saxena's avatar
Prerna Saxena committed
295
    trace_cpu_out(addr, val);
296
297
298
    ioport_write(2, addr, val);
}

299
uint8_t cpu_inb(pio_addr_t addr)
300
{
301
    uint8_t val;
302
    val = ioport_read(0, addr);
Prerna Saxena's avatar
Prerna Saxena committed
303
    trace_cpu_in(addr, val);
304
    LOG_IOPORT("inb : %04"FMT_pioaddr" %02"PRIx8"\n", addr, val);
305
306
307
    return val;
}

308
uint16_t cpu_inw(pio_addr_t addr)
309
{
310
    uint16_t val;
311
    val = ioport_read(1, addr);
Prerna Saxena's avatar
Prerna Saxena committed
312
    trace_cpu_in(addr, val);
313
    LOG_IOPORT("inw : %04"FMT_pioaddr" %04"PRIx16"\n", addr, val);
314
315
316
    return val;
}

317
uint32_t cpu_inl(pio_addr_t addr)
318
{
319
    uint32_t val;
320
    val = ioport_read(2, addr);
Prerna Saxena's avatar
Prerna Saxena committed
321
    trace_cpu_in(addr, val);
322
    LOG_IOPORT("inl : %04"FMT_pioaddr" %08"PRIx32"\n", addr, val);
323
324
    return val;
}
Avi Kivity's avatar
Avi Kivity committed
325
326
327
328
329
330
331
332
333
334
335
336
337
338

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);
339
    piolist->aliases = g_new0(MemoryRegion *, n);
Avi Kivity's avatar
Avi Kivity committed
340
341
342
343
344
345
346
347
    piolist->address_space = NULL;
    piolist->opaque = opaque;
    piolist->name = name;
}

void portio_list_destroy(PortioList *piolist)
{
    g_free(piolist->regions);
348
    g_free(piolist->aliases);
Avi Kivity's avatar
Avi Kivity committed
349
350
351
352
353
354
355
356
357
}

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;
358
    MemoryRegion *region, *alias;
Avi Kivity's avatar
Avi Kivity committed
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
    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);
375
376
377
378
379
    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.
     */
Avi Kivity's avatar
Avi Kivity committed
380
    memory_region_init_io(region, ops, piolist->opaque, piolist->name,
381
                          INT64_MAX);
382
383
    memory_region_init_alias(alias, piolist->name,
                             region, start + off_low, off_high - off_low);
Avi Kivity's avatar
Avi Kivity committed
384
    memory_region_add_subregion(piolist->address_space,
385
386
387
388
                                start + off_low, alias);
    piolist->regions[piolist->nr] = region;
    piolist->aliases[piolist->nr] = alias;
    ++piolist->nr;
Avi Kivity's avatar
Avi Kivity committed
389
390
391
392
393
394
395
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
}

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)
{
430
    MemoryRegion *mr, *alias;
Avi Kivity's avatar
Avi Kivity committed
431
432
433
434
    unsigned i;

    for (i = 0; i < piolist->nr; ++i) {
        mr = piolist->regions[i];
435
436
437
        alias = piolist->aliases[i];
        memory_region_del_subregion(piolist->address_space, alias);
        memory_region_destroy(alias);
Avi Kivity's avatar
Avi Kivity committed
438
439
440
        memory_region_destroy(mr);
        g_free((MemoryRegionOps *)mr->ops);
        g_free(mr);
441
        g_free(alias);
Avi Kivity's avatar
Avi Kivity committed
442
        piolist->regions[i] = NULL;
443
        piolist->aliases[i] = NULL;
Avi Kivity's avatar
Avi Kivity committed
444
445
    }
}