coreboot-v2/util/x86emu/yabel/mem.c
<<
>>
Prefs
   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
  30static u8 in_check = 0; // to avoid recursion...
  31u16 ebda_segment;
  32u32 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
  38u8 my_rdb(u32);
  39u16 my_rdw(u32);
  40u32 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
 174extern u64 get_time(void);
 175
 176void update_time(u32);
 177
 178// read byte from memory
 179u8
 180my_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
 211u16
 212my_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
 262u32
 263my_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
 325void
 326my_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
 350void
 351my_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}
 395void
 396my_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...
 451void
 452update_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}
 470
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.