/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * Copyright (c) 2009 Pattrick Hueper * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include #include "debug.h" #include "device.h" #include "x86emu/x86emu.h" #include "biosemu.h" #include "mem.h" #include "compat/time.h" #include #if !CONFIG(YABEL_DIRECTHW) || !CONFIG(YABEL_DIRECTHW) // define a check for access to certain (virtual) memory regions (interrupt handlers, BIOS Data Area, ...) #if CONFIG(X86EMU_DEBUG) static u8 in_check = 0; // to avoid recursion... static inline void DEBUG_CHECK_VMEM_READ(u32 _addr, u32 _rval) { u16 ebda_segment; u32 ebda_size; if (!((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0))) return; in_check = 1; /* determine ebda_segment and size * since we are using my_rdx calls, make sure, this is after setting in_check! */ /* offset 03 in BDA is EBDA segment */ ebda_segment = my_rdw(0x40e); /* first value in ebda is size in KB */ ebda_size = my_rdb(ebda_segment << 4) * 1024; /* check Interrupt Vector Access (0000:0000h - 0000:0400h) */ if (_addr < 0x400) { DEBUG_PRINTF_CS_IP("%s: read from Interrupt Vector %x --> %x\n", __func__, _addr / 4, _rval); } /* access to BIOS Data Area (0000:0400h - 0000:0500h)*/ else if ((_addr >= 0x400) && (_addr < 0x500)) { DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Area: addr: %x --> %x\n", __func__, _addr, _rval); /* dump registers */ /* x86emu_dump_xregs(); */ } /* access to first 64k of memory... */ else if (_addr < 0x10000) { DEBUG_PRINTF_CS_IP("%s: read from segment 0000h: addr: %x --> %x\n", __func__, _addr, _rval); /* dump registers */ /* x86emu_dump_xregs(); */ } /* read from PMM_CONV_SEGMENT */ else if ((_addr <= ((PMM_CONV_SEGMENT << 4) | 0xffff)) && (_addr >= (PMM_CONV_SEGMENT << 4))) { DEBUG_PRINTF_CS_IP("%s: read from PMM Segment %04xh: addr: %x --> %x\n", __func__, PMM_CONV_SEGMENT, _addr, _rval); /* HALT_SYS(); */ /* dump registers */ /* x86emu_dump_xregs(); */ } /* read from PNP_DATA_SEGMENT */ else if ((_addr <= ((PNP_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (PNP_DATA_SEGMENT << 4))) { DEBUG_PRINTF_CS_IP("%s: read from PnP Data Segment %04xh: addr: %x --> %x\n", __func__, PNP_DATA_SEGMENT, _addr, _rval); /* HALT_SYS(); */ /* dump registers */ /* x86emu_dump_xregs(); */ } /* read from EBDA Segment */ else if ((_addr <= ((ebda_segment << 4) | (ebda_size - 1))) && (_addr >= (ebda_segment << 4))) { DEBUG_PRINTF_CS_IP("%s: read from Extended BIOS Data Area %04xh, size: %04x: addr: %x --> %x\n", __func__, ebda_segment, ebda_size, _addr, _rval); } /* read from BIOS_DATA_SEGMENT */ else if ((_addr <= ((BIOS_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (BIOS_DATA_SEGMENT << 4))) { DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Segment %04xh: addr: %x --> %x\n", __func__, BIOS_DATA_SEGMENT, _addr, _rval); /* for PMM debugging */ /*if (_addr == BIOS_DATA_SEGMENT << 4) { X86EMU_trace_on(); M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; }*/ /* dump registers */ /* x86emu_dump_xregs(); */ } in_check = 0; } static inline void DEBUG_CHECK_VMEM_WRITE(u32 _addr, u32 _val) { u16 ebda_segment; u32 ebda_size; if (!((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0))) return; in_check = 1; /* determine ebda_segment and size * since we are using my_rdx calls, make sure that this is after * setting in_check! */ /* offset 03 in BDA is EBDA segment */ ebda_segment = my_rdw(0x40e); /* first value in ebda is size in KB */ ebda_size = my_rdb(ebda_segment << 4) * 1024; /* check Interrupt Vector Access (0000:0000h - 0000:0400h) */ if (_addr < 0x400) { DEBUG_PRINTF_CS_IP("%s: write to Interrupt Vector %x <-- %x\n", __func__, _addr / 4, _val); } /* access to BIOS Data Area (0000:0400h - 0000:0500h)*/ else if ((_addr >= 0x400) && (_addr < 0x500)) { DEBUG_PRINTF_CS_IP("%s: write to BIOS Data Area: addr: %x <-- %x\n", __func__, _addr, _val); /* dump registers */ /* x86emu_dump_xregs(); */ } /* access to first 64k of memory...*/ else if (_addr < 0x10000) { DEBUG_PRINTF_CS_IP("%s: write to segment 0000h: addr: %x <-- %x\n", __func__, _addr, _val); /* dump registers */ /* x86emu_dump_xregs(); */ } /* write to PMM_CONV_SEGMENT... */ else if ((_addr <= ((PMM_CONV_SEGMENT << 4) | 0xffff)) && (_addr >= (PMM_CONV_SEGMENT << 4))) { DEBUG_PRINTF_CS_IP("%s: write to PMM Segment %04xh: addr: %x <-- %x\n", __func__, PMM_CONV_SEGMENT, _addr, _val); /* dump registers */ /* x86emu_dump_xregs(); */ } /* write to PNP_DATA_SEGMENT... */ else if ((_addr <= ((PNP_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (PNP_DATA_SEGMENT << 4))) { DEBUG_PRINTF_CS_IP("%s: write to PnP Data Segment %04xh: addr: %x <-- %x\n", __func__, PNP_DATA_SEGMENT, _addr, _val); /* dump registers */ /* x86emu_dump_xregs(); */ } /* write to EBDA Segment... */ else if ((_addr <= ((ebda_segment << 4) | (ebda_size - 1))) && (_addr >= (ebda_segment << 4))) { DEBUG_PRINTF_CS_IP("%s: write to Extended BIOS Data Area %04xh, size: %04x: addr: %x <-- %x\n", __func__, ebda_segment, ebda_size, _addr, _val); } /* write to BIOS_DATA_SEGMENT... */ else if ((_addr <= ((BIOS_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (BIOS_DATA_SEGMENT << 4))) { DEBUG_PRINTF_CS_IP("%s: write to BIOS Data Segment %04xh: addr: %x <-- %x\n", __func__, BIOS_DATA_SEGMENT, _addr, _val); /* dump registers */ /* x86emu_dump_xregs(); */ } /* write to current CS segment... */ else if ((_addr < ((M.x86.R_CS << 4) | 0xffff)) && (_addr > (M.x86.R_CS << 4))) { DEBUG_PRINTF_CS_IP("%s: write to CS segment %04xh: addr: %x <-- %x\n", __func__, M.x86.R_CS, _addr, _val); /* dump registers */ /* x86emu_dump_xregs(); */ } in_check = 0; } #else static inline void DEBUG_CHECK_VMEM_READ(u32 _addr, u32 _rval) {}; static inline void DEBUG_CHECK_VMEM_WRITE(u32 _addr, u32 _val) {}; #endif //update time in BIOS Data Area //DWord at offset 0x6c is the timer ticks since midnight, timer is running at 18Hz //byte at 0x70 is timer overflow (set if midnight passed since last call to interrupt 1a function 00 //cur_val is the current value, of offset 6c... static void update_time(u32 cur_val) { //for convenience, we let the start of timebase be at midnight, we currently don't support //real daytime anyway... u64 ticks_per_day = tb_freq * 60 * 24; // at 18Hz a period is ~55ms, converted to ticks (tb_freq is ticks/second) u32 period_ticks = (55 * tb_freq) / 1000; u64 curr_time = get_time(); u64 ticks_since_midnight = curr_time % ticks_per_day; u32 periods_since_midnight = ticks_since_midnight / period_ticks; // if periods since midnight is smaller than last value, set overflow // at BDA Offset 0x70 if (periods_since_midnight < cur_val) { my_wrb(0x470, 1); } // store periods since midnight at BDA offset 0x6c my_wrl(0x46c, periods_since_midnight); } // read byte from memory u8 my_rdb(u32 addr) { unsigned long translated_addr = addr; u8 translated = biosemu_dev_translate_address(IORESOURCE_MEM, &translated_addr); u8 rval; if (translated != 0) { //translation successful, access VGA Memory (BAR or Legacy...) DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n", __func__, addr); //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr); set_ci(); rval = *((u8 *) translated_addr); clr_ci(); DEBUG_PRINTF_MEM("%s(%08x) VGA --> %02x\n", __func__, addr, rval); return rval; } else if (addr > M.mem_size) { DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", __func__, addr); //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); HALT_SYS(); } else { /* read from virtual memory */ rval = *((u8 *) (M.mem_base + addr)); DEBUG_CHECK_VMEM_READ(addr, rval); return rval; } return -1; } //read word from memory u16 my_rdw(u32 addr) { unsigned long translated_addr = addr; u8 translated = biosemu_dev_translate_address(IORESOURCE_MEM, &translated_addr); u16 rval; if (translated != 0) { //translation successful, access VGA Memory (BAR or Legacy...) DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n", __func__, addr); //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr); // check for legacy memory, because of the remapping to BARs, the reads must // be byte reads... if ((addr >= 0xa0000) && (addr < 0xc0000)) { //read bytes a using my_rdb, because of the remapping to BARs //words may not be contiguous in memory, so we need to translate //every address... rval = ((u8) my_rdb(addr)) | (((u8) my_rdb(addr + 1)) << 8); } else { if ((translated_addr & (u64) 0x1) == 0) { // 16 bit aligned access... set_ci(); rval = in16le((void *) translated_addr); clr_ci(); } else { // unaligned access, read single bytes set_ci(); rval = (*((u8 *) translated_addr)) | (*((u8 *) translated_addr + 1) << 8); clr_ci(); } } DEBUG_PRINTF_MEM("%s(%08x) VGA --> %04x\n", __func__, addr, rval); return rval; } else if (addr > M.mem_size) { DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", __func__, addr); //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); HALT_SYS(); } else { /* read from virtual memory */ rval = in16le((void *) (M.mem_base + addr)); DEBUG_CHECK_VMEM_READ(addr, rval); return rval; } return -1; } //read long from memory u32 my_rdl(u32 addr) { unsigned long translated_addr = addr; u8 translated = biosemu_dev_translate_address(IORESOURCE_MEM, &translated_addr); u32 rval; if (translated != 0) { //translation successful, access VGA Memory (BAR or Legacy...) DEBUG_PRINTF_MEM("%s(%x): access to VGA Memory\n", __func__, addr); //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr); // check for legacy memory, because of the remapping to BARs, the reads must // be byte reads... if ((addr >= 0xa0000) && (addr < 0xc0000)) { //read bytes a using my_rdb, because of the remapping to BARs //dwords may not be contiguous in memory, so we need to translate //every address... rval = ((u8) my_rdb(addr)) | (((u8) my_rdb(addr + 1)) << 8) | (((u8) my_rdb(addr + 2)) << 16) | (((u8) my_rdb(addr + 3)) << 24); } else { if ((translated_addr & (u64) 0x3) == 0) { // 32 bit aligned access... set_ci(); rval = in32le((void *) translated_addr); clr_ci(); } else { // unaligned access, read single bytes set_ci(); rval = (*((u8 *) translated_addr)) | (*((u8 *) translated_addr + 1) << 8) | (*((u8 *) translated_addr + 2) << 16) | (*((u8 *) translated_addr + 3) << 24); clr_ci(); } } DEBUG_PRINTF_MEM("%s(%08x) VGA --> %08x\n", __func__, addr, rval); //HALT_SYS(); return rval; } else if (addr > M.mem_size) { DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", __func__, addr); //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); HALT_SYS(); } else { /* read from virtual memory */ rval = in32le((void *) (M.mem_base + addr)); switch (addr) { case 0x46c: //BDA Time Data, update it, before reading update_time(rval); rval = in32le((void *) (M.mem_base + addr)); break; } DEBUG_CHECK_VMEM_READ(addr, rval); return rval; } return -1; } //write byte to memory void my_wrb(u32 addr, u8 val) { unsigned long translated_addr = addr; u8 translated = biosemu_dev_translate_address(IORESOURCE_MEM, &translated_addr); if (translated != 0) { //translation successful, access VGA Memory (BAR or Legacy...) DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n", __func__, addr, val); //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr); set_ci(); *((u8 *) translated_addr) = val; clr_ci(); } else if (addr > M.mem_size) { DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", __func__, addr); //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); HALT_SYS(); } else { /* write to virtual memory */ DEBUG_CHECK_VMEM_WRITE(addr, val); *((u8 *) (M.mem_base + addr)) = val; } } void my_wrw(u32 addr, u16 val) { unsigned long translated_addr = addr; u8 translated = biosemu_dev_translate_address(IORESOURCE_MEM, &translated_addr); if (translated != 0) { //translation successful, access VGA Memory (BAR or Legacy...) DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n", __func__, addr, val); //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr); // check for legacy memory, because of the remapping to BARs, the reads must // be byte reads... if ((addr >= 0xa0000) && (addr < 0xc0000)) { //read bytes a using my_rdb, because of the remapping to BARs //words may not be contiguous in memory, so we need to translate //every address... my_wrb(addr, (u8) (val & 0x00FF)); my_wrb(addr + 1, (u8) ((val & 0xFF00) >> 8)); } else { if ((translated_addr & (u64) 0x1) == 0) { // 16 bit aligned access... set_ci(); out16le((void *) translated_addr, val); clr_ci(); } else { // unaligned access, write single bytes set_ci(); *((u8 *) translated_addr) = (u8) (val & 0x00FF); *((u8 *) translated_addr + 1) = (u8) ((val & 0xFF00) >> 8); clr_ci(); } } } else if (addr > M.mem_size) { DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", __func__, addr); //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); HALT_SYS(); } else { /* write to virtual memory */ DEBUG_CHECK_VMEM_WRITE(addr, val); out16le((void *) (M.mem_base + addr), val); } } void my_wrl(u32 addr, u32 val) { unsigned long translated_addr = addr; u8 translated = biosemu_dev_translate_address(IORESOURCE_MEM, &translated_addr); if (translated != 0) { //translation successful, access VGA Memory (BAR or Legacy...) DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n", __func__, addr, val); //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr); // check for legacy memory, because of the remapping to BARs, the reads must // be byte reads... if ((addr >= 0xa0000) && (addr < 0xc0000)) { //read bytes a using my_rdb, because of the remapping to BARs //words may not be contiguous in memory, so we need to translate //every address... my_wrb(addr, (u8) (val & 0x000000FF)); my_wrb(addr + 1, (u8) ((val & 0x0000FF00) >> 8)); my_wrb(addr + 2, (u8) ((val & 0x00FF0000) >> 16)); my_wrb(addr + 3, (u8) ((val & 0xFF000000) >> 24)); } else { if ((translated_addr & (u64) 0x3) == 0) { // 32 bit aligned access... set_ci(); out32le((void *) translated_addr, val); clr_ci(); } else { // unaligned access, write single bytes set_ci(); *((u8 *) translated_addr) = (u8) (val & 0x000000FF); *((u8 *) translated_addr + 1) = (u8) ((val & 0x0000FF00) >> 8); *((u8 *) translated_addr + 2) = (u8) ((val & 0x00FF0000) >> 16); *((u8 *) translated_addr + 3) = (u8) ((val & 0xFF000000) >> 24); clr_ci(); } } } else if (addr > M.mem_size) { DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", __func__, addr); //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); HALT_SYS(); } else { /* write to virtual memory */ DEBUG_CHECK_VMEM_WRITE(addr, val); out32le((void *) (M.mem_base + addr), val); } } #else u8 my_rdb(u32 addr) { return rdb(addr); } u16 my_rdw(u32 addr) { return rdw(addr); } u32 my_rdl(u32 addr) { return rdl(addr); } void my_wrb(u32 addr, u8 val) { wrb(addr, val); } void my_wrw(u32 addr, u16 val) { wrw(addr, val); } void my_wrl(u32 addr, u32 val) { wrl(addr, val); } #endif