1 /******************************************************************************
2 * Copyright (c) 2004, 2008 IBM Corporation
3 * Copyright (c) 2009 Pattrick Hueper <phueper@hueper.net>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
11 * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer
16 * in the documentation and/or other materials provided with the
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 * IBM Corporation - initial implementation
33 *****************************************************************************/
38 #include "x86emu/x86emu.h"
41 #include "compat/time.h"
42 #include <device/resource.h>
44 #if !CONFIG(YABEL_DIRECTHW) || !CONFIG(YABEL_DIRECTHW)
46 // define a check for access to certain (virtual) memory regions (interrupt handlers, BIOS Data Area, ...)
47 #if CONFIG(X86EMU_DEBUG)
48 static u8 in_check
= 0; // to avoid recursion...
50 static inline void DEBUG_CHECK_VMEM_READ(u32 _addr
, u32 _rval
)
54 if (!((debug_flags
& DEBUG_CHECK_VMEM_ACCESS
) && (in_check
== 0)))
57 /* determine ebda_segment and size
58 * since we are using my_rdx calls, make sure, this is after setting in_check! */
59 /* offset 03 in BDA is EBDA segment */
60 ebda_segment
= my_rdw(0x40e);
61 /* first value in ebda is size in KB */
62 ebda_size
= my_rdb(ebda_segment
<< 4) * 1024;
63 /* check Interrupt Vector Access (0000:0000h - 0000:0400h) */
65 DEBUG_PRINTF_CS_IP("%s: read from Interrupt Vector %x --> %x\n",
66 __func__
, _addr
/ 4, _rval
);
68 /* access to BIOS Data Area (0000:0400h - 0000:0500h)*/
69 else if ((_addr
>= 0x400) && (_addr
< 0x500)) {
70 DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Area: addr: %x --> %x\n",
71 __func__
, _addr
, _rval
);
73 /* x86emu_dump_xregs(); */
75 /* access to first 64k of memory... */
76 else if (_addr
< 0x10000) {
77 DEBUG_PRINTF_CS_IP("%s: read from segment 0000h: addr: %x --> %x\n",
78 __func__
, _addr
, _rval
);
80 /* x86emu_dump_xregs(); */
82 /* read from PMM_CONV_SEGMENT */
83 else if ((_addr
<= ((PMM_CONV_SEGMENT
<< 4) | 0xffff)) && (_addr
>= (PMM_CONV_SEGMENT
<< 4))) {
84 DEBUG_PRINTF_CS_IP("%s: read from PMM Segment %04xh: addr: %x --> %x\n",
85 __func__
, PMM_CONV_SEGMENT
, _addr
, _rval
);
88 /* x86emu_dump_xregs(); */
90 /* read from PNP_DATA_SEGMENT */
91 else if ((_addr
<= ((PNP_DATA_SEGMENT
<< 4) | 0xffff)) && (_addr
>= (PNP_DATA_SEGMENT
<< 4))) {
92 DEBUG_PRINTF_CS_IP("%s: read from PnP Data Segment %04xh: addr: %x --> %x\n",
93 __func__
, PNP_DATA_SEGMENT
, _addr
, _rval
);
96 /* x86emu_dump_xregs(); */
98 /* read from EBDA Segment */
99 else if ((_addr
<= ((ebda_segment
<< 4) | (ebda_size
- 1))) && (_addr
>= (ebda_segment
<< 4))) {
100 DEBUG_PRINTF_CS_IP("%s: read from Extended BIOS Data Area %04xh, size: %04x: addr: %x --> %x\n",
101 __func__
, ebda_segment
, ebda_size
, _addr
, _rval
);
103 /* read from BIOS_DATA_SEGMENT */
104 else if ((_addr
<= ((BIOS_DATA_SEGMENT
<< 4) | 0xffff)) && (_addr
>= (BIOS_DATA_SEGMENT
<< 4))) {
105 DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Segment %04xh: addr: %x --> %x\n",
106 __func__
, BIOS_DATA_SEGMENT
, _addr
, _rval
);
107 /* for PMM debugging */
108 /*if (_addr == BIOS_DATA_SEGMENT << 4) {
110 M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F;
113 /* x86emu_dump_xregs(); */
118 static inline void DEBUG_CHECK_VMEM_WRITE(u32 _addr
, u32 _val
)
122 if (!((debug_flags
& DEBUG_CHECK_VMEM_ACCESS
) && (in_check
== 0)))
125 /* determine ebda_segment and size
126 * since we are using my_rdx calls, make sure that this is after
127 * setting in_check! */
128 /* offset 03 in BDA is EBDA segment */
129 ebda_segment
= my_rdw(0x40e);
130 /* first value in ebda is size in KB */
131 ebda_size
= my_rdb(ebda_segment
<< 4) * 1024;
132 /* check Interrupt Vector Access (0000:0000h - 0000:0400h) */
134 DEBUG_PRINTF_CS_IP("%s: write to Interrupt Vector %x <-- %x\n",
135 __func__
, _addr
/ 4, _val
);
137 /* access to BIOS Data Area (0000:0400h - 0000:0500h)*/
138 else if ((_addr
>= 0x400) && (_addr
< 0x500)) {
139 DEBUG_PRINTF_CS_IP("%s: write to BIOS Data Area: addr: %x <-- %x\n",
140 __func__
, _addr
, _val
);
142 /* x86emu_dump_xregs(); */
144 /* access to first 64k of memory...*/
145 else if (_addr
< 0x10000) {
146 DEBUG_PRINTF_CS_IP("%s: write to segment 0000h: addr: %x <-- %x\n",
147 __func__
, _addr
, _val
);
149 /* x86emu_dump_xregs(); */
151 /* write to PMM_CONV_SEGMENT... */
152 else if ((_addr
<= ((PMM_CONV_SEGMENT
<< 4) | 0xffff)) && (_addr
>= (PMM_CONV_SEGMENT
<< 4))) {
153 DEBUG_PRINTF_CS_IP("%s: write to PMM Segment %04xh: addr: %x <-- %x\n",
154 __func__
, PMM_CONV_SEGMENT
, _addr
, _val
);
156 /* x86emu_dump_xregs(); */
158 /* write to PNP_DATA_SEGMENT... */
159 else if ((_addr
<= ((PNP_DATA_SEGMENT
<< 4) | 0xffff)) && (_addr
>= (PNP_DATA_SEGMENT
<< 4))) {
160 DEBUG_PRINTF_CS_IP("%s: write to PnP Data Segment %04xh: addr: %x <-- %x\n",
161 __func__
, PNP_DATA_SEGMENT
, _addr
, _val
);
163 /* x86emu_dump_xregs(); */
165 /* write to EBDA Segment... */
166 else if ((_addr
<= ((ebda_segment
<< 4) | (ebda_size
- 1))) && (_addr
>= (ebda_segment
<< 4))) {
167 DEBUG_PRINTF_CS_IP("%s: write to Extended BIOS Data Area %04xh, size: %04x: addr: %x <-- %x\n",
168 __func__
, ebda_segment
, ebda_size
, _addr
, _val
);
170 /* write to BIOS_DATA_SEGMENT... */
171 else if ((_addr
<= ((BIOS_DATA_SEGMENT
<< 4) | 0xffff)) && (_addr
>= (BIOS_DATA_SEGMENT
<< 4))) {
172 DEBUG_PRINTF_CS_IP("%s: write to BIOS Data Segment %04xh: addr: %x <-- %x\n",
173 __func__
, BIOS_DATA_SEGMENT
, _addr
, _val
);
175 /* x86emu_dump_xregs(); */
177 /* write to current CS segment... */
178 else if ((_addr
< ((M
.x86
.R_CS
<< 4) | 0xffff)) && (_addr
> (M
.x86
.R_CS
<< 4))) {
179 DEBUG_PRINTF_CS_IP("%s: write to CS segment %04xh: addr: %x <-- %x\n",
180 __func__
, M
.x86
.R_CS
, _addr
, _val
);
182 /* x86emu_dump_xregs(); */
187 static inline void DEBUG_CHECK_VMEM_READ(u32 _addr
, u32 _rval
) {};
188 static inline void DEBUG_CHECK_VMEM_WRITE(u32 _addr
, u32 _val
) {};
191 //update time in BIOS Data Area
192 //DWord at offset 0x6c is the timer ticks since midnight, timer is running at 18Hz
193 //byte at 0x70 is timer overflow (set if midnight passed since last call to interrupt 1a function 00
194 //cur_val is the current value, of offset 6c...
196 update_time(u32 cur_val
)
198 //for convenience, we let the start of timebase be at midnight, we currently don't support
199 //real daytime anyway...
200 u64 ticks_per_day
= tb_freq
* 60 * 24;
201 // at 18Hz a period is ~55ms, converted to ticks (tb_freq is ticks/second)
202 u32 period_ticks
= (55 * tb_freq
) / 1000;
203 u64 curr_time
= get_time();
204 u64 ticks_since_midnight
= curr_time
% ticks_per_day
;
205 u32 periods_since_midnight
= ticks_since_midnight
/ period_ticks
;
206 // if periods since midnight is smaller than last value, set overflow
207 // at BDA Offset 0x70
208 if (periods_since_midnight
< cur_val
) {
211 // store periods since midnight at BDA offset 0x6c
212 my_wrl(0x46c, periods_since_midnight
);
215 // read byte from memory
219 unsigned long translated_addr
= addr
;
220 u8 translated
= biosemu_dev_translate_address(IORESOURCE_MEM
, &translated_addr
);
222 if (translated
!= 0) {
223 //translation successful, access VGA Memory (BAR or Legacy...)
224 DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n",
226 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
228 rval
= *((u8
*) translated_addr
);
230 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %02x\n", __func__
, addr
,
233 } else if (addr
> M
.mem_size
) {
234 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
236 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
239 /* read from virtual memory */
240 rval
= *((u8
*) (M
.mem_base
+ addr
));
241 DEBUG_CHECK_VMEM_READ(addr
, rval
);
247 //read word from memory
251 unsigned long translated_addr
= addr
;
252 u8 translated
= biosemu_dev_translate_address(IORESOURCE_MEM
, &translated_addr
);
254 if (translated
!= 0) {
255 //translation successful, access VGA Memory (BAR or Legacy...)
256 DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n",
258 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
259 // check for legacy memory, because of the remapping to BARs, the reads must
261 if ((addr
>= 0xa0000) && (addr
< 0xc0000)) {
262 //read bytes a using my_rdb, because of the remapping to BARs
263 //words may not be contiguous in memory, so we need to translate
265 rval
= ((u8
) my_rdb(addr
)) |
266 (((u8
) my_rdb(addr
+ 1)) << 8);
268 if ((translated_addr
& (u64
) 0x1) == 0) {
269 // 16 bit aligned access...
271 rval
= in16le((void *) translated_addr
);
274 // unaligned access, read single bytes
276 rval
= (*((u8
*) translated_addr
)) |
277 (*((u8
*) translated_addr
+ 1) << 8);
281 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %04x\n", __func__
, addr
,
284 } else if (addr
> M
.mem_size
) {
285 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
287 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
290 /* read from virtual memory */
291 rval
= in16le((void *) (M
.mem_base
+ addr
));
292 DEBUG_CHECK_VMEM_READ(addr
, rval
);
298 //read long from memory
302 unsigned long translated_addr
= addr
;
303 u8 translated
= biosemu_dev_translate_address(IORESOURCE_MEM
, &translated_addr
);
305 if (translated
!= 0) {
306 //translation successful, access VGA Memory (BAR or Legacy...)
307 DEBUG_PRINTF_MEM("%s(%x): access to VGA Memory\n",
309 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
310 // check for legacy memory, because of the remapping to BARs, the reads must
312 if ((addr
>= 0xa0000) && (addr
< 0xc0000)) {
313 //read bytes a using my_rdb, because of the remapping to BARs
314 //dwords may not be contiguous in memory, so we need to translate
316 rval
= ((u8
) my_rdb(addr
)) |
317 (((u8
) my_rdb(addr
+ 1)) << 8) |
318 (((u8
) my_rdb(addr
+ 2)) << 16) |
319 (((u8
) my_rdb(addr
+ 3)) << 24);
321 if ((translated_addr
& (u64
) 0x3) == 0) {
322 // 32 bit aligned access...
324 rval
= in32le((void *) translated_addr
);
327 // unaligned access, read single bytes
329 rval
= (*((u8
*) translated_addr
)) |
330 (*((u8
*) translated_addr
+ 1) << 8) |
331 (*((u8
*) translated_addr
+ 2) << 16) |
332 (*((u8
*) translated_addr
+ 3) << 24);
336 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %08x\n", __func__
, addr
,
340 } else if (addr
> M
.mem_size
) {
341 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
343 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
346 /* read from virtual memory */
347 rval
= in32le((void *) (M
.mem_base
+ addr
));
350 //BDA Time Data, update it, before reading
352 rval
= in32le((void *) (M
.mem_base
+ addr
));
355 DEBUG_CHECK_VMEM_READ(addr
, rval
);
361 //write byte to memory
363 my_wrb(u32 addr
, u8 val
)
365 unsigned long translated_addr
= addr
;
366 u8 translated
= biosemu_dev_translate_address(IORESOURCE_MEM
, &translated_addr
);
367 if (translated
!= 0) {
368 //translation successful, access VGA Memory (BAR or Legacy...)
369 DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n",
370 __func__
, addr
, val
);
371 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
373 *((u8
*) translated_addr
) = val
;
375 } else if (addr
> M
.mem_size
) {
376 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
378 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
381 /* write to virtual memory */
382 DEBUG_CHECK_VMEM_WRITE(addr
, val
);
383 *((u8
*) (M
.mem_base
+ addr
)) = val
;
388 my_wrw(u32 addr
, u16 val
)
390 unsigned long translated_addr
= addr
;
391 u8 translated
= biosemu_dev_translate_address(IORESOURCE_MEM
, &translated_addr
);
392 if (translated
!= 0) {
393 //translation successful, access VGA Memory (BAR or Legacy...)
394 DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n",
395 __func__
, addr
, val
);
396 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
397 // check for legacy memory, because of the remapping to BARs, the reads must
399 if ((addr
>= 0xa0000) && (addr
< 0xc0000)) {
400 //read bytes a using my_rdb, because of the remapping to BARs
401 //words may not be contiguous in memory, so we need to translate
403 my_wrb(addr
, (u8
) (val
& 0x00FF));
404 my_wrb(addr
+ 1, (u8
) ((val
& 0xFF00) >> 8));
406 if ((translated_addr
& (u64
) 0x1) == 0) {
407 // 16 bit aligned access...
409 out16le((void *) translated_addr
, val
);
412 // unaligned access, write single bytes
414 *((u8
*) translated_addr
) =
416 *((u8
*) translated_addr
+ 1) =
417 (u8
) ((val
& 0xFF00) >> 8);
421 } else if (addr
> M
.mem_size
) {
422 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
424 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
427 /* write to virtual memory */
428 DEBUG_CHECK_VMEM_WRITE(addr
, val
);
429 out16le((void *) (M
.mem_base
+ addr
), val
);
433 my_wrl(u32 addr
, u32 val
)
435 unsigned long translated_addr
= addr
;
436 u8 translated
= biosemu_dev_translate_address(IORESOURCE_MEM
, &translated_addr
);
437 if (translated
!= 0) {
438 //translation successful, access VGA Memory (BAR or Legacy...)
439 DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n",
440 __func__
, addr
, val
);
441 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
442 // check for legacy memory, because of the remapping to BARs, the reads must
444 if ((addr
>= 0xa0000) && (addr
< 0xc0000)) {
445 //read bytes a using my_rdb, because of the remapping to BARs
446 //words may not be contiguous in memory, so we need to translate
448 my_wrb(addr
, (u8
) (val
& 0x000000FF));
449 my_wrb(addr
+ 1, (u8
) ((val
& 0x0000FF00) >> 8));
450 my_wrb(addr
+ 2, (u8
) ((val
& 0x00FF0000) >> 16));
451 my_wrb(addr
+ 3, (u8
) ((val
& 0xFF000000) >> 24));
453 if ((translated_addr
& (u64
) 0x3) == 0) {
454 // 32 bit aligned access...
456 out32le((void *) translated_addr
, val
);
459 // unaligned access, write single bytes
461 *((u8
*) translated_addr
) =
462 (u8
) (val
& 0x000000FF);
463 *((u8
*) translated_addr
+ 1) =
464 (u8
) ((val
& 0x0000FF00) >> 8);
465 *((u8
*) translated_addr
+ 2) =
466 (u8
) ((val
& 0x00FF0000) >> 16);
467 *((u8
*) translated_addr
+ 3) =
468 (u8
) ((val
& 0xFF000000) >> 24);
472 } else if (addr
> M
.mem_size
) {
473 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
475 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
478 /* write to virtual memory */
479 DEBUG_CHECK_VMEM_WRITE(addr
, val
);
480 out32le((void *) (M
.mem_base
+ addr
), val
);
503 my_wrb(u32 addr
, u8 val
)
509 my_wrw(u32 addr
, u16 val
)
515 my_wrl(u32 addr
, u32 val
)