1. Move run_bios prototype to device.h
[coreboot.git] / util / x86emu / yabel / mem.c
1 /******************************************************************************
2  * Copyright (c) 2004, 2008 IBM Corporation
3  * Copyright (c) 2009 Pattrick Hueper <phueper@hueper.net>
4  * All rights reserved.
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
9  *
10  * Contributors:
11  *     IBM Corporation - initial implementation
12  *****************************************************************************/
13
14 #include <types.h>
15 #include "debug.h"
16 #include "device.h"
17 #include "x86emu/x86emu.h"
18 #include "biosemu.h"
19 #include "mem.h"
20 #include "compat/time.h"
21
22 // define a check for access to certain (virtual) memory regions (interrupt handlers, BIOS Data Area, ...)
23 #if CONFIG_X86EMU_DEBUG
24 static u8 in_check = 0; // to avoid recursion...
25 u16 ebda_segment;
26 u32 ebda_size;
27
28 //TODO: these macros have grown so large, that they should be changed to an inline function,
29 //just for the sake of readability...
30
31 #define DEBUG_CHECK_VMEM_READ(_addr, _rval) \
32    if ((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)) { \
33          in_check = 1; \
34          /* determine ebda_segment and size \
35           * since we are using my_rdx calls, make sure, this is after setting in_check! */ \
36          /* offset 03 in BDA is EBDA segment */ \
37          ebda_segment = my_rdw(0x40e); \
38          /* first value in ebda is size in KB */ \
39          ebda_size = my_rdb(ebda_segment << 4) * 1024; \
40                         /* check Interrupt Vector Access (0000:0000h - 0000:0400h) */ \
41                         if (_addr < 0x400) { \
42                                 DEBUG_PRINTF_CS_IP("%s: read from Interrupt Vector %x --> %x\n", \
43                                                 __func__, _addr / 4, _rval); \
44                         } \
45                         /* access to BIOS Data Area (0000:0400h - 0000:0500h)*/ \
46                         else if ((_addr >= 0x400) && (addr < 0x500)) { \
47                                 DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Area: addr: %x --> %x\n", \
48                                                 __func__, _addr, _rval); \
49                                 /* dump registers */ \
50                                 /* x86emu_dump_xregs(); */ \
51                         } \
52                         /* access to first 64k of memory... */ \
53                         else if (_addr < 0x10000) { \
54                                 DEBUG_PRINTF_CS_IP("%s: read from segment 0000h: addr: %x --> %x\n", \
55                                                 __func__, _addr, _rval); \
56                                 /* dump registers */ \
57                                 /* x86emu_dump_xregs(); */ \
58                         } \
59                         /* read from PMM_CONV_SEGMENT */ \
60                         else if ((_addr <= ((PMM_CONV_SEGMENT << 4) | 0xffff)) && (_addr >= (PMM_CONV_SEGMENT << 4))) { \
61                                 DEBUG_PRINTF_CS_IP("%s: read from PMM Segment %04xh: addr: %x --> %x\n", \
62                                                 __func__, PMM_CONV_SEGMENT, _addr, _rval); \
63                                 /* HALT_SYS(); */ \
64                                 /* dump registers */ \
65                                 /* x86emu_dump_xregs(); */ \
66                         } \
67                         /* read from PNP_DATA_SEGMENT */ \
68                         else if ((_addr <= ((PNP_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (PNP_DATA_SEGMENT << 4))) { \
69                                 DEBUG_PRINTF_CS_IP("%s: read from PnP Data Segment %04xh: addr: %x --> %x\n", \
70                                                 __func__, PNP_DATA_SEGMENT, _addr, _rval); \
71                                 /* HALT_SYS(); */ \
72                                 /* dump registers */ \
73                                 /* x86emu_dump_xregs(); */ \
74                         } \
75                         /* read from EBDA Segment */ \
76                         else if ((_addr <= ((ebda_segment << 4) | (ebda_size - 1))) && (_addr >= (ebda_segment << 4))) { \
77                                 DEBUG_PRINTF_CS_IP("%s: read from Extended BIOS Data Area %04xh, size: %04x: addr: %x --> %x\n", \
78                                                 __func__, ebda_segment, ebda_size, _addr, _rval); \
79                         } \
80                         /* read from BIOS_DATA_SEGMENT */ \
81                         else if ((_addr <= ((BIOS_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (BIOS_DATA_SEGMENT << 4))) { \
82                                 DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Segment %04xh: addr: %x --> %x\n", \
83                                                 __func__, BIOS_DATA_SEGMENT, _addr, _rval); \
84                                 /* for PMM debugging */ \
85                                 /*if (_addr == BIOS_DATA_SEGMENT << 4) { \
86                                         X86EMU_trace_on(); \
87                                         M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; \
88                                 }*/ \
89                                 /* dump registers */ \
90                                 /* x86emu_dump_xregs(); */ \
91                         } \
92          in_check = 0; \
93    }
94 #define DEBUG_CHECK_VMEM_WRITE(_addr, _val) \
95    if ((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)) { \
96          in_check = 1; \
97          /* determine ebda_segment and size \
98           * since we are using my_rdx calls, make sure, this is after setting in_check! */ \
99          /* offset 03 in BDA is EBDA segment */ \
100          ebda_segment = my_rdw(0x40e); \
101          /* first value in ebda is size in KB */ \
102          ebda_size = my_rdb(ebda_segment << 4) * 1024; \
103                         /* check Interrupt Vector Access (0000:0000h - 0000:0400h) */ \
104                         if (_addr < 0x400) { \
105                                 DEBUG_PRINTF_CS_IP("%s: write to Interrupt Vector %x <-- %x\n", \
106                                                 __func__, _addr / 4, _val); \
107                         } \
108                         /* access to BIOS Data Area (0000:0400h - 0000:0500h)*/ \
109                         else if ((_addr >= 0x400) && (addr < 0x500)) { \
110                                 DEBUG_PRINTF_CS_IP("%s: write to BIOS Data Area: addr: %x <-- %x\n", \
111                                                 __func__, _addr, _val); \
112                                 /* dump registers */ \
113                                 /* x86emu_dump_xregs(); */ \
114                         } \
115                         /* access to first 64k of memory...*/ \
116                         else if (_addr < 0x10000) { \
117                                 DEBUG_PRINTF_CS_IP("%s: write to segment 0000h: addr: %x <-- %x\n", \
118                                                 __func__, _addr, _val); \
119                                 /* dump registers */ \
120                                 /* x86emu_dump_xregs(); */ \
121                         } \
122                         /* write to PMM_CONV_SEGMENT... */ \
123                         else if ((_addr <= ((PMM_CONV_SEGMENT << 4) | 0xffff)) && (_addr >= (PMM_CONV_SEGMENT << 4))) { \
124                                 DEBUG_PRINTF_CS_IP("%s: write to PMM Segment %04xh: addr: %x <-- %x\n", \
125                                                 __func__, PMM_CONV_SEGMENT, _addr, _val); \
126                                 /* dump registers */ \
127                                 /* x86emu_dump_xregs(); */ \
128                         } \
129                         /* write to PNP_DATA_SEGMENT... */ \
130                         else if ((_addr <= ((PNP_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (PNP_DATA_SEGMENT << 4))) { \
131                                 DEBUG_PRINTF_CS_IP("%s: write to PnP Data Segment %04xh: addr: %x <-- %x\n", \
132                                                 __func__, PNP_DATA_SEGMENT, _addr, _val); \
133                                 /* dump registers */ \
134                                 /* x86emu_dump_xregs(); */ \
135                         } \
136                         /* write to EBDA Segment... */ \
137                         else if ((_addr <= ((ebda_segment << 4) | (ebda_size - 1))) && (_addr >= (ebda_segment << 4))) { \
138                                 DEBUG_PRINTF_CS_IP("%s: write to Extended BIOS Data Area %04xh, size: %04x: addr: %x <-- %x\n", \
139                                                 __func__, ebda_segment, ebda_size, _addr, _val); \
140                         } \
141                         /* write to BIOS_DATA_SEGMENT... */ \
142                         else if ((_addr <= ((BIOS_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (BIOS_DATA_SEGMENT << 4))) { \
143                                 DEBUG_PRINTF_CS_IP("%s: write to BIOS Data Segment %04xh: addr: %x <-- %x\n", \
144                                                 __func__, BIOS_DATA_SEGMENT, _addr, _val); \
145                                 /* dump registers */ \
146                                 /* x86emu_dump_xregs(); */ \
147                         } \
148                         /* write to current CS segment... */ \
149                         else if ((_addr < ((M.x86.R_CS << 4) | 0xffff)) && (_addr > (M.x86.R_CS << 4))) { \
150                                 DEBUG_PRINTF_CS_IP("%s: write to CS segment %04xh: addr: %x <-- %x\n", \
151                                                 __func__, M.x86.R_CS, _addr, _val); \
152                                 /* dump registers */ \
153                                 /* x86emu_dump_xregs(); */ \
154                         } \
155          in_check = 0; \
156    }
157 #else
158 #define DEBUG_CHECK_VMEM_READ(_addr, _rval)
159 #define DEBUG_CHECK_VMEM_WRITE(_addr, _val)
160 #endif
161
162 void update_time(u32);
163
164 #if !defined(CONFIG_YABEL_DIRECTHW) || (!CONFIG_YABEL_DIRECTHW)
165 // read byte from memory
166 u8
167 my_rdb(u32 addr)
168 {
169         unsigned long translated_addr = addr;
170         u8 translated = biosemu_dev_translate_address(&translated_addr);
171         u8 rval;
172         if (translated != 0) {
173                 //translation successfull, access VGA Memory (BAR or Legacy...)
174                 DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n",
175                                  __func__, addr);
176                 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
177                 set_ci();
178                 rval = *((u8 *) translated_addr);
179                 clr_ci();
180                 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %02x\n", __func__, addr,
181                                  rval);
182                 return rval;
183         } else if (addr > M.mem_size) {
184                 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
185                              __func__, addr);
186                 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
187                 HALT_SYS();
188         } else {
189                 /* read from virtual memory */
190                 rval = *((u8 *) (M.mem_base + addr));
191                 DEBUG_CHECK_VMEM_READ(addr, rval);
192                 return rval;
193         }
194         return -1;
195 }
196
197 //read word from memory
198 u16
199 my_rdw(u32 addr)
200 {
201         unsigned long translated_addr = addr;
202         u8 translated = biosemu_dev_translate_address(&translated_addr);
203         u16 rval;
204         if (translated != 0) {
205                 //translation successfull, access VGA Memory (BAR or Legacy...)
206                 DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n",
207                                  __func__, addr);
208                 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
209                 // check for legacy memory, because of the remapping to BARs, the reads must
210                 // be byte reads...
211                 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
212                         //read bytes a using my_rdb, because of the remapping to BARs
213                         //words may not be contiguous in memory, so we need to translate
214                         //every address...
215                         rval = ((u8) my_rdb(addr)) |
216                             (((u8) my_rdb(addr + 1)) << 8);
217                 } else {
218                         if ((translated_addr & (u64) 0x1) == 0) {
219                                 // 16 bit aligned access...
220                                 set_ci();
221                                 rval = in16le((void *) translated_addr);
222                                 clr_ci();
223                         } else {
224                                 // unaligned access, read single bytes
225                                 set_ci();
226                                 rval = (*((u8 *) translated_addr)) |
227                                     (*((u8 *) translated_addr + 1) << 8);
228                                 clr_ci();
229                         }
230                 }
231                 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %04x\n", __func__, addr,
232                                  rval);
233                 return rval;
234         } else if (addr > M.mem_size) {
235                 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
236                              __func__, addr);
237                 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
238                 HALT_SYS();
239         } else {
240                 /* read from virtual memory */
241                 rval = in16le((void *) (M.mem_base + addr));
242                 DEBUG_CHECK_VMEM_READ(addr, rval);
243                 return rval;
244         }
245         return -1;
246 }
247
248 //read long from memory
249 u32
250 my_rdl(u32 addr)
251 {
252         unsigned long translated_addr = addr;
253         u8 translated = biosemu_dev_translate_address(&translated_addr);
254         u32 rval;
255         if (translated != 0) {
256                 //translation successfull, access VGA Memory (BAR or Legacy...)
257                 DEBUG_PRINTF_MEM("%s(%x): access to VGA Memory\n",
258                                  __func__, addr);
259                 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
260                 // check for legacy memory, because of the remapping to BARs, the reads must
261                 // be byte reads...
262                 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
263                         //read bytes a using my_rdb, because of the remapping to BARs
264                         //dwords may not be contiguous in memory, so we need to translate
265                         //every address...
266                         rval = ((u8) my_rdb(addr)) |
267                             (((u8) my_rdb(addr + 1)) << 8) |
268                             (((u8) my_rdb(addr + 2)) << 16) |
269                             (((u8) my_rdb(addr + 3)) << 24);
270                 } else {
271                         if ((translated_addr & (u64) 0x3) == 0) {
272                                 // 32 bit aligned access...
273                                 set_ci();
274                                 rval = in32le((void *) translated_addr);
275                                 clr_ci();
276                         } else {
277                                 // unaligned access, read single bytes
278                                 set_ci();
279                                 rval = (*((u8 *) translated_addr)) |
280                                     (*((u8 *) translated_addr + 1) << 8) |
281                                     (*((u8 *) translated_addr + 2) << 16) |
282                                     (*((u8 *) translated_addr + 3) << 24);
283                                 clr_ci();
284                         }
285                 }
286                 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %08x\n", __func__, addr,
287                                  rval);
288                 //HALT_SYS();
289                 return rval;
290         } else if (addr > M.mem_size) {
291                 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
292                              __func__, addr);
293                 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
294                 HALT_SYS();
295         } else {
296                 /* read from virtual memory */
297                 rval = in32le((void *) (M.mem_base + addr));
298                 switch (addr) {
299                 case 0x46c:
300                         //BDA Time Data, update it, before reading
301                         update_time(rval);
302                         rval = in32le((void *) (M.mem_base + addr));
303                         break;
304                 }
305                 DEBUG_CHECK_VMEM_READ(addr, rval);
306                 return rval;
307         }
308         return -1;
309 }
310
311 //write byte to memory
312 void
313 my_wrb(u32 addr, u8 val)
314 {
315         unsigned long translated_addr = addr;
316         u8 translated = biosemu_dev_translate_address(&translated_addr);
317         if (translated != 0) {
318                 //translation successfull, access VGA Memory (BAR or Legacy...)
319                 DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n",
320                                  __func__, addr, val);
321                 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
322                 set_ci();
323                 *((u8 *) translated_addr) = val;
324                 clr_ci();
325         } else if (addr > M.mem_size) {
326                 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
327                              __func__, addr);
328                 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
329                 HALT_SYS();
330         } else {
331                 /* write to virtual memory */
332                 DEBUG_CHECK_VMEM_WRITE(addr, val);
333                 *((u8 *) (M.mem_base + addr)) = val;
334         }
335 }
336
337 void
338 my_wrw(u32 addr, u16 val)
339 {
340         unsigned long translated_addr = addr;
341         u8 translated = biosemu_dev_translate_address(&translated_addr);
342         if (translated != 0) {
343                 //translation successfull, access VGA Memory (BAR or Legacy...)
344                 DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n",
345                                  __func__, addr, val);
346                 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
347                 // check for legacy memory, because of the remapping to BARs, the reads must
348                 // be byte reads...
349                 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
350                         //read bytes a using my_rdb, because of the remapping to BARs
351                         //words may not be contiguous in memory, so we need to translate
352                         //every address...
353                         my_wrb(addr, (u8) (val & 0x00FF));
354                         my_wrb(addr + 1, (u8) ((val & 0xFF00) >> 8));
355                 } else {
356                         if ((translated_addr & (u64) 0x1) == 0) {
357                                 // 16 bit aligned access...
358                                 set_ci();
359                                 out16le((void *) translated_addr, val);
360                                 clr_ci();
361                         } else {
362                                 // unaligned access, write single bytes
363                                 set_ci();
364                                 *((u8 *) translated_addr) =
365                                     (u8) (val & 0x00FF);
366                                 *((u8 *) translated_addr + 1) =
367                                     (u8) ((val & 0xFF00) >> 8);
368                                 clr_ci();
369                         }
370                 }
371         } else if (addr > M.mem_size) {
372                 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
373                              __func__, addr);
374                 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
375                 HALT_SYS();
376         } else {
377                 /* write to virtual memory */
378                 DEBUG_CHECK_VMEM_WRITE(addr, val);
379                 out16le((void *) (M.mem_base + addr), val);
380         }
381 }
382 void
383 my_wrl(u32 addr, u32 val)
384 {
385         unsigned long translated_addr = addr;
386         u8 translated = biosemu_dev_translate_address(&translated_addr);
387         if (translated != 0) {
388                 //translation successfull, access VGA Memory (BAR or Legacy...)
389                 DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n",
390                                  __func__, addr, val);
391                 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n",  __func__, addr, translated_addr);
392                 // check for legacy memory, because of the remapping to BARs, the reads must
393                 // be byte reads...
394                 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
395                         //read bytes a using my_rdb, because of the remapping to BARs
396                         //words may not be contiguous in memory, so we need to translate
397                         //every address...
398                         my_wrb(addr, (u8) (val & 0x000000FF));
399                         my_wrb(addr + 1, (u8) ((val & 0x0000FF00) >> 8));
400                         my_wrb(addr + 2, (u8) ((val & 0x00FF0000) >> 16));
401                         my_wrb(addr + 3, (u8) ((val & 0xFF000000) >> 24));
402                 } else {
403                         if ((translated_addr & (u64) 0x3) == 0) {
404                                 // 32 bit aligned access...
405                                 set_ci();
406                                 out32le((void *) translated_addr, val);
407                                 clr_ci();
408                         } else {
409                                 // unaligned access, write single bytes
410                                 set_ci();
411                                 *((u8 *) translated_addr) =
412                                     (u8) (val & 0x000000FF);
413                                 *((u8 *) translated_addr + 1) =
414                                     (u8) ((val & 0x0000FF00) >> 8);
415                                 *((u8 *) translated_addr + 2) =
416                                     (u8) ((val & 0x00FF0000) >> 16);
417                                 *((u8 *) translated_addr + 3) =
418                                     (u8) ((val & 0xFF000000) >> 24);
419                                 clr_ci();
420                         }
421                 }
422         } else if (addr > M.mem_size) {
423                 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
424                              __func__, addr);
425                 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
426                 HALT_SYS();
427         } else {
428                 /* write to virtual memory */
429                 DEBUG_CHECK_VMEM_WRITE(addr, val);
430                 out32le((void *) (M.mem_base + addr), val);
431         }
432 }
433 #else
434 u8
435 my_rdb(u32 addr)
436 {
437         return rdb(addr);
438 }
439
440 u16
441 my_rdw(u32 addr)
442 {
443         return rdw(addr);
444 }
445
446 u32
447 my_rdl(u32 addr)
448 {
449         return rdl(addr);
450 }
451
452 void
453 my_wrb(u32 addr, u8 val)
454 {
455         wrb(addr, val);
456 }
457
458 void
459 my_wrw(u32 addr, u16 val)
460 {
461         wrw(addr, val);
462 }
463
464 void
465 my_wrl(u32 addr, u32 val)
466 {
467         wrl(addr, val);
468 }
469 #endif
470
471 //update time in BIOS Data Area
472 //DWord at offset 0x6c is the timer ticks since midnight, timer is running at 18Hz
473 //byte at 0x70 is timer overflow (set if midnight passed since last call to interrupt 1a function 00
474 //cur_val is the current value, of offset 6c...
475 void
476 update_time(u32 cur_val)
477 {
478         //for convenience, we let the start of timebase be at midnight, we currently dont support
479         //real daytime anyway...
480         u64 ticks_per_day = tb_freq * 60 * 24;
481         // at 18Hz a period is ~55ms, converted to ticks (tb_freq is ticks/second)
482         u32 period_ticks = (55 * tb_freq) / 1000;
483         u64 curr_time = get_time();
484         u64 ticks_since_midnight = curr_time % ticks_per_day;
485         u32 periods_since_midnight = ticks_since_midnight / period_ticks;
486         // if periods since midnight is smaller than last value, set overflow
487         // at BDA Offset 0x70
488         if (periods_since_midnight < cur_val) {
489                 my_wrb(0x470, 1);
490         }
491         // store periods since midnight at BDA offset 0x6c
492         my_wrl(0x46c, periods_since_midnight);
493 }