]>
Commit | Line | Data |
---|---|---|
32993977 IY |
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 "ioport.h" | |
bd3c9aa5 | 29 | #include "trace.h" |
6bf9fd43 | 30 | #include "memory.h" |
32993977 IY |
31 | |
32 | /***********************************************************/ | |
33 | /* IO Port */ | |
34 | ||
35 | //#define DEBUG_UNUSED_IOPORT | |
36 | //#define DEBUG_IOPORT | |
37 | ||
fc7083b5 IY |
38 | #ifdef DEBUG_UNUSED_IOPORT |
39 | # define LOG_UNUSED_IOPORT(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__) | |
40 | #else | |
41 | # define LOG_UNUSED_IOPORT(fmt, ...) do{ } while (0) | |
42 | #endif | |
43 | ||
32993977 IY |
44 | #ifdef DEBUG_IOPORT |
45 | # define LOG_IOPORT(...) qemu_log_mask(CPU_LOG_IOPORT, ## __VA_ARGS__) | |
46 | #else | |
47 | # define LOG_IOPORT(...) do { } while (0) | |
48 | #endif | |
49 | ||
50 | /* XXX: use a two level table to limit memory usage */ | |
51 | ||
52 | static void *ioport_opaque[MAX_IOPORTS]; | |
53 | static IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS]; | |
54 | static IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS]; | |
55 | ||
56 | static IOPortReadFunc default_ioport_readb, default_ioport_readw, default_ioport_readl; | |
57 | static IOPortWriteFunc default_ioport_writeb, default_ioport_writew, default_ioport_writel; | |
58 | ||
59 | static uint32_t ioport_read(int index, uint32_t address) | |
60 | { | |
1dde6fcc | 61 | static IOPortReadFunc * const default_func[3] = { |
32993977 IY |
62 | default_ioport_readb, |
63 | default_ioport_readw, | |
64 | default_ioport_readl | |
65 | }; | |
66 | IOPortReadFunc *func = ioport_read_table[index][address]; | |
67 | if (!func) | |
68 | func = default_func[index]; | |
69 | return func(ioport_opaque[address], address); | |
70 | } | |
71 | ||
72 | static void ioport_write(int index, uint32_t address, uint32_t data) | |
73 | { | |
1dde6fcc | 74 | static IOPortWriteFunc * const default_func[3] = { |
32993977 IY |
75 | default_ioport_writeb, |
76 | default_ioport_writew, | |
77 | default_ioport_writel | |
78 | }; | |
79 | IOPortWriteFunc *func = ioport_write_table[index][address]; | |
80 | if (!func) | |
81 | func = default_func[index]; | |
82 | func(ioport_opaque[address], address, data); | |
83 | } | |
84 | ||
85 | static uint32_t default_ioport_readb(void *opaque, uint32_t address) | |
86 | { | |
fc7083b5 | 87 | LOG_UNUSED_IOPORT("unused inb: port=0x%04"PRIx32"\n", address); |
32993977 IY |
88 | return 0xff; |
89 | } | |
90 | ||
91 | static void default_ioport_writeb(void *opaque, uint32_t address, uint32_t data) | |
92 | { | |
fc7083b5 IY |
93 | LOG_UNUSED_IOPORT("unused outb: port=0x%04"PRIx32" data=0x%02"PRIx32"\n", |
94 | address, data); | |
32993977 IY |
95 | } |
96 | ||
97 | /* default is to make two byte accesses */ | |
98 | static uint32_t default_ioport_readw(void *opaque, uint32_t address) | |
99 | { | |
100 | uint32_t data; | |
101 | data = ioport_read(0, address); | |
d56dd6cf | 102 | address = (address + 1) & IOPORTS_MASK; |
32993977 IY |
103 | data |= ioport_read(0, address) << 8; |
104 | return data; | |
105 | } | |
106 | ||
107 | static void default_ioport_writew(void *opaque, uint32_t address, uint32_t data) | |
108 | { | |
109 | ioport_write(0, address, data & 0xff); | |
d56dd6cf | 110 | address = (address + 1) & IOPORTS_MASK; |
32993977 IY |
111 | ioport_write(0, address, (data >> 8) & 0xff); |
112 | } | |
113 | ||
114 | static uint32_t default_ioport_readl(void *opaque, uint32_t address) | |
115 | { | |
fc7083b5 | 116 | LOG_UNUSED_IOPORT("unused inl: port=0x%04"PRIx32"\n", address); |
32993977 IY |
117 | return 0xffffffff; |
118 | } | |
119 | ||
120 | static void default_ioport_writel(void *opaque, uint32_t address, uint32_t data) | |
121 | { | |
fc7083b5 IY |
122 | LOG_UNUSED_IOPORT("unused outl: port=0x%04"PRIx32" data=0x%02"PRIx32"\n", |
123 | address, data); | |
32993977 IY |
124 | } |
125 | ||
23e0affd IY |
126 | static int ioport_bsize(int size, int *bsize) |
127 | { | |
128 | if (size == 1) { | |
129 | *bsize = 0; | |
130 | } else if (size == 2) { | |
131 | *bsize = 1; | |
132 | } else if (size == 4) { | |
133 | *bsize = 2; | |
134 | } else { | |
135 | return -1; | |
136 | } | |
137 | return 0; | |
138 | } | |
139 | ||
32993977 | 140 | /* size is the word size in byte */ |
c227f099 | 141 | int register_ioport_read(pio_addr_t start, int length, int size, |
32993977 IY |
142 | IOPortReadFunc *func, void *opaque) |
143 | { | |
144 | int i, bsize; | |
145 | ||
23e0affd | 146 | if (ioport_bsize(size, &bsize)) { |
32993977 IY |
147 | hw_error("register_ioport_read: invalid size"); |
148 | return -1; | |
149 | } | |
bf3fb0e1 | 150 | for(i = start; i < start + length; ++i) { |
32993977 IY |
151 | ioport_read_table[bsize][i] = func; |
152 | if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque) | |
f66a99d7 AF |
153 | hw_error("register_ioport_read: invalid opaque for address 0x%x", |
154 | i); | |
32993977 IY |
155 | ioport_opaque[i] = opaque; |
156 | } | |
157 | return 0; | |
158 | } | |
159 | ||
160 | /* size is the word size in byte */ | |
c227f099 | 161 | int register_ioport_write(pio_addr_t start, int length, int size, |
32993977 IY |
162 | IOPortWriteFunc *func, void *opaque) |
163 | { | |
164 | int i, bsize; | |
165 | ||
23e0affd | 166 | if (ioport_bsize(size, &bsize)) { |
32993977 IY |
167 | hw_error("register_ioport_write: invalid size"); |
168 | return -1; | |
169 | } | |
bf3fb0e1 | 170 | for(i = start; i < start + length; ++i) { |
32993977 IY |
171 | ioport_write_table[bsize][i] = func; |
172 | if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque) | |
f66a99d7 AF |
173 | hw_error("register_ioport_write: invalid opaque for address 0x%x", |
174 | i); | |
32993977 IY |
175 | ioport_opaque[i] = opaque; |
176 | } | |
177 | return 0; | |
178 | } | |
179 | ||
acd1c812 AK |
180 | static uint32_t ioport_readb_thunk(void *opaque, uint32_t addr) |
181 | { | |
182 | IORange *ioport = opaque; | |
183 | uint64_t data; | |
184 | ||
185 | ioport->ops->read(ioport, addr - ioport->base, 1, &data); | |
186 | return data; | |
187 | } | |
188 | ||
189 | static uint32_t ioport_readw_thunk(void *opaque, uint32_t addr) | |
190 | { | |
191 | IORange *ioport = opaque; | |
192 | uint64_t data; | |
193 | ||
194 | ioport->ops->read(ioport, addr - ioport->base, 2, &data); | |
195 | return data; | |
196 | } | |
197 | ||
198 | static uint32_t ioport_readl_thunk(void *opaque, uint32_t addr) | |
199 | { | |
200 | IORange *ioport = opaque; | |
201 | uint64_t data; | |
202 | ||
203 | ioport->ops->read(ioport, addr - ioport->base, 4, &data); | |
204 | return data; | |
205 | } | |
206 | ||
207 | static void ioport_writeb_thunk(void *opaque, uint32_t addr, uint32_t data) | |
208 | { | |
209 | IORange *ioport = opaque; | |
210 | ||
211 | ioport->ops->write(ioport, addr - ioport->base, 1, data); | |
212 | } | |
213 | ||
214 | static void ioport_writew_thunk(void *opaque, uint32_t addr, uint32_t data) | |
215 | { | |
216 | IORange *ioport = opaque; | |
217 | ||
218 | ioport->ops->write(ioport, addr - ioport->base, 2, data); | |
219 | } | |
220 | ||
221 | static void ioport_writel_thunk(void *opaque, uint32_t addr, uint32_t data) | |
222 | { | |
223 | IORange *ioport = opaque; | |
224 | ||
225 | ioport->ops->write(ioport, addr - ioport->base, 4, data); | |
226 | } | |
227 | ||
228 | void ioport_register(IORange *ioport) | |
229 | { | |
230 | register_ioport_read(ioport->base, ioport->len, 1, | |
231 | ioport_readb_thunk, ioport); | |
232 | register_ioport_read(ioport->base, ioport->len, 2, | |
233 | ioport_readw_thunk, ioport); | |
234 | register_ioport_read(ioport->base, ioport->len, 4, | |
235 | ioport_readl_thunk, ioport); | |
236 | register_ioport_write(ioport->base, ioport->len, 1, | |
237 | ioport_writeb_thunk, ioport); | |
238 | register_ioport_write(ioport->base, ioport->len, 2, | |
239 | ioport_writew_thunk, ioport); | |
240 | register_ioport_write(ioport->base, ioport->len, 4, | |
241 | ioport_writel_thunk, ioport); | |
242 | } | |
243 | ||
c227f099 | 244 | void isa_unassign_ioport(pio_addr_t start, int length) |
32993977 IY |
245 | { |
246 | int i; | |
247 | ||
248 | for(i = start; i < start + length; i++) { | |
6141dbfe PB |
249 | ioport_read_table[0][i] = NULL; |
250 | ioport_read_table[1][i] = NULL; | |
251 | ioport_read_table[2][i] = NULL; | |
32993977 | 252 | |
6141dbfe PB |
253 | ioport_write_table[0][i] = NULL; |
254 | ioport_write_table[1][i] = NULL; | |
255 | ioport_write_table[2][i] = NULL; | |
32993977 IY |
256 | |
257 | ioport_opaque[i] = NULL; | |
258 | } | |
259 | } | |
260 | ||
6141dbfe PB |
261 | bool isa_is_ioport_assigned(pio_addr_t start) |
262 | { | |
263 | return (ioport_read_table[0][start] || ioport_write_table[0][start] || | |
264 | ioport_read_table[1][start] || ioport_write_table[1][start] || | |
265 | ioport_read_table[2][start] || ioport_write_table[2][start]); | |
266 | } | |
267 | ||
32993977 IY |
268 | /***********************************************************/ |
269 | ||
c227f099 | 270 | void cpu_outb(pio_addr_t addr, uint8_t val) |
32993977 | 271 | { |
07323531 | 272 | LOG_IOPORT("outb: %04"FMT_pioaddr" %02"PRIx8"\n", addr, val); |
bd3c9aa5 | 273 | trace_cpu_out(addr, val); |
32993977 | 274 | ioport_write(0, addr, val); |
32993977 IY |
275 | } |
276 | ||
c227f099 | 277 | void cpu_outw(pio_addr_t addr, uint16_t val) |
32993977 | 278 | { |
07323531 | 279 | LOG_IOPORT("outw: %04"FMT_pioaddr" %04"PRIx16"\n", addr, val); |
bd3c9aa5 | 280 | trace_cpu_out(addr, val); |
32993977 | 281 | ioport_write(1, addr, val); |
32993977 IY |
282 | } |
283 | ||
c227f099 | 284 | void cpu_outl(pio_addr_t addr, uint32_t val) |
32993977 | 285 | { |
07323531 | 286 | LOG_IOPORT("outl: %04"FMT_pioaddr" %08"PRIx32"\n", addr, val); |
bd3c9aa5 | 287 | trace_cpu_out(addr, val); |
32993977 | 288 | ioport_write(2, addr, val); |
32993977 IY |
289 | } |
290 | ||
c227f099 | 291 | uint8_t cpu_inb(pio_addr_t addr) |
32993977 | 292 | { |
07323531 | 293 | uint8_t val; |
32993977 | 294 | val = ioport_read(0, addr); |
bd3c9aa5 | 295 | trace_cpu_in(addr, val); |
07323531 | 296 | LOG_IOPORT("inb : %04"FMT_pioaddr" %02"PRIx8"\n", addr, val); |
32993977 IY |
297 | return val; |
298 | } | |
299 | ||
c227f099 | 300 | uint16_t cpu_inw(pio_addr_t addr) |
32993977 | 301 | { |
07323531 | 302 | uint16_t val; |
32993977 | 303 | val = ioport_read(1, addr); |
bd3c9aa5 | 304 | trace_cpu_in(addr, val); |
07323531 | 305 | LOG_IOPORT("inw : %04"FMT_pioaddr" %04"PRIx16"\n", addr, val); |
32993977 IY |
306 | return val; |
307 | } | |
308 | ||
c227f099 | 309 | uint32_t cpu_inl(pio_addr_t addr) |
32993977 | 310 | { |
07323531 | 311 | uint32_t val; |
32993977 | 312 | val = ioport_read(2, addr); |
bd3c9aa5 | 313 | trace_cpu_in(addr, val); |
07323531 | 314 | LOG_IOPORT("inl : %04"FMT_pioaddr" %08"PRIx32"\n", addr, val); |
32993977 IY |
315 | return val; |
316 | } | |
6bf9fd43 AK |
317 | |
318 | void portio_list_init(PortioList *piolist, | |
319 | const MemoryRegionPortio *callbacks, | |
320 | void *opaque, const char *name) | |
321 | { | |
322 | unsigned n = 0; | |
323 | ||
324 | while (callbacks[n].size) { | |
325 | ++n; | |
326 | } | |
327 | ||
328 | piolist->ports = callbacks; | |
329 | piolist->nr = 0; | |
330 | piolist->regions = g_new0(MemoryRegion *, n); | |
331 | piolist->address_space = NULL; | |
332 | piolist->opaque = opaque; | |
333 | piolist->name = name; | |
334 | } | |
335 | ||
336 | void portio_list_destroy(PortioList *piolist) | |
337 | { | |
338 | g_free(piolist->regions); | |
339 | } | |
340 | ||
341 | static void portio_list_add_1(PortioList *piolist, | |
342 | const MemoryRegionPortio *pio_init, | |
343 | unsigned count, unsigned start, | |
344 | unsigned off_low, unsigned off_high) | |
345 | { | |
346 | MemoryRegionPortio *pio; | |
347 | MemoryRegionOps *ops; | |
348 | MemoryRegion *region; | |
349 | unsigned i; | |
350 | ||
351 | /* Copy the sub-list and null-terminate it. */ | |
352 | pio = g_new(MemoryRegionPortio, count + 1); | |
353 | memcpy(pio, pio_init, sizeof(MemoryRegionPortio) * count); | |
354 | memset(pio + count, 0, sizeof(MemoryRegionPortio)); | |
355 | ||
356 | /* Adjust the offsets to all be zero-based for the region. */ | |
357 | for (i = 0; i < count; ++i) { | |
358 | pio[i].offset -= off_low; | |
359 | } | |
360 | ||
361 | ops = g_new0(MemoryRegionOps, 1); | |
362 | ops->old_portio = pio; | |
363 | ||
364 | region = g_new(MemoryRegion, 1); | |
365 | memory_region_init_io(region, ops, piolist->opaque, piolist->name, | |
366 | off_high - off_low); | |
367 | memory_region_set_offset(region, start + off_low); | |
368 | memory_region_add_subregion(piolist->address_space, | |
369 | start + off_low, region); | |
370 | piolist->regions[piolist->nr++] = region; | |
371 | } | |
372 | ||
373 | void portio_list_add(PortioList *piolist, | |
374 | MemoryRegion *address_space, | |
375 | uint32_t start) | |
376 | { | |
377 | const MemoryRegionPortio *pio, *pio_start = piolist->ports; | |
378 | unsigned int off_low, off_high, off_last, count; | |
379 | ||
380 | piolist->address_space = address_space; | |
381 | ||
382 | /* Handle the first entry specially. */ | |
383 | off_last = off_low = pio_start->offset; | |
384 | off_high = off_low + pio_start->len; | |
385 | count = 1; | |
386 | ||
387 | for (pio = pio_start + 1; pio->size != 0; pio++, count++) { | |
388 | /* All entries must be sorted by offset. */ | |
389 | assert(pio->offset >= off_last); | |
390 | off_last = pio->offset; | |
391 | ||
392 | /* If we see a hole, break the region. */ | |
393 | if (off_last > off_high) { | |
394 | portio_list_add_1(piolist, pio_start, count, start, off_low, | |
395 | off_high); | |
396 | /* ... and start collecting anew. */ | |
397 | pio_start = pio; | |
398 | off_low = off_last; | |
399 | off_high = off_low + pio->len; | |
400 | count = 0; | |
401 | } else if (off_last + pio->len > off_high) { | |
402 | off_high = off_last + pio->len; | |
403 | } | |
404 | } | |
405 | ||
406 | /* There will always be an open sub-list. */ | |
407 | portio_list_add_1(piolist, pio_start, count, start, off_low, off_high); | |
408 | } | |
409 | ||
410 | void portio_list_del(PortioList *piolist) | |
411 | { | |
412 | MemoryRegion *mr; | |
413 | unsigned i; | |
414 | ||
415 | for (i = 0; i < piolist->nr; ++i) { | |
416 | mr = piolist->regions[i]; | |
417 | memory_region_del_subregion(piolist->address_space, mr); | |
418 | memory_region_destroy(mr); | |
419 | g_free((MemoryRegionOps *)mr->ops); | |
420 | g_free(mr); | |
421 | piolist->regions[i] = NULL; | |
422 | } | |
423 | } |