f9bd2b74be
2. Fixed bug [ 989478 ] I-Cache and undefined Instruktions The L4 microkernel uses an undefined instruction to trap for a special requests into the kernel (LOCK NOP). The handler fixes this up and gives the user a special code page with syscall stubs. If you're not using the I-Cache optimization everthing works find on bochs. But if you enable the I-Cache (--enable-icache), then the undefined opcode exception is thrown only once for ever virtual address it occurs. See the demodisk of the L4KA::pistachio (http://www.l4ka.org/projects/pistachio/download.php). In this case the pingpong benchmark of this demo is of interest. Everything runs fine until the program tries to spawn a new task for its measurements. This new task shares the code of the creating program. But the new task stops executing at the undefined instruction explained above and no exception is thrown.
466 lines
13 KiB
C++
466 lines
13 KiB
C++
/////////////////////////////////////////////////////////////////////////
|
|
// $Id: misc_mem.cc,v 1.45 2004-07-29 20:15:19 sshwarts Exp $
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2002 MandrakeSoft S.A.
|
|
//
|
|
// MandrakeSoft S.A.
|
|
// 43, rue d'Aboukir
|
|
// 75002 Paris - France
|
|
// http://www.linux-mandrake.com/
|
|
// http://www.mandrakesoft.com/
|
|
//
|
|
// I/O memory handlers API Copyright (C) 2003 by Frank Cornelis
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 2 of the License, or (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
|
|
|
|
|
#include "iodev/iodev.h"
|
|
#define LOG_THIS BX_MEM(0)->
|
|
|
|
|
|
|
|
#if BX_PROVIDE_CPU_MEMORY
|
|
Bit32u BX_MEM_C::get_memory_in_k(void)
|
|
{
|
|
return(BX_MEM_THIS megabytes * 1024);
|
|
}
|
|
#endif // #if BX_PROVIDE_CPU_MEMORY
|
|
|
|
|
|
#if BX_PROVIDE_CPU_MEMORY
|
|
// BX_MEM_C constructor
|
|
BX_MEM_C::BX_MEM_C(void)
|
|
{
|
|
char mem[6];
|
|
snprintf(mem, 6, "MEM%d", BX_SIM_ID);
|
|
put(mem);
|
|
settype(MEMLOG);
|
|
|
|
vector = NULL;
|
|
actual_vector = NULL;
|
|
len = 0;
|
|
megabytes = 0;
|
|
|
|
memory_handlers = NULL;
|
|
}
|
|
#endif // #if BX_PROVIDE_CPU_MEMORY
|
|
|
|
|
|
|
|
#if BX_PROVIDE_CPU_MEMORY
|
|
void BX_CPP_AttrRegparmN(2)
|
|
BX_MEM_C::alloc_vector_aligned (size_t bytes, size_t alignment)
|
|
{
|
|
if (actual_vector != NULL) {
|
|
BX_INFO (("freeing existing memory vector"));
|
|
delete [] actual_vector;
|
|
actual_vector = NULL;
|
|
vector = NULL;
|
|
}
|
|
Bit64u test_mask = alignment - 1;
|
|
actual_vector = new Bit8u [bytes+test_mask];
|
|
// round address forward to nearest multiple of alignment. Alignment
|
|
// MUST BE a power of two for this to work.
|
|
Bit64u masked = ((Bit64u)(actual_vector + test_mask)) & ~test_mask;
|
|
vector = (Bit8u *)masked;
|
|
// sanity check: no lost bits during pointer conversion
|
|
BX_ASSERT (sizeof(masked) >= sizeof(vector));
|
|
// sanity check: after realignment, everything fits in allocated space
|
|
BX_ASSERT (vector+bytes <= actual_vector+bytes+test_mask);
|
|
BX_INFO (("allocated memory at %p. after alignment, vector=%p",
|
|
actual_vector, vector));
|
|
}
|
|
#endif
|
|
|
|
// We can't use this because alloc_vector_aligned uses BX_INFO, but the object does not yet exists
|
|
/*
|
|
#if BX_PROVIDE_CPU_MEMORY
|
|
// BX_MEM_C constructor
|
|
|
|
BX_MEM_C::BX_MEM_C(size_t memsize)
|
|
{
|
|
char mem[6];
|
|
snprintf(mem, 6, "MEM%d", BX_SIM_ID);
|
|
put(mem);
|
|
settype(MEMLOG);
|
|
|
|
vector = NULL;
|
|
actual_vector = NULL;
|
|
alloc_vector_aligned (memsize, BX_MEM_VECTOR_ALIGN);
|
|
len = memsize;
|
|
megabytes = len / (1024*1024);
|
|
}
|
|
#endif // #if BX_PROVIDE_CPU_MEMORY
|
|
*/
|
|
|
|
|
|
#if BX_PROVIDE_CPU_MEMORY
|
|
// BX_MEM_C destructor
|
|
BX_MEM_C::~BX_MEM_C(void)
|
|
{
|
|
if (this-> vector != NULL) {
|
|
delete [] actual_vector;
|
|
actual_vector = NULL;
|
|
vector = NULL;
|
|
delete [] memory_handlers;
|
|
memory_handlers = NULL;
|
|
}
|
|
else {
|
|
BX_DEBUG(("(%u) memory not freed as it wasn't allocated!", BX_SIM_ID));
|
|
}
|
|
}
|
|
#endif // #if BX_PROVIDE_CPU_MEMORY
|
|
|
|
|
|
#if BX_PROVIDE_CPU_MEMORY
|
|
void
|
|
BX_MEM_C::init_memory(int memsize)
|
|
{
|
|
BX_DEBUG(("Init $Id: misc_mem.cc,v 1.45 2004-07-29 20:15:19 sshwarts Exp $"));
|
|
// you can pass 0 if memory has been allocated already through
|
|
// the constructor, or the desired size of memory if it hasn't
|
|
// BX_INFO(("%.2fMB", (float)(BX_MEM_THIS megabytes) ));
|
|
|
|
if (BX_MEM_THIS vector == NULL) {
|
|
// memory not already allocated, do now...
|
|
alloc_vector_aligned (memsize, BX_MEM_VECTOR_ALIGN);
|
|
BX_MEM_THIS len = memsize;
|
|
BX_MEM_THIS megabytes = memsize / (1024*1024);
|
|
BX_MEM_THIS memory_handlers = new struct memory_handler_struct *[1024 * 1024];
|
|
for (int idx = 0; idx < 1024 * 1024; idx++)
|
|
BX_MEM_THIS memory_handlers[idx] = NULL;
|
|
BX_INFO(("%.2fMB", (float)(BX_MEM_THIS megabytes) ));
|
|
}
|
|
|
|
#if BX_DEBUGGER
|
|
if (megabytes > BX_MAX_DIRTY_PAGE_TABLE_MEGS) {
|
|
BX_INFO(("Error: memory larger than dirty page table can handle"));
|
|
BX_PANIC(("Error: increase BX_MAX_DIRTY_PAGE_TABLE_MEGS"));
|
|
}
|
|
#endif
|
|
|
|
}
|
|
#endif // #if BX_PROVIDE_CPU_MEMORY
|
|
|
|
|
|
#if BX_PROVIDE_CPU_MEMORY
|
|
void
|
|
// Values for type :
|
|
// 0 : System Bios
|
|
// 1 : VGA Bios
|
|
// 2 : Optional ROM Bios
|
|
BX_MEM_C::load_ROM(const char *path, Bit32u romaddress, Bit8u type)
|
|
{
|
|
struct stat stat_buf;
|
|
int fd, ret;
|
|
unsigned long size, offset;
|
|
|
|
if (*path == '\0') {
|
|
if (type == 2) {
|
|
BX_PANIC(( "ROM: Optional BIOS image undefined."));
|
|
}
|
|
else if (type == 1) {
|
|
BX_PANIC(( "ROM: VGA BIOS image undefined."));
|
|
}
|
|
else {
|
|
BX_PANIC(( "ROM: System BIOS image undefined."));
|
|
}
|
|
return;
|
|
}
|
|
// read in ROM BIOS image file
|
|
fd = open(path, O_RDONLY
|
|
#ifdef O_BINARY
|
|
| O_BINARY
|
|
#endif
|
|
);
|
|
if (fd < 0) {
|
|
if (type < 2) {
|
|
BX_PANIC(( "ROM: couldn't open ROM image file '%s'.", path));
|
|
}
|
|
else {
|
|
BX_ERROR(( "ROM: couldn't open ROM image file '%s'.", path));
|
|
}
|
|
return;
|
|
}
|
|
ret = fstat(fd, &stat_buf);
|
|
if (ret) {
|
|
if (type < 2) {
|
|
BX_PANIC(( "ROM: couldn't stat ROM image file '%s'.", path));
|
|
}
|
|
else {
|
|
BX_ERROR(( "ROM: couldn't stat ROM image file '%s'.", path));
|
|
}
|
|
return;
|
|
}
|
|
|
|
size = stat_buf.st_size;
|
|
|
|
if ( (romaddress + size) > BX_MEM_THIS len ) {
|
|
BX_PANIC(( "ROM: ROM address range > physical memsize!"));
|
|
return;
|
|
}
|
|
|
|
offset = 0;
|
|
while (size > 0) {
|
|
ret = read(fd, (bx_ptr_t) &BX_MEM_THIS vector[romaddress + offset], size);
|
|
if (ret <= 0) {
|
|
BX_PANIC(( "ROM: read failed on BIOS image: '%s'",path));
|
|
}
|
|
size -= ret;
|
|
offset += ret;
|
|
}
|
|
close(fd);
|
|
BX_INFO(("rom at 0x%05x/%u ('%s')",
|
|
(unsigned) romaddress,
|
|
(unsigned) stat_buf.st_size,
|
|
path
|
|
));
|
|
}
|
|
#endif // #if BX_PROVIDE_CPU_MEMORY
|
|
|
|
#if BX_PCI_SUPPORT
|
|
Bit8u* BX_CPP_AttrRegparmN(1)
|
|
BX_MEM_C::pci_fetch_ptr(Bit32u addr)
|
|
{
|
|
if (bx_options.Oi440FXSupport->get ()) {
|
|
switch (DEV_pci_rd_memtype (addr)) {
|
|
case 0x1: // Read from ShadowRAM
|
|
return (&BX_MEM_THIS shadow[addr - 0xc0000]);
|
|
|
|
case 0x0: // Read from ROM
|
|
return (&BX_MEM_THIS vector[addr]);
|
|
default:
|
|
BX_PANIC(("pci_fetch_ptr(): default case"));
|
|
return(0);
|
|
}
|
|
}
|
|
else
|
|
return (&BX_MEM_THIS vector[addr]);
|
|
}
|
|
#endif
|
|
|
|
|
|
#if ( BX_DEBUGGER || BX_DISASM || BX_GDBSTUB)
|
|
bx_bool
|
|
BX_MEM_C::dbg_fetch_mem(Bit32u addr, unsigned len, Bit8u *buf)
|
|
{
|
|
if ( (addr + len) > this->len ) {
|
|
BX_INFO(("dbg_fetch_mem out of range. 0x%x > 0x%x",
|
|
addr+len, this->len));
|
|
return(0); // error, beyond limits of memory
|
|
}
|
|
for (; len>0; len--) {
|
|
if ( (addr & 0xfffe0000) == 0x000a0000 ) {
|
|
*buf = DEV_vga_mem_read(addr);
|
|
}
|
|
else {
|
|
#if BX_PCI_SUPPORT == 0
|
|
*buf = vector[addr];
|
|
#else
|
|
if ( bx_options.Oi440FXSupport->get () &&
|
|
((addr >= 0x000C0000) && (addr <= 0x000FFFFF)) ) {
|
|
switch (DEV_pci_rd_memtype (addr)) {
|
|
case 0x1: // Fetch from ShadowRAM
|
|
*buf = shadow[addr - 0xc0000];
|
|
// BX_INFO(("Fetching from ShadowRAM %06x, len %u !", (unsigned)addr, (unsigned)len));
|
|
break;
|
|
|
|
case 0x0: // Fetch from ROM
|
|
*buf = vector[addr];
|
|
// BX_INFO(("Fetching from ROM %06x, Data %02x ", (unsigned)addr, *buf));
|
|
break;
|
|
default:
|
|
BX_PANIC(("dbg_fetch_mem: default case"));
|
|
}
|
|
}
|
|
else
|
|
*buf = vector[addr];
|
|
#endif // #if BX_PCI_SUPPORT == 0
|
|
}
|
|
buf++;
|
|
addr++;
|
|
}
|
|
return(1);
|
|
}
|
|
#endif
|
|
|
|
#if BX_DEBUGGER || BX_GDBSTUB
|
|
bx_bool
|
|
BX_MEM_C::dbg_set_mem(Bit32u addr, unsigned len, Bit8u *buf)
|
|
{
|
|
if ( (addr + len) > this->len ) {
|
|
return(0); // error, beyond limits of memory
|
|
}
|
|
for (; len>0; len--) {
|
|
if ( (addr & 0xfffe0000) == 0x000a0000 ) {
|
|
DEV_vga_mem_write(addr, *buf);
|
|
}
|
|
else
|
|
vector[addr] = *buf;
|
|
buf++;
|
|
addr++;
|
|
}
|
|
return(1);
|
|
}
|
|
#endif
|
|
|
|
bx_bool
|
|
BX_MEM_C::dbg_crc32(unsigned long (*f)(unsigned char *buf, int len),
|
|
Bit32u addr1, Bit32u addr2, Bit32u *crc)
|
|
{
|
|
unsigned len;
|
|
|
|
*crc = 0;
|
|
if (addr1 > addr2)
|
|
return(0);
|
|
|
|
if (addr2 >= this->len) {
|
|
return(0); // error, specified address past last phy mem addr
|
|
}
|
|
|
|
len = 1 + addr2 - addr1;
|
|
*crc = f(vector + addr1, len);
|
|
|
|
return(1);
|
|
}
|
|
|
|
|
|
Bit8u * BX_CPP_AttrRegparmN(3)
|
|
BX_MEM_C::getHostMemAddr(BX_CPU_C *cpu, Bit32u a20Addr, unsigned op)
|
|
// Return a host address corresponding to the guest physical memory
|
|
// address (with A20 already applied), given that the calling
|
|
// code will perform an 'op' operation. This address will be
|
|
// used for direct access to guest memory as an acceleration by
|
|
// a few instructions, like REP {MOV, INS, OUTS, etc}.
|
|
// Values of 'op' are { BX_READ, BX_WRITE, BX_RW }.
|
|
|
|
// The other assumption is that the calling code _only_ accesses memory
|
|
// directly within the page that encompasses the address requested.
|
|
{
|
|
if ( a20Addr >= BX_MEM_THIS len )
|
|
return(NULL); // Error, requested addr is out of bounds.
|
|
if (op == BX_READ) {
|
|
if ( (a20Addr > 0x9ffff) && (a20Addr < 0xc0000) )
|
|
return(NULL); // Vetoed! Mem mapped IO (VGA)
|
|
#if !BX_PCI_SUPPORT
|
|
return( (Bit8u *) & vector[a20Addr] );
|
|
#else
|
|
else if ( (a20Addr < 0xa0000) || (a20Addr > 0xfffff)
|
|
|| (!bx_options.Oi440FXSupport->get ()) )
|
|
return( (Bit8u *) & vector[a20Addr] );
|
|
else {
|
|
switch (DEV_pci_rd_memtype (a20Addr)) {
|
|
case 0x0: // Read from ROM
|
|
return ( (Bit8u *) & vector[a20Addr]);
|
|
case 0x1: // Read from ShadowRAM
|
|
return( (Bit8u *) & shadow[a20Addr - 0xc0000]);
|
|
default:
|
|
BX_PANIC(("getHostMemAddr(): default case"));
|
|
return(0);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else { // op == {BX_WRITE, BX_RW}
|
|
Bit8u *retAddr;
|
|
|
|
if ( (a20Addr < 0xa0000) || (a20Addr > 0xfffff) ) {
|
|
retAddr = (Bit8u *) & vector[a20Addr];
|
|
}
|
|
#if !BX_PCI_SUPPORT
|
|
else
|
|
return(NULL); // Vetoed! Mem mapped IO (VGA) and ROMs
|
|
#else
|
|
else if ( (a20Addr < 0xc0000) || (!bx_options.Oi440FXSupport->get ()) )
|
|
return(NULL); // Vetoed! Mem mapped IO (VGA) and ROMs
|
|
else if (DEV_pci_wr_memtype (a20Addr) == 1) {
|
|
// Write to ShadowRAM
|
|
retAddr = (Bit8u *) & shadow[a20Addr - 0xc0000];
|
|
}
|
|
else
|
|
return(NULL); // Vetoed! ROMs
|
|
#endif
|
|
|
|
#if BX_SUPPORT_ICACHE
|
|
cpu->iCache.decWriteStamp(cpu, a20Addr);
|
|
#endif
|
|
|
|
return(retAddr);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* One needs to provide both a read_handler and a write_handler.
|
|
* XXX: maybe we should check for overlapping memory handlers
|
|
*/
|
|
bx_bool
|
|
BX_MEM_C::registerMemoryHandlers(memory_handler_t read_handler, void *read_param,
|
|
memory_handler_t write_handler, void *write_param,
|
|
unsigned long begin_addr, unsigned long end_addr)
|
|
{
|
|
if (end_addr < begin_addr)
|
|
return false;
|
|
if (!read_handler)
|
|
return false;
|
|
if (!write_handler)
|
|
return false;
|
|
for (unsigned page_idx = begin_addr >> 20; page_idx <= end_addr >> 20; page_idx++) {
|
|
struct memory_handler_struct *memory_handler = new struct memory_handler_struct;
|
|
memory_handler->next = memory_handlers[page_idx];
|
|
memory_handlers[page_idx] = memory_handler;
|
|
memory_handler->read_handler = read_handler;
|
|
memory_handler->write_handler = write_handler;
|
|
memory_handler->read_param = read_param;
|
|
memory_handler->write_param = write_param;
|
|
memory_handler->begin = begin_addr;
|
|
memory_handler->end = end_addr;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bx_bool
|
|
BX_MEM_C::unregisterMemoryHandlers(memory_handler_t read_handler, memory_handler_t write_handler,
|
|
unsigned long begin_addr, unsigned long end_addr)
|
|
{
|
|
bx_bool ret = true;
|
|
for (unsigned page_idx = begin_addr >> 20; page_idx <= end_addr >> 20; page_idx++) {
|
|
struct memory_handler_struct *memory_handler = memory_handlers[page_idx];
|
|
struct memory_handler_struct *prev = NULL;
|
|
while (memory_handler &&
|
|
memory_handler->read_handler != read_handler &&
|
|
memory_handler->write_handler != write_handler &&
|
|
memory_handler->begin != begin_addr &&
|
|
memory_handler->end != end_addr) {
|
|
prev = memory_handler;
|
|
memory_handler = memory_handler->next;
|
|
}
|
|
if (!memory_handler) {
|
|
ret = false; // we should have found it
|
|
continue; // anyway, try the other pages
|
|
}
|
|
if (prev)
|
|
prev->next = memory_handler->next;
|
|
else
|
|
memory_handlers[page_idx] = memory_handler->next;
|
|
delete memory_handler;
|
|
}
|
|
return ret;
|
|
}
|