Initial Import

This commit is contained in:
Tristan Miller 2012-01-18 00:28:58 -07:00
commit 164818292a
99 changed files with 7385 additions and 0 deletions

3
README Normal file
View file

@ -0,0 +1,3 @@
This is an LLE Xbox emulator targeting Linux with KVM. Very hacky currently and with little support for system software. Currently completes Phase 0 Initialization in MSBIOS and goes to etherboot on Cromwell.
This assumes that you have a 512 byte copy of the 1.0 MCPX secret ROM as the file /opt/xbvm/mcpx.bin and your 1MiB copy of your BIOS in /opt/xbvm/tsop.bin

62
src/CMakeLists.txt Normal file
View file

@ -0,0 +1,62 @@
CMAKE_MINIMUM_REQUIRED( VERSION 2.8 )
PROJECT( xbvm )
FIND_PACKAGE( Threads REQUIRED )
FIND_PACKAGE( SDL REQUIRED )
ADD_DEFINITIONS( -Werror -Wextra -Wall )
INCLUDE_DIRECTORIES( ${xbvm_SOURCE_DIR} )
ADD_EXECUTABLE( xbvm audio/Ac97Codec
audio/Apu
bridges/AgpBridge
bridges/DramController
bridges/HostBridge
bridges/IsaBridge
graphics/BusControlEngine
graphics/CrtcEngine
graphics/Engine
graphics/MasterControlEngine
graphics/MemoryInterfaceEngine
graphics/Nv2a
graphics/RamDacEngine
graphics/StrapsEngine
graphics/UnknownEngine
graphics/VgaEmulationEngine
graphics/VgaSequencerEngine
graphics/VideoOverlayEngine
ide/DvdDrive
ide/FlatDiskImage
ide/HardDrive
ide/IdeController
ide/IdeDevice
interp/InterpreterCpu
interp/InterpreterCpu_sys
kvm/KvmCpu
net/NetworkAdapter
smbus/ConexantEncoder
smbus/PIC16L
smbus/SerialEeprom
smbus/SmBusController
smbus/SmBusDevice
smbus/TemperatureMonitor
superio/Cmos
superio/DmaController
superio/Pic
superio/Pit
superio/SuperIoController
usb/OhciController
Cpu
HostDevice
Main
Mem
PciBus
PciDevice
Sdl
Xbox )
TARGET_LINK_LIBRARIES( xbvm ${CMAKE_THREAD_LIBS_INIT}
${SDL_LIBRARY} )

63
src/Cpu.cpp Normal file
View file

@ -0,0 +1,63 @@
#include "Cpu.h"
#include "Log.h"
Cpu *g_cpu;
//TODO: Verify EDX
const u64 Cpu::m_initValues[NUM_CPU_REGS] =
{
0x00000010, 0x00000000, 0x00000000, 0x00000633, // EAX EBX ECX EDX
0x00000000, 0x00000000, 0x00000000, 0x00000000, // ESP EBP ESI EDI
0xFFFFFE0A, 0x00000046, 0x60000011, 0x00000000, // EIP EFLAGS CR0 CR2
0x00000000, 0x00000000, 0x00000000, 0xFEE00000, // CR3 CR4 EFER APICBASE
0x00000000, 0x00000000, 0x00000000, 0x00000000, // MM0 MM1 MM2 MM3
0x00000000, 0x00000000, 0x00000000, 0x00000000, // MM4 MM5 MM6 MM7
0x0008, 0x00000000, 0xFFFFFFFF, 0x00C09B00, // SELEC BASE LIMIT FLAGS {CS}
0x0010, 0x00000000, 0xFFFFFFFF, 0x00C09300, // SELEC BASE LIMIT FLAGS {DS}
0x0010, 0x00000000, 0xFFFFFFFF, 0x00C09300, // SELEC BASE LIMIT FLAGS {ES}
0x0000, 0x00000000, 0x0000FFFF, 0x00009300, // SELEC BASE LIMIT FLAGS {FS}
0x0000, 0x00000000, 0x0000FFFF, 0x00009300, // SELEC BASE LIMIT FLAGS {GS}
0x0010, 0x00000000, 0xFFFFFFFF, 0x00C09300, // SELEC BASE LIMIT FLAGS {SS}
0x0000, 0x00000000, 0x0000FFFF, 0x00008200, // SELEC BASE LIMIT FLAGS {LDT}
0x0000, 0x00000000, 0x0000FFFF, 0x00008B00, // SELEC BASE LIMIT FLAGS {TR}
0xFFFFFFD8, 0x0018, 0xFFFFFFD8, 0x0018, // GDTB GDTL IDTB IDTL
0x037F, 0x0000, 0x00, 0x00001F80, // FCW FSW FTW MXCSR
0x00000000, 0x00000000, 0x00000000, 0x00000000, // DR0 DR1 DR2 DR3
0xFFFF0FF0, 0x00000400 // DR6 DR7
};
Cpu::Cpu()
{
}
Cpu::~Cpu()
{
}
u64
Cpu::GetInitialValue(CpuRegisterEnum reg)
{
if( reg >= NUM_CPU_REGS )
return 0;
return m_initValues[reg];
}
int
Cpu::GetInitialFlag( CpuRegisterEnum flagReg, CpuSegmentFlagEnum flagNum )
{
u64 flags = GetInitialValue( flagReg );
switch( flagNum ) {
case CPU_SEGMENT_FLAG_PRESENT:
return (flags >> 15) & 1;
case CPU_SEGMENT_FLAG_PRIVILEGE:
return (flags >> 13) & 3;
default:
EPRINT( "Unknown flag: %d", flagReg );
return 0;
}
}

113
src/Cpu.h Normal file
View file

@ -0,0 +1,113 @@
#ifndef XBVM_CPU_H
#define XBVM_CPU_H
#include "Types.h"
enum CpuRegisterEnum
{
CPU_EAX, CPU_EBX, CPU_ECX, CPU_EDX,
CPU_ESP, CPU_EBP, CPU_ESI, CPU_EDI,
CPU_EIP, CPU_EFLAGS, CPU_CR0, CPU_CR2,
CPU_CR3, CPU_CR4, CPU_EFER, CPU_APICBASE,
CPU_MM0, CPU_MM1, CPU_MM2, CPU_MM3,
CPU_MM4, CPU_MM5, CPU_MM6, CPU_MM7,
CPU_CSSELEC, CPU_CSBASE, CPU_CSLIMIT, CPU_CSFLAGS,
CPU_DSSELEC, CPU_DSBASE, CPU_DSLIMIT, CPU_DSFLAGS,
CPU_ESSELEC, CPU_ESBASE, CPU_ESLIMIT, CPU_ESFLAGS,
CPU_FSSELEC, CPU_FSBASE, CPU_FSLIMIT, CPU_FSFLAGS,
CPU_GSSELEC, CPU_GSBASE, CPU_GSLIMIT, CPU_GSFLAGS,
CPU_SSSELEC, CPU_SSBASE, CPU_SSLIMIT, CPU_SSFLAGS,
CPU_LDSELEC, CPU_LDBASE, CPU_LDLIMIT, CPU_LDFLAGS,
CPU_TRSELEC, CPU_TRBASE, CPU_TRLIMIT, CPU_TRFLAGS,
CPU_GDTB, CPU_GDTL, CPU_IDTB, CPU_IDTL,
CPU_FCW, CPU_FSW, CPU_FTW, CPU_MXCSR,
CPU_DR0, CPU_DR1, CPU_DR2, CPU_DR3,
CPU_DR6, CPU_DR7, NUM_CPU_REGS
};
enum CpuSegmentFlagEnum
{
CPU_SEGMENT_FLAG_PRESENT,
CPU_SEGMENT_FLAG_PRIVILEGE
};
struct CpuRegs
{
u32 eax;
u32 ebx;
u32 ecx;
u32 edx;
u32 esi;
u32 edi;
u32 esp;
u32 ebp;
u32 eip;
u32 eflags;
};
struct CpuSegment
{
u32 base;
u32 limit;
u32 selector;
u8 type;
u8 present;
u8 dpl;
u8 db;
u8 s;
u8 l;
u8 g;
u8 avl;
};
struct CpuDescriptorTable
{
u32 base;
u16 limit;
};
struct CpuSystemRegs
{
CpuSegment cs;
CpuSegment ds;
CpuSegment es;
CpuSegment fs;
CpuSegment gs;
CpuSegment ss;
CpuSegment tr;
CpuSegment ldt;
CpuDescriptorTable gdt;
CpuDescriptorTable idt;
u32 cr0;
u32 cr2;
u32 cr3;
u32 cr4;
};
class Cpu
{
public:
Cpu();
virtual ~Cpu();
virtual int Initialize( void ) = 0;
virtual int Run( void ) = 0;
virtual int SingleStep( void ) = 0;
virtual int PrintRegs( void ) = 0;
virtual u32 GetReg( CpuRegisterEnum reg ) = 0;
virtual bool GetRegs( CpuRegs &regs ) = 0;
virtual bool GetSystemRegs( CpuSystemRegs &sregs ) = 0;
static u64 GetInitialValue( CpuRegisterEnum reg );
static int GetInitialFlag( CpuRegisterEnum flagReg, CpuSegmentFlagEnum flag );
private:
const static u64 m_initValues[ NUM_CPU_REGS ];
const static char *m_regNames[ NUM_CPU_REGS ];
};
extern Cpu *g_cpu;
#endif /*XBVM_CPU_H*/

15
src/Device.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef XBVM_DEVICE_H
#define XBVM_DEVICE_H
#include "Types.h"
class Device
{
public:
Device() {}
virtual ~Device() {}
virtual const char* GetName( void ) = 0;
};
#endif /*XBVM_DEVICE_H*/

90
src/HostDevice.cpp Normal file
View file

@ -0,0 +1,90 @@
#include "HostDevice.h"
#include "Log.h"
bool
HostDevice::IoRead32( u16 port, u32 &data )
{
EPRINT( "Unknown I/O read u32 from %x on %s", port, GetName() );
data = 0xFFFFFFFF;
return false;
}
bool
HostDevice::IoRead16( u16 port, u16 &data )
{
EPRINT( "Unknown I/O read u16 from %x on %s", port, GetName() );
data = 0xFFFF;
return false;
}
bool
HostDevice::IoRead8( u16 port, u8 &data )
{
EPRINT( "Unknown I/O read u8 from %x on %s", port, GetName() );
data = 0xFF;
return false;
}
bool
HostDevice::IoWrite32( u16 port, u32 &data )
{
EPRINT( "Unknown I/O write u32 of %08x to %x on %s", data, port, GetName() );
return false;
}
bool
HostDevice::IoWrite16( u16 port, u16 &data )
{
EPRINT( "Unknown I/O write u16 of %04x to %x on %s", data, port, GetName() );
return false;
}
bool
HostDevice::IoWrite8( u16 port, u8 &data )
{
EPRINT( "Unknown I/O write u8 of %02x to %x on %s", data, port, GetName() );
return false;
}
bool
HostDevice::MmioWrite32( u32 addr, u32 data )
{
EPRINT( "Unknown MMIO write u32 of %08x to %08x on %s", data, addr, GetName() );
return false;
}
bool
HostDevice::MmioWrite16( u32 addr, u16 data )
{
EPRINT( "Unknown MMIO write u16 of %04x to %08x on %s", data, addr, GetName() );
return false;
}
bool
HostDevice::MmioWrite8( u32 addr, u8 data )
{
EPRINT( "Unknown MMIO write u8 of %02x to %08x on %s", data, addr, GetName() );
return false;
}
u32
HostDevice::MmioRead32( u32 addr )
{
EPRINT( "Unknown MMIO read u32 from %08x on %s", addr, GetName() );
return 0xFFFFFFFF;
}
u16
HostDevice::MmioRead16( u32 addr )
{
EPRINT( "Unknown MMIO read u16 from %08x on %s", addr, GetName() );
return 0xFFFF;
}
u8
HostDevice::MmioRead8( u32 addr )
{
EPRINT( "Unknown MMIO read u8 from %08x on %s", addr, GetName() );
return 0xFF;
}

30
src/HostDevice.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef XBVM_HOSTDEVICE_H
#define XBVM_HOSTDEVICE_H
#include "Device.h"
class HostDevice : public Device
{
public:
HostDevice() {}
virtual ~HostDevice() {}
virtual bool IoRead32( u16 port, u32 &data );
virtual bool IoRead16( u16 port, u16 &data );
virtual bool IoRead8( u16 port, u8 &data );
virtual bool IoWrite32( u16 port, u32 &data );
virtual bool IoWrite16( u16 port, u16 &data );
virtual bool IoWrite8( u16 port, u8 &data );
virtual bool MmioWrite32( u32 addr, u32 data );
virtual bool MmioWrite16( u32 addr, u16 data );
virtual bool MmioWrite8( u32 addr, u8 data );
virtual u32 MmioRead32( u32 addr );
virtual u16 MmioRead16( u32 addr );
virtual u8 MmioRead8( u32 addr );
};
#endif /*XBMV_HOSTDEVICE_H*/

20
src/Log.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef XBVM_LOG_H
#define XBVM_LOG_H
#include <cstdio>
#define DPRINT(format,...) \
do { \
std::printf( format, ##__VA_ARGS__ ); \
std::printf( "\n" ); \
} while(0)
#define EPRINT(format,...) \
do { \
std::fprintf( stderr, "ERROR: " ); \
std::fprintf( stderr, format, ##__VA_ARGS__ ); \
std::fprintf( stderr, "\n" ); \
} while(0)
#endif /*XBVM_LOG_H*/

273
src/Main.cpp Normal file
View file

@ -0,0 +1,273 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "audio/Ac97Codec.h"
#include "audio/Apu.h"
#include "bridges/AgpBridge.h"
#include "bridges/DramController.h"
#include "bridges/HostBridge.h"
#include "bridges/IsaBridge.h"
#include "graphics/Nv2a.h"
#include "ide/IdeController.h"
#include "interp/InterpreterCpu.h"
#include "kvm/KvmCpu.h"
#include "net/NetworkAdapter.h"
#include "smbus/SmBusController.h"
#include "superio/Cmos.h"
#include "superio/DmaController.h"
#include "superio/Pic.h"
#include "superio/Pit.h"
#include "superio/SuperIoController.h"
#include "usb/OhciController.h"
#include "Cpu.h"
#include "Log.h"
#include "Mem.h"
#include "PciBus.h"
#include "Sdl.h"
char tokens [32][256];
void
CliPrint()
{
if( strcmp( tokens[1], "r" ) == 0 ) {
g_cpu->PrintRegs();
}
else if( strcmp( tokens[1], "pw" ) == 0 ) {
u32 addr = strtol( tokens[2], NULL, 16 );
u32 data = g_mem->ReadPhysU32( addr );
DPRINT( "%08x: %08x", addr, data );
}
else if( strcmp( tokens[1], "vw" ) == 0 ) {
CpuSystemRegs sregs;
u32 addr = strtol( tokens[2], NULL, 16 );
if( !g_cpu->GetSystemRegs( sregs ) ) {
EPRINT( "Can not get system regs" );
return;
}
u32 pdebase = sregs.cr3 & 0xFFFFF000;
u32 pde = g_mem->ReadPhysU32( pdebase + ((addr >> 22 ) << 2) );
if( (pde & 1) != 1 ) {
EPRINT( "PDE Not Present" );
return;
}
u32 pte = g_mem->ReadPhysU32( (pde & 0xFFFFF000) | (((addr >> 12) << 2) & 0x0000FFF) );
if( (pte & 1) != 1 ) {
EPRINT( "PTE Not Present" );
return;
}
u32 value = g_mem->ReadPhysU32( (pte & 0xFFFFF000) | (addr & 0x00000FFF) );
DPRINT( "%08x: %08x", addr, value );
return;
}
else if( strcmp( tokens[1], "sr" ) == 0 ) {
CpuSystemRegs sregs;
if( !g_cpu->GetSystemRegs( sregs ) ) {
EPRINT( "Can not get system regs" );
return;
}
DPRINT( "CR0: %08x CR2: %08x CR3: %08x CR4: %08x",
sregs.cr0, sregs.cr2, sregs.cr3, sregs.cr4 );
}
else if( strcmp( tokens[1], "sg" ) == 0 ) {
DPRINT( "~~SEGMENTS~~" );
}
else if( strcmp( tokens[1], "f" ) == 0 ) {
u32 value = strtol( tokens[2], NULL, 16 );
std::vector<u32> addresses = g_mem->FindAlignedDwords( value );
for( size_t i = 0; i < addresses.size(); i++ ) {
DPRINT( "%08x: %08x", addresses[i], value );
}
}
else if( strcmp( tokens[1], "pg" ) == 0 ) {
CpuSystemRegs sregs;
if( !g_cpu->GetSystemRegs( sregs ) ) {
EPRINT( "Can not get system regs" );
return;
}
u32 pdebase = sregs.cr3 & 0xFFFFF000;
for(int i = 0; i < 1024; i++) {
u32 pdeEntry = g_mem->ReadPhysU32( pdebase + (i * sizeof(u32)) );
if( pdeEntry & 1 ) {
DPRINT( "%08x:->%08x %s", i << 22, pdeEntry & 0xFFFFF000, (pdeEntry & 0x80) ? "4MiB" : "4KiB" );
}
}
}
else {
EPRINT( "Unknown p arg \"%s\"", tokens[1] );
}
}
void
CliDump()
{
if( strcmp ( tokens[1], "range" ) == 0 ) {
int base = atoi( tokens[2] );
int len = atoi( tokens[3] );
char *filename = tokens[4];
DPRINT( "dumping phys range %08x->%08x to file %s", base, base + len, filename );
}
else {
EPRINT( "Unknown d arg \"%s\"", tokens[1] );
}
}
void
CliRunUntil()
{
u32 breakpoint = strtol( tokens[1], NULL, 16 );
do {
g_cpu->SingleStep();
} while( g_cpu->GetReg( CPU_EIP ) != breakpoint );
}
int
RunCli( void )
{
char *line = NULL;
bool running = true;
size_t len = 0;
while( running )
{
printf( ">" );
if( getline( &line, &len, stdin ) == -1 )
break;
char* pch;
int numtokens = 0;
//printf( "Splitting string \"%s\" into tokens:\n", line );
//pch = strtok( line, " \n" );
//while( pch != NULL ) {
// printf( "\"%s\"\n", pch );
// pch = strtok( NULL, " \n" );
//}
pch = strtok( line, " \n" );
for( int i = 0; i < 32; i++ ) {
if( pch == NULL )
break;
strncpy( &tokens[i][0], pch, 256 );
numtokens++;
pch = strtok( NULL, " \n" );
}
if( strcmp( "quit", tokens[0] ) == 0 ) {
running = false;
}
else if( strcmp( "exit", tokens[0] ) == 0 ) {
running = false;
}
else if( strcmp( "r", tokens[0] ) == 0 ) {
g_cpu->Run();
}
else if( strcmp( "ru", tokens[0] ) == 0 ) {
CliRunUntil();
}
else if( strcmp( "s", tokens[0] ) == 0 ) {
g_cpu->SingleStep();
DPRINT( "%08x", g_cpu->GetReg( CPU_EIP ) );
}
else if( strcmp( "p", tokens[0] ) == 0 ) {
CliPrint();
}
else if( strcmp( "d", tokens[0] ) == 0 ) {
CliDump();
}
else {
EPRINT( "Unknown command \"%s\"", tokens[0] );
}
}
if( line )
free( line );
return 0;
}
int
main()
{
g_mem = new Mem();
if( !g_mem->Initialize() ) {
return -1;
}
g_superIo = new SuperIoController();
g_cmos = new Cmos();
g_pic = new Pic();
g_pit = new Pit();
g_dma0 = new DmaController( 0x00, false );
g_dma1 = new DmaController( 0xC0, true );
g_pciBus = new PciBus();
g_hostBridge = new HostBridge();
g_pciBus->AddPciDevice( g_hostBridge );
g_dramController = new DramController();
g_pciBus->AddPciDevice( g_dramController );
g_isaBridge = new IsaBridge();
g_pciBus->AddPciDevice( g_isaBridge );
g_smBus = new SmBusController();
g_pciBus->AddPciDevice( g_smBus );
g_net = new NetworkAdapter();
g_pciBus->AddPciDevice( g_net );
g_usb0 = new OhciController( 2 );
g_pciBus->AddPciDevice( g_usb0 );
g_usb1 = new OhciController( 3 );
g_pciBus->AddPciDevice( g_usb1 );
g_apu = new Apu();
g_pciBus->AddPciDevice( g_apu );
g_ac97 = new Ac97Codec();
g_pciBus->AddPciDevice( g_ac97 );
g_ide = new IdeController();
g_pciBus->AddPciDevice( g_ide );
g_agpBridge = new AgpBridge();
g_pciBus->AddPciDevice( g_agpBridge );
g_nv2a = new Nv2a();
g_pciBus->AddPciDevice( g_nv2a );
g_cpu = new KvmCpu();
if( g_cpu->Initialize() < 0 ) {
DPRINT( "Could not initialize Cpu" );
return -2;
}
StartSdlThread();
return RunCli();
}

311
src/Mem.cpp Normal file
View file

@ -0,0 +1,311 @@
#include <cstring>
#include <fcntl.h>
#include "Log.h"
#include "Mem.h"
//#include "PciBus.h"
Mem *g_mem;
const u32 Mem::RETAIL_RAM_SIZE = 64 * 1024 * 1024;
const u32 Mem::DEBUG_RAM_SIZE = 128 * 1024 * 1024;
const u32 Mem::ROM_PHYS_SIZE = 16 * 1024 * 1024;
const u32 Mem::MCPX_SIZE = 512;
const u32 Mem::TSOP_SIZE = 1024 * 1024;
const char *Mem::DEFAULT_MCPX_FILE_PATH = "/opt/xbvm/mcpx.bin";
const char *Mem::DEFAULT_TSOP_FILE_PATH = "/opt/xbvm/tsop.bin";
bool
Mem::Initialize( void )
{
const char *mcpxFilePath = DEFAULT_MCPX_FILE_PATH;
const char *tsopFilePath = DEFAULT_TSOP_FILE_PATH;
m_ramSize = DEBUG_RAM_SIZE;
if( !AllocateRam() ) {
EPRINT( "Unable to allocate RAM" );
return false;
}
if( !AllocateRom() ) {
EPRINT( "Unable to allocate ROM" );
return false;
}
if( !LoadMcpx( mcpxFilePath) ) {
EPRINT( "Unable to load MCPX ROM from: \"%s\"", mcpxFilePath );
return false;
}
if( !LoadTsop( tsopFilePath ) ) {
EPRINT( "Unable to load TSOP flash from: \"%s\"", tsopFilePath );
return false;
}
if( !LoadRom() ) {
EPRINT( "Unable to compile rom" );
return false;
}
return true;
}
u32
Mem::ReadPhysU32( u32 addr, bool isDebug )
{
if( isDebug )
return false;
switch( addr >> 24 )
{
// RAM
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
return *(u32*)(&((u8*)m_ram)[ addr ]);
// GPU RAM Mirror
case 0xF0:
case 0xF1:
case 0xF2:
case 0xF3:
case 0xF4:
case 0xF5:
case 0xF6:
case 0xF7:
return *(u32*)(&((u8*)m_ram)[ addr & 0x0FFFFFFF ]);
// ROM
case 0xFF:
return *(u32*)(&((u8*)m_rom)[ addr & 0x00FFFFFF ]);
default:
HostDevice *device = m_mmioPageMapping[ addr >> 12 ];
if( NULL == device ) {
EPRINT( "Read U32 From Unknown MMIO address %08x", addr );
return 0xFFFFFFFF;
}
return device->MmioRead32( addr );
}
return 0xFFFFFFFF;
}
u16
Mem::ReadPhysU16( u32 addr, bool isDebug )
{
if( isDebug )
return false;
switch( addr >> 24 ) {
// RAM
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
return *(u16*)(&((u8*)m_ram)[ addr ]);
// GPU RAM Mirror
case 0xF0:
case 0xF1:
case 0xF2:
case 0xF3:
case 0xF4:
case 0xF5:
case 0xF6:
case 0xF7:
return *(u16*)(&((u8*)m_ram)[ addr & 0x0FFFFFFF ]);
// ROM
case 0xFF:
return *(u16*)(&((u8*)m_rom)[ addr & 0x00FFFFFF ]);
default:
EPRINT( "Read u16 from unknown phys addr %08x", addr );
break;
}
return 0xFFFF;
}
u8
Mem::ReadPhysU8( u32 addr, bool isDebug )
{
if( isDebug )
return false;
switch( addr >> 24 ) {
// RAM
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
return ((u8*)m_ram)[ addr ];
// GPU RAM Mirror
case 0xF0:
case 0xF1:
case 0xF2:
case 0xF3:
case 0xF4:
case 0xF5:
case 0xF6:
case 0xF7:
return ((u8*)m_ram)[ addr ];
// ROM
case 0xFF:
return ((u8*)m_rom)[ addr & 0x00FFFFFF ];
default:
HostDevice *device = m_mmioPageMapping[ addr >> 12 ];
if( NULL == device ) {
EPRINT( "Read U8 From Unknown MMIO address %08x", addr );
return 0xFF;
}
return device->MmioRead8( addr );
}
return 0xFF;
}
bool
Mem::WritePhysU32( u32 addr, u32 data )
{
switch( addr >> 24 ) {
default:
HostDevice *device = m_mmioPageMapping[ addr >> 12 ];
if( NULL == device ) {
EPRINT( "Write of %08x to Unknown MMIO address %08x", data, addr );
return false;
}
return device->MmioWrite32( addr, data );
}
return false;
}
bool
Mem::WritePhysU16( u32 addr, u16 data )
{
EPRINT( "Unimplemented: U16 Write of %04x to %08x", data, addr );
return false;
}
bool
Mem::WritePhysU8( u32 addr, u8 data )
{
HostDevice *device = m_mmioPageMapping[ addr >> 12 ];
if( NULL == device ) {
EPRINT( "Write u8 of %02x to Unknown MMIO address %08x", data, addr );
return false;
}
return device->MmioWrite8( addr, data );
}
bool
Mem::DeassignMmioRange( u32 base, int size )
{
for( u32 i = base; i < (base + size); i += 0x1000 ) {
m_mmioPageMapping[ i >> 12 ] = NULL;
}
return true;
}
bool
Mem::AssignMmioRange( u32 base, int size, HostDevice *device )
{
for( u32 i = base; i < (base + size); i += 0x1000 ) {
m_mmioPageMapping[ i >> 12 ] = device;
}
return true;
}
std::vector<u32>
Mem::FindAlignedDwords( u32 value )
{
u32 *ram = reinterpret_cast<u32*>(m_ram);
std::vector<u32> addresses;
for( size_t i = 0; i < (m_ramSize / sizeof(u32)); i++ ) {
if( ram[i] == value ) {
addresses.push_back( i * sizeof(u32) );
}
}
return addresses;
}
bool
Mem::AllocateRam( void )
{
return ( posix_memalign( &m_ram, GetPageSize(), GetRamSize() ) == 0 );
}
bool
Mem::AllocateRom( void )
{
return ( posix_memalign( &m_rom, GetPageSize(), GetRomSize() ) == 0 );
}
bool
Mem::LoadMcpx( const char *path )
{
return LoadFile( path, m_mcpx, MEM_MCPX_SIZE );
}
bool
Mem::LoadTsop( const char *path )
{
return LoadFile( path, m_tsop, MEM_TSOP_SIZE );
}
bool
Mem::LoadRom( void )
{
u8 *rom = (u8*)m_rom;
for( int i = 0; i < 16; i++ )
std::memcpy( &rom[i * MEM_TSOP_SIZE], m_tsop, MEM_TSOP_SIZE );
std::memcpy( &rom[ROM_PHYS_SIZE - MEM_MCPX_SIZE], m_mcpx, MEM_MCPX_SIZE );
return true;
}
bool
Mem::LoadFile( const char *path, void *buffer, u32 bufferSize )
{
int ret = 0;
int fd = 0;
fd = open( path, O_RDONLY );
if( fd < 0 ) {
EPRINT( "Failed to open %s", path );
return false;
}
ret = read( fd, buffer, bufferSize );
if( ret < 0 ) {
EPRINT( "Failed to read %s", path );
return false;
}
close( fd );
return true;
}

168
src/Mem.h Normal file
View file

@ -0,0 +1,168 @@
#ifndef XBVM_MEM_H
#define XBVM_MEM_H
#include <cstring>
#include <vector>
#include <unistd.h>
#include "Log.h"
#include "HostDevice.h"
#include "Types.h"
#define MEM_MCPX_SIZE (512)
#define MEM_TSOP_SIZE (1024 * 1024)
#define MEM_NUM_IOPORTS (64 * 1024)
#define MEM_NUM_PHYS_PAGES ( 1024 * 1024 )
class PciDevice;
class Mem
{
public:
Mem() : m_ram(NULL), m_ramSize(0), m_rom(NULL), m_pageSize(0)
{
memset( m_ioPortMapping, 0, sizeof(m_ioPortMapping) );
}
bool Initialize( void );
u32 ReadPhysU32( u32 addr, bool isDebug = false );
u16 ReadPhysU16( u32 addr, bool isDebug = false );
u8 ReadPhysU8( u32 addr, bool isDebug = false );
bool WritePhysU32( u32 addr, u32 data );
bool WritePhysU16( u32 addr, u16 data );
bool WritePhysU8( u32 addr, u8 data );
bool ReadIo8( u16 port, u8 &data ) {
HostDevice *device = m_ioPortMapping[ port ];
if( NULL == device ) {
EPRINT( "Unknown u8 read from I/O port %x: %02x", port, data );
return false;
}
return device->IoRead8( port, data );
}
bool ReadIo16( u16 port, u16 &data ) {
HostDevice *device = m_ioPortMapping[ port ];
if( NULL == device ) {
EPRINT( "Unknown u16 read from I/O port %x: %02x", port, data );
return false;
}
return device->IoRead16( port, data );
}
bool ReadIo32( u16 port, u32 &data ) {
HostDevice *device = m_ioPortMapping[ port ];
if( NULL == device ) {
EPRINT( "Unknown u32 read from I/O port %x: %02x", port, data );
return false;
}
return device->IoRead32( port, data );
}
bool WriteIo8( u16 port, u8 data ) {
HostDevice *device = m_ioPortMapping[ port ];
if( NULL == device ) {
EPRINT( "Unknown u8 write to I/O port %x: %02x", port, data );
return false;
}
return device->IoWrite8( port, data );
}
bool WriteIo16( u16 port, u16 data ) {
HostDevice *device = m_ioPortMapping[ port ];
if( NULL == device ) {
EPRINT( "Unknown u16 write to I/O port %x: %02x", port, data );
return false;
}
return device->IoWrite16( port, data );
}
bool WriteIo32( u16 port, u32 data ) {
HostDevice *device = m_ioPortMapping[ port ];
if( NULL == device ) {
EPRINT( "Unknown u32 write to I/O port %x: %08x", port, data );
return false;
}
return device->IoWrite32( port, data );
}
void *GetRamAddr( void )
{
return m_ram;
}
void *GetRomAddr( void )
{
return m_rom;
}
u32 GetRamSize( void )
{
return m_ramSize;
}
u32 GetRomSize( void )
{
return ROM_PHYS_SIZE;
}
u32 GetPageSize( void )
{
if( 0 == m_pageSize ) {
m_pageSize = sysconf( _SC_PAGESIZE );
}
return m_pageSize;
}
bool
AddDeviceToIoMap( HostDevice *device, u16 port )
{
if( m_ioPortMapping[ port ] != NULL ) {
EPRINT( "Trying to add ioPortMapping to existing mapping: %Xh", port );
return false;
}
m_ioPortMapping[ port ] = device;
return true;
}
bool DeassignMmioRange( u32 base, int size );
bool AssignMmioRange( u32 base, int size, HostDevice *device );
std::vector<u32> FindAlignedDwords( u32 value );
private:
static const u32 RETAIL_RAM_SIZE;
static const u32 DEBUG_RAM_SIZE;
static const u32 ROM_PHYS_SIZE;
static const u32 TSOP_SIZE;
static const u32 MCPX_SIZE;
static const char *DEFAULT_MCPX_FILE_PATH;
static const char *DEFAULT_TSOP_FILE_PATH;
void* m_ram;
u32 m_ramSize;
void* m_rom;
u8 m_mcpx[MEM_MCPX_SIZE];
u8 m_tsop[MEM_TSOP_SIZE];
u32 m_pageSize;
HostDevice *m_ioPortMapping[ MEM_NUM_IOPORTS ];
HostDevice *m_mmioPageMapping[ MEM_NUM_PHYS_PAGES ];
bool AllocateRam( void );
bool AllocateRom( void );
bool LoadMcpx( const char *path );
bool LoadTsop( const char *path );
bool LoadRom( void );
bool LoadFile( const char *path, void *buffer, u32 bufferSize );
};
extern Mem *g_mem;
#endif /*XBVM_MEM_H*/

27
src/PciBus.cpp Normal file
View file

@ -0,0 +1,27 @@
#include <cstring>
#include "Mem.h"
#include "PciBus.h"
PciBus *g_pciBus;
PciBus::PciBus( void ) :
m_configAddress( 0 )
{
memset( m_bus0Devices, 0, sizeof(m_bus0Devices) );
memset( m_bus1Devices, 0, sizeof(m_bus1Devices) );
g_mem->AddDeviceToIoMap( this, 0xcf8 );
g_mem->AddDeviceToIoMap( this, 0xcf9 );
g_mem->AddDeviceToIoMap( this, 0xcfa );
g_mem->AddDeviceToIoMap( this, 0xcfb );
g_mem->AddDeviceToIoMap( this, 0xcfc );
g_mem->AddDeviceToIoMap( this, 0xcfd );
g_mem->AddDeviceToIoMap( this, 0xcfe );
g_mem->AddDeviceToIoMap( this, 0xcff );
}
PciBus::~PciBus( void )
{
}

235
src/PciBus.h Normal file
View file

@ -0,0 +1,235 @@
#ifndef XBVM_PCIBUS_H
#define XBVM_PCIBUS_H
#include "Log.h"
#include "HostDevice.h"
#include "PciDevice.h"
#include "Types.h"
#define PCI_BUS_CONFIG_ADDRESS_PORT (0xCF8)
#define PCI_BUS_CONFIG_DATA_PORT (0xCFC)
class PciBus : public HostDevice
{
public:
PciBus();
virtual ~PciBus();
virtual bool IoRead32( u16 port, u32 &data )
{
switch( port ) {
case PCI_BUS_CONFIG_DATA_PORT:
return ReadConfigData( data );
default:
return HostDevice::IoRead32( port, data );
}
}
virtual bool IoWrite32( u16 port, u32 &data )
{
switch( port ) {
case PCI_BUS_CONFIG_ADDRESS_PORT:
return WriteConfigAddress( data );
case PCI_BUS_CONFIG_DATA_PORT:
return WriteConfigData( data );
default:
return HostDevice::IoWrite32( port, data );
}
}
virtual bool IoRead8( u16 port, u8 &data )
{
int bus = ((m_configAddress >> 16) & 0xFF);
int dev = ((m_configAddress >> 11) & 0x1F);
int func = ((m_configAddress >> 8) & 0x7);
int reg = (m_configAddress & 0xFF) + (port - PCI_BUS_CONFIG_DATA_PORT);
int offset = (dev << 3) | func;
switch( port ) {
case PCI_BUS_CONFIG_DATA_PORT:
case PCI_BUS_CONFIG_DATA_PORT+1:
case PCI_BUS_CONFIG_DATA_PORT+2:
case PCI_BUS_CONFIG_DATA_PORT+3:
if( 0 == bus ) {
if( NULL == m_bus0Devices[offset] ) {
EPRINT( "Read %d:%d.%d[%d]", bus, dev, func, reg );
return false;
}
return m_bus0Devices[offset]->ReadConfigSpace8( reg, data );
}
else if( 1 == bus ) {
if( NULL == m_bus1Devices[offset] ) {
EPRINT( "Read %d:%d.%d[%d]", bus, dev, func, reg );
return false;
}
return m_bus1Devices[offset]->ReadConfigSpace8( reg, data );
}
else {
EPRINT( "Unknown read from %d:%d.%d[%d]",
bus, dev, func, reg );
return false;
}
default:
return HostDevice::IoWrite8( port, data );
}
}
virtual bool IoWrite8( u16 port, u8 &data )
{
int bus = ((m_configAddress >> 16) & 0xFF);
int dev = ((m_configAddress >> 11) & 0x1F);
int func = ((m_configAddress >> 8) & 0x7);
int reg = (m_configAddress & 0xFF) + (port - PCI_BUS_CONFIG_DATA_PORT);
int offset = (dev << 3) | func;
DPRINT( "Write 8 %d:%d.%d[%d] = %02x", bus,dev,func,reg,data );
switch( port ) {
case PCI_BUS_CONFIG_ADDRESS_PORT+1:
m_configAddress = (m_configAddress & 0xFFFF00FF) + (((u32)data)<<8 );
return true;
case PCI_BUS_CONFIG_DATA_PORT:
case PCI_BUS_CONFIG_DATA_PORT+1:
case PCI_BUS_CONFIG_DATA_PORT+2:
case PCI_BUS_CONFIG_DATA_PORT+3:
if( 0 == bus ) {
if( NULL == m_bus0Devices[offset] ) {
EPRINT( "Read %d:%d.%d[%d]", bus, dev, func, reg );
return false;
}
return m_bus0Devices[offset]->WriteConfigSpace8( reg, data );
}
else if( 1 == bus ) {
if( NULL == m_bus1Devices[offset] ) {
EPRINT( "Read %d:%d.%d[%d]", bus, dev, func, reg );
return false;
}
return m_bus1Devices[offset]->WriteConfigSpace8( reg, data );
}
else {
EPRINT( "Unknown read from %d:%d.%d[%d]",
bus, dev, func, reg );
return false;
}
default:
EPRINT( "Yo" );
return HostDevice::IoWrite8( port, data );
}
}
bool
ReadConfigData( u32 &data )
{
int bus = ((m_configAddress >> 16) & 0xFF);
int dev = ((m_configAddress >> 11) & 0x1F);
int func = ((m_configAddress >> 8) & 0x7);
int reg = m_configAddress & 0xFF;
int offset = (dev << 3) | func;
if( 0 == bus ) {
if( NULL == m_bus0Devices[offset] ) {
EPRINT( "Read %d:%d.%d[%d]", bus, dev, func, reg );
return false;
}
return m_bus0Devices[offset]->ReadConfigSpace( reg, data );
}
else if( 1 == bus ) {
if( NULL == m_bus1Devices[offset] ) {
EPRINT( "Read %d:%d.%d[%d]", bus, dev, func, reg );
return false;
}
return m_bus1Devices[offset]->ReadConfigSpace( reg, data );
}
else {
EPRINT( "Unknonw read from %d:%d.%d[%d]",
bus, dev, func, reg );
return false;
}
}
bool
WriteConfigData( u32 data )
{
int bus = ((m_configAddress >> 16) & 0xFF);
int dev = ((m_configAddress >> 11) & 0x1F);
int func = ((m_configAddress >> 8) & 0x7);
int reg = m_configAddress & 0xFF;
int offset = (dev << 3) | func;
if( 0 == bus ) {
if( NULL == m_bus0Devices[offset] ) {
EPRINT( "Write %d:%d.%d[%d] = %Xh", bus, dev, func, reg, data );
return false;
}
return m_bus0Devices[offset]->WriteConfigSpace( reg, data );
}
else if( 1 == bus ) {
if( NULL == m_bus1Devices[offset] ) {
EPRINT( "Write %d:%d.%d[%d] = %Xh", bus, dev, func, reg, data );
return false;
}
return m_bus1Devices[offset]->WriteConfigSpace( reg, data );
}
else {
EPRINT( "%d:%d.%d[%d] = %Xh",
bus, dev, func, reg, data );
return false;
}
}
bool
WriteConfigAddress( u32 addr )
{
m_configAddress = addr;
return true;
}
bool
AddPciDevice( PciDevice *device )
{
DPRINT( "adding %d:%d.%d %p", device->GetBus(), device->GetDev(), device->GetFunc(), device );
int bus = device->GetBus();
int offset = (device->GetDev() << 3) | device->GetFunc();
if( 0 == bus ) {
m_bus0Devices[offset] = device;
}
else if( 1 == bus ) {
m_bus1Devices[offset] = device;
}
else {
EPRINT( "Unknown bus %d for PciDevice %p", bus, device );
return false;
}
return true;
}
virtual
const char*
GetName( void )
{
return "PCI Bridge";
}
private:
u32 m_configAddress;
PciBus( PciBus& );
const PciBus& operator=( PciBus& );
PciDevice* m_bus0Devices[256];
PciDevice* m_bus1Devices[256];
};
extern PciBus *g_pciBus;
#endif /*XBVM_PCIBUS_H*/

131
src/PciDevice.cpp Normal file
View file

@ -0,0 +1,131 @@
#include "Log.h"
#include "PciDevice.h"
PciDevice::~PciDevice( void )
{
}
bool
PciDevice::WriteConfigSpace( u8 reg, u32 &data )
{
switch( reg ) {
case PCI_CONFIG_REG_IDS:
return true;
case PCI_CONFIG_REG_COMMAND:
DPRINT( "Write Command %x to %s Config Space", data, GetName() );
return true;
case PCI_CONFIG_REG_BAR0:
return WriteBAR( 0, data );
case PCI_CONFIG_REG_BAR1:
return WriteBAR( 1, data );
case PCI_CONFIG_REG_BAR2:
return WriteBAR( 2, data );
case PCI_CONFIG_REG_BAR3:
return WriteBAR( 3, data );
case PCI_CONFIG_REG_BAR4:
return WriteBAR( 4, data );
case PCI_CONFIG_REG_BAR5:
return WriteBAR( 5, data );
default:
//EPRINT( "Write of %08x to reg %d of %s Config Space", data, reg, GetName() );
return true;
}
}
bool
PciDevice::WriteConfigSpace8( u8 reg, u8 &data )
{
switch( reg ) {
case 0:
return true;
default:
EPRINT( "Write of %02x to config reg %d of %s", data, reg, GetName() );
return false;
}
}
bool
PciDevice::ReadConfigSpace( u8 reg, u32 &data )
{
switch( reg ) {
case PCI_CONFIG_REG_IDS:
data = (m_deviceId << 16) | (m_vendorId);
return true;
case PCI_CONFIG_REG_COMMAND:
data = 0;
return true;
case PCI_CONFIG_REG_BAR0:
data = m_bar[0];
return true;
case PCI_CONFIG_REG_BAR1:
data = m_bar[1];
return true;
case PCI_CONFIG_REG_BAR2:
data = m_bar[2];
return true;
case PCI_CONFIG_REG_BAR3:
data = m_bar[3];
return true;
case PCI_CONFIG_REG_BAR4:
data = m_bar[4];
return true;
case PCI_CONFIG_REG_BAR5:
data = m_bar[5];
return true;
default:
//EPRINT( "Read u32 from reg %d of %s Config Space", reg, GetName() );
data = 0xFFFFFFFF;
return true;
}
}
bool
PciDevice::ReadConfigSpace8( u8 reg, u8 &data )
{
switch( reg ) {
case 8:
data = GetClassCode();
return true;
case 9:
data = GetSubClass();
return true;
case 10:
data = GetProg1F();
return true;
case PCI_CONFIG_REG_HEADER_TYPE:
data = GetHeaderType();
return true;
default:
DPRINT( "Read u8 from reg %02x of %s Config Space", reg, GetName() );
data = 0xFF;
return false;
}
}
bool
PciDevice::WriteBAR( int num, u32 &data )
{
EPRINT( "Unimplemented: BAR%d set to %08x on %s", num, data, GetName() );
return false;
}

84
src/PciDevice.h Normal file
View file

@ -0,0 +1,84 @@
#ifndef XBVM_PCIDEVICE_H
#define XBVM_PCIDEVICE_H
#include "HostDevice.h"
#include "Types.h"
#define PCI_NVIDIA_VENDOR_ID (0x10DE)
#define PCI_NVIDIA_AUDIO_APU_DEVICE_ID (0x01B0)
#define PCI_NVIDIA_AUDIO_AC97_DEVICE_ID (0x01B1)
#define PCI_NVIDIA_ISA_BRIDGE_DEVICE_ID (0x01B2)
#define PCI_NVIDIA_SMBUS_DEVICE_ID (0x01B4)
#define PCI_NVIDIA_AGP_BRIDGE_DEVICE_ID (0x01B7)
#define PCI_NVIDIA_PCI_BRIDGE_DEVICE_ID (0x01B8)
#define PCI_NVIDIA_IDE_DEVICE_ID (0x01BC)
#define PCI_NVIDIA_MODEM_DEVICE_ID (0x01C1)
#define PCI_NVIDIA_OHCI_DEVICE_ID (0x01C2)
#define PCI_NVIDIA_NETWORK_DEVICE_ID (0x01C3)
#define PCI_NVIDIA_NV2A_DEVICE_ID (0x02A0)
#define PCI_NVIDIA_HOSTBRIDGE_DEVICE_ID (0x02A5)
#define PCI_NVIDIA_DRAM_DEVICE_ID (0x02A6)
#define PCI_CONFIG_REG_IDS (0)
#define PCI_CONFIG_REG_COMMAND (4)
#define PCI_CONFIG_REG_HEADER_TYPE (14)
#define PCI_CONFIG_REG_BAR0 (16)
#define PCI_CONFIG_REG_BAR1 (20)
#define PCI_CONFIG_REG_BAR2 (24)
#define PCI_CONFIG_REG_BAR3 (28)
#define PCI_CONFIG_REG_BAR4 (32)
#define PCI_CONFIG_REG_BAR5 (36)
#define PCI_CONFIG_REG_HEADER_TYPE_NORMAL (0)
#define PCI_CONFIG_REG_HEADER_TYPE_BRIDGE (1)
#define PCI_CONFIG_REG_HEADER_TYPE_CARDBUS (2)
class PciDevice : public HostDevice
{
public:
PciDevice( int bus, int dev, int func, u16 deviceId ) :
m_bus(bus),
m_dev(dev),
m_func(func),
m_deviceId( deviceId ),
m_vendorId( PCI_NVIDIA_VENDOR_ID )
{
m_bar[0] = 0;
m_bar[1] = 0;
m_bar[2] = 0;
m_bar[3] = 0;
m_bar[4] = 0;
m_bar[5] = 0;
}
virtual ~PciDevice();
virtual bool WriteConfigSpace( u8 reg, u32 &data );
virtual bool WriteConfigSpace8( u8 reg, u8 &data );
virtual bool ReadConfigSpace( u8 reg, u32 &data );
virtual bool ReadConfigSpace8( u8 reg, u8 &data );
virtual bool WriteBAR( int num, u32 &data );
int GetBus() { return m_bus; }
int GetDev() { return m_dev; }
int GetFunc() { return m_func; }
virtual u8 GetClassCode() { return 0; }
virtual u8 GetSubClass() { return 0; }
virtual u8 GetProg1F() { return 0; }
virtual u8 GetHeaderType() { return PCI_CONFIG_REG_HEADER_TYPE_NORMAL; }
private:
int m_bus;
int m_dev;
int m_func;
u16 m_deviceId;
u16 m_vendorId;
protected:
u32 m_bar[6];
};
#endif /*XBVM_PCIDEVICE_H*/

43
src/Sdl.cpp Normal file
View file

@ -0,0 +1,43 @@
#include <SDL/SDL.h>
#include <pthread.h>
#include "graphics/Nv2a.h"
#include "Log.h"
#include "Mem.h"
#include "Sdl.h"
#include "Types.h"
pthread_t SdlThread;
static void* SDLUpdateThread()
{
SDL_Surface *screen = NULL;
SDL_Init( SDL_INIT_EVERYTHING );
screen = SDL_SetVideoMode( 640, 480, 32, SDL_SWSURFACE );
SDL_WM_SetCaption( "XBVM", "XBVM" );
for(;;) {
u32* pixels = (u32*)screen->pixels;
if( g_nv2a->IsVideoOverlayValid() ) {
u32 overlayAddress = g_nv2a->GetOverlayAddress();
for( int i = 0; i < 640 * 480; i++ ) {
pixels[i] = g_mem->ReadPhysU32( overlayAddress + (i * 4) );
}
}
else {
for( int i = 0; i < 640 * 480; i++ ) {
pixels[i] = 0;
}
}
SDL_Flip( screen );
SDL_Delay( 16 );
}
return 0;
}
void StartSdlThread()
{
pthread_create( &SdlThread, NULL, (void*(*)(void*))SDLUpdateThread, NULL );
}

7
src/Sdl.h Normal file
View file

@ -0,0 +1,7 @@
#ifndef SDL_H
#define SDL_H
void StartSdlThread();
#endif /*SDL_H*/

18
src/Types.h Normal file
View file

@ -0,0 +1,18 @@
#ifndef XBVM_TYPES_H
#define XBVM_TYPES_H
#include <stdint.h>
#include <stdlib.h>
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;
#endif /*XBVM_TYPES_H*/

4
src/Xbox.cpp Normal file
View file

@ -0,0 +1,4 @@
#include "Xbox.h"
Xbox *g_xbox;

14
src/Xbox.h Normal file
View file

@ -0,0 +1,14 @@
#ifndef XBVM_XBOX_H
#define XBVM_XBOX_H
class Xbox
{
public:
Xbox();
virtual ~Xbox();
void Initilialize();
};
#endif /*XBVM_XBOX_H*/

60
src/audio/Ac97Codec.cpp Normal file
View file

@ -0,0 +1,60 @@
#include "audio/Ac97Codec.h"
#include "Log.h"
#include "Mem.h"
Ac97Codec *g_ac97;
Ac97Codec::Ac97Codec() : PciDevice( 0, 6, 0, PCI_NVIDIA_AUDIO_AC97_DEVICE_ID ),
m_ioport1base( 0 ),
m_ioport2base( 0 ),
m_mmiobase( 0 )
{
}
Ac97Codec::~Ac97Codec()
{
}
bool
Ac97Codec::WriteBAR( int num, u32 &data )
{
switch( num ) {
case 0:
if( (data & 1) != 1 ) {
EPRINT( "Can not map %s BAR%d to non IO space %08x", GetName(), num, data );
return false;
}
m_ioport1base = data & 0xFFFE;
for( int i = 0; i < 16; i++ ) {
g_mem->AddDeviceToIoMap( this, m_ioport1base + i );
}
return true;
case 1:
if( (data & 1) != 1 ) {
EPRINT( "Can not map %s BAR%d to non IO space %08x", GetName(), num, data );
return false;
}
m_ioport2base = data & 0xFFFE;
for( int i = 0; i < 16; i++ ) {
g_mem->AddDeviceToIoMap( this, m_ioport2base + i );
}
return true;
case 2:
if( (data & 1) != 0 ) {
EPRINT( "Can not map %s BAR%d to IO space %08x", GetName(), num, data );
return false;
}
m_mmiobase = data;
g_mem->AssignMmioRange( m_mmiobase, 0x1000, this );
return true;
default:
if( data != 0 ) {
return PciDevice::WriteBAR( num, data );
}
return true;
}
}

24
src/audio/Ac97Codec.h Normal file
View file

@ -0,0 +1,24 @@
#ifndef XBVM_AUDIO_AC97CODEC_H
#define XBVM_AUDIO_AC97CODEC_H
#include "PciDevice.h"
class Ac97Codec : public PciDevice
{
public:
Ac97Codec();
virtual ~Ac97Codec();
virtual const char* GetName( void ) { return "AC97 Codec"; }
virtual bool WriteBAR( int num, u32 &data );
private:
u32 m_ioport1base;
u32 m_ioport2base;
u32 m_mmiobase;
};
extern Ac97Codec *g_ac97;
#endif /*XBVM_AUDIO_AC97CODEC_H*/

36
src/audio/Apu.cpp Normal file
View file

@ -0,0 +1,36 @@
#include "audio/Apu.h"
#include "Log.h"
#include "Mem.h"
Apu *g_apu;
Apu::Apu() : PciDevice( 0, 5, 0, PCI_NVIDIA_AUDIO_APU_DEVICE_ID ),
m_mmiobase( 0 )
{
}
Apu::~Apu()
{
}
bool
Apu::WriteBAR( int num, u32 &data )
{
switch( num ) {
case 0:
if( (data & 1) != 0 ) {
EPRINT( "Can not map %s BAR%d to IO space %08x", GetName(), num, data );
return false;
}
m_mmiobase = data;
g_mem->AssignMmioRange( m_mmiobase, 512*1024, this );
return true;
default:
if( data != 0 ) {
return PciDevice::WriteBAR( num, data );
}
return true;
}
}

23
src/audio/Apu.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef XBVM_AUDIO_APU_H
#define XBVM_AUDIO_APU_H
#include "PciDevice.h"
class Apu : public PciDevice
{
public:
Apu();
virtual ~Apu();
virtual const char* GetName( void ) { return "APU"; }
virtual bool WriteBAR( int num, u32 &data );
private:
u32 m_mmiobase;
};
extern Apu *g_apu;
#endif /*XBVM_AUDIO_APU_H*/

50
src/bridges/AgpBridge.cpp Normal file
View file

@ -0,0 +1,50 @@
#include "bridges/AgpBridge.h"
AgpBridge *g_agpBridge;
AgpBridge::~AgpBridge()
{
}
bool
AgpBridge::WriteConfigSpace( u8 reg, u32 &data )
{
switch( reg ) {
case 0x10:
case 0x14:
case 0x1C:
return true;
case 24:
m_configTimers = data;
return true;
case 32:
m_configLimit = data;
return true;
case 36:
m_configPreFetchLimit = data;
return true;
case 76:
m_configUnknown76 = data;
return true;
default:
return PciDevice::WriteConfigSpace( reg, data );
}
}
bool
AgpBridge::WriteBAR( int num, u32 &data )
{
switch( num ) {
default:
if( data != 0 ) {
return PciDevice::WriteBAR( num, data );
}
return true;
}
}

34
src/bridges/AgpBridge.h Normal file
View file

@ -0,0 +1,34 @@
#ifndef XBVM_AGPBRIDGE_H
#define XBVM_AGPBRIDGE_H
#include "PciDevice.h"
class AgpBridge : public PciDevice
{
public:
AgpBridge() : PciDevice( 0, 30, 0, PCI_NVIDIA_AGP_BRIDGE_DEVICE_ID ),
m_configLimit( 0 ),
m_configTimers( 0 ),
m_configPreFetchLimit( 0 ),
m_configUnknown76( 0 )
{
}
virtual ~AgpBridge();
virtual const char* GetName( void ) { return "AGP Bridge"; }
virtual bool WriteConfigSpace( u8 reg, u32 &data );
virtual bool WriteBAR( int num, u32 &data );
private:
u32 m_configLimit;
u32 m_configTimers;
u32 m_configPreFetchLimit;
u32 m_configUnknown76;
};
extern AgpBridge *g_agpBridge;
#endif /*XBVM_AGPBRIDGE_H*/

View file

@ -0,0 +1,29 @@
#include "bridges/DramController.h"
DramController* g_dramController;
bool
DramController::WriteConfigSpace( u8 reg, u32 &data )
{
switch( reg ) {
case 64:
m_reg64 = data;
return true;
case 68:
m_reg68 = data;
return true;
case 92:
m_reg92 = data;
return true;
case 108:
m_reg108 = data;
return true;
default:
return PciDevice::WriteConfigSpace( reg, data );
}
}

View file

@ -0,0 +1,31 @@
#ifndef XBVM_BRIDGES_H
#define XBVM_BRIDGES_H
#include "PciDevice.h"
class DramController : public PciDevice
{
public:
DramController() : PciDevice( 0, 0, 3, PCI_NVIDIA_DRAM_DEVICE_ID ),
m_reg64( 0 ),
m_reg68( 0 ),
m_reg92( 0 ),
m_reg108( 0 )
{
}
virtual ~DramController() {}
virtual bool WriteConfigSpace( u8 reg, u32 &data );
virtual const char* GetName( void ) { return "DRAM Controller"; }
private:
u32 m_reg64;
u32 m_reg68;
u32 m_reg92;
u32 m_reg108;
};
extern DramController* g_dramController;
#endif /*XBVM_BRIDGES_H*/

View file

@ -0,0 +1,63 @@
#include "bridges/HostBridge.h"
HostBridge *g_hostBridge;
HostBridge::~HostBridge()
{
}
bool
HostBridge::ReadConfigSpace( u8 reg, u32 &data )
{
switch( reg ) {
case 100:
data = m_configReg100;
return true;
case 108:
data = m_configReg108;
return true;
default:
return PciDevice::ReadConfigSpace( reg, data );
}
}
bool
HostBridge::WriteConfigSpace( u8 reg, u32 &data )
{
switch( reg ) {
case 100:
m_configReg100 = data;
return true;
case 108:
m_configReg108 = data;
return true;
case 128:
m_configReg128 = data;
return true;
case 132:
m_configReg132 = data;
return true;
default:
return PciDevice::WriteConfigSpace( reg, data );
}
}
bool
HostBridge::WriteConfigSpace8( u8 reg, u8 &data )
{
switch( reg ) {
case 135:
m_configReg135 = data;
return true;
default:
return PciDevice::WriteConfigSpace8( reg, data );
}
}

37
src/bridges/HostBridge.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef XBVM_HOSTBRIDGE_H
#define XBVM_HOSTBRIDGE_H
#include "PciDevice.h"
class HostBridge : public PciDevice
{
public:
HostBridge() : PciDevice( 0, 0, 0, PCI_NVIDIA_HOSTBRIDGE_DEVICE_ID ),
m_configReg100(0),
m_configReg108(0),
m_configReg128(0),
m_configReg132(0),
m_configReg135(0)
{
}
virtual ~HostBridge();
virtual const char* GetName( void ) { return "Host Bridge"; }
virtual bool ReadConfigSpace( u8 reg, u32 &data );
virtual bool WriteConfigSpace( u8 reg, u32 &data );
virtual bool WriteConfigSpace8( u8 reg, u8 &data );
private:
u32 m_configReg100;
u32 m_configReg108;
u32 m_configReg128;
u32 m_configReg132;
u8 m_configReg135;
};
extern HostBridge *g_hostBridge;
#endif /*XBVM_HOSTBRIDGE_H*/

393
src/bridges/IsaBridge.cpp Normal file
View file

@ -0,0 +1,393 @@
#include "bridges/IsaBridge.h"
#include "Mem.h"
IsaBridge *g_isaBridge;
IsaBridge::IsaBridge() : PciDevice( 0, 1, 0, PCI_NVIDIA_ISA_BRIDGE_DEVICE_ID ),
m_ioBase( 0 ),
m_hardwareMonitorPinPulses( 0 ),
m_configReg76( 0 ),
m_configReg84( 0 ),
m_configReg96( 0 ),
m_configReg100( 0 ),
m_configReg106( 0 ),
m_configReg108( 0 ),
m_configReg129( 0 ),
m_configReg132( 0 ),
m_configReg140( 0 ),
m_configReg156( 0 ),
m_configReg180( 0 ),
m_timerReg( 0 ),
m_portB4( 0 ),
m_port02( 0 ),
m_port04( 0 ),
m_port20( 0 ),
m_port22( 0 ),
m_port23( 0 ),
m_port28( 0 ),
m_port26( 0 ),
m_port49( 0 ),
m_portC0( 0 ),
m_portCC( 0 ),
m_portCD( 0 ),
m_portCE( 0 ),
m_portCF( 0 ),
m_portD3( 0 ),
m_portD6( 0 ),
m_portD8( 0 ),
m_portD9( 0 )
{
//EISA IRQ Control according to Bochs PORTS.LST. Makes sense in
// context. This is the best place I could think of to put this.
// Maybe there is a better place?
g_mem->AddDeviceToIoMap( this, 0x4d0 );
g_mem->AddDeviceToIoMap( this, 0x4d1 );
g_mem->AddDeviceToIoMap( this, 0x92 );
u32 barAddress = 0x8001;
WriteBAR( 0, barAddress );
}
IsaBridge::~IsaBridge()
{
}
bool
IsaBridge::ReadConfigSpace8( u8 reg, u8 &data )
{
switch( reg ) {
case 129:
data = m_configReg129;
return true;
default:
return PciDevice::ReadConfigSpace8( reg, data );
}
}
bool
IsaBridge::ReadConfigSpace( u8 reg, u32 &data )
{
switch( reg ) {
case 96:
data = m_configReg96;
return true;
case 84:
data = m_configReg84;
return true;
case 180:
data = m_configReg180;
return true;
default:
return PciDevice::ReadConfigSpace( reg, data );
}
}
bool
IsaBridge::WriteConfigSpace8( u8 reg, u8 &data )
{
switch( reg ) {
case 106:
m_configReg106 = data;
return true;
case 128:
EPRINT( "TODO: Deactivate MCPX ROM" );
return true;
case 129:
m_configReg129 = data;
return true;
default:
return PciDevice::WriteConfigSpace8( reg, data );
}
}
bool
IsaBridge::WriteConfigSpace( u8 reg, u32 &data )
{
switch( reg ) {
case 76:
m_configReg76 = data;
return true;
case 84:
m_configReg84 = data;
return true;
case 96:
m_configReg96 = data;
return true;
case 100:
m_configReg100 = data;
return data;
case 108:
m_configReg108 = data;
return true;
case 132:
m_configReg132 = data;
return true;
case 140:
m_configReg140 = data;
return true;
case 156:
m_configReg156 = data;
return true;
case 180:
m_configReg180 = data;
return true;
default:
return PciDevice::WriteConfigSpace( reg, data );
}
}
bool
IsaBridge::WriteBAR( int num, u32 &data )
{
u16 addr = data & 0xFFFE;
switch( num ) {
case 0:
if( (data & 1) == 0 ) {
EPRINT( "Setting %s BAR0 to non I/O Space %xh", GetName(), data );
return false;
}
if( (data & 0xFFFE) == m_ioBase ) {
return true;
}
// Mask off I/O bit
m_ioBase = data & 0xFFFE;
for( int i = addr; i < addr + 0x100; i++ ) {
g_mem->AddDeviceToIoMap( this, i );
}
return true;
default:
return PciDevice::WriteBAR( num, data );
}
}
bool
IsaBridge::IoWrite8( u16 port, u8 &data )
{
u16 portOffset = port - m_ioBase;
if( port == 0x4D0 || port == 0x4D1 || port == 0x92 ) {
return true;
}
switch( portOffset ) {
case 0x02:
m_port02 = data;
return true;
case 0x04:
m_port04 = data;
return true;
case 0x22:
m_port22 = data;
return true;
case 0x23:
m_port23 = data;
return true;
case 0x26:
m_port26 = data;
return true;
case 0x49:
m_port49 = data;
return true;
case 0x28:
m_port28 = data;
return true;
case 0xC0:
m_portC0 = data;
return true;
case 0xCC:
m_portCC = data;
return true;
case 0xCD:
m_portCD = data;
return true;
case 0xCE:
m_portCE = data;
return true;
case 0xCF:
if( (m_portCF == 0x05) && (data == 0x04) ) {
m_hardwareMonitorPinPulses++;
DPRINT( "Hardware Pulses: %d", m_hardwareMonitorPinPulses );
}
m_portCF = data;
return true;
case 0xD3:
m_portD3 = data;
return true;
case 0xD6:
m_portD6 = data;
return true;
case 0xD8:
m_portD8 = data;
return true;
case 0xD9:
m_portD9 = data;
return true;
default:
return PciDevice::IoWrite8( port, data );
}
}
bool
IsaBridge::IoRead8( u16 port, u8 &data )
{
u16 portOffset = port - m_ioBase;
switch( portOffset ) {
case 0x02:
m_port02 = data;
return true;
case 0x04:
m_port04 = data;
return true;
case 0x22:
m_port22 = data;
return true;
case 0x23:
m_port23 = data;
return true;
case 0x28:
m_port28 = data;
return true;
default:
return PciDevice::IoRead8( port, data );
}
}
bool
IsaBridge::IoWrite16( u16 port, u16 &data )
{
u16 portOffset = port - m_ioBase;
switch( portOffset ) {
case 0x2:
m_port02 = data;
return true;
case 0x4:
m_port04 = data;
return true;
case 0x20:
m_port20 = data;
return true;
case 0x22:
m_port22 = data;
return true;
case 0x23:
m_port23 = data;
return true;
case 0x28:
m_port28 = data;
return true;
default:
return PciDevice::IoWrite16( port, data );
}
}
bool
IsaBridge::IoRead16( u16 port, u16 &data )
{
u16 portOffset = port - m_ioBase;
switch( portOffset ) {
case 0x2:
data = m_port02;
return true;
case 0x4:
data = m_port04;
return true;
case 0x20:
m_port20 = data;
return true;
case 0x22:
data = m_port22;
return true;
case 0x28:
data = m_port28;
return true;
default:
return PciDevice::IoRead16( port, data );
}
}
bool
IsaBridge::IoWrite32( u16 port, u32 &data )
{
u16 portOffset = port - m_ioBase;
switch( portOffset ) {
case 0xB4:
m_portB4 = data;
return true;
default:
return PciDevice::IoWrite32( port, data );
}
}
bool
IsaBridge::IoRead32( u16 port, u32 &data )
{
u16 portOffset = port - m_ioBase;
switch( portOffset ) {
case 0x08:
m_timerReg += 10;
data = m_timerReg;
return true;
default:
return PciDevice::IoRead32( port, data );
}
}

74
src/bridges/IsaBridge.h Normal file
View file

@ -0,0 +1,74 @@
#ifndef XBVM_ISABRIDGE_H
#define XBVM_ISABRIDGE_H
#include "PciDevice.h"
class IsaBridge : public PciDevice
{
public:
IsaBridge( void );
virtual ~IsaBridge( void );
virtual bool ReadConfigSpace8( u8 reg, u8 &data );
virtual bool ReadConfigSpace( u8 reg, u32 &data );
virtual bool WriteConfigSpace8( u8 reg, u8 &data );
virtual bool WriteConfigSpace( u8 reg, u32 &data );
virtual bool WriteBAR( int num, u32 &data );
virtual
const char*
GetName( void )
{
return "ISA Bridge";
}
virtual bool IoRead32( u16 port, u32 &data );
virtual bool IoWrite32( u16 port, u32 &data );
virtual bool IoRead16( u16 port, u16 &data );
virtual bool IoWrite16( u16 port, u16 &data );
virtual bool IoRead8( u16 port, u8 &data );
virtual bool IoWrite8( u16 port, u8 &data );
private:
u16 m_ioBase;
int m_hardwareMonitorPinPulses;
u32 m_configReg76;
u32 m_configReg84;
u32 m_configReg96;
u8 m_configReg100;
u8 m_configReg106;
u32 m_configReg108;
u8 m_configReg129;
u32 m_configReg132;
u32 m_configReg140;
u32 m_configReg156;
u32 m_configReg180;
u32 m_timerReg;
u32 m_portB4;
u16 m_port02;
u16 m_port04;
u16 m_port20;
u16 m_port22;
u16 m_port23;
u16 m_port28;
u8 m_port26;
u8 m_port49;
u8 m_portC0;
u8 m_portCC;
u8 m_portCD;
u8 m_portCE;
u8 m_portCF;
u8 m_portD3;
u8 m_portD6;
u8 m_portD8;
u8 m_portD9;
};
extern IsaBridge *g_isaBridge;
#endif /*XBVM_ISABRIDGE_H*/

View file

@ -0,0 +1,58 @@
#include "graphics/BusControlEngine.h"
#include "graphics/Nv2a.h"
BusControlEngine::BusControlEngine( Nv2a &nv2a ) : Engine( nv2a )
{
m_nv2a.RegisterEngineMmio( NV2A_MMIO_BASE_PBUS, this );
for( int i = 0; i < 1024; i++ ) {
m_regs[ i ] = 0;
}
}
BusControlEngine::~BusControlEngine()
{
}
bool
BusControlEngine::MmioWrite32( u32 reg, u32 data )
{
switch( reg ) {
case NV2A_REG_PBUS_UNKNOWN_0B0:
case NV2A_REG_PBUS_UNKNOWN_0B4:
case NV2A_REG_PBUS_UNKNOWN_0B8:
case NV2A_REG_PBUS_UNKNOWN_0BC:
case NV2A_REG_PBUS_UNKNOWN_0C4:
case NV2A_REG_PBUS_UNKNOWN_0C8:
case NV2A_REG_PBUS_UNKNOWN_0CC:
case NV2A_REG_PBUS_UNKNOWN_0D4:
case NV2A_REG_PBUS_UNKNOWN_0D8:
case NV2A_REG_PBUS_UNKNOWN_0DC:
case NV2A_REG_PBUS_UNKNOWN_0E8:
case NV2A_REG_PBUS_ROM_TIMINGS:
case NV2A_REG_PBUS_UNKNOWN_210:
case NV2A_REG_PBUS_UNKNOWN_214:
case NV2A_REG_PBUS_UNKNOWN_218:
case NV2A_REG_PBUS_UNKNOWN_220:
case NV2A_REG_PBUS_UNKNOWN_228:
case NV2A_REG_PBUS_UNKNOWN_22C:
case NV2A_REG_PBUS_UNKNOWN_230:
case NV2A_REG_PBUS_UNKNOWN_234:
case NV2A_REG_PBUS_UNKNOWN_238:
case NV2A_REG_PBUS_UNKNOWN_23C:
case NV2A_REG_PBUS_UNKNOWN_240:
case NV2A_REG_PBUS_UNKNOWN_244:
case NV2A_REG_PBUS_UNKNOWN_248:
case NV2A_REG_PBUS_UNKNOWN_24C:
case NV2A_REG_PBUS_UNKNOWN_250:
case NV2A_REG_PBUS_UNKNOWN_264:
case NV2A_REG_PBUS_CLOCK_GATING_2:
case NV2A_REG_PBUS_CLOCK_GATING_4:
m_regs[ reg / sizeof(u32) ] = data;
return true;
default:
return Engine::MmioWrite32( reg, data );
}
}

View file

@ -0,0 +1,57 @@
#ifndef GRAPHICS_BUSCONTROLENGINE_H
#define GRAPHICS_BUSCONTROLENGINE_H
#include "graphics/Engine.h"
#define NV2A_REG_PBUS_UNKNOWN_0B0 (0x0B0)
#define NV2A_REG_PBUS_UNKNOWN_0B4 (0x0B4)
#define NV2A_REG_PBUS_UNKNOWN_0B8 (0x0B8)
#define NV2A_REG_PBUS_UNKNOWN_0BC (0x0BC)
#define NV2A_REG_PBUS_UNKNOWN_0C4 (0x0C4)
#define NV2A_REG_PBUS_UNKNOWN_0C8 (0x0C8)
#define NV2A_REG_PBUS_UNKNOWN_0CC (0x0CC)
#define NV2A_REG_PBUS_UNKNOWN_0D4 (0x0D4)
#define NV2A_REG_PBUS_UNKNOWN_0D8 (0x0D8)
#define NV2A_REG_PBUS_UNKNOWN_0DC (0x0DC)
#define NV2A_REG_PBUS_UNKNOWN_0E8 (0x0E8)
#define NV2A_REG_PBUS_ROM_TIMINGS (0x200)
#define NV2A_REG_PBUS_UNKNOWN_210 (0x210)
#define NV2A_REG_PBUS_UNKNOWN_214 (0x214)
#define NV2A_REG_PBUS_UNKNOWN_218 (0x218)
#define NV2A_REG_PBUS_UNKNOWN_220 (0x220)
#define NV2A_REG_PBUS_UNKNOWN_228 (0x228)
#define NV2A_REG_PBUS_UNKNOWN_22C (0x22C)
#define NV2A_REG_PBUS_UNKNOWN_230 (0x230)
#define NV2A_REG_PBUS_UNKNOWN_234 (0x234)
#define NV2A_REG_PBUS_UNKNOWN_238 (0x238)
#define NV2A_REG_PBUS_UNKNOWN_23C (0x23C)
#define NV2A_REG_PBUS_UNKNOWN_240 (0x240)
#define NV2A_REG_PBUS_UNKNOWN_244 (0x244)
#define NV2A_REG_PBUS_UNKNOWN_248 (0x248)
#define NV2A_REG_PBUS_UNKNOWN_24C (0x24C)
#define NV2A_REG_PBUS_UNKNOWN_250 (0x250)
#define NV2A_REG_PBUS_UNKNOWN_264 (0x264)
#define NV2A_REG_PBUS_CLOCK_GATING_2 (0x588)
#define NV2A_REG_PBUS_CLOCK_GATING_4 (0x590)
class Nv2a;
class BusControlEngine : public Engine
{
public:
BusControlEngine( Nv2a &nv2a );
virtual ~BusControlEngine();
virtual const char* GetName() { return "NV2A Bus Control Engine"; }
virtual bool MmioWrite32( u32 reg, u32 data );
virtual u32 GetRegBase() { return NV2A_MMIO_BASE_PBUS; }
private:
u32 m_regs[ 1024 ];
};
#endif /*GRAPHICS_BUSCONTROLENGINE_H*/

View file

@ -0,0 +1,50 @@
#include "graphics/CrtcEngine.h"
#include "graphics/Nv2a.h"
#include "Log.h"
CrtcEngine::CrtcEngine( Nv2a &nv2a ) : Engine( nv2a )
{
m_nv2a.RegisterEngineMmio( NV2A_MMIO_BASE_PCRTC, this );
for( int i = 0; i< 1024; i++ ) {
m_regs[ i ] = 0;
}
}
CrtcEngine::~CrtcEngine()
{
}
bool
CrtcEngine::MmioWrite32( u32 reg, u32 data )
{
switch( reg ) {
case NV2A_REG_PCRTC_INTR:
case NV2A_REG_PCRTC_INTR_EN:
m_regs[ reg / sizeof(u32) ] = data;
return true;
case NV2A_REG_PCRTC_START:
m_regs[ reg / sizeof(u32) ] = data;
DPRINT( "CRTC: FB set to %08x", data );
return true;
default:
return Engine::MmioWrite32( reg, data );
}
}
u32
CrtcEngine::MmioRead32( u32 reg )
{
switch( reg ) {
case NV2A_REG_PCRTC_INTR:
case NV2A_REG_PCRTC_INTR_EN:
case NV2A_REG_PCRTC_START:
return m_regs[ reg / sizeof(u32) ];
default:
return Engine::MmioRead32( reg );
}
}

30
src/graphics/CrtcEngine.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef GRAPHICS_CRTCENGINE_H
#define GRAPHICS_CRTCENGINE_H
#include "graphics/Engine.h"
#define NV2A_REG_PCRTC_INTR (0x100)
#define NV2A_REG_PCRTC_INTR_EN (0x140)
#define NV2A_REG_PCRTC_START (0x800)
class Nv2a;
class CrtcEngine : public Engine
{
public:
CrtcEngine( Nv2a &nv2a );
virtual ~CrtcEngine();
virtual const char* GetName() { return "NV2A CRTC"; }
virtual bool MmioWrite32( u32 reg, u32 data );
virtual u32 MmioRead32( u32 reg );
virtual u32 GetRegBase() { return NV2A_MMIO_BASE_PCRTC; }
private:
u32 m_regs[ 1024 ];
};
#endif /*GRAPHICS_CRTCENGINE*/

40
src/graphics/Engine.cpp Normal file
View file

@ -0,0 +1,40 @@
#include "graphics/Engine.h"
#include "Log.h"
Engine::Engine( Nv2a &nv2a ) :
m_nv2a( nv2a )
{
}
Engine::~Engine()
{
}
bool
Engine::MmioWrite32( u32 reg, u32 data )
{
EPRINT( "Write to unknown MMIO reg %08x->%06x in %s", data, reg, GetName() );
return false;
}
bool
Engine::MmioWrite8( u32 reg, u8 data )
{
EPRINT( "Write to unknown MMIO reg %02x->%06x in %s", data, reg, GetName() );
return false;
}
u32
Engine::MmioRead32( u32 reg )
{
EPRINT( "Read from unknown u32 MMIO reg %06x in %s", reg, GetName() );
return 0xFFFFFFFF;
}
u8
Engine::MmioRead8( u32 reg )
{
EPRINT( "Read from unknown u8 MMIO reg %06x in %s", reg, GetName() );
return 0xFF;
}

25
src/graphics/Engine.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef GRAPHICS_ENGINE_H
#define GRAPHICS_ENGINE_H
#include "graphics/Nv2a.h"
#include "Device.h"
class Engine : public Device
{
public:
Engine( Nv2a& nv2a );
virtual ~Engine();
virtual bool MmioWrite32( u32 reg, u32 data );
virtual bool MmioWrite8( u32 reg, u8 data );
virtual u32 MmioRead32( u32 reg );
virtual u8 MmioRead8( u32 reg );
virtual u32 GetRegBase() = 0;
protected:
Nv2a &m_nv2a;
};
#endif /*GRAPHICS_ENGINE_H*/

View file

@ -0,0 +1,38 @@
#include "graphics/MasterControlEngine.h"
#include "graphics/Nv2a.h"
#include "Log.h"
MasterControlEngine::MasterControlEngine( Nv2a &nv2a ) : Engine( nv2a )
{
nv2a.RegisterEngineMmio( NV2A_MMIO_BASE_PMC, this );
}
MasterControlEngine::~MasterControlEngine()
{
}
bool
MasterControlEngine::MmioWrite32( u32 reg, u32 data )
{
switch( reg ) {
case NV2A_REG_PMC_INTR_0:
// I thought this was read-only register? cromwell writes 0x00000001 to it
return true;
case NV2A_REG_PMC_INTR_EN_0:
if( data & NV2A_REG_PMC_INTR_EN_0_INTA_HARDWARE ) {
DPRINT( "Enabled Hardware interrupts on NV2A" );
}
if( data & NV2A_REG_PMC_INTR_EN_0_INTA_SOFTWARE ) {
DPRINT( "Enabled Software interrupts on NV2A" );
}
if( 0 == data ) {
DPRINT( "Disabled interrupts on NV2A via PMC_INTR_EN_0" );
}
return true;
default:
return Engine::MmioWrite32( reg, data );
}
}

View file

@ -0,0 +1,25 @@
#ifndef GRAPHICS_MASTERCONTROLENGINE_H
#define GRAPHICS_MASTERCONTROLENGINE_H
#include "graphics/Engine.h"
#define NV2A_REG_PMC_INTR_0 (0x000100)
#define NV2A_REG_PMC_INTR_EN_0 (0x000140)
#define NV2A_REG_PMC_INTR_EN_0_INTA_HARDWARE (0x00000001)
#define NV2A_REG_PMC_INTR_EN_0_INTA_SOFTWARE (0x00000002)
class MasterControlEngine : public Engine
{
public:
MasterControlEngine( Nv2a &nv2a );
virtual ~MasterControlEngine();
virtual const char* GetName() { return "NV2A Master Control Engine"; }
virtual u32 GetRegBase() { return NV2A_MMIO_BASE_PMC; }
virtual bool MmioWrite32( u32 reg, u32 data );
};
#endif /*GRAPHICS_MASTERCONTROLENGINE_H*/

View file

@ -0,0 +1,83 @@
#include "graphics/MemoryInterfaceEngine.h"
#include "graphics/Nv2a.h"
MemoryInterfaceEngine::MemoryInterfaceEngine( Nv2a &nv2a ) : Engine( nv2a )
{
m_nv2a.RegisterEngineMmio( NV2A_MMIO_BASE_PFB, this );
for( int i = 0; i < 1024; i++ ) {
m_regs[i] = 0;
}
}
MemoryInterfaceEngine::~MemoryInterfaceEngine()
{
}
bool
MemoryInterfaceEngine::MmioWrite32( u32 reg, u32 data )
{
switch( reg ) {
case NV2A_REG_PFB_CFG0:
case NV2A_REG_PFB_CFG1:
case NV2A_REG_PFB_MEM_AMOUNT:
case NV2A_REG_PFB_REFCTRL:
case NV2A_REG_PFB_PRAMIN_WR_PROT:
case NV2A_REG_PFB_MEM_TIMINGS_0:
case NV2A_REG_PFB_MEM_TIMINGS_1:
case NV2A_REG_PFB_MEM_TIMINGS_2:
case NV2A_REG_PFB_UNKNOWN_2C0:
case NV2A_REG_PFB_UNKNOWN_2C4:
case NV2A_REG_PFB_UNKNOWN_2C8:
case NV2A_REG_PFB_UNKNOWN_2CC:
case NV2A_REG_PFB_REFRESH:
case NV2A_REG_PFB_PRECHARGE:
case NV2A_REG_PFB_UNKNOWN_2D8:
case NV2A_REG_PFB_SELF_REFRESH:
case NV2A_REG_pFB_COMP_MAX_TAG:
case NV2A_REG_PFB_UNKNOWN_328:
case NV2A_REG_PFB_UNKNOWN_32C:
case NV2A_REG_PFB_UNKNOWN_330:
case NV2A_REG_PFB_UNKNOWN_338:
case NV2A_REG_PFB_UNKNOWN_410:
m_regs[ reg / sizeof(u32) ] = data;
return true;
default:
return Engine::MmioWrite32( reg, data );
}
}
u32
MemoryInterfaceEngine::MmioRead32( u32 reg )
{
switch( reg ) {
case NV2A_REG_PFB_CFG0:
case NV2A_REG_PFB_CFG1:
case NV2A_REG_PFB_MEM_AMOUNT:
case NV2A_REG_PFB_REFCTRL:
case NV2A_REG_PFB_PRAMIN_WR_PROT:
case NV2A_REG_PFB_MEM_TIMINGS_0:
case NV2A_REG_PFB_MEM_TIMINGS_1:
case NV2A_REG_PFB_MEM_TIMINGS_2:
case NV2A_REG_PFB_UNKNOWN_2C0:
case NV2A_REG_PFB_UNKNOWN_2C4:
case NV2A_REG_PFB_UNKNOWN_2C8:
case NV2A_REG_PFB_UNKNOWN_2CC:
case NV2A_REG_PFB_REFRESH:
case NV2A_REG_PFB_PRECHARGE:
case NV2A_REG_PFB_UNKNOWN_2D8:
case NV2A_REG_PFB_SELF_REFRESH:
case NV2A_REG_pFB_COMP_MAX_TAG:
case NV2A_REG_PFB_UNKNOWN_328:
case NV2A_REG_PFB_UNKNOWN_32C:
case NV2A_REG_PFB_UNKNOWN_330:
case NV2A_REG_PFB_UNKNOWN_338:
case NV2A_REG_PFB_UNKNOWN_410:
return m_regs[ reg / sizeof(u32) ];
default:
return Engine::MmioRead32( reg );
}
}

View file

@ -0,0 +1,49 @@
#ifndef GRAPHICS_MEMORYINTERFACEENGINE_H
#define GRAPHICS_MEMORYINTERFACEENGINE_H
#include "graphics/Engine.h"
#define NV2A_REG_PFB_CFG0 (0x200)
#define NV2A_REG_PFB_CFG1 (0x204)
#define NV2A_REG_PFB_MEM_AMOUNT (0x20C)
#define NV2A_REG_PFB_REFCTRL (0x210)
#define NV2A_REG_PFB_PRAMIN_WR_PROT (0x214)
#define NV2A_REG_PFB_MEM_TIMINGS_0 (0x220)
#define NV2A_REG_PFB_MEM_TIMINGS_1 (0x224)
#define NV2A_REG_PFB_MEM_TIMINGS_2 (0x228)
#define NV2A_REG_PFB_UNKNOWN_2C0 (0x2C0)
#define NV2A_REG_PFB_UNKNOWN_2C4 (0x2C4)
#define NV2A_REG_PFB_UNKNOWN_2C8 (0x2C8)
#define NV2A_REG_PFB_UNKNOWN_2CC (0x2CC)
#define NV2A_REG_PFB_REFRESH (0x2D0)
#define NV2A_REG_PFB_PRECHARGE (0x2D4)
#define NV2A_REG_PFB_UNKNOWN_2D8 (0x2D8)
#define NV2A_REG_PFB_SELF_REFRESH (0x2DC)
#define NV2A_REG_pFB_COMP_MAX_TAG (0x320)
#define NV2A_REG_PFB_UNKNOWN_328 (0x328)
#define NV2A_REG_PFB_UNKNOWN_32C (0x32C)
#define NV2A_REG_PFB_UNKNOWN_330 (0x330)
#define NV2A_REG_PFB_UNKNOWN_338 (0x338)
#define NV2A_REG_PFB_UNKNOWN_410 (0x410)
class Nv2a;
class MemoryInterfaceEngine : public Engine
{
public:
MemoryInterfaceEngine( Nv2a& );
virtual ~MemoryInterfaceEngine();
virtual const char* GetName() { return "NV2A Memory Interface Engine"; }
virtual bool MmioWrite32( u32 reg, u32 data );
virtual u32 MmioRead32( u32 reg );
virtual u32 GetRegBase() { return NV2A_MMIO_BASE_PFB; }
private:
u32 m_regs[1024];
};
#endif /*GRAPHICS_MEMORYINTERFACEENGINE_H*/

174
src/graphics/Nv2a.cpp Normal file
View file

@ -0,0 +1,174 @@
#include "graphics/BusControlEngine.h"
#include "graphics/CrtcEngine.h"
#include "graphics/Engine.h"
#include "graphics/MasterControlEngine.h"
#include "graphics/MemoryInterfaceEngine.h"
#include "graphics/Nv2a.h"
#include "graphics/RamDacEngine.h"
#include "graphics/StrapsEngine.h"
#include "graphics/UnknownEngine.h"
#include "graphics/VgaEmulationEngine.h"
#include "graphics/VgaSequencerEngine.h"
#include "graphics/VideoOverlayEngine.h"
#include "Log.h"
#include "Mem.h"
Nv2a *g_nv2a;
Nv2a::Nv2a() : PciDevice( 1, 0, 0, PCI_NVIDIA_NV2A_DEVICE_ID ),
m_mmioBase( 0 ),
m_mmioBaseAssigned( false ),
m_unknownEngine( NULL ),
m_bus( NULL ),
m_mc( NULL ),
m_fb( NULL ),
m_ramdac( NULL ),
m_crtc( NULL ),
m_straps( NULL ),
m_vga( NULL ),
m_vgaSeq( NULL ),
m_video( NULL )
{
m_unknownEngine = new UnknownEngine( *this );
for( int i = 0; i < NV2A_NUM_ENGINE_MMIO_PAGES; i++ ) {
m_engineMmioTable[i] = m_unknownEngine;
}
m_bus = new BusControlEngine( *this );
m_mc = new MasterControlEngine( *this );
m_fb = new MemoryInterfaceEngine( *this );
m_ramdac = new RamDacEngine( *this );
m_crtc = new CrtcEngine( *this );
m_straps = new StrapsEngine( *this );
m_vga = new VgaEmulationEngine( *this );
m_vgaSeq = new VgaSequencerEngine( *this );
m_video = new VideoOverlayEngine( *this );
u32 mmioBase = 0xFD000000;
WriteBAR( 0, mmioBase );
u32 ramBase = 0xF0000000;
WriteBAR( 1, ramBase );
}
Nv2a::~Nv2a()
{
}
bool
Nv2a::WriteBAR( int num, u32 &data )
{
switch( num ) {
case 0:
if( (data & 1) == 1 ) {
EPRINT( "%s MMIO BAR set to I/O space: %08x", GetName(), data );
return false;
}
DPRINT( "%s MMIO set to %08x", GetName(), data );
m_bar[0] = data;
if( m_mmioBaseAssigned )
g_mem->DeassignMmioRange( m_mmioBase, NV2A_MMIO_SIZE );
m_mmioBase = data;
m_mmioBaseAssigned = true;
return g_mem->AssignMmioRange( m_mmioBase, 16 * 1025 * 1024, this );
case 1:
if( data != 0xF0000000 ) {
EPRINT( "%s RAM mirror set to unimplemented addr %08x", GetName(), data );
return false;
}
m_bar[1] = data;
return true;
case 2:
//ignore. This shouldn't be setup to anything. Cromwell writes 00000008
return true;
default:
if( data != 0 ) {
return PciDevice::WriteBAR( num, data );
}
return true;
}
}
bool
Nv2a::MmioWrite32( u32 addr, u32 data )
{
Engine *engine = GetEngineForAddress( addr );
if( NULL == engine ) {
EPRINT( "Write u32 of %08x to unknown NV2A MMIO register %06x", data, addr );
return false;
}
//EPRINT( "%08x %08x %08x %s %08x", data, addr, engine->GetRegBase(), engine->GetName(), addr - engine->GetRegBase() );
//return false;
return engine->MmioWrite32( addr - m_mmioBase - engine->GetRegBase(), data );
}
bool
Nv2a::MmioWrite8( u32 addr, u8 data )
{
Engine *engine = GetEngineForAddress( addr );
if( NULL == engine ) {
EPRINT( "Write u8 of %02x to unknown NV2A MMIO register %06x", data, addr );
return false;
}
return engine->MmioWrite8( addr - m_mmioBase - engine->GetRegBase(), data );
}
u32
Nv2a::MmioRead32( u32 addr )
{
Engine *engine = GetEngineForAddress( addr );
if( NULL == engine ) {
EPRINT( "Read u32 from unknown NV2A MMIO register %06x", addr );
return 0xFFFFFFFF;
}
return engine->MmioRead32( addr - m_mmioBase - engine->GetRegBase() );
}
u8
Nv2a::MmioRead8( u32 addr )
{
Engine *engine = GetEngineForAddress( addr );
if( NULL == engine ) {
EPRINT( "Read u8 from unknown NV2A MMIO register %06x", addr );
return 0xFF;
}
return engine->MmioRead8( addr - m_mmioBase - engine->GetRegBase() );
}
Engine*
Nv2a::GetEngineForAddress( u32 addr )
{
if( (addr < m_mmioBase) || (addr >= (m_mmioBase + NV2A_MMIO_SIZE )) || !m_mmioBaseAssigned ) {
EPRINT( "Somehow got an MMIO read request from non mapped region %08x %08x", addr, m_mmioBase );
return NULL;
}
u32 reg = addr - m_mmioBase;
return m_engineMmioTable[ reg / NV2A_ENGINE_PAGE_SIZE ];
}
bool
Nv2a::IsVideoOverlayValid()
{
return GetOverlayAddress() != 0;
}
u32
Nv2a::GetOverlayAddress()
{
return m_video->GetOverlayAddress();
}

120
src/graphics/Nv2a.h Normal file
View file

@ -0,0 +1,120 @@
#ifndef XBVM_NV2A_H
#define XBVM_NV2A_H
#include "PciDevice.h"
#include "Types.h"
#define NV2A_REG_PMC_BOOT_0 (0x000000)
#define NV2A_REG_PMC_0010B0 (0x0010B0)
#define NV2A_REG_PMC_0010B4 (0x0010B4)
#define NV2A_REG_PMC_0010B8 (0x0010B8)
#define NV2A_REG_PMC_0010BC (0x0010BC)
#define NV2A_REG_PMC_0010C4 (0x0010C4)
#define NV2A_REG_PMC_0010C8 (0x0010C8)
#define NV2A_REG_PMC_0010CC (0x0010CC)
#define NV2A_REG_PMC_0010D4 (0x0010D4)
#define NV2A_REG_PMC_0010D8 (0x0010D8)
#define NV2A_REG_PMC_0010DC (0x0010DC)
#define NV2A_REG_PMC_0010E8 (0x0010E8)
#define NV2A_REG_PMC_001210 (0x001210)
#define NV2A_REG_PMC_001214 (0x001214)
#define NV2A_REG_PMC_001218 (0x001218)
#define NV2A_REG_PMC_001220 (0x001220)
#define NV2A_REG_PMC_001228 (0x001228)
#define NV2A_REG_PMC_00122C (0x00122C)
#define NV2A_REG_PMC_001230 (0x001230)
#define NV2A_REG_PMC_001234 (0x001234)
#define NV2A_REG_PMC_001238 (0x001238)
#define NV2A_REG_PMC_00123C (0x00123C)
#define NV2A_REG_PMC_001240 (0x001240)
#define NV2A_REG_PMC_001244 (0x001244)
#define NV2A_REG_PMC_001248 (0x001248)
#define NV2A_REG_PMC_00124C (0x00124C)
#define NV2A_REG_PMC_001250 (0x001250)
#define NV2A_REG_PMC_001264 (0x001264)
#define NV2A_MMIO_SIZE (16*1024*1024)
#define NV2A_ENGINE_PAGE_SIZE (4096)
#define NV2A_NUM_ENGINE_MMIO_PAGES (NV2A_MMIO_SIZE / NV2A_ENGINE_PAGE_SIZE)
#define NV2A_MMIO_BASE_UNKNOWN (0x000000) // Catches all unknown engine mmio accesses
#define NV2A_MMIO_BASE_PMC (0x000000) // Card Master Control
#define NV2A_MMIO_BASE_PBUS (0x001000) // Bus Control
#define NV2A_MMIO_BASE_PFIFO (0x002000) // FIFO
#define NV2A_MMIO_BASE_PRMA (0x007000) // BAR0/1 Access From Real Mode
#define NV2A_MMIO_BASE_PVIDEO (0x008000) // Video Overlay
#define NV2A_MMIO_BASE_PTIMER (0x009000) // Time management and alarms
#define NV2A_MMIO_BASE_PCOUNTER (0x00A000) // Performance monitoring
#define NV2A_MMIO_BASE_PRM (0x0A0000) // Alias VGA memory window
#define NV2A_MMIO_BASE_PRMVIO (0x0C0000) // Alias VGA sequencer regs
#define NV2A_MMIO_BASE_PFB (0x100000) // Memory interface
#define NV2A_MMIO_BASE_PSTRAPS (0x101000) // Straps readout / override
#define NV2A_MMIO_BASE_PGRAPH (0x400000) // Graphics engine
#define NV2A_MMIO_BASE_PCRTC (0x600000) // More CRTC controls
#define NV2A_MMIO_BASE_PRMCIO (0x601000) // Aliases VGA CRTC and attribute controller registers
#define NV2A_MMIO_BASE_PRAMDAC (0x680000) // RAMDAC, cursor, and PLL control
#define NV2A_MMIO_BASE_PRMDIO (0x681000) // Aliases VGA palette registers
#define NV2A_MMIO_BASE_PRAMIN (0x700000) // RAMIN Access
#define NV2A_MMIO_BASE_USER (0x800000) // PFIFO MMIO and DMA submission area
class BusControlEngine;
class Engine;
class MasterControlEngine;
class MemoryInterfaceEngine;
class RamDacEngine;
class CrtcEngine;
class StrapsEngine;
class UnknownEngine;
class VgaEmulationEngine;
class VgaSequencerEngine;
class VideoOverlayEngine;
class Nv2a : public PciDevice
{
public:
Nv2a();
virtual ~Nv2a();
virtual bool MmioWrite32( u32 addr, u32 data );
virtual bool MmioWrite8( u32 addr, u8 data );
virtual u32 MmioRead32( u32 addr );
virtual u8 MmioRead8( u32 addr );
virtual const char* GetName( void ) { return "NV2A GPU"; }
virtual bool WriteBAR( int num, u32 &data );
void RegisterEngineMmio( u32 pageAddr, Engine *engine ) {
m_engineMmioTable[ pageAddr / NV2A_ENGINE_PAGE_SIZE ] = engine;
}
bool IsVideoOverlayValid();
u32 GetOverlayAddress();
private:
Engine* GetEngineForAddress( u32 address );
u32 m_mmioBase;
bool m_mmioBaseAssigned;
Nv2a( Nv2a& );
const Nv2a& operator=( Nv2a& );
Engine* m_engineMmioTable[ NV2A_NUM_ENGINE_MMIO_PAGES ];
UnknownEngine *m_unknownEngine;
BusControlEngine *m_bus;
MasterControlEngine *m_mc;
MemoryInterfaceEngine *m_fb;
RamDacEngine *m_ramdac;
CrtcEngine *m_crtc;
StrapsEngine *m_straps;
VgaEmulationEngine *m_vga;
VgaSequencerEngine *m_vgaSeq;
VideoOverlayEngine *m_video;
};
extern Nv2a *g_nv2a;
#endif /*XBVM_NV2A_H*/

View file

@ -0,0 +1,74 @@
#include "graphics/RamDacEngine.h"
#include "graphics/Nv2a.h"
RamDacEngine::RamDacEngine( Nv2a &nv2a ) : Engine( nv2a )
{
m_nv2a.RegisterEngineMmio( NV2A_MMIO_BASE_PRAMDAC, this );
for( int i = 0; i < 1024; i++ ) {
m_regs[ i ] = 0;
}
}
RamDacEngine::~RamDacEngine()
{
}
bool
RamDacEngine::MmioWrite32( u32 reg, u32 data )
{
switch( reg ) {
case NV2A_REG_PRAMDAC_CURSOR_POS:
case NV2A_REG_PRAMDAC_NVPLL:
case NV2A_REG_PRAMDAC_MPLL:
case NV2A_REG_PRAMDAC_VPLL:
case NV2A_REG_PRAMDAC_PLL_CONTROL:
case NV2A_REG_PRAMDAC_UNKNOWN_630:
case NV2A_REG_PRAMDAC_VDISPEND:
case NV2A_REG_PRAMDAC_VTOTAL:
case NV2A_REG_PRAMDAC_VCRTC:
case NV2A_REG_PRAMDAC_VSYNCSTART:
case NV2A_REG_PRAMDAC_VSYNCEND:
case NV2A_REG_PRAMDAC_VVALIDSTART:
case NV2A_REG_PRAMDAC_VVALIDEND:
case NV2A_REG_PRAMDAC_HDISPEND:
case NV2A_REG_PRAMDAC_HTOTAL:
case NV2A_REG_PRAMDAC_HCRTC:
case NV2A_REG_PRAMDAC_HSYNCSTART:
case NV2A_REG_PRAMDAC_HSYNCEND:
case NV2A_REG_PRAMDAC_HVALIDSTART:
case NV2A_REG_PRAMDAC_HVALIDEND:
case NV2A_REG_PRAMDAC_UNKNOWN_84C:
case NV2A_REG_PRAMDAC_UNKNOWN_880:
case NV2A_REG_PRAMDAC_UNKNOWN_884:
case NV2A_REG_PRAMDAC_UNKNOWN_888:
case NV2A_REG_PRAMDAC_UNKNOWN_88C:
case NV2A_REG_PRAMDAC_UNKNOWN_890:
case NV2A_REG_PRAMDAC_UNKNOWN_894:
case NV2A_REG_PRAMDAC_UNKNOWN_898:
case NV2A_REG_PRAMDAC_UNKNOWN_89C:
case NV2A_REG_PRAMDAC_UNKNOWN_8C4:
m_regs[ reg / sizeof(u32) ] = data;
return true;
default:
return Engine::MmioWrite32( reg, data );
}
}
u32
RamDacEngine::MmioRead32( u32 reg )
{
switch( reg ) {
case NV2A_REG_PRAMDAC_CURSOR_POS:
case NV2A_REG_PRAMDAC_NVPLL:
case NV2A_REG_PRAMDAC_MPLL:
case NV2A_REG_PRAMDAC_VPLL:
case NV2A_REG_PRAMDAC_PLL_CONTROL:
return m_regs[ reg / sizeof(u32) ];
default:
return Engine::MmioRead32( reg );
}
}

View file

@ -0,0 +1,58 @@
#ifndef GRAPHICS_RAMDACENGINE_H
#define GRAPHICS_RAMDACENGINE_H
#include "graphics/Engine.h"
#define NV2A_REG_PRAMDAC_CURSOR_POS (0x300)
#define NV2A_REG_PRAMDAC_NVPLL (0x500)
#define NV2A_REG_PRAMDAC_MPLL (0x504)
#define NV2A_REG_PRAMDAC_VPLL (0x508)
#define NV2A_REG_PRAMDAC_PLL_CONTROL (0x50C)
#define NV2A_REG_PRAMDAC_UNKNOWN_630 (0x630)
#define NV2A_REG_PRAMDAC_VDISPEND (0x800)
#define NV2A_REG_PRAMDAC_VTOTAL (0x804)
#define NV2A_REG_PRAMDAC_VCRTC (0x808)
#define NV2A_REG_PRAMDAC_VSYNCSTART (0x80C)
#define NV2A_REG_PRAMDAC_VSYNCEND (0x810)
#define NV2A_REG_PRAMDAC_VVALIDSTART (0x814)
#define NV2A_REG_PRAMDAC_VVALIDEND (0x818)
#define NV2A_REG_PRAMDAC_HDISPEND (0x820)
#define NV2A_REG_PRAMDAC_HTOTAL (0x824)
#define NV2A_REG_PRAMDAC_HCRTC (0x828)
#define NV2A_REG_PRAMDAC_HSYNCSTART (0x82C)
#define NV2A_REG_PRAMDAC_HSYNCEND (0x830)
#define NV2A_REG_PRAMDAC_HVALIDSTART (0x834)
#define NV2A_REG_PRAMDAC_HVALIDEND (0x838)
#define NV2A_REG_PRAMDAC_UNKNOWN_84C (0x84C)
#define NV2A_REG_PRAMDAC_UNKNOWN_880 (0x880)
#define NV2A_REG_PRAMDAC_UNKNOWN_884 (0x884)
#define NV2A_REG_PRAMDAC_UNKNOWN_888 (0x888)
#define NV2A_REG_PRAMDAC_UNKNOWN_88C (0x88C)
#define NV2A_REG_PRAMDAC_UNKNOWN_890 (0x890)
#define NV2A_REG_PRAMDAC_UNKNOWN_894 (0x894)
#define NV2A_REG_PRAMDAC_UNKNOWN_898 (0x898)
#define NV2A_REG_PRAMDAC_UNKNOWN_89C (0x89C)
#define NV2A_REG_PRAMDAC_UNKNOWN_8C4 (0x8C4)
class Nv2a;
class RamDacEngine : public Engine
{
public:
RamDacEngine( Nv2a &nv2a );
virtual ~RamDacEngine();
virtual const char* GetName() { return "NV2A RAMDAC"; }
virtual bool MmioWrite32( u32 reg, u32 data );
virtual u32 MmioRead32( u32 reg );
virtual u32 GetRegBase() { return NV2A_MMIO_BASE_PRAMDAC; }
private:
u32 m_regs[ 1024 ];
};
#endif /*GRAPHICS_RAMDACENGINE_H*/

View file

@ -0,0 +1,38 @@
#include "graphics/StrapsEngine.h"
#include "graphics/Nv2a.h"
StrapsEngine::StrapsEngine( Nv2a &nv2a ) : Engine( nv2a ),
m_reg_straps0_primary( 0 )
{
m_nv2a.RegisterEngineMmio( NV2A_MMIO_BASE_PSTRAPS, this );
}
StrapsEngine::~StrapsEngine()
{
}
bool
StrapsEngine::MmioWrite32( u32 reg, u32 data )
{
switch( reg ) {
case NV2A_REG_PSTRAPS_STRAPS0_PRIMARY:
m_reg_straps0_primary = data;
return true;
default:
return Engine::MmioWrite32( reg, data );
}
}
u32
StrapsEngine::MmioRead32( u32 reg )
{
switch( reg ) {
case NV2A_REG_PSTRAPS_STRAPS0_PRIMARY:
return m_reg_straps0_primary;
default:
return Engine::MmioRead32( reg );
}
}

View file

@ -0,0 +1,28 @@
#ifndef GRAPHICS_STRAPSENGINE_H
#define GRAPHICS_STRAPSENGINE_H
#include "graphics/Engine.h"
#define NV2A_REG_PSTRAPS_STRAPS0_PRIMARY (0x000)
class Nv2a;
class StrapsEngine : public Engine
{
public:
StrapsEngine( Nv2a& );
virtual ~StrapsEngine();
virtual const char* GetName() { return "NV2A Straps Engine"; }
virtual bool MmioWrite32( u32 reg, u32 data );
virtual u32 MmioRead32( u32 reg );
virtual u32 GetRegBase() { return NV2A_MMIO_BASE_PSTRAPS; }
private:
u32 m_reg_straps0_primary;
};
#endif /*GRAPHICS_STRAPSENGINE_H*/

View file

@ -0,0 +1,25 @@
#include "graphics/UnknownEngine.h"
UnknownEngine::UnknownEngine( Nv2a &nv2a ) : Engine( nv2a ),
m_fb( 0 )
{
}
UnknownEngine::~UnknownEngine()
{
}
bool
UnknownEngine::MmioWrite32( u32 reg, u32 data )
{
switch( reg ) {
// Cromwell writes the framebuffer address here as a video overlay location.
case 0x608000:
m_fb = data;
return true;
default:
return Engine::MmioWrite32( reg, data );
}
}

View file

@ -0,0 +1,24 @@
#ifndef GRAPHICS_UNKNOWNENGINE_H
#define GRAPHICS_UNKNOWNENGINE_H
#include "graphics/Engine.h"
class Nv2a;
class UnknownEngine : public Engine
{
public:
UnknownEngine( Nv2a& );
virtual ~UnknownEngine();
virtual const char* GetName() { return "Unknown Engine"; }
virtual u32 GetRegBase() { return 0; }
virtual bool MmioWrite32( u32 reg, u32 data );
private:
u32 m_fb;
};
#endif /*GRAPHICS_UNKNOWNENGINE_H*/

View file

@ -0,0 +1,37 @@
#include "graphics/VgaEmulationEngine.h"
#include "graphics/Nv2a.h"
#include "Log.h"
VgaEmulationEngine::VgaEmulationEngine( Nv2a &nv2a ) : Engine( nv2a ),
m_crtIndex( 0 ),
m_crtData( 0 )
{
m_nv2a.RegisterEngineMmio( NV2A_MMIO_BASE_PRMCIO, this );
}
VgaEmulationEngine::~VgaEmulationEngine()
{
}
bool
VgaEmulationEngine::MmioWrite8( u32 reg, u8 data )
{
switch( reg ) {
case 0x3C0:
//ignore
return true;
case 0x3D4:
m_crtIndex = data;
return true;
case 0x3D5:
DPRINT( "CRT write %02x->%02x", m_crtIndex, data );
m_crtData = data;
return true;
default:
return Engine::MmioWrite8( reg, data );
}
}

View file

@ -0,0 +1,26 @@
#ifndef GRAPHICS_VGAEMULATIONENGINE_H
#define GRAPHICS_VGAEMULATIONENGINE_H
#include "graphics/Engine.h"
class Nv2a;
class VgaEmulationEngine : public Engine
{
public:
VgaEmulationEngine( Nv2a& );
virtual ~VgaEmulationEngine();
virtual const char* GetName() { return "NV2A VGA Emulation Engine"; }
virtual bool MmioWrite8( u32 reg, u8 data );
virtual u32 GetRegBase() { return NV2A_MMIO_BASE_PRMCIO; }
private:
u8 m_crtIndex;
u8 m_crtData;
};
#endif /*GRAPHICS_VGAEMULATION_H*/

View file

@ -0,0 +1,28 @@
#include "graphics/Nv2a.h"
#include "graphics/VgaSequencerEngine.h"
VgaSequencerEngine::VgaSequencerEngine( Nv2a &nv2a ) : Engine( nv2a )
{
m_nv2a.RegisterEngineMmio( NV2A_MMIO_BASE_PRMVIO, this );
}
VgaSequencerEngine::~VgaSequencerEngine()
{
}
bool
VgaSequencerEngine::MmioWrite8( u32 reg, u8 data )
{
switch( reg ) {
case 0x3C4:
case 0x3C5:
case 0x3CE:
case 0x3CF:
//ignore for now
return true;
default:
return Engine::MmioWrite8( reg, data );
}
}

View file

@ -0,0 +1,20 @@
#ifndef GRAPHICS_VGASEQUENCERENGINE_H
#define GRAPHICS_VGASEQUENCERENGINE_H
#include "graphics/Engine.h"
class VgaSequencerEngine : public Engine
{
public:
VgaSequencerEngine( Nv2a &nv2a );
virtual ~VgaSequencerEngine();
virtual const char* GetName() { return "NV2A VGA Sequencer Engine"; }
virtual bool MmioWrite8( u32 reg, u8 data );
virtual u32 GetRegBase() { return NV2A_MMIO_BASE_PRMVIO; }
};
#endif /*GRAPHICS_VGASEQUENCERENGINE_H*/

View file

@ -0,0 +1,26 @@
#include "graphics/VideoOverlayEngine.h"
#include "Log.h"
VideoOverlayEngine::VideoOverlayEngine( Nv2a &nv2a ) : Engine( nv2a ),
m_overlayBase( 0 )
{
m_nv2a.RegisterEngineMmio( NV2A_MMIO_BASE_PVIDEO, this );
}
VideoOverlayEngine::~VideoOverlayEngine()
{
}
bool
VideoOverlayEngine::MmioWrite32( u32 reg, u32 data )
{
switch( reg ) {
case 0:
m_overlayBase = data;
return true;
default:
return Engine::MmioWrite32( reg, data );
}
}

View file

@ -0,0 +1,25 @@
#ifndef GRAPHICS_VIDEOOVERLAYENGINE_H
#define GRAPHICS_VIDEOOVERLAYENGINE_H
#include "graphics/Engine.h"
class VideoOverlayEngine : public Engine
{
public:
VideoOverlayEngine( Nv2a &nv2a );
virtual ~VideoOverlayEngine();
virtual const char* GetName() { return "Video Overlay Engine"; }
virtual u32 GetRegBase() { return NV2A_MMIO_BASE_PVIDEO; }
virtual bool MmioWrite32( u32 reg, u32 data );
u32 GetOverlayAddress() { return m_overlayBase; }
private:
u32 m_overlayBase;
};
#endif /*GRAPHICS_VIDEOOVERLAYENGINE_H*/

25
src/ide/DiskImage.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef XBVM_IDE_DISKIMAGE_H
#define XBVM_IDE_DISKIMAGE_H
#include "Types.h"
class DiskImage
{
public:
DiskImage() :
m_imageSize( 0 )
{
}
virtual ~DiskImage() {}
virtual bool FillBuffer( u8* buffer, u64 offset, int size ) = 0;
u64 GetImageSize() { return m_imageSize; }
protected:
u64 m_imageSize;
};
#endif /*XBVM_IDE_DISKIMAGE_H*/

43
src/ide/DvdDrive.cpp Normal file
View file

@ -0,0 +1,43 @@
#include <cstring>
#include "ide/DvdDrive.h"
#include "Log.h"
void
DvdDrive::OnAtapiSoftReset()
{
m_errorRegister &= ~(1 << 7);
m_status.writeFault = false;
m_status.drq = false;
m_status.correctedData = false;
m_status.busy = false;
DPRINT( "Yo yo yo" );
}
void
DvdDrive::OnIdentifyPacketDevice()
{
m_currentCommand = IDE_COMMAND_PACKET_IDENTIFY;
m_errorRegister = 0x00;
m_status.busy = false;
m_status.driveReady = true;
m_status.writeFault = false;
m_status.drq = true;
m_status.seekComplete = true;
m_status.correctedData = false;
m_bufferIndex = 0;
PopulateIdSector( (u16*)m_buffer );
//@todo raise irql
}
void
DvdDrive::PopulateIdSector( u16 *buffer )
{
memset( buffer, 0, IDE_DEVICE_SECTOR_SIZE );
}

25
src/ide/DvdDrive.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef XBVM_IDE_DVDDRIVE_H
#define XBVM_IDE_DVDDRIVE_H
#include "ide/IdeDevice.h"
class DvdDrive : public IdeDevice
{
public:
DvdDrive() : IdeDevice()
{
m_sectorSize = 2048;
}
virtual ~DvdDrive() {}
virtual const char* GetName() { return "IDE DVD Drive"; }
virtual void OnAtapiSoftReset();
virtual void OnIdentifyPacketDevice();
void PopulateIdSector( u16 *buffer );
};
#endif /*XBVM_IDE_DVDDRIVE_H*/

View file

@ -0,0 +1,2 @@
#include "ide/FlatDiskImage.h"

11
src/ide/FlatDiskImage.h Normal file
View file

@ -0,0 +1,11 @@
#ifndef XBVM_IDE_FLATDISKIMAGE_H
#define XBVM_IDE_FLATDISKIMAGE_H
#include "ide/DiskImage.h"
class FlatDiskImage : public DiskImage
{
};
#endif /*XBVM_IDE_FLATDISKIMAGE_H*/

226
src/ide/HardDrive.cpp Normal file
View file

@ -0,0 +1,226 @@
#include <cstring>
#include "ide/HardDrive.h"
#include "Log.h"
void
HardDrive::OnIdentifyCommand()
{
m_currentCommand = IDE_COMMAND_IDENTIFY;
m_errorRegister = 0;
m_status.busy = false;
m_status.driveReady = true;
m_status.writeFault = false;
m_status.drq = true;
m_status.seekComplete = true;
m_status.correctedData = false;
m_bufferIndex = 0;
u16 idSector[ 256 ];
PopulateIdSector( idSector );
for( int i = 0; i < 256; i++ ) {
m_buffer[ (i * 2) + 0 ] = idSector[ i ] & 0x00FF;
m_buffer[ (i * 2) + 1 ] = idSector[ i ] >> 8;
}
for( int i = 0; i < 256; i+= 8 ) {
}
//@todo raise an interrupt
}
void
HardDrive::OnSecurityUnlockCommand()
{
m_currentCommand = IDE_COMMAND_SECURITY_UNLOCK;
m_errorRegister = 0;
m_status.busy = false;
m_status.driveReady = true;
m_status.writeFault = false;
m_status.drq = true;
m_status.seekComplete = true;
m_status.correctedData = false;
m_bufferIndex = 0;
}
void
HardDrive::PopulateIdSector( u16 *buffer )
{
memset( buffer, 0, IDE_DEVICE_SECTOR_SIZE );
// Word 0: general config bit significant info
buffer[ 0 ] = 0x0040;
// Word 1: number of cylinders
buffer[ 1 ] = 16383;
// word 2: reserved
// Word 3: number of heads
buffer[ 3 ] = 16;
// Word 4: number of bytes per track
buffer[ 4 ] = 32256;
// Word 5: number of bytes per sector
buffer[ 5 ] = 512;
// Word 6: number of tracks
buffer[ 6 ] = 63;
// Words 7-9: vendor specific
// Words 10-19: Serial number
const char *serial = "6DB2WQW2 ";
for( int i = 0; i < 10; i++ ) {
buffer[ 10 + i ] = (serial[ i * 2 ] << 8) | serial[ (i * 2) + 1 ];
}
// Word 20: Buffer Type
buffer[ 20 ] = 3;
// Word 21: Buffer Size in 512 byte increments
buffer[ 21 ] = 1024;
// Word 22: # of ECC bytes available on read/write long cmds
buffer[ 22 ] = 4;
// Words 23-26: Firmware Revision
const char *firmware = "6.55 ";
for( int i = 0; i < 4; i++ ) {
buffer[ 23 + i ] = (firmware[ i * 2 ] << 8) | firmware[ (i * 2) + 1 ];
}
// Words 27-46: Model number
const char *model = "ST310211A ";
for( int i = 0; i < 40; i++ ) {
buffer[ 27 + i ] = (model[ i * 2 ] << 8) | model[ (i * 2) + 1 ];
}
// Word 47: 15-8: vendor unique
// 7-0: max number of sectors that can be transferred per interrupt
buffer[ 47 ] = 16;
// Word 48: Set to 1 if can perform dword IO, 0 if otherwise
buffer[ 48 ] = 1;
// Word 49: Capabilities
// 15-8: reserved
// 9: 1 = LBA supported
// 8: 1 = DMA supported
// 7-0: vendor unique
buffer[ 49 ] = (1<<9) | (1<<8);
// Word 50: Reserved
// Word 51: 15-8: PIO data transfer cycle timing mode
// 7-0: Vendor unique
buffer[ 51 ] = 0x0200;
// Word 52: 15-8: DMA data transfer cycle timing mode
// 7-0: Vendor unique
buffer[ 52 ] = 0x0200;
// Word 53: 15-3 Reserved
// 2 1=the fields reported in word 88 are valid
// 1 1=the fields reported in words 64-70 are valid
// 0 1=the fields reported in words 54-58 are valid
buffer[ 53 ] = 0x0007;
// Word 54: cylinders
buffer[ 54 ] = 16383;
// Word 55: heads
buffer[ 55 ] = 16;
// Word 56: sectors/track
buffer[ 56 ] = 63;
// Word 57: LSW current capacity in sectors
buffer[ 57 ] = (16383*16*63) & 0xFFFF;
// Word 58: MSW current capacity in sectors
buffer[ 58 ] = (16383*16*63) >> 16;
// Word 59: 15-9 reserved
// 8 1=multiple sector setting is valid
// 7-0 current setting for number of sectors that can be
// transferred per interrupt on R/W multiple commands
buffer[ 59 ] = 0x0000;
// Words 60-61: Total number of LBA sectors
int numSectors = 19541088;
buffer[ 60 ] = numSectors & 0xFFFF;
buffer[ 61 ] = numSectors >> 16;
// Word 62: 15-8 single word DMA transfer mode active
// 7-0 single word DMA transfer mode supported
buffer[ 62 ] = 0x0000;
// Word 63: 15-8 multiword DMA transfer mode active
// 7-0 multiword DMA transfer mode supported
buffer[ 63 ] = 0x0;
// Word 64: PIO modes supported
buffer[ 64 ] = 0x0000;
// Word 65-68: PIO/DMA cycle time (nanoseconds)
for( int i = 65; i <= 68; i++ ) {
buffer[ i ] = 120;
}
// Words 69-79: Reserved
// Word 80: 15-7 reserved
// 6 supports ATA/ATAPI-6
// 5 supports ATA/ATAPI-5
// 4 supports ATA/ATAPI-4
// 3 supports ATA-3
// 2 supports ATA-2
// 1 supports ATA-1
// 0 reserved
buffer[ 80 ] = 0x007E;
// Word 81: Minor Revision Number
buffer[ 81 ] = 0x0000;
// Word 82: 15 obsolete
// 14 NOP supported
// 13 READ BUFFER command supported
// 12 WRITE BUFFER command supported
// 11 obsolete
// 10 Host protected area feature set supported
// 9 DEVICE RESET command supported
// 8 SERVICE interrupt supported
// 7 RELEASE interrupt supported
// 6 look-ahead supported
// 5 write cache supported
// 4 PACKET command feature set supported
// 3 supports power management feature set
// 2 supports removable media feature set
// 1 supports securite mode feature set
// 0 SMART feature set supported
buffer[ 82 ] = (1 << 14);
buffer[ 83 ] = (1 << 14) | (1 << 13 ) | (1 << 12) | (1 << 10);
buffer[ 84 ] = (1 << 14);
buffer[ 85 ] = (1 << 14);
buffer[ 86 ] = (1 << 14) | (1 << 13 ) | (1 << 12) | (1 << 10);
buffer[ 87 ] = (1 << 14);
buffer[ 88 ] = 0x0000;
buffer[ 93 ] = (1 << 14) | (1 << 13) | (1 << 0);
// Words 100-103: 48-bit total number of sectors
buffer[ 100 ] = numSectors & 0xffff;
buffer[ 101 ] = (numSectors >> 16) & 0xffff;
buffer[ 102 ] = 0;
buffer[ 103 ] = 0;
// Word 128: Security Settings
buffer[ 128 ] = (1 << 2);
}

30
src/ide/HardDrive.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef XBVM_IDE_HARDDRIVE_H
#define XBVM_IDE_HARDDRIVE_H
#include "ide/IdeDevice.h"
#include "ide/NullDiskImage.h"
class HardDrive : public IdeDevice
{
public:
HardDrive() : IdeDevice()
{
m_diskImage = new NullDiskImage( 19541088L * 512 );
m_sectorSize = 512;
}
virtual ~HardDrive()
{
delete m_diskImage;
}
virtual const char* GetName() { return "IDE Hard Drive"; }
virtual void OnIdentifyCommand();
virtual void OnSecurityUnlockCommand();
void PopulateIdSector( u16 *sector );
};
#endif /*XBVM_IDE_HARDDRIVE_H*/

231
src/ide/IdeController.cpp Normal file
View file

@ -0,0 +1,231 @@
#include "ide/DvdDrive.h"
#include "ide/HardDrive.h"
#include "ide/IdeController.h"
#include "Log.h"
#include "Mem.h"
IdeController *g_ide;
IdeController::IdeController() : PciDevice( 0, 9, 0, PCI_NVIDIA_IDE_DEVICE_ID ),
m_iobase( 0 ),
m_features( 0 ),
m_sectorCount( 0 ),
m_lbaLow( 0 ),
m_lbaMid( 0 ),
m_lbaHigh( 0 ),
m_status( 0x0 ),
m_dataOffset( 0 ),
m_currentDrive( 0 )
{
g_mem->AddDeviceToIoMap( this, 0x1f0 );
g_mem->AddDeviceToIoMap( this, 0x1f1 );
g_mem->AddDeviceToIoMap( this, 0x1f2 );
g_mem->AddDeviceToIoMap( this, 0x1f3 );
g_mem->AddDeviceToIoMap( this, 0x1f4 );
g_mem->AddDeviceToIoMap( this, 0x1f5 );
g_mem->AddDeviceToIoMap( this, 0x1f6 );
g_mem->AddDeviceToIoMap( this, 0x1f7 );
g_mem->AddDeviceToIoMap( this, 0x3f6 );
m_drives[ 0 ] = new HardDrive();
m_drives[ 1 ] = new DvdDrive();
}
IdeController::~IdeController()
{
}
bool
IdeController::IoRead16( u16 port, u16 &data )
{
switch( port ) {
case IDE_PORT_DATA:
if( CurrentDrive() == NULL ) {
data = 0xFFFF;
return true;
}
return CurrentDrive()->ReadData( data );
default:
return PciDevice::IoRead16( port, data );
}
}
bool
IdeController::IoRead8( u16 port, u8 &data )
{
switch( port ) {
case IDE_PORT_ALT_STATUS:
if( CurrentDrive() == NULL ) {
data = 0;
return true;
}
data = CurrentDrive()->ReadStatus();
return true;
case IDE_PORT_STATUS:
if( CurrentDrive() == NULL ) {
data = 0;
return true;
}
data = CurrentDrive()->ReadStatus();
return true;
default:
break;
}
//
// if( port >= m_iobase && port < m_iobase + 6 ) {
// switch( port - m_iobase ) {
// default:
// return PciDevice::IoRead8( port, data );
// }
// }
//
return PciDevice::IoRead8( port, data );
}
bool
IdeController::IoWrite16( u16 port, u16 &data )
{
switch( port ) {
case IDE_PORT_DATA:
if( CurrentDrive() == NULL ) {
return true;
}
return CurrentDrive()->WriteData( data );
default:
return PciDevice::IoWrite16( port, data );
}
}
bool
IdeController::IoWrite8( u16 port, u8 &data )
{
switch( port ) {
case IDE_PORT_ALT_STATUS:
DPRINT( "IDE DevCntrl %02x", data );
return true;
case IDE_PORT_COMMAND:
if( CurrentDrive() == NULL ) {
return true;
}
return CurrentDrive()->WriteCommand( data );
case IDE_PORT_CYLINDER_HIGH:
for( int i = 0; i < IDE_NUM_DRIVES; i++ ) {
if( m_drives[ i ] == NULL ) {
continue;
}
m_drives[ i ]->WriteCylinderHigh( data );
}
return true;
case IDE_PORT_CYLINDER_LOW:
for( int i = 0; i < IDE_NUM_DRIVES; i++ ) {
if( m_drives[ i ] == NULL ) {
continue;
}
m_drives[ i ]->WriteCylinderLow( data );
}
return true;
case IDE_PORT_DRIVE_AND_HEAD:
if( (data & IDE_DRIVE_SELECT_RESERVED) != IDE_DRIVE_SELECT_RESERVED ) {
EPRINT( "IO Write 0x%04x (%02x): not 1x1xxxxxb", IDE_PORT_DRIVE_AND_HEAD, data );
return false;
}
if( m_currentDrive != ((data >> 4) & 0x01) ) {
DPRINT( "Switching drive to %d", ((data >> 4) & 0x01) );
}
m_currentDrive = ( (data >> 4) & 0x01 );
for( int i = 0; i < IDE_NUM_DRIVES; i++ ) {
if( m_drives[ i ] == NULL ) {
continue;
}
m_drives[ i ]->WriteLbaMode( (data >> 6) & 0x01 );
m_drives[ i ]->WriteHeadNum( data & 0x0F );
}
return true;
case IDE_PORT_FEATURES:
for( int i = 0; i < IDE_NUM_DRIVES; i++ ) {
if( m_drives[ i ] == NULL ) {
continue;
}
m_drives[ i ]->WriteFeatures( data );
}
return true;
case IDE_PORT_SECTOR_COUNT:
for( int i = 0; i < IDE_NUM_DRIVES; i++ ) {
if( m_drives[ i ] == NULL ) {
continue;
}
m_drives[ i ]->WriteSectorCount( data );
}
return true;
case IDE_PORT_SECTOR_NUM:
for( int i = 0; i < IDE_NUM_DRIVES; i++ ) {
if( m_drives[ i ] == NULL ) {
continue;
}
m_drives[ i ]->WriteSectorNum( data );
}
return true;
default:
break;
}
if( port >= m_iobase && port < m_iobase + 6 ) {
switch( port - m_iobase ) {
case 0: // this is labeled as "stop bus mastering" by cromwell (writes 0x00)
case 2: // this is labeled as "DMA possible for both drives" by cromwell (writes 0x62)
return true;
default:
return PciDevice::IoWrite8( port, data );
}
}
return PciDevice::IoWrite8( port, data );
}
bool
IdeController::WriteBAR( int num, u32 &data )
{
switch( num ) {
case 0:
case 1:
case 2:
case 3:
case 5:
if( data != 0 ) {
return PciDevice::WriteBAR( num, data );
}
return true;
case 4:
if( (data & 1) != 1 ) {
EPRINT( "Trying to map IDE to non io space %08x", data );
return false;
}
m_iobase = data & 0xFFFE;
DPRINT( "Assigning IDE to %x", data );
for( int i = 0; i < 8; i++ ) {
g_mem->AddDeviceToIoMap( this, m_iobase + i );
}
return true;
default:
return PciDevice::WriteBAR( num, data );
}
}

69
src/ide/IdeController.h Normal file
View file

@ -0,0 +1,69 @@
#ifndef XBVM_IDE_IDECONTROLLER_H
#define XBVM_IDE_IDECONTROLLER_H
#include "PciDevice.h"
#define IDE_PORT_DATA (0x1F0)
#define IDE_PORT_FEATURES (0x1F1)
#define IDE_PORT_SECTOR_COUNT (0x1F2)
#define IDE_PORT_SECTOR_NUM (0x1F3)
#define IDE_PORT_CYLINDER_LOW (0x1F4)
#define IDE_PORT_CYLINDER_HIGH (0x1F5)
#define IDE_PORT_DRIVE_AND_HEAD (0x1F6)
#define IDE_PORT_COMMAND (0x1F7)
#define IDE_PORT_STATUS (0x1F7)
#define IDE_PORT_DEVICE_CONTROL (0x3F6)
#define IDE_PORT_ALT_STATUS (0x3F6)
#define IDE_DRIVE_SELECT_RESERVED (0xA0) // 1010 0000
#define IDE_NUM_DRIVES (2)
class IdeDevice;
class IdeController : public PciDevice
{
public:
IdeController();
virtual ~IdeController();
virtual bool IoRead16( u16 port, u16 &data );
virtual bool IoRead8( u16 port, u8 &data );
virtual bool IoWrite16( u16 port, u16 &data );
virtual bool IoWrite8( u16 port, u8 &data );
virtual bool WriteBAR( int num, u32 &data );
virtual const char* GetName( void ) { return "IDE Controller"; }
private:
IdeController( const IdeController& );
const IdeController& operator=(IdeController&);
u16 m_iobase;
u8 m_features;
u8 m_sectorCount;
u8 m_lbaLow;
u8 m_lbaMid;
u8 m_lbaHigh;
u8 m_status;
u16 m_dataBuffer[256];
int m_dataOffset;
u16 m_idDrive[256];
int m_currentDrive;
IdeDevice* m_drives[ IDE_NUM_DRIVES ];
IdeDevice* CurrentDrive()
{
return m_drives[ m_currentDrive ];
}
};
extern IdeController *g_ide;
#endif /*XBVM_IDE_IDECONTROLLER_H*/

345
src/ide/IdeDevice.cpp Normal file
View file

@ -0,0 +1,345 @@
#include "ide/IdeController.h"
#include "ide/IdeDevice.h"
#include "Log.h"
#define INDEX_PULSE_CYCLE (10)
bool
IdeDevice::ReadData( u16 &data )
{
if( m_status.drq == false ) {
EPRINT( "IO read(0x%04x) with drq == 0; last command was 0x%02x %d %d %d",
IDE_PORT_DATA, m_currentCommand, m_numSectors, m_bufferIndex, m_bufferSize );
return false;
}
switch( m_currentCommand ) {
case IDE_COMMAND_IDENTIFY:
m_status.busy = false;
m_status.driveReady = true;
m_status.writeFault = false;
m_status.seekComplete = true;
m_status.correctedData = false;
m_status.err = false;
data = (m_buffer[ m_bufferIndex ]) | (m_buffer[ m_bufferIndex+1 ] << 8);
m_bufferIndex += 2;
if( m_bufferIndex >= IDE_DEVICE_SECTOR_SIZE ) {
m_status.drq = false;
}
return true;
case IDE_COMMAND_READ_SECTORS_EXT: // 0x24
case IDE_COMMAND_READ_SECTORS_W_RETRIES: // 0x20
case IDE_COMMAND_READ_SECTORS_WO_RETRIES: // 0x21
data = m_buffer[ m_bufferIndex++ ];
data |= m_buffer[ m_bufferIndex++ ] << 8;
//DPRINT( "%04x: %04x", m_bufferIndex - 2, data );
if( m_bufferIndex >= m_bufferSize ) {
m_status.busy = false;
m_status.driveReady = true;
m_status.writeFault = false;
m_status.seekComplete = true;
m_status.correctedData = false;
m_status.err = false;
if( m_numSectors == 0 ) {
m_status.drq = false;
}
else {
m_status.drq = true;
m_status.seekComplete = true;
EPRINT( "More sectors %d %d", m_numSectors, m_bufferIndex );
return false;
}
}
return true;
default:
EPRINT( "Unknown command in read %02x on %s", m_currentCommand, GetName() );
return false;
}
}
bool
IdeDevice::WriteData( u16 &data )
{
// check bochs
switch( m_currentCommand ) {
case IDE_COMMAND_SECURITY_UNLOCK:
m_status.busy = false;
m_status.driveReady = true;
m_status.writeFault = false;
m_status.seekComplete = true;
m_status.correctedData = false;
m_status.err = false;
return true;
default:
EPRINT( "Unknown command in write %02x %04x on %s", m_currentCommand, data, GetName() );
return false;
}
}
bool
IdeDevice::WriteCommand( u8 &data )
{
int command;
//@todo lower irql on command writes
//@todo error on write during busy bit set
// not really sure why we do this, but bochs does it
if( (data & 0xF0) == 0x10 ) {
command = 0x10;
}
else {
command = data;
}
DPRINT( "Command 0x%02x", command );
switch( command ) {
case IDE_COMMAND_ATAPI_SOFT_RESET: // 0x08
OnAtapiSoftReset();
return true;
case IDE_COMMAND_READ_SECTORS_EXT: // 0x24
case IDE_COMMAND_READ_MULTIPLE_SECTORS_EXT: // 0x29
m_lba48 = true;
// fallthrough
case IDE_COMMAND_READ_SECTORS_W_RETRIES: // 0x20
case IDE_COMMAND_READ_SECTORS_WO_RETRIES: // 0x21
case IDE_COMMAND_READ_MULTIPLE_SECTORS: // 0xC4
return PerformRead( command );
case IDE_COMMAND_IDENTIFY: // 0xEC
//@todo abort command if device not present
OnIdentifyCommand();
return true;
case IDE_COMMAND_PACKET_IDENTIFY: // 0xA1
OnIdentifyPacketDevice();
return true;
case IDE_COMMAND_SECURITY_UNLOCK: // 0xF2
//@todo abort command if device not present
OnSecurityUnlockCommand();
return true;
default:
EPRINT( "Unknown command %02x sent to %s", data, GetName() );
return false;
}
}
void
IdeDevice::WriteCylinderHigh( u8 &data )
{
m_hob.hcyl = m_cylinderNum >> 8;
m_cylinderNum = (data << 8) | (m_cylinderNum & 0x00FF);
}
void
IdeDevice::WriteCylinderLow( u8 &data )
{
m_hob.lcyl = m_cylinderNum & 0x00FF;
m_cylinderNum = (m_cylinderNum & 0xFF00) | data;
}
void
IdeDevice::WriteFeatures( u8 &data )
{
m_hob.feature = m_features;
m_features = data;
}
void
IdeDevice::WriteHeadNum( u8 headNum )
{
m_headNum = headNum;
}
void
IdeDevice::WriteLbaMode( u8 lbaMode )
{
m_lbaMode = lbaMode;
}
void
IdeDevice::WriteSectorCount( u8 &data )
{
m_hob.nsector = m_sectorCount;
m_sectorCount = data;
}
void
IdeDevice::WriteSectorNum( u8 &data )
{
m_hob.sector = m_sectorNum;
m_sectorNum = data;
}
u8
IdeDevice::ReadStatus()
{
u8 status = 0;
status |= m_status.busy << 7;
status |= m_status.driveReady << 6;
status |= m_status.writeFault << 5;
status |= m_status.seekComplete << 4;
status |= m_status.drq << 3;
status |= m_status.correctedData << 2;
status |= m_status.indexPulse << 1;
status |= m_status.err << 0;
m_status.indexPulseCount++;
m_status.indexPulse = false;
if( m_status.indexPulseCount >= INDEX_PULSE_CYCLE ) {
m_status.indexPulse = true;
m_status.indexPulseCount = 0;
}
return status;
}
void
IdeDevice::AbortCommand()
{
m_currentCommand = 0x00;
m_status.busy = false;
m_status.driveReady = true;
m_status.err = true;
m_status.drq = false;
m_status.correctedData = false;
m_errorRegister = 0x04; // command ABORTED
m_bufferIndex = 0;
//@todo raise irql
}
bool
IdeDevice::IncrementAddresses( u64 sector )
{
m_sectorCount--;
m_numSectors--;
if( m_lbaMode ) {
sector++;
if( m_lba48 ) {
m_hob.hcyl = (u8)((sector >> 40) & 0xFF);
m_hob.lcyl = (u8)((sector >> 32) & 0xFF);
m_hob.sector = (u8)((sector >> 24) & 0xFF);
m_cylinderNum = (u16)((sector >> 8) & 0xFFFF);
m_sectorNum = (u8)((sector >> 0) & 0xFF);
}
else {
m_headNum = (u8)((sector >> 24) & 0xF);
m_cylinderNum = (u8)((sector >> 8) & 0xFFFF);
m_sectorNum = (u8)((sector >> 0) & 0xFF);
}
return true;
}
else {
EPRINT( "C/H/S mode not supported" );
return false;
}
}
bool
IdeDevice::PerformRead( u8 command )
{
u64 sectorNum = 0;
u8 *buffer = m_buffer;
u32 sectorCount;
if( !m_lbaMode && ( m_headNum == 0 ) &&
( m_cylinderNum == 0 ) && ( m_sectorNum == 0 ) ) {
AbortCommand();
return true;
}
if( m_lbaMode ) {
if( m_lba48 ) {
sectorNum = ((u64)m_hob.hcyl << 40) | ((u64)m_hob.lcyl << 32) |
((u64)m_hob.sector << 24) | ((u64)m_cylinderNum << 8) |
((u64)m_sectorNum);
}
else {
sectorNum = (m_headNum << 24) | (m_cylinderNum << 8) | m_sectorNum;
}
}
else {
EPRINT( "C/H/S mode not supported" );
return false;
}
DPRINT( "Yo, READ SECTORS %02x %lx", command, sectorNum * m_sectorSize );
if( m_lba48 ) {
if( m_sectorCount == 0 ) {
m_numSectors = 256;
}
else {
m_numSectors = m_sectorCount;
}
}
else {
if( (m_sectorCount == 0) && (m_hob.nsector == 0) ) {
m_numSectors = 0;
}
else {
m_numSectors = (m_hob.nsector << 8) | m_sectorCount;
}
}
if( (command == IDE_COMMAND_READ_MULTIPLE_SECTORS) || (command == IDE_COMMAND_READ_MULTIPLE_SECTORS_EXT) ) {
if( m_multipleSectors == 0 ) {
AbortCommand();
return true;
}
if( m_numSectors > m_multipleSectors ) {
sectorCount = m_multipleSectors;
}
else {
sectorCount = m_numSectors;
}
}
else {
sectorCount = 1;
}
m_currentCommand = command;
m_bufferSize = sectorCount * m_sectorSize;
do {
if( !m_diskImage->FillBuffer( buffer, sectorNum * m_sectorSize, m_sectorSize ) ) {
AbortCommand();
return true;
}
IncrementAddresses( sectorCount );
buffer += 512;
} while( --sectorCount > 0 );
m_errorRegister = 0;
m_status.busy = false;
m_status.driveReady = true;
m_status.seekComplete = true;
m_status.drq = true;
m_status.correctedData = false;
m_bufferIndex = 0;
//@todo raise irql
//DPRINT( "Raise IRQL" );
return true;
}

166
src/ide/IdeDevice.h Normal file
View file

@ -0,0 +1,166 @@
#ifndef XBVM_IDE_IDEDEVICE_H
#define XBVM_IDE_IDEDEVICE_H
#include "ide/DiskImage.h"
#include "Device.h"
#define IDE_DEVICE_SECTOR_SIZE (512)
#define IDE_DEVICE_MAX_MULTIPLE_SECTORS (16)
#define IDE_COMMAND_ATAPI_SOFT_RESET (0x08)
#define IDE_COMMAND_READ_SECTORS_W_RETRIES (0x20)
#define IDE_COMMAND_READ_SECTORS_WO_RETRIES (0x21)
#define IDE_COMMAND_READ_SECTORS_EXT (0x24)
#define IDE_COMMAND_READ_MULTIPLE_SECTORS_EXT (0x29)
#define IDE_COMMAND_PACKET_IDENTIFY (0xA1)
#define IDE_COMMAND_READ_MULTIPLE_SECTORS (0xC4)
#define IDE_COMMAND_IDENTIFY (0xEC)
#define IDE_COMMAND_SECURITY_UNLOCK (0xF2)
class DiskImage;
class IdeDevice : public Device
{
public:
IdeDevice() :
m_status(),
m_errorRegister( 0x01 ), //diagnostic code: no error
m_headNum( 0 ),
m_sectorCount( 1 ),
m_sectorNum( 1 ),
m_cylinderNum( 0 ),
m_bufferSize( 0 ),
m_bufferIndex( 0 ),
m_drqIndex( 0 ),
m_currentCommand( 0x00 ),
m_multipleSectors( 0 ),
m_lbaMode( 0 ),
m_packetDma( false ),
m_mdmaMode( 0 ),
m_udmaMode( 0 ),
m_control(),
m_resetInProgress( 0 ),
m_features( 0 ),
m_hob(),
m_numSectors( 0 ),
m_lba48( false ),
m_diskImage( NULL ),
m_sectorSize( 0 )
{
m_status.busy = false;
m_status.driveReady = true;
m_status.writeFault = false;
m_status.seekComplete = true;
m_status.drq = false;
m_status.correctedData = false;
m_status.indexPulse = false;
m_status.indexPulseCount = 0;
m_status.err = false;
m_control.reset = false;
m_control.disableIrq = false;
m_hob.feature = 0;
m_hob.nsector = 0;
m_hob.sector = 0;
m_hob.lcyl = 0;
m_hob.hcyl = 0;
}
virtual ~IdeDevice()
{ }
bool ReadData( u16 &data );
bool WriteData( u16 &data );
bool WriteCommand( u8 &data );
void WriteCylinderHigh( u8 &data );
void WriteCylinderLow( u8 &data );
void WriteFeatures( u8 &data );
void WriteHeadNum( u8 headNum );
void WriteLbaMode( u8 lbaMode );
void WriteSectorCount( u8 &data );
void WriteSectorNum( u8 &data );
u8 ReadStatus();
protected:
struct {
bool busy;
bool driveReady;
bool writeFault;
bool seekComplete;
bool drq;
bool correctedData;
bool indexPulse;
int indexPulseCount;
bool err;
} m_status;
u8 m_errorRegister;
u8 m_headNum;
union {
u8 m_sectorCount;
struct {
//@todo Only little endian
u8 c_d : 1;
u8 i_o : 1;
u8 rel : 1;
u8 tag : 5;
} m_interruptReason;
};
u8 m_sectorNum;
union {
u16 m_cylinderNum;
u16 m_byteCount;
};
u8 m_buffer[ (IDE_DEVICE_MAX_MULTIPLE_SECTORS * IDE_DEVICE_SECTOR_SIZE) + 4 ];
u32 m_bufferSize;
u32 m_bufferIndex;
u32 m_drqIndex;
u8 m_currentCommand;
u8 m_multipleSectors;
u8 m_lbaMode;
bool m_packetDma;
u8 m_mdmaMode;
u8 m_udmaMode;
struct {
bool reset;
bool disableIrq;
} m_control;
u8 m_resetInProgress;
u8 m_features;
struct {
u8 feature;
u8 nsector;
u8 sector;
u8 lcyl;
u8 hcyl;
} m_hob;
u32 m_numSectors;
bool m_lba48;
DiskImage* m_diskImage;
int m_sectorSize;
void AbortCommand();
bool IncrementAddresses( u64 sector );
bool PerformRead( u8 command );
virtual void OnAtapiSoftReset() { AbortCommand(); }
virtual void OnIdentifyCommand() { AbortCommand(); }
virtual void OnIdentifyPacketDevice() { AbortCommand(); }
virtual void OnSecurityUnlockCommand() { AbortCommand(); }
};
#endif /*XBVM_IDE_IDEDEVICE_H*/

33
src/ide/NullDiskImage.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef XBVM_IDE_NULLDISKIMAGE_H
#define XBVM_IDE_NULLDISKIMAGE_H
#include <cstring>
#include "ide/DiskImage.h"
class NullDiskImage : public DiskImage
{
public:
NullDiskImage( u64 imageSize ) : DiskImage()
{
m_imageSize = imageSize;
}
virtual bool FillBuffer( u8 *buffer, u64 offset, int size )
{
if( (offset + size) > m_imageSize ) {
return false;
}
memset( buffer, 0, size );
if( offset == 0x600 ) {
buffer[ 0 ] = 'B';
buffer[ 1 ] = 'R';
buffer[ 2 ] = 'F';
buffer[ 3 ] = 'R';
}
return true;
}
};
#endif /*XBVM_IDE_NULLDISKIMAGE_H*/

View file

@ -0,0 +1,158 @@
#include "interp/InterpreterCpu.h"
#include "Log.h"
#include "Mem.h"
InterpreterCpu::InterpreterCpu() :
eax( 0 ),
ebx( 0 ),
ecx( 0 ),
edx( 0 ),
edi( 0 ),
esi( 0 ),
ebp( 0 ),
esp( 0 ),
eip( 0 ),
eflags( 0 ),
cs_base( 0 ),
cs_limit( 0 ),
gdt_size( 0 ),
gdt_offset( 0 ),
idt_size( 0 ),
idt_offset( 0 ),
m_instrOffset( 0 ),
m_modRm( 0 ),
m_operandSizeOverride( false ),
m_addressSizeOverride( false ),
m_csSegmentOverride( false ),
m_dsSegmentOverride( false ),
m_esSegmentOverride( false ),
m_fsSegmentOverride( false ),
m_gsSegmentOverride( false ),
m_ssSegmentOverride( false )
{
}
InterpreterCpu::~InterpreterCpu()
{
}
int
InterpreterCpu::Initialize()
{
return 0;
}
int
InterpreterCpu::Run( void )
{
int status = 0;
while( (status = SingleStep()) == 0 );
return status;
}
int
InterpreterCpu::SingleStep( void )
{
int ret = 0;
u8 opcode = 0;
bool loopForPrefix = true;
m_instrOffset = 0;
m_modRm = 0;
m_operandSizeOverride = false;
m_addressSizeOverride = false;
m_csSegmentOverride = false;
m_dsSegmentOverride = false;
m_esSegmentOverride = false;
m_fsSegmentOverride = false;
m_gsSegmentOverride = false;
m_ssSegmentOverride = false;
while( loopForPrefix )
{
opcode = ReadNextByte();
switch( opcode ) {
case 0x2e:
m_csSegmentOverride = true;
break;
case 0x66:
m_operandSizeOverride = true;
break;
case 0x67:
m_addressSizeOverride = true;
break;
default:
loopForPrefix = false;
}
}
switch( opcode ) {
case 0xEB: //jmp absolute one byte
eip += 2 + (s8)ReadNextByte();
eip -= m_instrOffset;
break;
case 0x0f:
ret = DecodeMultibyte();
break;
default:
EPRINT( "Unknown Opcode %02x at %08x", opcode, eip );
ret = -1;
}
if( 0 == ret ) {
eip += m_instrOffset;
}
return ret;
}
int
InterpreterCpu::DecodeMultibyte( void )
{
u8 opcode = ReadNextByte();
switch( opcode ) {
case 0x01:
return DecodeGroup7();
default:
EPRINT( "Unknown Opcode 0f %02x at %08x", opcode, eip );
return -1;
}
}
int
InterpreterCpu::DecodeGroup7( void )
{
m_modRm = ReadNextByte();
switch( ((m_modRm >> 3) & 0x7) )
{
case 2:
return Lgdt();
case 3:
return Lidt();
default:
EPRINT( "Unknown Group 7 Opcode 0f 01 %02x %d", m_modRm, ((m_modRm >> 3) & 0x7) );
return -1;
}
}
int
InterpreterCpu::PrintRegs( void )
{
return -1;
}

106
src/interp/InterpreterCpu.h Normal file
View file

@ -0,0 +1,106 @@
#ifndef XBVM_INTERPRETERCPU_H
#define XBVM_INTERPRETERCPU_H
#include "Cpu.h"
#include "Mem.h"
#include "Types.h"
enum CpuSegmenEnum {
CPU_SEGMENT_CS,
CPU_SEGMENT_DS,
CPU_SEGMENT_ES,
CPU_SEGMENT_FS,
CPU_SEGMENT_GS,
CPU_SEGMENT_SS,
CPU_SEGMENT_LDTs,
CPU_SEGMENT_TR,
};
class InterpreterCpu : public Cpu
{
public:
InterpreterCpu();
virtual ~InterpreterCpu();
virtual int Initialize( void );
virtual int Run( void );
virtual int SingleStep( void );
virtual int PrintRegs( void );
private:
u32 eax;
u32 ebx;
u32 ecx;
u32 edx;
u32 edi;
u32 esi;
u32 ebp;
u32 esp;
u32 eip;
u32 eflags;
u32 cs_base;
u32 cs_limit;
u16 gdt_size;
u32 gdt_offset;
u16 idt_size;
u32 idt_offset;
u32 AddrToPhys( u32 virt )
{
if ( 0 == (eflags & 1) ) {
//real mode
return (virt & 0xffff) + cs_base;
}
else {
//protected mode
return virt + cs_base;
}
}
u8 ReadNextByte( void )
{
return g_mem->ReadPhysU32( AddrToPhys(eip + m_instrOffset++) );
}
u16 ReadNextWord( void )
{
u16 word = ReadNextByte();
word |= ReadNextByte() << 8;
return word;
}
u32 ReadNextDWord( void )
{
u32 dword = ReadNextWord();
dword |= ReadNextWord() << 16;
return dword;
}
int m_instrOffset;
u8 m_modRm;
bool m_operandSizeOverride;
bool m_addressSizeOverride;
bool m_csSegmentOverride;
bool m_dsSegmentOverride;
bool m_esSegmentOverride;
bool m_fsSegmentOverride;
bool m_gsSegmentOverride;
bool m_ssSegmentOverride;
int DecodeMultibyte( void );
int DecodeGroup7( void );
//In InterpreterCpu_sys.cpp
int Lgdt( void );
int Lidt( void );
};
#endif /*XBVM_INTERPRETERCPU_H*/

View file

@ -0,0 +1,33 @@
#include "interp/InterpreterCpu.h"
#include "Log.h"
int
InterpreterCpu::Lgdt( void )
{
if( !m_csSegmentOverride ) {
EPRINT( "can't lgdt without cs segment override" );
return -1;
}
u32 description_offset = ReadNextWord() + cs_base;
gdt_size = g_mem->ReadPhysU16( description_offset );
gdt_offset = g_mem->ReadPhysU32( description_offset + 2 );
return 0;
}
int
InterpreterCpu::Lidt( void )
{
if( !m_csSegmentOverride ) {
EPRINT( "can't lidt without cs segment override" );
return -1;
}
u32 description_offset = ReadNextWord() + cs_base;
idt_size = g_mem->ReadPhysU16( description_offset );
idt_offset = g_mem->ReadPhysU32( description_offset + 2 );
return 0;
}

671
src/kvm/KvmCpu.cpp Normal file
View file

@ -0,0 +1,671 @@
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include "kvm/KvmCpu.h"
#include "Log.h"
#include "Mem.h"
KvmCpu::KvmCpu() :
m_kvmFd(0), m_vmFd(0), m_vcpuFd(0), m_kvmRun(NULL),
m_kvmRunSize(0)
{
}
KvmCpu::~KvmCpu()
{
}
int
KvmCpu::Initialize()
{
if( !OpenKvmInterface() ) {
EPRINT( "Error opening /dev/kvm" );
return -1;
}
if( !ExtensionExists( KVM_CAP_IRQCHIP ) ) {
EPRINT( "KVM Interface doesn't have KVM_CAP_IRQCHIP capability" );
return -2;
}
if( !KvmIoctl( KVM_CREATE_VM, 0, m_vmFd ) ) {
EPRINT( "Error creating VM" );
return -3;
}
if( !VmIoctl( KVM_SET_TSS_ADDR, (void*)0xE0000000 ) ) {
EPRINT( "Error setting TSS address range" );
return -4;
}
if( !VmIoctl( KVM_CREATE_VCPU, 0, m_vcpuFd ) ) {
EPRINT( "Error creating VPCU" );
return -5;
}
if( !KvmIoctl( KVM_GET_VCPU_MMAP_SIZE, 0, m_kvmRunSize ) ) {
EPRINT( "Error getting kvm_run size" );
return -6;
}
if( !MmapKvmRun() ) {
EPRINT( "Error mmaping kvm_run" );
return -7;
}
if( !SetupMemoryRegions() ) {
return -8;
}
if( !SetupRegisters() ) {
return -9;
}
return 0;
}
int
KvmCpu::Run( void )
{
int exitReason;
bool running = true;
while( running ) {
if( !VcpuIoctl( KVM_RUN, 0 ) ) {
DPRINT( "Can't KVM_RUN" );
return -1;
}
exitReason = m_kvmRun->exit_reason;
switch( exitReason ) {
case KVM_EXIT_DEBUG:
running = false;
break;
case KVM_EXIT_IO:
if( KVM_EXIT_IO_OUT == m_kvmRun->io.direction ) {
u8 *dataPtr = (u8*)m_kvmRun + m_kvmRun->io.data_offset;
if( 1 == m_kvmRun->io.count ) {
switch( m_kvmRun->io.size ) {
case 1:
running = g_mem->WriteIo8( m_kvmRun->io.port, *(u8*)dataPtr );
break;
case 2:
running = g_mem->WriteIo16( m_kvmRun->io.port, *(u16*)dataPtr );
break;
case 4:
running = g_mem->WriteIo32( m_kvmRun->io.port, *(u32*)dataPtr );
break;
default:
EPRINT( "Unknown io size %d in write", m_kvmRun->io.size );
running = false;
break;
}
}
else {
EPRINT( "Count of one not handled" );
running = false;
}
break;
}
else {
u8 *dataPtr = (u8*)m_kvmRun + m_kvmRun->io.data_offset;
if( 1 == m_kvmRun->io.count ) {
switch( m_kvmRun->io.size ) {
case 1:
running = g_mem->ReadIo8( m_kvmRun->io.port, *(u8*)dataPtr );
break;
case 2:
running = g_mem->ReadIo16( m_kvmRun->io.port, *(u16*)dataPtr );
break;
case 4:
running = g_mem->ReadIo32( m_kvmRun->io.port, *(u32*)dataPtr );
break;
default:
EPRINT( "Unknown io size %d in read", m_kvmRun->io.size );
running = false;
break;
}
}
else {
EPRINT( "Count of one not handled" );
running = false;
}
break;
}
break;
case KVM_EXIT_MMIO:
if( m_kvmRun->mmio.is_write ) {
switch( m_kvmRun->mmio.len ) {
case 1:
running = g_mem->WritePhysU8( (u32)m_kvmRun->mmio.phys_addr, m_kvmRun->mmio.data[0] );
break;
case 4:
running = g_mem->WritePhysU32( (u32)m_kvmRun->mmio.phys_addr, *(u32*)m_kvmRun->mmio.data );
break;
default:
EPRINT( "Unknown MMIO Size %d in write", m_kvmRun->mmio.len );
running = false;
break;
}
}
else {
switch( m_kvmRun->mmio.len ) {
case 1:
*(u8*)m_kvmRun->mmio.data = g_mem->ReadPhysU8( (u32)m_kvmRun->mmio.phys_addr );
break;
case 4:
*(u32*)m_kvmRun->mmio.data = g_mem->ReadPhysU32( (u32)m_kvmRun->mmio.phys_addr );
break;
default:
EPRINT( "Unknown MMIO Size %d in read", m_kvmRun->mmio.len );
running = false;
break;
}
}
break;
case KVM_EXIT_SHUTDOWN:
struct kvm_regs regs;
VcpuIoctl( KVM_GET_REGS, &regs );
// Hack for cromwell setting invalid cr3
if( regs.rip == 0xFFFC1023 ) {
DPRINT( "Executing horrible cromwell hack" );
struct kvm_sregs sregs;
VcpuIoctl( KVM_GET_SREGS, &sregs );
sregs.cr3 = regs.rip & 0xFFFFF000;
VcpuIoctl( KVM_SET_SREGS, &sregs );
}
// If we didn't fit one of the hacks, then we're in a very weird state
else {
DPRINT( "regs->eip = %08llx", regs.rip );
DPRINT( "KVM_EXIT_SHUTDOWN" );
running = false;
}
break;
case KVM_EXIT_UNKNOWN:
DPRINT( "KVM_EXIT_UNKNOWN -> %08llx", m_kvmRun->hw.hardware_exit_reason );
running = false;
break;
default:
DPRINT( "Unknown KVM exit reason of %d", exitReason );
running = false;
}
//fixup eip and wrap around
if( running ) {
struct kvm_regs regs;
if( !VcpuIoctl( KVM_GET_REGS, &regs ) ) {
EPRINT( "Can not get regs" );
running = false;
}
else {
if( 0x100000000UL == regs.rip ) {
DPRINT( "fixing up eip" );
regs.rip = 0;
if( !VcpuIoctl( KVM_SET_REGS, &regs ) ) {
EPRINT( "Can not set regs" );
running = false;
}
}
}
}
}
return exitReason;
}
int
KvmCpu::SingleStep( void )
{
int ret = 0;
struct kvm_guest_debug debug;
debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP;
if( !VcpuIoctl( KVM_SET_GUEST_DEBUG, &debug ) ) {
EPRINT( "Error getting debug flags" );
return -1;
}
ret = Run();
debug.control = 0;
if( !VcpuIoctl( KVM_SET_GUEST_DEBUG, &debug ) ) {
EPRINT( "Error getting debug flags" );
return -1;
}
return ret;
}
int
KvmCpu::PrintRegs( void )
{
struct kvm_regs regs;
if( !VcpuIoctl( KVM_GET_REGS, &regs ) ) {
EPRINT( "Can not get regs" );
return -1;
}
DPRINT( "EAX: %08llx EBX: %08llx ECX: %08llx EDX: %08llx",
regs.rax, regs.rbx, regs.rcx, regs.rdx );
DPRINT( "ESP: %08llx EBP: %08llx ESI: %08llx EDI: %08llx",
regs.rsp, regs.rbp, regs.rsi, regs.rdi );
DPRINT( "EFLAGS: %08llx EIP: %08llx",
regs.rflags, regs.rip );
return 0;
}
bool
KvmCpu::GetRegs( CpuRegs &retregs )
{
struct kvm_regs regs;
if( !VcpuIoctl( KVM_GET_REGS, &regs ) ) {
EPRINT( "Can not get regs" );
return false;
}
retregs.eax = regs.rax;
retregs.ebx = regs.rbx;
retregs.ecx = regs.rcx;
retregs.edx = regs.rdx;
retregs.esi = regs.rsi;
retregs.edi = regs.rdi;
retregs.esp = regs.rsp;
retregs.ebp = regs.rbp;
retregs.eip = regs.rip;
retregs.eflags = regs.rflags;
return true;
}
bool
KvmCpu::GetSystemRegs( CpuSystemRegs &retsregs )
{
struct kvm_sregs sregs;
if( !VcpuIoctl( KVM_GET_SREGS, &sregs ) ) {
EPRINT( "Can not get sregs" );
return false;
}
retsregs.cs.base = sregs.cs.base;
retsregs.cs.limit = sregs.cs.limit;
retsregs.cs.selector = sregs.cs.selector;
retsregs.cs.type = sregs.cs.type;
retsregs.cs.present = sregs.cs.present;
retsregs.cs.dpl = sregs.cs.dpl;
retsregs.cs.db = sregs.cs.db;
retsregs.cs.s = sregs.cs.s;
retsregs.cs.l = sregs.cs.l;
retsregs.cs.g = sregs.cs.g;
retsregs.cs.avl = sregs.cs.avl;
retsregs.ds.base = sregs.ds.base;
retsregs.ds.limit = sregs.ds.limit;
retsregs.ds.selector = sregs.ds.selector;
retsregs.ds.type = sregs.ds.type;
retsregs.ds.present = sregs.ds.present;
retsregs.ds.dpl = sregs.ds.dpl;
retsregs.ds.db = sregs.ds.db;
retsregs.ds.s = sregs.ds.s;
retsregs.ds.l = sregs.ds.l;
retsregs.ds.g = sregs.ds.g;
retsregs.ds.avl = sregs.ds.avl;
retsregs.es.base = sregs.es.base;
retsregs.es.limit = sregs.es.limit;
retsregs.es.selector = sregs.es.selector;
retsregs.es.type = sregs.es.type;
retsregs.es.present = sregs.es.present;
retsregs.es.dpl = sregs.es.dpl;
retsregs.es.db = sregs.es.db;
retsregs.es.s = sregs.es.s;
retsregs.es.l = sregs.es.l;
retsregs.es.g = sregs.es.g;
retsregs.es.avl = sregs.es.avl;
retsregs.fs.base = sregs.fs.base;
retsregs.fs.limit = sregs.fs.limit;
retsregs.fs.selector = sregs.fs.selector;
retsregs.fs.type = sregs.fs.type;
retsregs.fs.present = sregs.fs.present;
retsregs.fs.dpl = sregs.fs.dpl;
retsregs.fs.db = sregs.fs.db;
retsregs.fs.s = sregs.fs.s;
retsregs.fs.l = sregs.fs.l;
retsregs.fs.g = sregs.fs.g;
retsregs.fs.avl = sregs.fs.avl;
retsregs.gs.base = sregs.gs.base;
retsregs.gs.limit = sregs.gs.limit;
retsregs.gs.selector = sregs.gs.selector;
retsregs.gs.type = sregs.gs.type;
retsregs.gs.present = sregs.gs.present;
retsregs.gs.dpl = sregs.gs.dpl;
retsregs.gs.db = sregs.gs.db;
retsregs.gs.s = sregs.gs.s;
retsregs.gs.l = sregs.gs.l;
retsregs.gs.g = sregs.gs.g;
retsregs.gs.avl = sregs.gs.avl;
retsregs.ss.base = sregs.ss.base;
retsregs.ss.limit = sregs.ss.limit;
retsregs.ss.selector = sregs.ss.selector;
retsregs.ss.type = sregs.ss.type;
retsregs.ss.present = sregs.ss.present;
retsregs.ss.dpl = sregs.ss.dpl;
retsregs.ss.db = sregs.ss.db;
retsregs.ss.s = sregs.ss.s;
retsregs.ss.l = sregs.ss.l;
retsregs.ss.g = sregs.ss.g;
retsregs.ss.avl = sregs.ss.avl;
retsregs.tr.base = sregs.tr.base;
retsregs.tr.limit = sregs.tr.limit;
retsregs.tr.selector = sregs.tr.selector;
retsregs.tr.type = sregs.tr.type;
retsregs.tr.present = sregs.tr.present;
retsregs.tr.dpl = sregs.tr.dpl;
retsregs.tr.db = sregs.tr.db;
retsregs.tr.s = sregs.tr.s;
retsregs.tr.l = sregs.tr.l;
retsregs.tr.g = sregs.tr.g;
retsregs.tr.avl = sregs.tr.avl;
retsregs.ldt.base = sregs.ldt.base;
retsregs.ldt.limit = sregs.ldt.limit;
retsregs.ldt.selector = sregs.ldt.selector;
retsregs.ldt.type = sregs.ldt.type;
retsregs.ldt.present = sregs.ldt.present;
retsregs.ldt.dpl = sregs.ldt.dpl;
retsregs.ldt.db = sregs.ldt.db;
retsregs.ldt.s = sregs.ldt.s;
retsregs.ldt.l = sregs.ldt.l;
retsregs.ldt.g = sregs.ldt.g;
retsregs.ldt.avl = sregs.ldt.avl;
retsregs.gdt.base = sregs.gdt.base;
retsregs.gdt.limit = sregs.gdt.limit;
retsregs.idt.base = sregs.idt.base;
retsregs.idt.limit = sregs.idt.limit;
retsregs.cr0 = sregs.cr0;
retsregs.cr2 = sregs.cr2;
retsregs.cr3 = sregs.cr3;
retsregs.cr4 = sregs.cr4;
return true;
}
u32
KvmCpu::GetReg( CpuRegisterEnum reg )
{
struct kvm_regs regs;
if( !VcpuIoctl( KVM_GET_REGS, &regs ) ) {
EPRINT( "Can not get regs" );
return 0xFFFFFFFF;
}
switch( reg ) {
case CPU_EAX:
return regs.rax;
case CPU_EIP:
return regs.rip;
default:
EPRINT( "Unknown reg %d", reg );
return 0xFFFFFFFF;
}
}
bool
KvmCpu::OpenKvmInterface( void )
{
if( (m_kvmFd = open( "/dev/kvm", O_RDWR )) < 0 ) {
EPRINT( "Error opening /dev/kvm" );
return false;
}
return true;
}
bool
KvmCpu::MmapKvmRun( void )
{
m_kvmRun = reinterpret_cast<struct kvm_run*>(
mmap( NULL,
m_kvmRunSize,
PROT_READ | PROT_WRITE,
MAP_SHARED,
m_vcpuFd,
0
)
);
if( MAP_FAILED == m_kvmRun ) {
return false;
}
return true;
}
bool
KvmCpu::SetupMemoryRegions( void )
{
struct kvm_userspace_memory_region ramRegion;
struct kvm_userspace_memory_region gpuRamRegion;
struct kvm_userspace_memory_region romRegion;
ramRegion.slot = 0;
ramRegion.guest_phys_addr = 0x0UL;
ramRegion.memory_size = g_mem->GetRamSize();
ramRegion.userspace_addr = reinterpret_cast<unsigned long>(g_mem->GetRamAddr());
if( !VmIoctl( KVM_SET_USER_MEMORY_REGION, &ramRegion ) ) {
return false;
}
gpuRamRegion.slot = 1;
gpuRamRegion.guest_phys_addr = 0xF0000000;
gpuRamRegion.memory_size = g_mem->GetRamSize();
gpuRamRegion.userspace_addr = reinterpret_cast<unsigned long>(g_mem->GetRamAddr());
if( !VmIoctl( KVM_SET_USER_MEMORY_REGION, &gpuRamRegion ) ) {
return false;
}
romRegion.slot = 2;
romRegion.guest_phys_addr = 0xFF000000UL;
romRegion.memory_size = g_mem->GetRomSize();
romRegion.userspace_addr = reinterpret_cast<unsigned long>(g_mem->GetRomAddr());
if( !VmIoctl( KVM_SET_USER_MEMORY_REGION, &romRegion ) ) {
return false;
}
return true;
}
bool
KvmCpu::SetupRegisters( void )
{
struct kvm_regs regs;
struct kvm_sregs sregs;
if( !VcpuIoctl( KVM_GET_REGS, &regs ) ) {
EPRINT( "Can not get regs" );
return false;
}
if( !VcpuIoctl( KVM_GET_SREGS, &sregs ) ) {
EPRINT( "Can not get sregs" );
return false;
}
regs.rax = Cpu::GetInitialValue( CPU_EAX );
regs.rbx = Cpu::GetInitialValue( CPU_EBX );
regs.rcx = Cpu::GetInitialValue( CPU_ECX );
regs.rdx = Cpu::GetInitialValue( CPU_EDX );
regs.rsi = Cpu::GetInitialValue( CPU_ESI );
regs.rdi = Cpu::GetInitialValue( CPU_EDI );
regs.rsp = Cpu::GetInitialValue( CPU_ESP );
regs.rbp = Cpu::GetInitialValue( CPU_EBP );
regs.rip = Cpu::GetInitialValue( CPU_EIP );
regs.rdx = Cpu::GetInitialValue( CPU_EDX );
regs.rflags = Cpu::GetInitialValue( CPU_EFLAGS );
sregs.cr0 = Cpu::GetInitialValue( CPU_CR0 );
sregs.cr2 = Cpu::GetInitialValue( CPU_CR2 );
sregs.cr3 = Cpu::GetInitialValue( CPU_CR3 );
sregs.cr4 = Cpu::GetInitialValue( CPU_CR4 );
sregs.efer = Cpu::GetInitialValue( CPU_EFER );
sregs.apic_base = Cpu::GetInitialValue( CPU_APICBASE );
sregs.cs.selector = Cpu::GetInitialValue( CPU_CSSELEC );
sregs.cs.base = Cpu::GetInitialValue( CPU_CSBASE );
sregs.cs.limit = Cpu::GetInitialValue( CPU_CSLIMIT );
SetSegmentFlags( &sregs.cs, Cpu::GetInitialValue( CPU_CSFLAGS ) );
sregs.cs.unusable = 0;
sregs.ds.selector = Cpu::GetInitialValue( CPU_DSSELEC );
sregs.ds.base = Cpu::GetInitialValue( CPU_DSBASE );
sregs.ds.limit = Cpu::GetInitialValue( CPU_DSLIMIT );
SetSegmentFlags( &sregs.ds, Cpu::GetInitialValue( CPU_DSFLAGS ) );
sregs.ds.unusable = 0;
sregs.es.selector = Cpu::GetInitialValue( CPU_ESSELEC );
sregs.es.base = Cpu::GetInitialValue( CPU_ESBASE );
sregs.es.limit = Cpu::GetInitialValue( CPU_ESLIMIT );
SetSegmentFlags( &sregs.es, Cpu::GetInitialValue( CPU_ESFLAGS ) );
sregs.es.unusable = 0;
sregs.fs.selector = Cpu::GetInitialValue( CPU_FSSELEC );
sregs.fs.base = Cpu::GetInitialValue( CPU_FSBASE );
sregs.fs.limit = Cpu::GetInitialValue( CPU_FSLIMIT );
SetSegmentFlags( &sregs.fs, Cpu::GetInitialValue( CPU_FSFLAGS ) );
sregs.fs.unusable = 0;
sregs.gs.selector = Cpu::GetInitialValue( CPU_GSSELEC );
sregs.gs.base = Cpu::GetInitialValue( CPU_GSBASE );
sregs.gs.limit = Cpu::GetInitialValue( CPU_GSLIMIT );
SetSegmentFlags( &sregs.gs, Cpu::GetInitialValue( CPU_GSFLAGS ) );
sregs.gs.unusable = 0;
sregs.ss.selector = Cpu::GetInitialValue( CPU_SSSELEC );
sregs.ss.base = Cpu::GetInitialValue( CPU_SSBASE );
sregs.ss.limit = Cpu::GetInitialValue( CPU_SSLIMIT );
SetSegmentFlags( &sregs.ss, Cpu::GetInitialValue( CPU_SSFLAGS ) );
sregs.ss.unusable = 0;
sregs.ldt.selector = Cpu::GetInitialValue( CPU_LDSELEC );
sregs.ldt.base = Cpu::GetInitialValue( CPU_LDBASE );
sregs.ldt.limit = Cpu::GetInitialValue( CPU_LDLIMIT );
SetSegmentFlags( &sregs.ldt, Cpu::GetInitialValue( CPU_LDFLAGS ) );
sregs.ldt.unusable = 0;
sregs.tr.selector = Cpu::GetInitialValue( CPU_TRSELEC );
sregs.tr.base = Cpu::GetInitialValue( CPU_TRBASE );
sregs.tr.limit = Cpu::GetInitialValue( CPU_TRLIMIT );
SetSegmentFlags( &sregs.tr, Cpu::GetInitialValue( CPU_TRFLAGS ) );
sregs.tr.unusable = 0;
sregs.gdt.base = Cpu::GetInitialValue( CPU_GDTB );
sregs.gdt.limit = Cpu::GetInitialValue( CPU_GDTL );
sregs.idt.base = Cpu::GetInitialValue( CPU_IDTB );
sregs.idt.limit = Cpu::GetInitialValue( CPU_IDTL );
if( !VcpuIoctl( KVM_SET_SREGS, &sregs ) ) {
EPRINT( "Can not set sregs" );
return false;
}
if( !VcpuIoctl( KVM_SET_REGS, &regs ) ) {
EPRINT( "Can not set regs" );
return false;
}
return true;
}
void
KvmCpu::SetSegmentFlags( struct kvm_segment *segment, u32 flags )
{
segment->type = (flags >> 8) & 0xf;
segment->present = (flags >> 15) & 1;
segment->dpl = (flags >> 13) & 3;
segment->db = (flags >> 22) & 1;
segment->s = (flags >> 12) & 1;
segment->l = (flags >> 21) & 1;
segment->g = (flags >> 23) & 1;
segment->avl = (flags >> 20) & 1;
}
bool
KvmCpu::KvmIoctl( int request, void *value, int &ret )
{
if( 0 == m_kvmFd )
return false;
if( (ret = ioctl( m_kvmFd, request, value )) < 0 )
return false;
return true;
}
bool
KvmCpu::VmIoctl( int request, void *value, int &ret )
{
if( 0 == m_vmFd )
return false;
if( (ret = ioctl( m_vmFd, request, value )) < 0 )
return false;
return true;
}
bool
KvmCpu::VcpuIoctl( int request, void *value, int &ret )
{
if( 0 == m_vcpuFd )
return false;
if( (ret = ioctl( m_vcpuFd, request, value )) < 0 )
return false;
return true;
}
bool
KvmCpu::ExtensionExists( int extension )
{
int ret = 0;
if( !KvmIoctl( KVM_CHECK_EXTENSION, (void*)extension, ret ) ) {
return false;
}
DPRINT( "Extension %d returns %d", extension, ret );
return (ret > 0);
}

52
src/kvm/KvmCpu.h Normal file
View file

@ -0,0 +1,52 @@
#ifndef XBVM_KVMCPU_H
#define XBVM_KVMCPU_H
#include <linux/kvm.h>
#include "Cpu.h"
#include "Types.h"
class KvmCpu : public Cpu
{
public:
KvmCpu();
virtual ~KvmCpu();
virtual int Initialize( void );
virtual int Run( void );
virtual int SingleStep( void );
virtual int PrintRegs( void );
virtual u32 GetReg( CpuRegisterEnum reg );
virtual bool GetRegs( CpuRegs &regs );
virtual bool GetSystemRegs( CpuSystemRegs &sregs );
private:
int m_kvmFd;
int m_vmFd;
int m_vcpuFd;
struct kvm_run *m_kvmRun;
int m_kvmRunSize;
KvmCpu( KvmCpu& );
const KvmCpu& operator=( KvmCpu& );
bool OpenKvmInterface( void );
bool MmapKvmRun( void );
bool SetupMemoryRegions( void );
bool SetupRegisters( void );
void SetSegmentFlags( struct kvm_segment *segment, u32 flags );
bool KvmIoctl( int request, void *value, int &ret );
bool VmIoctl( int request, void *value, int &ret );
bool VcpuIoctl( int request, void *value, int &ret );
bool ExtensionExists( int extension );
bool KvmIoctl( int request, void *value ) { int ret; return KvmIoctl( request, value, ret ); }
bool VmIoctl( int request, void *value ) { int ret; return VmIoctl( request, value, ret ); }
bool VcpuIoctl( int request, void *value ) { int ret; return VcpuIoctl( request, value, ret ); }
};
#endif /*XBVM_KVMCPU_H*/

View file

@ -0,0 +1,84 @@
#include "net/NetworkAdapter.h"
#include "Log.h"
#include "Mem.h"
NetworkAdapter *g_net;
NetworkAdapter::NetworkAdapter() : PciDevice( 0, 4, 0, PCI_NVIDIA_NETWORK_DEVICE_ID ),
m_mmiobase( 0 ),
m_ioportbase( 0 )
{
for( int i = 0; i < 6; i++ ) {
m_macAddress[i] = 0;
}
}
NetworkAdapter::~NetworkAdapter()
{
}
bool
NetworkAdapter::WriteBAR( int num, u32 &data )
{
switch( num ) {
case 0:
if( (data & 1) != 0 ) {
EPRINT( "Mapping Network Adapter BAR0 to IO space not supported %08x", data );
return false;
}
m_mmiobase = data;
g_mem->AssignMmioRange( m_mmiobase, 0x1000, this );
return true;
case 1:
if( (data & 1) != 1 ) {
EPRINT( "Mapping Network Adapter BAR1 to non IO space not supported %08x", data );
return false;
}
m_ioportbase = data & 0xFFFE;
for( int i = 0; i < 16; i++ ) {
g_mem->AddDeviceToIoMap( this, m_ioportbase + i );
}
return true;
case 2:
case 3:
case 4:
case 5:
if( data != 0 ) {
EPRINT( "Assigning a range to unknown BAR%d on %s %08x", num, GetName(), data );
return false;
}
return true;
default:
return PciDevice::WriteBAR( num, data );
}
}
bool
NetworkAdapter::MmioWrite8( u32 addr, u8 data )
{
u32 reg = addr - m_mmiobase;
switch( reg ) {
case 0xa8:
case 0xa9:
case 0xaa:
case 0xab:
case 0xac:
case 0xad:
m_macAddress[ 5 - (reg - 0xa8) ] = data;
if( reg == 0xad ) {
DPRINT( "MAC Address=%02x:%02x:%02x:%02x:%02x:%02x",
m_macAddress[ 0 ], m_macAddress[ 1 ],
m_macAddress[ 2 ], m_macAddress[ 3 ],
m_macAddress[ 4 ], m_macAddress[ 5 ] );
}
return true;
default:
return PciDevice::MmioWrite8( addr, data );
}
}

28
src/net/NetworkAdapter.h Normal file
View file

@ -0,0 +1,28 @@
#ifndef XBVM_NET_NETWORKADAPTER_H
#define XBVM_NET_NETWORKADAPTER_H
#include "PciDevice.h"
class NetworkAdapter : public PciDevice
{
public:
NetworkAdapter();
virtual ~NetworkAdapter();
virtual const char* GetName( void ) { return "Network Adapter"; }
virtual bool MmioWrite8( u32 reg, u8 data );
virtual bool WriteBAR( int num, u32 &data );
private:
u32 m_mmiobase;
u32 m_ioportbase;
u8 m_macAddress[6];
};
extern NetworkAdapter *g_net;
#endif /*XBVM_NET_NETWORKADAPTER_H*/

View file

@ -0,0 +1,18 @@
#include "smbus/ConexantEncoder.h"
ConexantEncoder *g_conexantEncoder;
bool
ConexantEncoder::ReadCommand8( u8 command, u8 *data )
{
*data = m_registers[ command ];
return true;
}
bool
ConexantEncoder::WriteCommand8( u8 command, u8 data )
{
m_registers[ command ] = data;
return true;
}

View file

@ -0,0 +1,27 @@
#ifndef XBVM_SMBUS_CONEXANTENCODER_H
#define XBVM_SMBUS_CONEXANTENCODER_H
#include <cstring>
#include "smbus/SmBusDevice.h"
class ConexantEncoder : public SmBusDevice
{
public:
ConexantEncoder() { memset( m_registers, 0, sizeof(m_registers) ); }
virtual ~ConexantEncoder() {}
virtual const char* GetName() { return "Conexant Encoder"; }
virtual bool ReadCommand8( u8 command, u8 *data );
virtual bool WriteCommand8( u8 command, u8 data );
virtual u8 GetAddr() { return 0x45; }
private:
u8 m_registers[256];
};
extern ConexantEncoder *g_conexantEncoder;
#endif /*XBVM_SMBUS_CONEXANTENCODER_H*/

152
src/smbus/PIC16L.cpp Normal file
View file

@ -0,0 +1,152 @@
#include "smbus/PIC16L.h"
#include "Log.h"
PIC16L *g_pic16l;
const char *PIC16L::VERSION_STRING_DEBUG = "DXB";
const char *PIC16L::VERSION_STRING_RETAIL_1_0 = "P05";
const char *PIC16L::VERSION_STRING_RETAIL_1_1 = "P01";
const char LED_COLOURS[] = { '-', 'R', 'G', 'O' };
bool
PIC16L::WriteCommand8( u8 command, u8 data )
{
switch( command ) {
case 0x1:
WriteVersion( data );
return true;
case 0x6:
m_powerFanSpeed = data;
return true;
case 0x7:
if( data == 1 ) {
DPRINT( "Using custom LED sequence" );
}
else {
DPRINT( "Using automatic LED sequence" );
}
return true;
case 0x8:
WriteLEDSequence( data );
return true;
case 0xB:
m_unknownReg0B = data;
return true;
case 0x12:
m_unknownReg12 = data;
return true;
case 0x19:
if( data == 1 ) {
m_resetOnEject = false;
}
else if( data == 0 ) {
m_resetOnEject = true;
}
return true;
case 0x1a:
if( data == 1 ) {
m_interruptEnabled = true;
}
return true;
case 0x1b:
m_scratchBuffer = data;
return true;
case 0x13:
m_unknownReg13 = data;
return true;
case 0x20:
if( data != 0x40 ) {
EPRINT( "Wrong response to PIC challenge %02x:%02x but what ev's, this is an emulator. We'll let it slide.", command, data );
}
return true;
case 0x21:
if( data != 0x7D ) {
EPRINT( "Wrong response to PIC challenge %02x:%02x but what ev's, this is an emulator. We'll let it slide.", command, data );
}
return true;
default:
return SmBusDevice::WriteCommand8( command, data );
}
}
bool
PIC16L::ReadCommand8( u8 command, u8 *data )
{
switch( command ) {
case 0x1:
*data = m_version[ m_versionCurrentOffset ];
m_versionCurrentOffset++;
if( m_versionCurrentOffset >= 3 )
m_versionCurrentOffset = 0;
return true;
case 0x4:
*data = 0x06; //Telling that a standard composite AV pack is plugged in
return true;
case 0x06:
*data = m_powerFanSpeed;
return true;
case 0x1C:
*data = 0x52;
return true;
case 0x1D:
*data = 0x72;
return true;
case 0x1E:
*data = 0xEA;
return true;
case 0x1F:
*data = 0x46;
return true;
default:
return SmBusDevice::ReadCommand8( command, data );
}
}
void
PIC16L::WriteVersion( u16 data )
{
if( 0 == data ) {
m_versionCurrentOffset = 0;
}
else {
EPRINT( "Unknown Versio command %d written to %s", data, GetName() );
}
}
void
PIC16L::WriteLEDSequence( u8 data )
{
char str[ 5 ];
int rbits = data >> 4;
int gbits = data & 4;
for( int i = 0; i < 4; i++ ) {
int offset = ((rbits & 1) << 1) | (gbits & 1);
str[ i ] = LED_COLOURS[ offset ];
}
str[ 4 ] = '\0';
DPRINT( "LED colour set to %s", str );
}

62
src/smbus/PIC16L.h Normal file
View file

@ -0,0 +1,62 @@
#ifndef XBVM_PIC_H
#define XBVM_PIC_H
#include "smbus/SmBusDevice.h"
class PIC16L : public SmBusDevice
{
public:
PIC16L() :
m_version( VERSION_STRING_RETAIL_1_0 ),
m_versionCurrentOffset(0),
m_ledSequence( 0 ),
m_powerFanSpeed( 0 ),
m_resetOnEject( true ),
m_interruptEnabled( false ),
m_scratchBuffer( 0 ),
m_unknownReg0B( 0 ),
m_unknownReg12( 0 ),
m_unknownReg13( 0 )
{
}
virtual ~PIC16L() {}
virtual const char* GetName( void ) { return "PIC"; }
virtual bool WriteCommand8( u8 command, u8 data );
virtual bool ReadCommand8( u8 command, u8 *data );
virtual u8 GetAddr( void ) { return 0x10; }
private:
static const char *VERSION_STRING_DEBUG;
static const char *VERSION_STRING_RETAIL_1_0;
static const char *VERSION_STRING_RETAIL_1_1;
const char *m_version;
int m_versionCurrentOffset;
u8 m_ledSequence;
u8 m_powerFanSpeed;
bool m_resetOnEject;
bool m_interruptEnabled;
u8 m_scratchBuffer;
u8 m_unknownReg0B;
u8 m_unknownReg12;
u8 m_unknownReg13;
PIC16L( PIC16L& );
const PIC16L& operator=( PIC16L& );
void WriteVersion( u16 data );
void WriteLEDSequence( u8 data );
};
extern PIC16L *g_pic16l;
#endif /*XBVM_PIC_H*/

View file

@ -0,0 +1,43 @@
#include <cstring>
#include "smbus/SerialEeprom.h"
SerialEeprom *g_eeprom;
const u8 defaultEepromData[256] =
{
0xe3, 0x1c, 0x5c, 0x23, 0x6a, 0x58, 0x68, 0x37, 0xb7, 0x12, 0x26, 0x6c, 0x99, 0x11, 0x30, 0xd1,
0xe2, 0x3e, 0x4d, 0x56, 0xf7, 0x73, 0x2b, 0x73, 0x85, 0xfe, 0x7f, 0x0a, 0x08, 0xef, 0x15, 0x3c,
0x77, 0xee, 0x6d, 0x4e, 0x93, 0x2f, 0x28, 0xee, 0xf8, 0x61, 0xf7, 0x94, 0x17, 0x1f, 0xfc, 0x11,
0x0b, 0x84, 0x44, 0xed, 0x31, 0x30, 0x35, 0x35, 0x38, 0x31, 0x31, 0x31, 0x34, 0x30, 0x30, 0x33,
0x00, 0x50, 0xf2, 0x4f, 0x65, 0x52, 0x00, 0x00, 0x0a, 0x1e, 0x35, 0x33, 0x71, 0x85, 0x31, 0x4d,
0x59, 0x12, 0x38, 0x48, 0x1c, 0x91, 0x53, 0x60, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x75, 0x61, 0x57, 0xfb, 0x2c, 0x01, 0x00, 0x00, 0x45, 0x53, 0x54, 0x00, 0x45, 0x44, 0x54, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x05, 0x00, 0x02, 0x04, 0x01, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc4, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
SerialEeprom::SerialEeprom()
{
std::memcpy( m_data, defaultEepromData, sizeof(defaultEepromData) );
}
SerialEeprom::~SerialEeprom()
{
}
bool
SerialEeprom::ReadCommand8( u8 command, u8 *data )
{
*data = m_data[ command ];
return true;
}

25
src/smbus/SerialEeprom.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef XBVM_SMBUS_SERIALEEPROM_H
#define XBVM_SMBUS_SERIALEEPROM_H
#include "smbus/SmBusDevice.h"
class SerialEeprom : public SmBusDevice
{
public:
SerialEeprom();
virtual ~SerialEeprom();
virtual const char* GetName() { return "EEPROM"; }
virtual bool ReadCommand8( u8 command, u8 *data );
virtual u8 GetAddr() { return 0x54; }
private:
u8 m_data[256];
};
extern SerialEeprom *g_eeprom;
#endif /*XBVM_SMBUS_SERIALEEPROM_H*/

View file

@ -0,0 +1,226 @@
#include "smbus/ConexantEncoder.h"
#include "smbus/PIC16L.h"
#include "smbus/SerialEeprom.h"
#include "smbus/SmBusController.h"
#include "smbus/TemperatureMonitor.h"
#include "Log.h"
#include "Mem.h"
SmBusController *g_smBus;
SmBusController::SmBusController() : PciDevice( 0, 1, 1, PCI_NVIDIA_SMBUS_DEVICE_ID ),
m_bar1Base( 0 ),
m_bar2Base( 0 ),
m_portc200( 0 ),
m_address( 0 ),
m_command( 0 ),
m_data( 0 ),
m_status( 0x10 )
{
g_conexantEncoder = new ConexantEncoder();
g_pic16l = new PIC16L();
g_eeprom = new SerialEeprom();
g_temp = new TemperatureMonitor();
}
SmBusController::~SmBusController( void )
{
}
bool
SmBusController::IoRead8( u16 port, u8 &data )
{
if( (port >= m_bar1Base) && (port < (m_bar1Base + 16)) ) {
u16 addr = port - m_bar1Base;
switch( addr ) {
case SMBUS_PORT_DATA:
data= m_data;
return true;
case SMBUS_PORT_STATUS:
data = m_status;
return true;
default:
EPRINT( "Read from unimplemented SMBus BAR1 %x", addr );
return false;
}
}
return PciDevice::IoRead8( port, data );
}
bool
SmBusController::IoRead16( u16 port, u16 &data )
{
if( (port >= m_bar1Base) && (port < (m_bar1Base + 16)) ) {
u16 addr = port - m_bar1Base;
switch( addr ) {
case SMBUS_PORT_DATA:
data = m_data;
return true;
case SMBUS_PORT_STATUS:
data = m_status;
return true;
default:
EPRINT( "Read from unimplemented SMBus BAR1 %x", addr );
return false;
}
}
return PciDevice::IoRead16( port, data );
}
bool
SmBusController::IoWrite8( u16 port, u8 &data )
{
if( (port >= m_bar1Base) && (port < (m_bar1Base + 16)) ) {
u16 addr = port - m_bar1Base;
switch( addr ) {
case SMBUS_PORT_ADDRESS:
m_address = data;
return true;
case SMBUS_PORT_COMMAND:
m_command = data;
return true;
case SMBUS_PORT_CONTROL:
return WriteControl( data );
case SMBUS_PORT_DATA:
m_data = data;
return true;
case SMBUS_PORT_STATUS:
//TODO: Do we ignore this?
return true;
default:
EPRINT( "Write of %02x to unknown BAR1 smbus port %x %x ", data, addr, port );
return false;
}
}
else if( (port >= m_bar2Base) && (port < (m_bar2Base + 32)) ) {
switch( port - m_bar2Base ) {
case 0:
m_portc200 = data;
return true;
default:
EPRINT( "Write of %02x to unknown BAR2 smbus port %x ", data, port );
return false;
}
}
return PciDevice::IoWrite8( port, data );
}
bool
SmBusController::IoWrite16( u16 port, u16 &data )
{
if( (port >= m_bar1Base) && (port < (m_bar1Base + 16)) ) {
u16 addr = port - m_bar1Base;
switch( addr ) {
case SMBUS_PORT_DATA:
m_data = data;
return true;
case SMBUS_PORT_STATUS:
//TODO: Do we ignore this?
return true;
default:
EPRINT( "Write of %04x to unknown BAR1 smbus port %x %x ", data, addr, port );
return false;
}
}
return PciDevice::IoWrite16( port, data );
}
bool
SmBusController::WriteBAR( int num, u32 &data )
{
u32 addr = data & 0xFFFFFFFE;
bool isIo = ((data & 1) == 1);
switch( num ) {
case 1:
if( !isIo ) {
EPRINT( "Setting SMBus BAR1 to non I/O space not implemented" );
return false;
}
m_bar1Base = addr;
for( int i = 0; i < 16; i++ ) {
g_mem->AddDeviceToIoMap( this, m_bar1Base + i );
}
return true;
case 2:
if( !isIo ) {
EPRINT( "Setting SMBus BAR1 to non I/O space not implemented" );
return false;
}
m_bar2Base = addr;
for( int i = 0; i < 32; i++ ) {
g_mem->AddDeviceToIoMap( this, m_bar2Base + i );
}
return true;
default:
return PciDevice::WriteBAR( num, data );
}
}
SmBusDevice*
SmBusController::GetDeviceForAddr( u8 addr )
{
if( addr == g_conexantEncoder->GetAddr() ) {
return g_conexantEncoder;
}
else if( addr == g_pic16l->GetAddr() ) {
return g_pic16l;
}
else if( addr == g_eeprom->GetAddr() ) {
return g_eeprom;
}
else if( addr == g_temp->GetAddr() ) {
return g_temp;
}
return NULL;
}
bool
SmBusController::WriteControl( u8 control )
{
if( (control & 0xE4) != 0 ) { //Checking if anything other than bits 0, 1, 3, or 5 are set
EPRINT( "Unimplemented bits in SMBUS Control: %02x", control );
return false;
}
if( (control & 8) != 0 ) { //check for start bit
SmBusDevice* device = GetDeviceForAddr( m_address >> 1 );
if( NULL == device ) {
EPRINT( "No Device at SmBus addr %02x", m_address >> 1 );
return false;
}
if( (control & 1) == 0 ) { //eight bit data
if( (m_address & 1) == 0 ) {
return device->WriteCommand8( m_command, (u8)m_data );
}
else {
return device->ReadCommand8( m_command, (u8*)&m_data );
}
}
else { //sixteen bit data
EPRINT( "16 bit data in SMBus control not implemented" );
return false;
}
}
return true;
}

View file

@ -0,0 +1,49 @@
#ifndef XBVM_SMBUSCONTROLLER_H
#define XBVM_SMBUSCONTROLLER_H
#include "PciDevice.h"
#define SMBUS_PORT_STATUS (0x00)
#define SMBUS_PORT_CONTROL (0x02)
#define SMBUS_PORT_ADDRESS (0x04)
#define SMBUS_PORT_DATA (0x06)
#define SMBUS_PORT_COMMAND (0x08)
class SmBusDevice;
class SmBusController : public PciDevice
{
public:
SmBusController( void );
virtual ~SmBusController( void );
virtual bool IoRead8( u16 port, u8 &data );
virtual bool IoRead16( u16 port, u16 &data );
virtual bool IoWrite8( u16 port, u8 &data );
virtual bool IoWrite16( u16 port, u16 &data );
virtual const char* GetName( void ) { return "SMBus Controller"; }
virtual bool WriteBAR( int num, u32 &data );
private:
u16 m_bar1Base;
u16 m_bar2Base;
u8 m_portc200;
u8 m_address;
u8 m_command;
u16 m_data;
u8 m_status;
bool WriteControl( u8 control );
SmBusDevice* GetDeviceForAddr( u8 addr );
};
extern SmBusController *g_smBus;
#endif /*XBVM_SMBUSCONTROLLER*/

25
src/smbus/SmBusDevice.cpp Normal file
View file

@ -0,0 +1,25 @@
#include "smbus/SmBusDevice.h"
#include "Log.h"
bool
SmBusDevice::WriteCommand8( u8 command, u8 data )
{
EPRINT( "Unimplemented write of %02x:%02x of %s", command, data, GetName() );
return false;
}
bool
SmBusDevice::WriteCommand16( u8 command, u16 data )
{
EPRINT( "Unimplemented write of %02x:%04x of %s", command, data, GetName() );
return false;
}
bool
SmBusDevice::ReadCommand8( u8 command, u8 *data )
{
EPRINT( "Unimplemented read from %02x of %s", command, GetName() );
*data = 0xFF;
return false;
}

21
src/smbus/SmBusDevice.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef XBVM_SMBUSDEVICE_H
#define XBVM_SMBUSDEVICE_H
#include "Device.h"
class SmBusDevice : public Device
{
public:
SmBusDevice() {}
virtual ~SmBusDevice() {}
virtual u8 GetAddr() = 0;
virtual bool WriteCommand8( u8 command, u8 data );
virtual bool WriteCommand16( u8 command, u16 data );
virtual bool ReadCommand8( u8 command, u8 *data );
};
#endif /*XBVM_SMBUSDEVICE_H*/

View file

@ -0,0 +1,21 @@
#include "smbus/TemperatureMonitor.h"
TemperatureMonitor *g_temp;
bool
TemperatureMonitor::ReadCommand8( u8 command, u8 *data )
{
switch( command ) {
case 0x0:
*data = 50;
return true;
case 0x1:
*data = 50;
return true;
default:
return SmBusDevice::ReadCommand8( command, data );
}
}

View file

@ -0,0 +1,27 @@
#ifndef SMBUS_TEMPERATUREMONITOR_H
#define SMBUS_TEMPERATUREMONITOR_H
#include "smbus/SmBusDevice.h"
class TemperatureMonitor : public SmBusDevice
{
public:
TemperatureMonitor()
{
}
virtual ~TemperatureMonitor()
{
}
virtual const char* GetName() { return "SMBus Temperature Monitor"; }
virtual u8 GetAddr() { return 0x4C; }
virtual bool ReadCommand8( u8 command, u8 *data );
};
extern TemperatureMonitor *g_temp;
#endif /*SMBUS_TEMPERATUREMONITOR_H*/

100
src/superio/Cmos.cpp Normal file
View file

@ -0,0 +1,100 @@
#include "superio/Cmos.h"
#include "Mem.h"
Cmos *g_cmos;
Cmos::Cmos( void ) :
m_reg( 0 ),
m_statusRegA( 0 ),
m_statusRegB( 0 ),
m_statusRegC( 0 ),
m_statusRegD( 0 )
{
g_mem->AddDeviceToIoMap( this, 0x70 );
g_mem->AddDeviceToIoMap( this, 0x71 );
}
Cmos::~Cmos( void )
{
}
bool
Cmos::IoRead8( u16 port, u8 &data )
{
switch( port )
{
case 0x71:
return ProcessRead( data );
default:
return HostDevice::IoRead8( port, data );
}
}
bool
Cmos::IoWrite8( u16 port, u8 &data )
{
switch( port )
{
case 0x70:
m_reg = data;
return true;
case 0x71:
return ProcessWrite( data );
default:
return HostDevice::IoWrite8( port, data );
}
}
bool
Cmos::ProcessRead( u8 &data )
{
switch( m_reg )
{
case 0x0b:
data = m_statusRegB;
return true;
case 0x0c:
data = m_statusRegC;
return true;
case 0x0d:
data = m_statusRegD;
return true;
default:
EPRINT( "Unknown CMOS register read from %02x", m_reg );
return false;
}
}
bool
Cmos::ProcessWrite( u8 data )
{
switch( m_reg )
{
case 0x0a:
m_statusRegA = data;
return true;
case 0x0b:
m_statusRegB = data;
return true;
case 0x0c:
m_statusRegC = data;
return true;
case 0x0d:
m_statusRegD = data;
return true;
default:
EPRINT( "Unknown CMOS register written to %02x->%02x", data, m_reg );
return false;
}
}

31
src/superio/Cmos.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef XBVM_SUPERIO_CMOS_H
#define XBVM_SUPERIO_CMOS_H
#include "HostDevice.h"
class Cmos : public HostDevice
{
public:
Cmos();
virtual ~Cmos();
virtual const char* GetName( void ) { return "CMOS"; }
virtual bool IoRead8( u16 port, u8 &data );
virtual bool IoWrite8( u16 port, u8 &data );
private:
bool ProcessRead( u8 &data );
bool ProcessWrite( u8 data );
u8 m_reg;
u8 m_statusRegA;
u8 m_statusRegB;
u8 m_statusRegC;
u8 m_statusRegD;
};
extern Cmos *g_cmos;
#endif /*XBVM_SUPERIO_CMOS_H*/

View file

@ -0,0 +1,28 @@
#include "superio/DmaController.h"
#include "Mem.h"
DmaController *g_dma0;
DmaController *g_dma1;
DmaController::DmaController( u16 basePort, bool doubleStep ) : HostDevice(),
m_basePort( basePort )
{
int interval = doubleStep ? 2 : 1;
for( int i = 0; i < 16; i++ ) {
g_mem->AddDeviceToIoMap( this, m_basePort + (i *interval) );
}
}
DmaController::~DmaController()
{
}
bool
DmaController::IoWrite8( u16 port, u8 &data )
{
// Ignore all writes for now, I like to think that no software is
// depending on ISA DMA controllers.
DPRINT( "Write of %02x to DMA%02x:%02x", data, m_basePort, port );
return true;
}

View file

@ -0,0 +1,24 @@
#ifndef SUPERIO_DMACONTROLLER_H
#define SUPERIO_DMACONTROLLER_H
#include "HostDevice.h"
class DmaController : public HostDevice
{
public:
DmaController( u16 basePort, bool doubleStep );
virtual ~DmaController();
virtual const char* GetName() { return "DMA Controller"; }
virtual bool IoWrite8( u16 port, u8 &data );
private:
u16 m_basePort;
};
extern DmaController *g_dma0;
extern DmaController *g_dma1;
#endif /*SUPERIO_DMACONTROLLER_H*/

53
src/superio/Pic.cpp Normal file
View file

@ -0,0 +1,53 @@
#include "superio/Pic.h"
#include "Mem.h"
Pic *g_pic;
Pic::Pic( void )
{
g_mem->AddDeviceToIoMap( this, 0x20 );
g_mem->AddDeviceToIoMap( this, 0x21 );
g_mem->AddDeviceToIoMap( this, 0xA0 );
g_mem->AddDeviceToIoMap( this, 0xA1 );
}
Pic::~Pic( void )
{
}
bool
Pic::IoWrite8( u16 port, u8 &data )
{
switch( port ) {
case 0x20:
return CommandWrite( data, true );
case 0x21:
return DataWrite( data, true );
case 0xA0:
return CommandWrite( data, false );
case 0xA1:
return DataWrite( data, false );
default:
EPRINT( "Pic %x <- %02x", port, data );
return HostDevice::IoWrite8( port, data );
}
}
bool
Pic::CommandWrite( u8 command, bool isMaster )
{
DPRINT( "Pic::CommandWrite( %02x, %s )", command, isMaster ? "true" : "false" );
return true;
}
bool
Pic::DataWrite( u8 data, bool isMaster )
{
DPRINT( "Pic::DataWrite( %02x, %s )", data, isMaster ? "true" : "false" );
return true;
}

25
src/superio/Pic.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef XBVM_SUPERIO_PIC_H
#define XBVM_SUPERIO_PIC_H
#include "HostDevice.h"
//Actually both pics, but put together for convenience sake
class Pic : public HostDevice
{
public:
Pic();
virtual ~Pic();
virtual bool IoWrite8( u16 port, u8 &data );
virtual const char* GetName( void ) { return "Programmable Interrupt Controller"; }
private:
bool CommandWrite( u8 command, bool isMaster );
bool DataWrite( u8 command, bool isMaster );
};
extern Pic *g_pic;
#endif /*XBVM_SUPERIO_PIC_H*/

61
src/superio/Pit.cpp Normal file
View file

@ -0,0 +1,61 @@
#include "superio/Pit.h"
#include "Log.h"
#include "Mem.h"
Pit *g_pit;
Pit::Pit( void ) :
m_highLowToggle( true ),
m_chan0Ctc( 0 )
{
g_mem->AddDeviceToIoMap( this, 0x40 );
g_mem->AddDeviceToIoMap( this, 0x41 );
g_mem->AddDeviceToIoMap( this, 0x42 );
g_mem->AddDeviceToIoMap( this, 0x43 );
}
Pit::~Pit( void )
{
}
bool
Pit::IoWrite8( u16 port, u8 &data )
{
switch( port )
{
case 0x40:
if( m_highLowToggle ) { //Write to low byte
m_chan0Ctc = data;
m_highLowToggle = !m_highLowToggle;
}
else { //Write to high byte
m_chan0Ctc = (m_chan0Ctc & 0xff) | (data << 8);
m_highLowToggle = !m_highLowToggle;
DPRINT( "Set PIT to %f hz", 1125000.0 / m_chan0Ctc );
}
return true;
case 0x41: //Channel 1 (DRAM Refresh) Data port
//ignore for now
return true;
case 0x42: //Channel 2 (Speaker) Data port
//ignore for now
return true;
case 0x43:
return WriteToMode( data );
default:
return HostDevice::IoWrite8( port, data );
}
}
bool
Pit::WriteToMode( u8 data )
{
DPRINT( "Write to PIT Mode: Channel %d, Access Mode %d, Operating Mode %d BCD/Binary Mode %d",
((data >> 6 ) & 3), ((data >> 4) & 3), ((data >> 1) & 7), (data & 1) );
return true;
}

25
src/superio/Pit.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef XBVM_SUPERIO_H
#define XBVM_SUPERIO_H
#include "HostDevice.h"
class Pit : public HostDevice
{
public:
Pit();
virtual ~Pit();
virtual const char* GetName( void ) { return "Programmable Interval Timer"; }
virtual bool IoWrite8( u16 port, u8 &data );
private:
bool WriteToMode( u8 data );
bool m_highLowToggle;
u16 m_chan0Ctc;
};
extern Pit *g_pit;
#endif /*XBVM_SUPERIO_H*/

View file

@ -0,0 +1,77 @@
#include "superio/SuperIoController.h"
#include "Mem.h"
SuperIoController *g_superIo;
SuperIoController::SuperIoController( void ) :
m_port2E( 0 ),
m_port2F( 0 ),
m_port4E( 0 ),
m_port4F( 0 ),
m_port61( 0 )
{
g_mem->AddDeviceToIoMap( this, 0x2e );
g_mem->AddDeviceToIoMap( this, 0x2f );
g_mem->AddDeviceToIoMap( this, 0x4e );
g_mem->AddDeviceToIoMap( this, 0x4f );
g_mem->AddDeviceToIoMap( this, 0x61 );
}
SuperIoController::~SuperIoController()
{
}
bool
SuperIoController::IoRead8( u16 port, u8 &data )
{
switch( port ) {
case 0x2E:
data = m_port2E;
return true;
case 0x2F:
data = m_port2F;
return true;
case 0x4E:
data = m_port4E;
return true;
case 0x4F:
data = m_port4F;
return true;
default:
return HostDevice::IoRead8( port, data );
}
}
bool
SuperIoController::IoWrite8( u16 port, u8 &data )
{
switch( port ) {
case 0x2E:
m_port2E = data;
return true;
case 0x2F:
m_port2F = data;
return true;
case 0x4E:
m_port4E = data;
return true;
case 0x4F:
m_port4F = data;
return true;
case 0x61:
m_port61 = data;
return true;
default:
return HostDevice::IoWrite8( port, data );
}
}

View file

@ -0,0 +1,28 @@
#ifndef XBVM_SUPERIO_SUPERIOCONTROLLER_H
#define XBVM_SUPERIO_SUPERIOCONTROLLER_H
#include "HostDevice.h"
class SuperIoController : public HostDevice
{
public:
SuperIoController();
virtual ~SuperIoController();
virtual bool IoRead8( u16 port, u8 &data );
virtual bool IoWrite8( u16 port, u8 &data );
virtual const char* GetName( void ) { return "Super I/O Controller"; }
private:
u8 m_port2E;
u8 m_port2F;
u8 m_port4E;
u8 m_port4F;
u8 m_port61;
};
extern SuperIoController *g_superIo;
#endif /*XBVM_SUPERIO_SUPERIOCONTROLLER_H*/

115
src/usb/OhciController.cpp Normal file
View file

@ -0,0 +1,115 @@
#include "usb/OhciController.h"
#include "Log.h"
#include "Mem.h"
OhciController *g_usb0;
OhciController *g_usb1;
bool
OhciController::WriteConfigSpace( u8 reg, u32 &data )
{
switch( reg ) {
case 60:
m_config60 = data;
return true;
default:
return PciDevice::WriteConfigSpace( reg, data );
}
}
bool
OhciController::ReadConfigSpace( u8 reg, u32 &data )
{
switch( reg ) {
case 60:
data = m_config60;
return true;
default:
return PciDevice::ReadConfigSpace( reg, data );
}
}
bool
OhciController::WriteBAR( int num, u32 &data )
{
switch( num ) {
case 0:
if( (data & 1) != 0 ) {
EPRINT( "Mapping Network Adapter BAR0 to IO space not supported %08x", data );
return false;
}
m_mmiobase = data;
g_mem->AssignMmioRange( m_mmiobase, 0x1000, this );
return true;
case 1:
case 2:
case 3:
case 4:
case 5:
if( data != 0 ) {
EPRINT( "Assigning a range to unknown BAR%d on %s %08x", num, GetName(), data );
return false;
}
return true;
default:
return PciDevice::WriteBAR( num, data );
}
}
u32
OhciController::MmioRead32( u32 addr )
{
u32 reg = addr - m_mmiobase;
switch( reg ) {
case OHCI_REG_REVISION:
return 0x10;
case OHCI_REG_CONTROL:
return 0x690;
case OHCI_REG_COMMANDSTATUS:
return 1;
case OHCI_REG_INTERRUPTSTATUS:
return 0;
default:
return PciDevice::MmioRead32( addr );
}
}
bool
OhciController::MmioWrite32( u32 addr, u32 data )
{
u32 reg = addr - m_mmiobase;
switch( reg ) {
case OHCI_REG_CONTROL:
return true;
case OHCI_REG_COMMANDSTATUS:
return true;
case OHCI_REG_INTERRUPTSTATUS:
return true;
case OHCI_REG_INTERRUPTDISABLE:
if( data == 0x80000000 ) {
m_interruptEnabled = false;
}
else {
EPRINT( "Unknown bits set in Ohci->InterruptDiable %08x", data );
return false;
}
return true;
default:
return PciDevice::MmioWrite32( addr, data );
}
}

61
src/usb/OhciController.h Normal file
View file

@ -0,0 +1,61 @@
#ifndef XBVM_OHCICONTROLLER_H
#define XBVM_OHCICONTROLLER_H
#include <cstdio>
#include "PciDevice.h"
#define OHCI_REG_REVISION (0x0000)
#define OHCI_REG_CONTROL (0x0004)
#define OHCI_REG_COMMANDSTATUS (0x0008)
#define OHCI_REG_INTERRUPTSTATUS (0x000C)
#define OHCI_REG_INTERRUPTENABLE (0x0010)
#define OHCI_REG_INTERRUPTDISABLE (0x0014)
#define OHCI_REG_HCCA (0x0018)
#define OHCI_REG_PERIODCURRENTED (0x001C)
#define OHCI_REG_CONTROLCURRENTED (0x0020)
#define OHCI_REG_CONTROLHEADED (0x0024)
#define OHCI_REG_BULKHEADED (0x0028)
#define OHCI_REG_BULKCURRENTED (0x002C)
#define OHCI_REG_DONEHEAD (0x0030)
#define OHCI_REG_FMINTERVAL (0x0034)
#define OHCI_REG_FMREMAINING (0x0038)
#define OHCI_REG_FMNUMBER (0x003C)
class OhciController : public PciDevice
{
public:
OhciController( int deviceNum ) : PciDevice( 0, deviceNum, 0, PCI_NVIDIA_OHCI_DEVICE_ID ),
m_mmiobase( 0 ),
m_config60( 0 ),
m_interruptEnabled( false )
{
snprintf( m_name, 32, "OHCI Controller %d:%d.%d", GetBus(), GetDev(), GetFunc() );
}
virtual ~OhciController() {}
virtual const char* GetName() { return m_name; }
virtual u32 MmioRead32( u32 addr );
virtual bool MmioWrite32( u32 addr, u32 data );
virtual bool WriteBAR( int num, u32 &data );
virtual bool WriteConfigSpace( u8 reg, u32 &data );
virtual bool ReadConfigSpace( u8 reg, u32 &data );
private:
char m_name[32];
u32 m_mmiobase;
u32 m_config60;
bool m_interruptEnabled;
};
extern OhciController *g_usb0;
extern OhciController *g_usb1;
#endif /*XBVM_OHCICONTROLLER_H*/