91dc3da3f9b50a8780cba0c0d971bc166f8ce947
[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 #ifndef CONFIG_COREBOOT_V2
16 #include <cpu.h>
17 #endif
18 #include "debug.h"
19 #include "device.h"
20 #include "x86emu/x86emu.h"
21 #include "biosemu.h"
22 #ifdef CONFIG_COREBOOT_V2
23 #include "compat/time.h"
24 #else
25 #include <time.h>
26 #endif
27
28 // define a check for access to certain (virtual) memory regions (interrupt handlers, BIOS Data Area, ...)
29 #ifdef CONFIG_DEBUG
30 static u8 in_check = 0; // to avoid recursion...
31 u16 ebda_segment;
32 u32 ebda_size;
33
34 //TODO: these macros have grown so large, that they should be changed to an inline function,
35 //just for the sake of readability...
36
37 //declare prototypes of the functions to follow, for use in DEBUG_CHECK_VMEM_ACCESS
38 u8 my_rdb(u32);
39 u16 my_rdw(u32);
40 u32 my_rdl(u32);
41
42 #define DEBUG_CHECK_VMEM_READ(_addr, _rval) \
43    if ((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)) { \
44          in_check = 1; \
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); \
55                         } \
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(); */ \
62                         } \
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(); */ \
69                         } \
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); \
74                                 /* HALT_SYS(); */ \
75                                 /* dump registers */ \
76                                 /* x86emu_dump_xregs(); */ \
77                         } \
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); \
82                                 /* HALT_SYS(); */ \
83                                 /* dump registers */ \
84                                 /* x86emu_dump_xregs(); */ \
85                         } \
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); \
90                         } \
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) { \
97                                         X86EMU_trace_on(); \
98                                         M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; \
99                                 }*/ \
100                                 /* dump registers */ \
101                                 /* x86emu_dump_xregs(); */ \
102                         } \
103          in_check = 0; \
104    }
105 #define DEBUG_CHECK_VMEM_WRITE(_addr, _val) \
106    if ((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)) { \
107          in_check = 1; \
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); \
118                         } \
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(); */ \
125                         } \
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(); */ \
132                         } \
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(); */ \
139                         } \
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(); */ \
146                         } \
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); \
151                         } \
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(); */ \
158                         } \
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(); */ \
165                         } \
166          in_check = 0; \
167    }
168 #else
169 #define DEBUG_CHECK_VMEM_READ(_addr, _rval)
170 #define DEBUG_CHECK_VMEM_WRITE(_addr, _val)
171 #endif
172
173 //defined in net-snk/kernel/timer.c
174 extern u64 get_time(void);
175
176 void update_time(u32);
177
178 // read byte from memory
179 u8
180 my_rdb(u32 addr)
181 {
182         unsigned long translated_addr = addr;
183         u8 translated = biosemu_dev_translate_address(&translated_addr);
184         u8 rval;
185         if (translated != 0) {
186                 //translation successfull, access VGA Memory (BAR or Legacy...)
187                 DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n",
188                                  __func__, addr);
189                 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
190                 set_ci();
191                 rval = *((u8 *) translated_addr);
192                 clr_ci();
193                 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %02x\n", __func__, addr,
194                                  rval);
195                 return rval;
196         } else if (addr > M.mem_size) {
197                 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
198                              __func__, addr);
199                 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
200                 HALT_SYS();
201         } else {
202                 /* read from virtual memory */
203                 rval = *((u8 *) (M.mem_base + addr));
204                 DEBUG_CHECK_VMEM_READ(addr, rval);
205                 return rval;
206         }
207         return -1;
208 }
209
210 //read word from memory
211 u16
212 my_rdw(u32 addr)
213 {
214         unsigned long translated_addr = addr;
215         u8 translated = biosemu_dev_translate_address(&translated_addr);
216         u16 rval;
217         if (translated != 0) {
218                 //translation successfull, access VGA Memory (BAR or Legacy...)
219                 DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n",
220                                  __func__, addr);
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
223                 // be byte reads...
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
227                         //every address...
228                         rval = ((u8) my_rdb(addr)) |
229                             (((u8) my_rdb(addr + 1)) << 8);
230                 } else {
231                         if ((translated_addr & (u64) 0x1) == 0) {
232                                 // 16 bit aligned access...
233                                 set_ci();
234                                 rval = in16le((void *) translated_addr);
235                                 clr_ci();
236                         } else {
237                                 // unaligned access, read single bytes
238                                 set_ci();
239                                 rval = (*((u8 *) translated_addr)) |
240                                     (*((u8 *) translated_addr + 1) << 8);
241                                 clr_ci();
242                         }
243                 }
244                 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %04x\n", __func__, addr,
245                                  rval);
246                 return rval;
247         } else if (addr > M.mem_size) {
248                 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
249                              __func__, addr);
250                 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
251                 HALT_SYS();
252         } else {
253                 /* read from virtual memory */
254                 rval = in16le((void *) (M.mem_base + addr));
255                 DEBUG_CHECK_VMEM_READ(addr, rval);
256                 return rval;
257         }
258         return -1;
259 }
260
261 //read long from memory
262 u32
263 my_rdl(u32 addr)
264 {
265         unsigned long translated_addr = addr;
266         u8 translated = biosemu_dev_translate_address(&translated_addr);
267         u32 rval;
268         if (translated != 0) {
269                 //translation successfull, access VGA Memory (BAR or Legacy...)
270                 DEBUG_PRINTF_MEM("%s(%x): access to VGA Memory\n",
271                                  __func__, addr);
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
274                 // be byte reads...
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
278                         //every address...
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);
283                 } else {
284                         if ((translated_addr & (u64) 0x3) == 0) {
285                                 // 32 bit aligned access...
286                                 set_ci();
287                                 rval = in32le((void *) translated_addr);
288                                 clr_ci();
289                         } else {
290                                 // unaligned access, read single bytes
291                                 set_ci();
292                                 rval = (*((u8 *) translated_addr)) |
293                                     (*((u8 *) translated_addr + 1) << 8) |
294                                     (*((u8 *) translated_addr + 2) << 16) |
295                                     (*((u8 *) translated_addr + 3) << 24);
296                                 clr_ci();
297                         }
298                 }
299                 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %08x\n", __func__, addr,
300                                  rval);
301                 //HALT_SYS();
302                 return rval;
303         } else if (addr > M.mem_size) {
304                 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
305                              __func__, addr);
306                 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
307                 HALT_SYS();
308         } else {
309                 /* read from virtual memory */
310                 rval = in32le((void *) (M.mem_base + addr));
311                 switch (addr) {
312                 case 0x46c:
313                         //BDA Time Data, update it, before reading
314                         update_time(rval);
315                         rval = in32le((void *) (M.mem_base + addr));
316                         break;
317                 }
318                 DEBUG_CHECK_VMEM_READ(addr, rval);
319                 return rval;
320         }
321         return -1;
322 }
323
324 //write byte to memory
325 void
326 my_wrb(u32 addr, u8 val)
327 {
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);
335                 set_ci();
336                 *((u8 *) translated_addr) = val;
337                 clr_ci();
338         } else if (addr > M.mem_size) {
339                 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
340                              __func__, addr);
341                 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
342                 HALT_SYS();
343         } else {
344                 /* write to virtual memory */
345                 DEBUG_CHECK_VMEM_WRITE(addr, val);
346                 *((u8 *) (M.mem_base + addr)) = val;
347         }
348 }
349
350 void
351 my_wrw(u32 addr, u16 val)
352 {
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
361                 // be byte reads...
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
365                         //every address...
366                         my_wrb(addr, (u8) (val & 0x00FF));
367                         my_wrb(addr + 1, (u8) ((val & 0xFF00) >> 8));
368                 } else {
369                         if ((translated_addr & (u64) 0x1) == 0) {
370                                 // 16 bit aligned access...
371                                 set_ci();
372                                 out16le((void *) translated_addr, val);
373                                 clr_ci();
374                         } else {
375                                 // unaligned access, write single bytes
376                                 set_ci();
377                                 *((u8 *) translated_addr) =
378                                     (u8) (val & 0x00FF);
379                                 *((u8 *) translated_addr + 1) =
380                                     (u8) ((val & 0xFF00) >> 8);
381                                 clr_ci();
382                         }
383                 }
384         } else if (addr > M.mem_size) {
385                 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
386                              __func__, addr);
387                 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
388                 HALT_SYS();
389         } else {
390                 /* write to virtual memory */
391                 DEBUG_CHECK_VMEM_WRITE(addr, val);
392                 out16le((void *) (M.mem_base + addr), val);
393         }
394 }
395 void
396 my_wrl(u32 addr, u32 val)
397 {
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
406                 // be byte reads...
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
410                         //every address...
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));
415                 } else {
416                         if ((translated_addr & (u64) 0x3) == 0) {
417                                 // 32 bit aligned access...
418                                 set_ci();
419                                 out32le((void *) translated_addr, val);
420                                 clr_ci();
421                         } else {
422                                 // unaligned access, write single bytes
423                                 set_ci();
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);
432                                 clr_ci();
433                         }
434                 }
435         } else if (addr > M.mem_size) {
436                 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
437                              __func__, addr);
438                 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
439                 HALT_SYS();
440         } else {
441                 /* write to virtual memory */
442                 DEBUG_CHECK_VMEM_WRITE(addr, val);
443                 out32le((void *) (M.mem_base + addr), val);
444         }
445 }
446
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...
451 void
452 update_time(u32 cur_val)
453 {
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) {
465                 my_wrb(0x470, 1);
466         }
467         // store periods since midnight at BDA offset 0x6c
468         my_wrl(0x46c, periods_since_midnight);
469 }