1 /******************************************************************************
2 * Copyright (c) 2004, 2008 IBM Corporation
3 * Copyright (c) 2009 Pattrick Hueper <phueper@hueper.net>
5 * This program and the accompanying materials
6 * are made available under the terms of the BSD License
7 * which accompanies this distribution, and is available at
8 * http://www.opensource.org/licenses/bsd-license.php
11 * IBM Corporation - initial implementation
12 *****************************************************************************/
17 #include "x86emu/x86emu.h"
20 #include "compat/time.h"
22 #if !CONFIG_YABEL_DIRECTHW || !CONFIG_YABEL_DIRECTHW
24 #if CONFIG_PCI_OPTION_ROM_RUN_YABEL
25 #include <device/resource.h>
28 // define a check for access to certain (virtual) memory regions (interrupt handlers, BIOS Data Area, ...)
29 #if CONFIG_X86EMU_DEBUG
30 static u8 in_check = 0; // to avoid recursion...
32 static inline void DEBUG_CHECK_VMEM_READ(u32 _addr, u32 _rval)
36 if (!((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)))
39 /* determine ebda_segment and size
40 * since we are using my_rdx calls, make sure, this is after setting in_check! */
41 /* offset 03 in BDA is EBDA segment */
42 ebda_segment = my_rdw(0x40e);
43 /* first value in ebda is size in KB */
44 ebda_size = my_rdb(ebda_segment << 4) * 1024;
45 /* check Interrupt Vector Access (0000:0000h - 0000:0400h) */
47 DEBUG_PRINTF_CS_IP("%s: read from Interrupt Vector %x --> %x\n",
48 __func__, _addr / 4, _rval);
50 /* access to BIOS Data Area (0000:0400h - 0000:0500h)*/
51 else if ((_addr >= 0x400) && (_addr < 0x500)) {
52 DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Area: addr: %x --> %x\n",
53 __func__, _addr, _rval);
55 /* x86emu_dump_xregs(); */
57 /* access to first 64k of memory... */
58 else if (_addr < 0x10000) {
59 DEBUG_PRINTF_CS_IP("%s: read from segment 0000h: addr: %x --> %x\n",
60 __func__, _addr, _rval);
62 /* x86emu_dump_xregs(); */
64 /* read from PMM_CONV_SEGMENT */
65 else if ((_addr <= ((PMM_CONV_SEGMENT << 4) | 0xffff)) && (_addr >= (PMM_CONV_SEGMENT << 4))) {
66 DEBUG_PRINTF_CS_IP("%s: read from PMM Segment %04xh: addr: %x --> %x\n",
67 __func__, PMM_CONV_SEGMENT, _addr, _rval);
70 /* x86emu_dump_xregs(); */
72 /* read from PNP_DATA_SEGMENT */
73 else if ((_addr <= ((PNP_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (PNP_DATA_SEGMENT << 4))) {
74 DEBUG_PRINTF_CS_IP("%s: read from PnP Data Segment %04xh: addr: %x --> %x\n",
75 __func__, PNP_DATA_SEGMENT, _addr, _rval);
78 /* x86emu_dump_xregs(); */
80 /* read from EBDA Segment */
81 else if ((_addr <= ((ebda_segment << 4) | (ebda_size - 1))) && (_addr >= (ebda_segment << 4))) {
82 DEBUG_PRINTF_CS_IP("%s: read from Extended BIOS Data Area %04xh, size: %04x: addr: %x --> %x\n",
83 __func__, ebda_segment, ebda_size, _addr, _rval);
85 /* read from BIOS_DATA_SEGMENT */
86 else if ((_addr <= ((BIOS_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (BIOS_DATA_SEGMENT << 4))) {
87 DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Segment %04xh: addr: %x --> %x\n",
88 __func__, BIOS_DATA_SEGMENT, _addr, _rval);
89 /* for PMM debugging */
90 /*if (_addr == BIOS_DATA_SEGMENT << 4) {
92 M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F;
95 /* x86emu_dump_xregs(); */
100 static inline void DEBUG_CHECK_VMEM_WRITE(u32 _addr, u32 _val)
104 if (!((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)))
107 /* determine ebda_segment and size
108 * since we are using my_rdx calls, make sure that this is after
109 * setting in_check! */
110 /* offset 03 in BDA is EBDA segment */
111 ebda_segment = my_rdw(0x40e);
112 /* first value in ebda is size in KB */
113 ebda_size = my_rdb(ebda_segment << 4) * 1024;
114 /* check Interrupt Vector Access (0000:0000h - 0000:0400h) */
116 DEBUG_PRINTF_CS_IP("%s: write to Interrupt Vector %x <-- %x\n",
117 __func__, _addr / 4, _val);
119 /* access to BIOS Data Area (0000:0400h - 0000:0500h)*/
120 else if ((_addr >= 0x400) && (_addr < 0x500)) {
121 DEBUG_PRINTF_CS_IP("%s: write to BIOS Data Area: addr: %x <-- %x\n",
122 __func__, _addr, _val);
124 /* x86emu_dump_xregs(); */
126 /* access to first 64k of memory...*/
127 else if (_addr < 0x10000) {
128 DEBUG_PRINTF_CS_IP("%s: write to segment 0000h: addr: %x <-- %x\n",
129 __func__, _addr, _val);
131 /* x86emu_dump_xregs(); */
133 /* write to PMM_CONV_SEGMENT... */
134 else if ((_addr <= ((PMM_CONV_SEGMENT << 4) | 0xffff)) && (_addr >= (PMM_CONV_SEGMENT << 4))) {
135 DEBUG_PRINTF_CS_IP("%s: write to PMM Segment %04xh: addr: %x <-- %x\n",
136 __func__, PMM_CONV_SEGMENT, _addr, _val);
138 /* x86emu_dump_xregs(); */
140 /* write to PNP_DATA_SEGMENT... */
141 else if ((_addr <= ((PNP_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (PNP_DATA_SEGMENT << 4))) {
142 DEBUG_PRINTF_CS_IP("%s: write to PnP Data Segment %04xh: addr: %x <-- %x\n",
143 __func__, PNP_DATA_SEGMENT, _addr, _val);
145 /* x86emu_dump_xregs(); */
147 /* write to EBDA Segment... */
148 else if ((_addr <= ((ebda_segment << 4) | (ebda_size - 1))) && (_addr >= (ebda_segment << 4))) {
149 DEBUG_PRINTF_CS_IP("%s: write to Extended BIOS Data Area %04xh, size: %04x: addr: %x <-- %x\n",
150 __func__, ebda_segment, ebda_size, _addr, _val);
152 /* write to BIOS_DATA_SEGMENT... */
153 else if ((_addr <= ((BIOS_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (BIOS_DATA_SEGMENT << 4))) {
154 DEBUG_PRINTF_CS_IP("%s: write to BIOS Data Segment %04xh: addr: %x <-- %x\n",
155 __func__, BIOS_DATA_SEGMENT, _addr, _val);
157 /* x86emu_dump_xregs(); */
159 /* write to current CS segment... */
160 else if ((_addr < ((M.x86.R_CS << 4) | 0xffff)) && (_addr > (M.x86.R_CS << 4))) {
161 DEBUG_PRINTF_CS_IP("%s: write to CS segment %04xh: addr: %x <-- %x\n",
162 __func__, M.x86.R_CS, _addr, _val);
164 /* x86emu_dump_xregs(); */
169 static inline void DEBUG_CHECK_VMEM_READ(u32 _addr, u32 _rval) {};
170 static inline void DEBUG_CHECK_VMEM_WRITE(u32 _addr, u32 _val) {};
173 //update time in BIOS Data Area
174 //DWord at offset 0x6c is the timer ticks since midnight, timer is running at 18Hz
175 //byte at 0x70 is timer overflow (set if midnight passed since last call to interrupt 1a function 00
176 //cur_val is the current value, of offset 6c...
178 update_time(u32 cur_val)
180 //for convenience, we let the start of timebase be at midnight, we currently dont support
181 //real daytime anyway...
182 u64 ticks_per_day = tb_freq * 60 * 24;
183 // at 18Hz a period is ~55ms, converted to ticks (tb_freq is ticks/second)
184 u32 period_ticks = (55 * tb_freq) / 1000;
185 u64 curr_time = get_time();
186 u64 ticks_since_midnight = curr_time % ticks_per_day;
187 u32 periods_since_midnight = ticks_since_midnight / period_ticks;
188 // if periods since midnight is smaller than last value, set overflow
189 // at BDA Offset 0x70
190 if (periods_since_midnight < cur_val) {
193 // store periods since midnight at BDA offset 0x6c
194 my_wrl(0x46c, periods_since_midnight);
197 // read byte from memory
201 unsigned long translated_addr = addr;
202 u8 translated = biosemu_dev_translate_address(IORESOURCE_MEM, &translated_addr);
204 if (translated != 0) {
205 //translation successfull, access VGA Memory (BAR or Legacy...)
206 DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n",
208 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
210 rval = *((u8 *) translated_addr);
212 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %02x\n", __func__, addr,
215 } else if (addr > M.mem_size) {
216 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
218 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
221 /* read from virtual memory */
222 rval = *((u8 *) (M.mem_base + addr));
223 DEBUG_CHECK_VMEM_READ(addr, rval);
229 //read word from memory
233 unsigned long translated_addr = addr;
234 u8 translated = biosemu_dev_translate_address(IORESOURCE_MEM, &translated_addr);
236 if (translated != 0) {
237 //translation successfull, access VGA Memory (BAR or Legacy...)
238 DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n",
240 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
241 // check for legacy memory, because of the remapping to BARs, the reads must
243 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
244 //read bytes a using my_rdb, because of the remapping to BARs
245 //words may not be contiguous in memory, so we need to translate
247 rval = ((u8) my_rdb(addr)) |
248 (((u8) my_rdb(addr + 1)) << 8);
250 if ((translated_addr & (u64) 0x1) == 0) {
251 // 16 bit aligned access...
253 rval = in16le((void *) translated_addr);
256 // unaligned access, read single bytes
258 rval = (*((u8 *) translated_addr)) |
259 (*((u8 *) translated_addr + 1) << 8);
263 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %04x\n", __func__, addr,
266 } else if (addr > M.mem_size) {
267 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
269 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
272 /* read from virtual memory */
273 rval = in16le((void *) (M.mem_base + addr));
274 DEBUG_CHECK_VMEM_READ(addr, rval);
280 //read long from memory
284 unsigned long translated_addr = addr;
285 u8 translated = biosemu_dev_translate_address(IORESOURCE_MEM, &translated_addr);
287 if (translated != 0) {
288 //translation successfull, access VGA Memory (BAR or Legacy...)
289 DEBUG_PRINTF_MEM("%s(%x): access to VGA Memory\n",
291 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
292 // check for legacy memory, because of the remapping to BARs, the reads must
294 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
295 //read bytes a using my_rdb, because of the remapping to BARs
296 //dwords may not be contiguous in memory, so we need to translate
298 rval = ((u8) my_rdb(addr)) |
299 (((u8) my_rdb(addr + 1)) << 8) |
300 (((u8) my_rdb(addr + 2)) << 16) |
301 (((u8) my_rdb(addr + 3)) << 24);
303 if ((translated_addr & (u64) 0x3) == 0) {
304 // 32 bit aligned access...
306 rval = in32le((void *) translated_addr);
309 // unaligned access, read single bytes
311 rval = (*((u8 *) translated_addr)) |
312 (*((u8 *) translated_addr + 1) << 8) |
313 (*((u8 *) translated_addr + 2) << 16) |
314 (*((u8 *) translated_addr + 3) << 24);
318 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %08x\n", __func__, addr,
322 } else if (addr > M.mem_size) {
323 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
325 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
328 /* read from virtual memory */
329 rval = in32le((void *) (M.mem_base + addr));
332 //BDA Time Data, update it, before reading
334 rval = in32le((void *) (M.mem_base + addr));
337 DEBUG_CHECK_VMEM_READ(addr, rval);
343 //write byte to memory
345 my_wrb(u32 addr, u8 val)
347 unsigned long translated_addr = addr;
348 u8 translated = biosemu_dev_translate_address(IORESOURCE_MEM, &translated_addr);
349 if (translated != 0) {
350 //translation successfull, access VGA Memory (BAR or Legacy...)
351 DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n",
352 __func__, addr, val);
353 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
355 *((u8 *) translated_addr) = val;
357 } else if (addr > M.mem_size) {
358 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
360 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
363 /* write to virtual memory */
364 DEBUG_CHECK_VMEM_WRITE(addr, val);
365 *((u8 *) (M.mem_base + addr)) = val;
370 my_wrw(u32 addr, u16 val)
372 unsigned long translated_addr = addr;
373 u8 translated = biosemu_dev_translate_address(IORESOURCE_MEM, &translated_addr);
374 if (translated != 0) {
375 //translation successfull, access VGA Memory (BAR or Legacy...)
376 DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n",
377 __func__, addr, val);
378 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
379 // check for legacy memory, because of the remapping to BARs, the reads must
381 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
382 //read bytes a using my_rdb, because of the remapping to BARs
383 //words may not be contiguous in memory, so we need to translate
385 my_wrb(addr, (u8) (val & 0x00FF));
386 my_wrb(addr + 1, (u8) ((val & 0xFF00) >> 8));
388 if ((translated_addr & (u64) 0x1) == 0) {
389 // 16 bit aligned access...
391 out16le((void *) translated_addr, val);
394 // unaligned access, write single bytes
396 *((u8 *) translated_addr) =
398 *((u8 *) translated_addr + 1) =
399 (u8) ((val & 0xFF00) >> 8);
403 } else if (addr > M.mem_size) {
404 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
406 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
409 /* write to virtual memory */
410 DEBUG_CHECK_VMEM_WRITE(addr, val);
411 out16le((void *) (M.mem_base + addr), val);
415 my_wrl(u32 addr, u32 val)
417 unsigned long translated_addr = addr;
418 u8 translated = biosemu_dev_translate_address(IORESOURCE_MEM, &translated_addr);
419 if (translated != 0) {
420 //translation successfull, access VGA Memory (BAR or Legacy...)
421 DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n",
422 __func__, addr, val);
423 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
424 // check for legacy memory, because of the remapping to BARs, the reads must
426 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
427 //read bytes a using my_rdb, because of the remapping to BARs
428 //words may not be contiguous in memory, so we need to translate
430 my_wrb(addr, (u8) (val & 0x000000FF));
431 my_wrb(addr + 1, (u8) ((val & 0x0000FF00) >> 8));
432 my_wrb(addr + 2, (u8) ((val & 0x00FF0000) >> 16));
433 my_wrb(addr + 3, (u8) ((val & 0xFF000000) >> 24));
435 if ((translated_addr & (u64) 0x3) == 0) {
436 // 32 bit aligned access...
438 out32le((void *) translated_addr, val);
441 // unaligned access, write single bytes
443 *((u8 *) translated_addr) =
444 (u8) (val & 0x000000FF);
445 *((u8 *) translated_addr + 1) =
446 (u8) ((val & 0x0000FF00) >> 8);
447 *((u8 *) translated_addr + 2) =
448 (u8) ((val & 0x00FF0000) >> 16);
449 *((u8 *) translated_addr + 3) =
450 (u8) ((val & 0xFF000000) >> 24);
454 } else if (addr > M.mem_size) {
455 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
457 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
460 /* write to virtual memory */
461 DEBUG_CHECK_VMEM_WRITE(addr, val);
462 out32le((void *) (M.mem_base + addr), val);
485 my_wrb(u32 addr, u8 val)
491 my_wrw(u32 addr, u16 val)
497 my_wrl(u32 addr, u32 val)