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 *****************************************************************************/
15 #ifndef CONFIG_COREBOOT_V2
20 #include "x86emu/x86emu.h"
22 #ifdef CONFIG_COREBOOT_V2
23 #include "compat/time.h"
28 // define a check for access to certain (virtual) memory regions (interrupt handlers, BIOS Data Area, ...)
30 static u8 in_check = 0; // to avoid recursion...
34 //TODO: these macros have grown so large, that they should be changed to an inline function,
35 //just for the sake of readability...
37 //declare prototypes of the functions to follow, for use in DEBUG_CHECK_VMEM_ACCESS
42 #define DEBUG_CHECK_VMEM_READ(_addr, _rval) \
43 if ((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)) { \
45 /* determine ebda_segment and size \
46 * since we are using my_rdx calls, make sure, this is after setting in_check! */ \
47 /* offset 03 in BDA is EBDA segment */ \
48 ebda_segment = my_rdw(0x40e); \
49 /* first value in ebda is size in KB */ \
50 ebda_size = my_rdb(ebda_segment << 4) * 1024; \
51 /* check Interrupt Vector Access (0000:0000h - 0000:0400h) */ \
52 if (_addr < 0x400) { \
53 DEBUG_PRINTF_CS_IP("%s: read from Interrupt Vector %x --> %x\n", \
54 __func__, _addr / 4, _rval); \
56 /* access to BIOS Data Area (0000:0400h - 0000:0500h)*/ \
57 else if ((_addr >= 0x400) && (addr < 0x500)) { \
58 DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Area: addr: %x --> %x\n", \
59 __func__, _addr, _rval); \
60 /* dump registers */ \
61 /* x86emu_dump_xregs(); */ \
63 /* access to first 64k of memory... */ \
64 else if (_addr < 0x10000) { \
65 DEBUG_PRINTF_CS_IP("%s: read from segment 0000h: addr: %x --> %x\n", \
66 __func__, _addr, _rval); \
67 /* dump registers */ \
68 /* x86emu_dump_xregs(); */ \
70 /* read from PMM_CONV_SEGMENT */ \
71 else if ((_addr <= ((PMM_CONV_SEGMENT << 4) | 0xffff)) && (_addr >= (PMM_CONV_SEGMENT << 4))) { \
72 DEBUG_PRINTF_CS_IP("%s: read from PMM Segment %04xh: addr: %x --> %x\n", \
73 __func__, PMM_CONV_SEGMENT, _addr, _rval); \
75 /* dump registers */ \
76 /* x86emu_dump_xregs(); */ \
78 /* read from PNP_DATA_SEGMENT */ \
79 else if ((_addr <= ((PNP_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (PNP_DATA_SEGMENT << 4))) { \
80 DEBUG_PRINTF_CS_IP("%s: read from PnP Data Segment %04xh: addr: %x --> %x\n", \
81 __func__, PNP_DATA_SEGMENT, _addr, _rval); \
83 /* dump registers */ \
84 /* x86emu_dump_xregs(); */ \
86 /* read from EBDA Segment */ \
87 else if ((_addr <= ((ebda_segment << 4) | (ebda_size - 1))) && (_addr >= (ebda_segment << 4))) { \
88 DEBUG_PRINTF_CS_IP("%s: read from Extended BIOS Data Area %04xh, size: %04x: addr: %x --> %x\n", \
89 __func__, ebda_segment, ebda_size, _addr, _rval); \
91 /* read from BIOS_DATA_SEGMENT */ \
92 else if ((_addr <= ((BIOS_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (BIOS_DATA_SEGMENT << 4))) { \
93 DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Segment %04xh: addr: %x --> %x\n", \
94 __func__, BIOS_DATA_SEGMENT, _addr, _rval); \
95 /* for PMM debugging */ \
96 /*if (_addr == BIOS_DATA_SEGMENT << 4) { \
98 M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; \
100 /* dump registers */ \
101 /* x86emu_dump_xregs(); */ \
105 #define DEBUG_CHECK_VMEM_WRITE(_addr, _val) \
106 if ((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)) { \
108 /* determine ebda_segment and size \
109 * since we are using my_rdx calls, make sure, this is after 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) */ \
115 if (_addr < 0x400) { \
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); \
123 /* dump registers */ \
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); \
130 /* dump registers */ \
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); \
137 /* dump registers */ \
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); \
144 /* dump registers */ \
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); \
156 /* dump registers */ \
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); \
163 /* dump registers */ \
164 /* x86emu_dump_xregs(); */ \
169 #define DEBUG_CHECK_VMEM_READ(_addr, _rval)
170 #define DEBUG_CHECK_VMEM_WRITE(_addr, _val)
173 //defined in net-snk/kernel/timer.c
174 extern u64 get_time(void);
176 void update_time(u32);
178 // read byte from memory
182 unsigned long translated_addr = addr;
183 u8 translated = biosemu_dev_translate_address(&translated_addr);
185 if (translated != 0) {
186 //translation successfull, access VGA Memory (BAR or Legacy...)
187 DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n",
189 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
191 rval = *((u8 *) translated_addr);
193 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %02x\n", __func__, addr,
196 } else if (addr > M.mem_size) {
197 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
199 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
202 /* read from virtual memory */
203 rval = *((u8 *) (M.mem_base + addr));
204 DEBUG_CHECK_VMEM_READ(addr, rval);
210 //read word from memory
214 unsigned long translated_addr = addr;
215 u8 translated = biosemu_dev_translate_address(&translated_addr);
217 if (translated != 0) {
218 //translation successfull, access VGA Memory (BAR or Legacy...)
219 DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n",
221 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
222 // check for legacy memory, because of the remapping to BARs, the reads must
224 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
225 //read bytes a using my_rdb, because of the remapping to BARs
226 //words may not be contiguous in memory, so we need to translate
228 rval = ((u8) my_rdb(addr)) |
229 (((u8) my_rdb(addr + 1)) << 8);
231 if ((translated_addr & (u64) 0x1) == 0) {
232 // 16 bit aligned access...
234 rval = in16le((void *) translated_addr);
237 // unaligned access, read single bytes
239 rval = (*((u8 *) translated_addr)) |
240 (*((u8 *) translated_addr + 1) << 8);
244 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %04x\n", __func__, addr,
247 } else if (addr > M.mem_size) {
248 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
250 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
253 /* read from virtual memory */
254 rval = in16le((void *) (M.mem_base + addr));
255 DEBUG_CHECK_VMEM_READ(addr, rval);
261 //read long from memory
265 unsigned long translated_addr = addr;
266 u8 translated = biosemu_dev_translate_address(&translated_addr);
268 if (translated != 0) {
269 //translation successfull, access VGA Memory (BAR or Legacy...)
270 DEBUG_PRINTF_MEM("%s(%x): access to VGA Memory\n",
272 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
273 // check for legacy memory, because of the remapping to BARs, the reads must
275 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
276 //read bytes a using my_rdb, because of the remapping to BARs
277 //dwords may not be contiguous in memory, so we need to translate
279 rval = ((u8) my_rdb(addr)) |
280 (((u8) my_rdb(addr + 1)) << 8) |
281 (((u8) my_rdb(addr + 2)) << 16) |
282 (((u8) my_rdb(addr + 3)) << 24);
284 if ((translated_addr & (u64) 0x3) == 0) {
285 // 32 bit aligned access...
287 rval = in32le((void *) translated_addr);
290 // unaligned access, read single bytes
292 rval = (*((u8 *) translated_addr)) |
293 (*((u8 *) translated_addr + 1) << 8) |
294 (*((u8 *) translated_addr + 2) << 16) |
295 (*((u8 *) translated_addr + 3) << 24);
299 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %08x\n", __func__, addr,
303 } else if (addr > M.mem_size) {
304 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
306 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
309 /* read from virtual memory */
310 rval = in32le((void *) (M.mem_base + addr));
313 //BDA Time Data, update it, before reading
315 rval = in32le((void *) (M.mem_base + addr));
318 DEBUG_CHECK_VMEM_READ(addr, rval);
324 //write byte to memory
326 my_wrb(u32 addr, u8 val)
328 unsigned long translated_addr = addr;
329 u8 translated = biosemu_dev_translate_address(&translated_addr);
330 if (translated != 0) {
331 //translation successfull, access VGA Memory (BAR or Legacy...)
332 DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n",
333 __func__, addr, val);
334 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
336 *((u8 *) translated_addr) = val;
338 } else if (addr > M.mem_size) {
339 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
341 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
344 /* write to virtual memory */
345 DEBUG_CHECK_VMEM_WRITE(addr, val);
346 *((u8 *) (M.mem_base + addr)) = val;
351 my_wrw(u32 addr, u16 val)
353 unsigned long translated_addr = addr;
354 u8 translated = biosemu_dev_translate_address(&translated_addr);
355 if (translated != 0) {
356 //translation successfull, access VGA Memory (BAR or Legacy...)
357 DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n",
358 __func__, addr, val);
359 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
360 // check for legacy memory, because of the remapping to BARs, the reads must
362 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
363 //read bytes a using my_rdb, because of the remapping to BARs
364 //words may not be contiguous in memory, so we need to translate
366 my_wrb(addr, (u8) (val & 0x00FF));
367 my_wrb(addr + 1, (u8) ((val & 0xFF00) >> 8));
369 if ((translated_addr & (u64) 0x1) == 0) {
370 // 16 bit aligned access...
372 out16le((void *) translated_addr, val);
375 // unaligned access, write single bytes
377 *((u8 *) translated_addr) =
379 *((u8 *) translated_addr + 1) =
380 (u8) ((val & 0xFF00) >> 8);
384 } else if (addr > M.mem_size) {
385 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
387 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
390 /* write to virtual memory */
391 DEBUG_CHECK_VMEM_WRITE(addr, val);
392 out16le((void *) (M.mem_base + addr), val);
396 my_wrl(u32 addr, u32 val)
398 unsigned long translated_addr = addr;
399 u8 translated = biosemu_dev_translate_address(&translated_addr);
400 if (translated != 0) {
401 //translation successfull, access VGA Memory (BAR or Legacy...)
402 DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n",
403 __func__, addr, val);
404 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
405 // check for legacy memory, because of the remapping to BARs, the reads must
407 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
408 //read bytes a using my_rdb, because of the remapping to BARs
409 //words may not be contiguous in memory, so we need to translate
411 my_wrb(addr, (u8) (val & 0x000000FF));
412 my_wrb(addr + 1, (u8) ((val & 0x0000FF00) >> 8));
413 my_wrb(addr + 2, (u8) ((val & 0x00FF0000) >> 16));
414 my_wrb(addr + 3, (u8) ((val & 0xFF000000) >> 24));
416 if ((translated_addr & (u64) 0x3) == 0) {
417 // 32 bit aligned access...
419 out32le((void *) translated_addr, val);
422 // unaligned access, write single bytes
424 *((u8 *) translated_addr) =
425 (u8) (val & 0x000000FF);
426 *((u8 *) translated_addr + 1) =
427 (u8) ((val & 0x0000FF00) >> 8);
428 *((u8 *) translated_addr + 2) =
429 (u8) ((val & 0x00FF0000) >> 16);
430 *((u8 *) translated_addr + 3) =
431 (u8) ((val & 0xFF000000) >> 24);
435 } else if (addr > M.mem_size) {
436 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
438 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
441 /* write to virtual memory */
442 DEBUG_CHECK_VMEM_WRITE(addr, val);
443 out32le((void *) (M.mem_base + addr), val);
447 //update time in BIOS Data Area
448 //DWord at offset 0x6c is the timer ticks since midnight, timer is running at 18Hz
449 //byte at 0x70 is timer overflow (set if midnight passed since last call to interrupt 1a function 00
450 //cur_val is the current value, of offset 6c...
452 update_time(u32 cur_val)
454 //for convenience, we let the start of timebase be at midnight, we currently dont support
455 //real daytime anyway...
456 u64 ticks_per_day = tb_freq * 60 * 24;
457 // at 18Hz a period is ~55ms, converted to ticks (tb_freq is ticks/second)
458 u32 period_ticks = (55 * tb_freq) / 1000;
459 u64 curr_time = get_time();
460 u64 ticks_since_midnight = curr_time % ticks_per_day;
461 u32 periods_since_midnight = ticks_since_midnight / period_ticks;
462 // if periods since midnight is smaller than last value, set overflow
463 // at BDA Offset 0x70
464 if (periods_since_midnight < cur_val) {
467 // store periods since midnight at BDA offset 0x6c
468 my_wrl(0x46c, periods_since_midnight);