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);