1 /******************************************************************************
2 * Copyright (c) 2004, 2008 IBM Corporation
4 * This program and the accompanying materials
5 * are made available under the terms of the BSD License
6 * which accompanies this distribution, and is available at
7 * http://www.opensource.org/licenses/bsd-license.php
10 * IBM Corporation - initial implementation
11 *****************************************************************************/
18 #include "x86emu/x86emu.h"
23 // define a check for access to certain (virtual) memory regions (interrupt handlers, BIOS Data Area, ...)
25 static uint8_t in_check = 0; // to avoid recursion...
26 uint16_t ebda_segment;
29 //TODO: these macros have grown so large, that they should be changed to an inline function,
30 //just for the sake of readability...
32 //declare prototypes of the functions to follow, for use in DEBUG_CHECK_VMEM_ACCESS
33 uint8_t my_rdb(uint32_t);
34 uint16_t my_rdw(uint32_t);
35 uint32_t my_rdl(uint32_t);
37 #define DEBUG_CHECK_VMEM_READ(_addr, _rval) \
38 if ((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)) { \
40 /* determine ebda_segment and size \
41 * since we are using my_rdx calls, make sure, this is after setting in_check! */ \
42 /* offset 03 in BDA is EBDA segment */ \
43 ebda_segment = my_rdw(0x40e); \
44 /* first value in ebda is size in KB */ \
45 ebda_size = my_rdb(ebda_segment << 4) * 1024; \
46 /* check Interrupt Vector Access (0000:0000h - 0000:0400h) */ \
47 if (_addr < 0x400) { \
48 DEBUG_PRINTF_CS_IP("%s: read from Interrupt Vector %x --> %x\n", \
49 __FUNCTION__, _addr / 4, _rval); \
51 /* access to BIOS Data Area (0000:0400h - 0000:0500h)*/ \
52 else if ((_addr >= 0x400) && (addr < 0x500)) { \
53 DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Area: addr: %x --> %x\n", \
54 __FUNCTION__, _addr, _rval); \
55 /* dump registers */ \
56 /* x86emu_dump_xregs(); */ \
58 /* access to first 64k of memory... */ \
59 else if (_addr < 0x10000) { \
60 DEBUG_PRINTF_CS_IP("%s: read from segment 0000h: addr: %x --> %x\n", \
61 __FUNCTION__, _addr, _rval); \
62 /* dump registers */ \
63 /* x86emu_dump_xregs(); */ \
65 /* read from PMM_CONV_SEGMENT */ \
66 else if ((_addr <= ((PMM_CONV_SEGMENT << 4) | 0xffff)) && (_addr >= (PMM_CONV_SEGMENT << 4))) { \
67 DEBUG_PRINTF_CS_IP("%s: read from PMM Segment %04xh: addr: %x --> %x\n", \
68 __FUNCTION__, PMM_CONV_SEGMENT, _addr, _rval); \
70 /* dump registers */ \
71 /* x86emu_dump_xregs(); */ \
73 /* read from PNP_DATA_SEGMENT */ \
74 else if ((_addr <= ((PNP_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (PNP_DATA_SEGMENT << 4))) { \
75 DEBUG_PRINTF_CS_IP("%s: read from PnP Data Segment %04xh: addr: %x --> %x\n", \
76 __FUNCTION__, PNP_DATA_SEGMENT, _addr, _rval); \
78 /* dump registers */ \
79 /* x86emu_dump_xregs(); */ \
81 /* read from EBDA Segment */ \
82 else if ((_addr <= ((ebda_segment << 4) | (ebda_size - 1))) && (_addr >= (ebda_segment << 4))) { \
83 DEBUG_PRINTF_CS_IP("%s: read from Extended BIOS Data Area %04xh, size: %04x: addr: %x --> %x\n", \
84 __FUNCTION__, ebda_segment, ebda_size, _addr, _rval); \
86 /* read from BIOS_DATA_SEGMENT */ \
87 else if ((_addr <= ((BIOS_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (BIOS_DATA_SEGMENT << 4))) { \
88 DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Segment %04xh: addr: %x --> %x\n", \
89 __FUNCTION__, BIOS_DATA_SEGMENT, _addr, _rval); \
90 /* for PMM debugging */ \
91 /*if (_addr == BIOS_DATA_SEGMENT << 4) { \
93 M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; \
95 /* dump registers */ \
96 /* x86emu_dump_xregs(); */ \
100 #define DEBUG_CHECK_VMEM_WRITE(_addr, _val) \
101 if ((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)) { \
103 /* determine ebda_segment and size \
104 * since we are using my_rdx calls, make sure, this is after setting in_check! */ \
105 /* offset 03 in BDA is EBDA segment */ \
106 ebda_segment = my_rdw(0x40e); \
107 /* first value in ebda is size in KB */ \
108 ebda_size = my_rdb(ebda_segment << 4) * 1024; \
109 /* check Interrupt Vector Access (0000:0000h - 0000:0400h) */ \
110 if (_addr < 0x400) { \
111 DEBUG_PRINTF_CS_IP("%s: write to Interrupt Vector %x <-- %x\n", \
112 __FUNCTION__, _addr / 4, _val); \
114 /* access to BIOS Data Area (0000:0400h - 0000:0500h)*/ \
115 else if ((_addr >= 0x400) && (addr < 0x500)) { \
116 DEBUG_PRINTF_CS_IP("%s: write to BIOS Data Area: addr: %x <-- %x\n", \
117 __FUNCTION__, _addr, _val); \
118 /* dump registers */ \
119 /* x86emu_dump_xregs(); */ \
121 /* access to first 64k of memory...*/ \
122 else if (_addr < 0x10000) { \
123 DEBUG_PRINTF_CS_IP("%s: write to segment 0000h: addr: %x <-- %x\n", \
124 __FUNCTION__, _addr, _val); \
125 /* dump registers */ \
126 /* x86emu_dump_xregs(); */ \
128 /* write to PMM_CONV_SEGMENT... */ \
129 else if ((_addr <= ((PMM_CONV_SEGMENT << 4) | 0xffff)) && (_addr >= (PMM_CONV_SEGMENT << 4))) { \
130 DEBUG_PRINTF_CS_IP("%s: write to PMM Segment %04xh: addr: %x <-- %x\n", \
131 __FUNCTION__, PMM_CONV_SEGMENT, _addr, _val); \
132 /* dump registers */ \
133 /* x86emu_dump_xregs(); */ \
135 /* write to PNP_DATA_SEGMENT... */ \
136 else if ((_addr <= ((PNP_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (PNP_DATA_SEGMENT << 4))) { \
137 DEBUG_PRINTF_CS_IP("%s: write to PnP Data Segment %04xh: addr: %x <-- %x\n", \
138 __FUNCTION__, PNP_DATA_SEGMENT, _addr, _val); \
139 /* dump registers */ \
140 /* x86emu_dump_xregs(); */ \
142 /* write to EBDA Segment... */ \
143 else if ((_addr <= ((ebda_segment << 4) | (ebda_size - 1))) && (_addr >= (ebda_segment << 4))) { \
144 DEBUG_PRINTF_CS_IP("%s: write to Extended BIOS Data Area %04xh, size: %04x: addr: %x <-- %x\n", \
145 __FUNCTION__, ebda_segment, ebda_size, _addr, _val); \
147 /* write to BIOS_DATA_SEGMENT... */ \
148 else if ((_addr <= ((BIOS_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (BIOS_DATA_SEGMENT << 4))) { \
149 DEBUG_PRINTF_CS_IP("%s: write to BIOS Data Segment %04xh: addr: %x <-- %x\n", \
150 __FUNCTION__, BIOS_DATA_SEGMENT, _addr, _val); \
151 /* dump registers */ \
152 /* x86emu_dump_xregs(); */ \
154 /* write to current CS segment... */ \
155 else if ((_addr < ((M.x86.R_CS << 4) | 0xffff)) && (_addr > (M.x86.R_CS << 4))) { \
156 DEBUG_PRINTF_CS_IP("%s: write to CS segment %04xh: addr: %x <-- %x\n", \
157 __FUNCTION__, M.x86.R_CS, _addr, _val); \
158 /* dump registers */ \
159 /* x86emu_dump_xregs(); */ \
164 #define DEBUG_CHECK_VMEM_READ(_addr, _rval)
165 #define DEBUG_CHECK_VMEM_WRITE(_addr, _val)
168 //defined in net-snk/kernel/timer.c
169 extern uint64_t get_time(void);
171 void update_time(uint32_t);
173 // read byte from memory
175 my_rdb(uint32_t addr)
177 uint64_t translated_addr = addr;
178 uint8_t translated = dev_translate_address(&translated_addr);
180 if (translated != 0) {
181 //translation successful, access VGA Memory (BAR or Legacy...)
182 DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n",
184 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr);
186 rval = *((uint8_t *) translated_addr);
188 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %02x\n", __FUNCTION__, addr,
191 } else if (addr > M.mem_size) {
192 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
194 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
197 /* read from virtual memory */
198 rval = *((uint8_t *) (M.mem_base + addr));
199 DEBUG_CHECK_VMEM_READ(addr, rval);
205 //read word from memory
207 my_rdw(uint32_t addr)
209 uint64_t translated_addr = addr;
210 uint8_t translated = dev_translate_address(&translated_addr);
212 if (translated != 0) {
213 //translation successful, access VGA Memory (BAR or Legacy...)
214 DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n",
216 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr);
217 // check for legacy memory, because of the remapping to BARs, the reads must
219 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
220 //read bytes a using my_rdb, because of the remapping to BARs
221 //words may not be contiguous in memory, so we need to translate
223 rval = ((uint8_t) my_rdb(addr)) |
224 (((uint8_t) my_rdb(addr + 1)) << 8);
226 if ((translated_addr & (uint64_t) 0x1) == 0) {
227 // 16 bit aligned access...
229 rval = in16le((void *) translated_addr);
232 // unaligned access, read single bytes
234 rval = (*((uint8_t *) translated_addr)) |
235 (*((uint8_t *) translated_addr + 1) << 8);
239 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %04x\n", __FUNCTION__, addr,
242 } else if (addr > M.mem_size) {
243 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
245 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
248 /* read from virtual memory */
249 rval = in16le((void *) (M.mem_base + addr));
250 DEBUG_CHECK_VMEM_READ(addr, rval);
256 //read long from memory
258 my_rdl(uint32_t addr)
260 uint64_t translated_addr = addr;
261 uint8_t translated = dev_translate_address(&translated_addr);
263 if (translated != 0) {
264 //translation successful, access VGA Memory (BAR or Legacy...)
265 DEBUG_PRINTF_MEM("%s(%x): access to VGA Memory\n",
267 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr);
268 // check for legacy memory, because of the remapping to BARs, the reads must
270 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
271 //read bytes a using my_rdb, because of the remapping to BARs
272 //dwords may not be contiguous in memory, so we need to translate
274 rval = ((uint8_t) my_rdb(addr)) |
275 (((uint8_t) my_rdb(addr + 1)) << 8) |
276 (((uint8_t) my_rdb(addr + 2)) << 16) |
277 (((uint8_t) my_rdb(addr + 3)) << 24);
279 if ((translated_addr & (uint64_t) 0x3) == 0) {
280 // 32 bit aligned access...
282 rval = in32le((void *) translated_addr);
285 // unaligned access, read single bytes
287 rval = (*((uint8_t *) translated_addr)) |
288 (*((uint8_t *) translated_addr + 1) << 8) |
289 (*((uint8_t *) translated_addr + 2) << 16) |
290 (*((uint8_t *) translated_addr + 3) << 24);
294 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %08x\n", __FUNCTION__, addr,
298 } else if (addr > M.mem_size) {
299 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
301 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
304 /* read from virtual memory */
305 rval = in32le((void *) (M.mem_base + addr));
308 //BDA Time Data, update it, before reading
310 rval = in32le((void *) (M.mem_base + addr));
313 DEBUG_CHECK_VMEM_READ(addr, rval);
319 //write byte to memory
321 my_wrb(uint32_t addr, uint8_t val)
323 uint64_t translated_addr = addr;
324 uint8_t translated = dev_translate_address(&translated_addr);
325 if (translated != 0) {
326 //translation successful, access VGA Memory (BAR or Legacy...)
327 DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n",
328 __FUNCTION__, addr, val);
329 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr);
331 *((uint8_t *) translated_addr) = val;
333 } else if (addr > M.mem_size) {
334 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
336 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
339 /* write to virtual memory */
340 DEBUG_CHECK_VMEM_WRITE(addr, val);
341 *((uint8_t *) (M.mem_base + addr)) = val;
346 my_wrw(uint32_t addr, uint16_t val)
348 uint64_t translated_addr = addr;
349 uint8_t translated = dev_translate_address(&translated_addr);
350 if (translated != 0) {
351 //translation successful, access VGA Memory (BAR or Legacy...)
352 DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n",
353 __FUNCTION__, addr, val);
354 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr);
355 // check for legacy memory, because of the remapping to BARs, the reads must
357 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
358 //read bytes a using my_rdb, because of the remapping to BARs
359 //words may not be contiguous in memory, so we need to translate
361 my_wrb(addr, (uint8_t) (val & 0x00FF));
362 my_wrb(addr + 1, (uint8_t) ((val & 0xFF00) >> 8));
364 if ((translated_addr & (uint64_t) 0x1) == 0) {
365 // 16 bit aligned access...
367 out16le((void *) translated_addr, val);
370 // unaligned access, write single bytes
372 *((uint8_t *) translated_addr) =
373 (uint8_t) (val & 0x00FF);
374 *((uint8_t *) translated_addr + 1) =
375 (uint8_t) ((val & 0xFF00) >> 8);
379 } else if (addr > M.mem_size) {
380 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
382 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
385 /* write to virtual memory */
386 DEBUG_CHECK_VMEM_WRITE(addr, val);
387 out16le((void *) (M.mem_base + addr), val);
391 my_wrl(uint32_t addr, uint32_t val)
393 uint64_t translated_addr = addr;
394 uint8_t translated = dev_translate_address(&translated_addr);
395 if (translated != 0) {
396 //translation successful, access VGA Memory (BAR or Legacy...)
397 DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n",
398 __FUNCTION__, addr, val);
399 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr);
400 // check for legacy memory, because of the remapping to BARs, the reads must
402 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
403 //read bytes a using my_rdb, because of the remapping to BARs
404 //words may not be contiguous in memory, so we need to translate
406 my_wrb(addr, (uint8_t) (val & 0x000000FF));
407 my_wrb(addr + 1, (uint8_t) ((val & 0x0000FF00) >> 8));
408 my_wrb(addr + 2, (uint8_t) ((val & 0x00FF0000) >> 16));
409 my_wrb(addr + 3, (uint8_t) ((val & 0xFF000000) >> 24));
411 if ((translated_addr & (uint64_t) 0x3) == 0) {
412 // 32 bit aligned access...
414 out32le((void *) translated_addr, val);
417 // unaligned access, write single bytes
419 *((uint8_t *) translated_addr) =
420 (uint8_t) (val & 0x000000FF);
421 *((uint8_t *) translated_addr + 1) =
422 (uint8_t) ((val & 0x0000FF00) >> 8);
423 *((uint8_t *) translated_addr + 2) =
424 (uint8_t) ((val & 0x00FF0000) >> 16);
425 *((uint8_t *) translated_addr + 3) =
426 (uint8_t) ((val & 0xFF000000) >> 24);
430 } else if (addr > M.mem_size) {
431 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
433 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
436 /* write to virtual memory */
437 DEBUG_CHECK_VMEM_WRITE(addr, val);
438 out32le((void *) (M.mem_base + addr), val);
442 //update time in BIOS Data Area
443 //DWord at offset 0x6c is the timer ticks since midnight, timer is running at 18Hz
444 //byte at 0x70 is timer overflow (set if midnight passed since last call to interrupt 1a function 00
445 //cur_val is the current value, of offset 6c...
447 update_time(uint32_t cur_val)
449 //for convenience, we let the start of timebase be at midnight, we currently dont support
450 //real daytime anyway...
451 uint64_t ticks_per_day = tb_freq * 60 * 24;
452 // at 18Hz a period is ~55ms, converted to ticks (tb_freq is ticks/second)
453 uint32_t period_ticks = (55 * tb_freq) / 1000;
454 uint64_t curr_time = get_time();
455 uint64_t ticks_since_midnight = curr_time % ticks_per_day;
456 uint32_t periods_since_midnight = ticks_since_midnight / period_ticks;
457 // if periods since midnight is smaller than last value, set overflow
458 // at BDA Offset 0x70
459 if (periods_since_midnight < cur_val) {
462 // store periods since midnight at BDA offset 0x6c
463 my_wrl(0x46c, periods_since_midnight);