Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / SLOF / clients / net-snk / app / biosemu / mem.c
1 /******************************************************************************
2  * Copyright (c) 2004, 2008 IBM Corporation
3  * All rights reserved.
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
8  *
9  * Contributors:
10  *     IBM Corporation - initial implementation
11  *****************************************************************************/
12
13 #include <stdio.h>
14 #include <stdint.h>
15 #include <cpu.h>
16 #include "debug.h"
17 #include "device.h"
18 #include "x86emu/x86emu.h"
19 #include "biosemu.h"
20 #include <time.h>
21 #include "mem.h"
22
23 // define a check for access to certain (virtual) memory regions (interrupt handlers, BIOS Data Area, ...)
24 #ifdef DEBUG
25 static uint8_t in_check = 0;    // to avoid recursion...
26 uint16_t ebda_segment;
27 uint32_t ebda_size;
28
29 //TODO: these macros have grown so large, that they should be changed to an inline function,
30 //just for the sake of readability...
31
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);
36
37 #define DEBUG_CHECK_VMEM_READ(_addr, _rval) \
38    if ((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)) { \
39          in_check = 1; \
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); \
50                         } \
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(); */ \
57                         } \
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(); */ \
64                         } \
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); \
69                                 /* HALT_SYS(); */ \
70                                 /* dump registers */ \
71                                 /* x86emu_dump_xregs(); */ \
72                         } \
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); \
77                                 /* HALT_SYS(); */ \
78                                 /* dump registers */ \
79                                 /* x86emu_dump_xregs(); */ \
80                         } \
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); \
85                         } \
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) { \
92                                         X86EMU_trace_on(); \
93                                         M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; \
94                                 }*/ \
95                                 /* dump registers */ \
96                                 /* x86emu_dump_xregs(); */ \
97                         } \
98          in_check = 0; \
99    }
100 #define DEBUG_CHECK_VMEM_WRITE(_addr, _val) \
101    if ((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)) { \
102          in_check = 1; \
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); \
113                         } \
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(); */ \
120                         } \
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(); */ \
127                         } \
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(); */ \
134                         } \
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(); */ \
141                         } \
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); \
146                         } \
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(); */ \
153                         } \
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(); */ \
160                         } \
161          in_check = 0; \
162    }
163 #else
164 #define DEBUG_CHECK_VMEM_READ(_addr, _rval)
165 #define DEBUG_CHECK_VMEM_WRITE(_addr, _val)
166 #endif
167
168 //defined in net-snk/kernel/timer.c
169 extern uint64_t get_time(void);
170
171 void update_time(uint32_t);
172
173 // read byte from memory
174 uint8_t
175 my_rdb(uint32_t addr)
176 {
177         uint64_t translated_addr = addr;
178         uint8_t translated = dev_translate_address(&translated_addr);
179         uint8_t rval;
180         if (translated != 0) {
181                 //translation successful, access VGA Memory (BAR or Legacy...)
182                 DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n",
183                                  __FUNCTION__, addr);
184                 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr);
185                 set_ci();
186                 rval = *((uint8_t *) translated_addr);
187                 clr_ci();
188                 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %02x\n", __FUNCTION__, addr,
189                                  rval);
190                 return rval;
191         } else if (addr > M.mem_size) {
192                 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
193                              __FUNCTION__, addr);
194                 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
195                 HALT_SYS();
196         } else {
197                 /* read from virtual memory */
198                 rval = *((uint8_t *) (M.mem_base + addr));
199                 DEBUG_CHECK_VMEM_READ(addr, rval);
200                 return rval;
201         }
202         return -1;
203 }
204
205 //read word from memory
206 uint16_t
207 my_rdw(uint32_t addr)
208 {
209         uint64_t translated_addr = addr;
210         uint8_t translated = dev_translate_address(&translated_addr);
211         uint16_t rval;
212         if (translated != 0) {
213                 //translation successful, access VGA Memory (BAR or Legacy...)
214                 DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n",
215                                  __FUNCTION__, addr);
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
218                 // be byte reads...
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
222                         //every address...
223                         rval = ((uint8_t) my_rdb(addr)) |
224                             (((uint8_t) my_rdb(addr + 1)) << 8);
225                 } else {
226                         if ((translated_addr & (uint64_t) 0x1) == 0) {
227                                 // 16 bit aligned access...
228                                 set_ci();
229                                 rval = in16le((void *) translated_addr);
230                                 clr_ci();
231                         } else {
232                                 // unaligned access, read single bytes
233                                 set_ci();
234                                 rval = (*((uint8_t *) translated_addr)) |
235                                     (*((uint8_t *) translated_addr + 1) << 8);
236                                 clr_ci();
237                         }
238                 }
239                 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %04x\n", __FUNCTION__, addr,
240                                  rval);
241                 return rval;
242         } else if (addr > M.mem_size) {
243                 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
244                              __FUNCTION__, addr);
245                 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
246                 HALT_SYS();
247         } else {
248                 /* read from virtual memory */
249                 rval = in16le((void *) (M.mem_base + addr));
250                 DEBUG_CHECK_VMEM_READ(addr, rval);
251                 return rval;
252         }
253         return -1;
254 }
255
256 //read long from memory
257 uint32_t
258 my_rdl(uint32_t addr)
259 {
260         uint64_t translated_addr = addr;
261         uint8_t translated = dev_translate_address(&translated_addr);
262         uint32_t rval;
263         if (translated != 0) {
264                 //translation successful, access VGA Memory (BAR or Legacy...)
265                 DEBUG_PRINTF_MEM("%s(%x): access to VGA Memory\n",
266                                  __FUNCTION__, addr);
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
269                 // be byte reads...
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
273                         //every address...
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);
278                 } else {
279                         if ((translated_addr & (uint64_t) 0x3) == 0) {
280                                 // 32 bit aligned access...
281                                 set_ci();
282                                 rval = in32le((void *) translated_addr);
283                                 clr_ci();
284                         } else {
285                                 // unaligned access, read single bytes
286                                 set_ci();
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);
291                                 clr_ci();
292                         }
293                 }
294                 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %08x\n", __FUNCTION__, addr,
295                                  rval);
296                 //HALT_SYS();
297                 return rval;
298         } else if (addr > M.mem_size) {
299                 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
300                              __FUNCTION__, addr);
301                 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
302                 HALT_SYS();
303         } else {
304                 /* read from virtual memory */
305                 rval = in32le((void *) (M.mem_base + addr));
306                 switch (addr) {
307                 case 0x46c:
308                         //BDA Time Data, update it, before reading
309                         update_time(rval);
310                         rval = in32le((void *) (M.mem_base + addr));
311                         break;
312                 }
313                 DEBUG_CHECK_VMEM_READ(addr, rval);
314                 return rval;
315         }
316         return -1;
317 }
318
319 //write byte to memory
320 void
321 my_wrb(uint32_t addr, uint8_t val)
322 {
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);
330                 set_ci();
331                 *((uint8_t *) translated_addr) = val;
332                 clr_ci();
333         } else if (addr > M.mem_size) {
334                 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
335                              __FUNCTION__, addr);
336                 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
337                 HALT_SYS();
338         } else {
339                 /* write to virtual memory */
340                 DEBUG_CHECK_VMEM_WRITE(addr, val);
341                 *((uint8_t *) (M.mem_base + addr)) = val;
342         }
343 }
344
345 void
346 my_wrw(uint32_t addr, uint16_t val)
347 {
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
356                 // be byte reads...
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
360                         //every address...
361                         my_wrb(addr, (uint8_t) (val & 0x00FF));
362                         my_wrb(addr + 1, (uint8_t) ((val & 0xFF00) >> 8));
363                 } else {
364                         if ((translated_addr & (uint64_t) 0x1) == 0) {
365                                 // 16 bit aligned access...
366                                 set_ci();
367                                 out16le((void *) translated_addr, val);
368                                 clr_ci();
369                         } else {
370                                 // unaligned access, write single bytes
371                                 set_ci();
372                                 *((uint8_t *) translated_addr) =
373                                     (uint8_t) (val & 0x00FF);
374                                 *((uint8_t *) translated_addr + 1) =
375                                     (uint8_t) ((val & 0xFF00) >> 8);
376                                 clr_ci();
377                         }
378                 }
379         } else if (addr > M.mem_size) {
380                 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
381                              __FUNCTION__, addr);
382                 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
383                 HALT_SYS();
384         } else {
385                 /* write to virtual memory */
386                 DEBUG_CHECK_VMEM_WRITE(addr, val);
387                 out16le((void *) (M.mem_base + addr), val);
388         }
389 }
390 void
391 my_wrl(uint32_t addr, uint32_t val)
392 {
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
401                 // be byte reads...
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
405                         //every address...
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));
410                 } else {
411                         if ((translated_addr & (uint64_t) 0x3) == 0) {
412                                 // 32 bit aligned access...
413                                 set_ci();
414                                 out32le((void *) translated_addr, val);
415                                 clr_ci();
416                         } else {
417                                 // unaligned access, write single bytes
418                                 set_ci();
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);
427                                 clr_ci();
428                         }
429                 }
430         } else if (addr > M.mem_size) {
431                 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
432                              __FUNCTION__, addr);
433                 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
434                 HALT_SYS();
435         } else {
436                 /* write to virtual memory */
437                 DEBUG_CHECK_VMEM_WRITE(addr, val);
438                 out32le((void *) (M.mem_base + addr), val);
439         }
440 }
441
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...
446 void
447 update_time(uint32_t cur_val)
448 {
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) {
460                 my_wrb(0x470, 1);
461         }
462         // store periods since midnight at BDA offset 0x6c
463         my_wrl(0x46c, periods_since_midnight);
464 }