Merge pull request #1440 from LukeUsher/interpret-larger-blocks

Interpret larger blocks during exception handler to reduce the number of exceptions per-frame
This commit is contained in:
PatrickvL 2018-09-18 10:50:57 +02:00 committed by GitHub
commit 338879ba96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 344 additions and 131 deletions

View File

@ -127,7 +127,7 @@ NT_TIB *GetNtTib()
}
xboxkrnl::KPCR* KeGetPcr();
xboxkrnl::KPCR* WINAPI KeGetPcr();
uint32_t fs_lock = 0;

View File

@ -131,7 +131,7 @@ xboxkrnl::PLIST_ENTRY RemoveTailList(xboxkrnl::PLIST_ENTRY pListHead)
// * Declaring this in a header causes errors with xboxkrnl
// * namespace, so we must declare it within any file that uses it
// ******************************************************************
xboxkrnl::KPCR* KeGetPcr();
xboxkrnl::KPCR* WINAPI KeGetPcr();
// Interrupts

View File

@ -124,7 +124,8 @@ XBSYSAPI EXPORTNUM(8) xboxkrnl::ULONG _cdecl xboxkrnl::DbgPrint
vsprintf(szBuffer, Format, argp);
va_end(argp);
printf(szBuffer); // Note : missing newlines can occur
// Allow DbgPrint to be disabled
EmuLog(LOG_PREFIX, LOG_LEVEL::INFO, "%s", szBuffer);
fflush(stdout);
}

View File

@ -76,7 +76,7 @@ LIST_ENTRY_DEFINE_HEAD(ShutdownRoutineList);
// * Declaring this in a header causes errors with xboxkrnl
// * namespace, so we must declare it within any file that uses it
// ******************************************************************
xboxkrnl::KPCR* KeGetPcr();
xboxkrnl::KPCR* WINAPI KeGetPcr();
// ******************************************************************
// * 0x0009 - HalReadSMCTrayState()

View File

@ -186,7 +186,7 @@ void FASTCALL KiWaitSatisfyAll
// * NOTE: This is a macro on the Xbox, however we implement it
// * as a function so it can suit our emulated KPCR structure
// ******************************************************************
xboxkrnl::KPCR* KeGetPcr()
xboxkrnl::KPCR* WINAPI KeGetPcr()
{
xboxkrnl::PKPCR Pcr;

View File

@ -119,6 +119,8 @@ void EmuX86_IOWrite(xbaddr addr, uint32_t value, int size)
uint32_t EmuX86_Mem_Read(xbaddr addr, int size)
{
__try {
switch (size) {
case sizeof(uint32_t) :
return *(uint32_t*)addr;
@ -131,9 +133,15 @@ uint32_t EmuX86_Mem_Read(xbaddr addr, int size)
return 0;
}
}
__except (true) {
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "EmuX86_Mem_Read Failed (0x%08X, %d)", addr, size);
return 0;
}
}
void EmuX86_Mem_Write(xbaddr addr, uint32_t value, int size)
{
__try {
switch (size) {
case sizeof(uint32_t) :
*(uint32_t*)addr = (uint32_t)value;
@ -149,6 +157,10 @@ void EmuX86_Mem_Write(xbaddr addr, uint32_t value, int size)
return;
}
}
__except (true) {
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "EmuX86_Mem_Write Failed (0x%08X, 0x%08X, %d)", addr, value, size);
}
}
uint32_t EmuFlash_Read32(xbaddr addr) // TODO : Move to EmuFlash.cpp
{
@ -192,13 +204,8 @@ uint32_t EmuX86_Read(xbaddr addr, int size)
return value;
}
if (g_bEmuException) {
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "EmuX86_Read(0x%08X, %d) [Unknown address]", addr, size);
value = 0;
} else {
// Outside EmuException, pass the memory-access through to normal memory :
//pass the memory-access through to normal memory :
value = EmuX86_Mem_Read(addr, size);
}
DbgPrintf(LOG_PREFIX, "Read(0x%08X, %d) = 0x%08X\n", addr, size, value);
}
@ -224,12 +231,7 @@ void EmuX86_Write(xbaddr addr, uint32_t value, int size)
return;
}
if (g_bEmuException) {
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "EmuX86_Write(0x%08X, 0x%08X) [Unknown address]", addr, value);
return;
}
// Outside EmuException, pass the memory-access through to normal memory :
// Pass the memory-access through to normal memory :
DbgPrintf(LOG_PREFIX, "Write(0x%.8X, 0x%.8X, %d)\n", addr, value, size);
EmuX86_Mem_Write(addr, value, size);
}
@ -862,6 +864,33 @@ bool EmuX86_Opcode_INC(LPEXCEPTION_POINTERS e, _DInst& info)
return true;
}
bool EmuX86_Opcode_JMP(LPEXCEPTION_POINTERS e, _DInst& info)
{
OperandAddress opAddr;
EmuX86_Operand_Addr_ForReadOnly(e, info, 0, opAddr);
e->ContextRecord->Eip += opAddr.addr;
return true;
}
// Jump if condition is met
// Returns true if branch was taken
bool EmuX86_Opcode_JXX(LPEXCEPTION_POINTERS e, _DInst& info, DWORD flag, bool condition)
{
OperandAddress opAddr;
EmuX86_Operand_Addr_ForReadOnly(e, info, 0, opAddr);
// Branch if flag = condition
bool flagValue = (e->ContextRecord->EFlags & flag);
if (flagValue == condition) {
e->ContextRecord->Eip += opAddr.addr;
return true;
}
return false;
}
bool EmuX86_Opcode_MOV(LPEXCEPTION_POINTERS e, _DInst& info)
{
// MOV reads value from source :
@ -924,6 +953,32 @@ bool EmuX86_Opcode_MOVZX(LPEXCEPTION_POINTERS e, _DInst& info)
return true;
}
bool EmuX86_Opcode_NOT(LPEXCEPTION_POINTERS e, _DInst& info)
{
// NOT reads and writes the same operand :
OperandAddress opAddr;
if (!EmuX86_Operand_Addr_ForReadWrite(e, info, 0, OUT opAddr))
return false;
uint32_t dest = EmuX86_Addr_Read(opAddr);
// NOT Destination with
uint32_t result = ~dest;
// Write back the result
EmuX86_Addr_Write(opAddr, result);
// The OF and CF flags are cleared; the SF, ZF, and PF flags are set according to the result. The state of the AF flag is undefined.
EmuX86_SetFlags_OSZPC(e,
/*EMUX86_EFLAG_OF*/0,
/*EMUX86_EFLAG_SF*/SFCalc(result),
/*EMUX86_EFLAG_ZF*/ZFCalc(result),
/*EMUX86_EFLAG_PF*/PFCalc(result),
/*EMUX86_EFLAG_CF*/0);
return true;
}
bool EmuX86_Opcode_OR(LPEXCEPTION_POINTERS e, _DInst& info)
{
// Read value from Source and Destination
@ -974,6 +1029,78 @@ bool EmuX86_Opcode_OUT(LPEXCEPTION_POINTERS e, _DInst& info)
return true;
}
bool EmuX86_Opcode_SBB(LPEXCEPTION_POINTERS e, _DInst& info)
{
// Read value from Source and Destination
uint32_t src = 0;
if (!EmuX86_Operand_Read(e, info, 1, &src))
return false;
// SBB reads and writes the same operand :
OperandAddress opAddr;
if (!EmuX86_Operand_Addr_ForReadWrite(e, info, 0, OUT opAddr))
return false;
uint32_t dest = EmuX86_Addr_Read(opAddr);
// SUB Destination with src
uint64_t result = (uint64_t)dest - (uint64_t)src;
// If the carry flag is set, subtract an additional 1
if (e->ContextRecord->EFlags & BITMASK(EMUX86_EFLAG_CF)) {
result -= 1;
}
// Write result back
EmuX86_Addr_Write(opAddr, static_cast<uint32_t>(result));
// The OF, SF, ZF, AF, PF, and CF flags are set according to the result.
EmuX86_SetFlags_OSZAPC(e,
/*EMUX86_EFLAG_OF*/OF_Sub(result, src, dest),
/*EMUX86_EFLAG_SF*/SFCalc(result),
/*EMUX86_EFLAG_ZF*/ZFCalc(result),
/*EMUX86_EFLAG_AF*/AFCalc(result, src, dest),
/*EMUX86_EFLAG_PF*/PFCalc(result),
/*EMUX86_EFLAG_CF*/CFCalc(result));
return true;
}
bool EmuX86_Opcode_SHR(LPEXCEPTION_POINTERS e, _DInst& info)
{
// Read value from Source and Destination
uint32_t src = 0;
if (!EmuX86_Operand_Read(e, info, 1, &src))
return false;
// SHR reads and writes the same operand :
OperandAddress opAddr;
if (!EmuX86_Operand_Addr_ForReadWrite(e, info, 0, OUT opAddr))
return false;
uint32_t dest = EmuX86_Addr_Read(opAddr);
// Shift Destination with src
uint8_t carryBit = dest & 1;
uint64_t result = (uint64_t)dest >> (uint64_t)src;
// Write result back
EmuX86_Addr_Write(opAddr, static_cast<uint32_t>(result));
// The OF, SF, ZF, AF, PF, and CF flags are set according to the result.
EmuX86_SetFlags_OSZAPC(e,
/*EMUX86_EFLAG_OF*/OF_Sub(result, src, dest),
/*EMUX86_EFLAG_SF*/SFCalc(result),
/*EMUX86_EFLAG_ZF*/ZFCalc(result),
/*EMUX86_EFLAG_AF*/AFCalc(result, src, dest),
/*EMUX86_EFLAG_PF*/PFCalc(result),
/*EMUX86_EFLAG_CF*/carryBit);
return true;
}
bool EmuX86_Opcode_SUB(LPEXCEPTION_POINTERS e, _DInst& info)
{
// Read value from Source and Destination
@ -1035,6 +1162,37 @@ bool EmuX86_Opcode_TEST(LPEXCEPTION_POINTERS e, _DInst& info)
return true;
}
bool EmuX86_Opcode_XOR(LPEXCEPTION_POINTERS e, _DInst& info)
{
// Read value from Source and Destination
uint32_t src = 0;
if (!EmuX86_Operand_Read(e, info, 1, &src))
return false;
// XOR reads and writes the same operand :
OperandAddress opAddr;
if (!EmuX86_Operand_Addr_ForReadWrite(e, info, 0, OUT opAddr))
return false;
uint32_t dest = EmuX86_Addr_Read(opAddr);
// XOR Destination with src
uint32_t result = dest ^ src;
// Write back the result
EmuX86_Addr_Write(opAddr, result);
// The OF and CF flags are cleared; the SF, ZF, and PF flags are set according to the result. The state of the AF flag is undefined.
EmuX86_SetFlags_OSZPC(e,
/*EMUX86_EFLAG_OF*/0,
/*EMUX86_EFLAG_SF*/SFCalc(result),
/*EMUX86_EFLAG_ZF*/ZFCalc(result),
/*EMUX86_EFLAG_PF*/PFCalc(result),
/*EMUX86_EFLAG_CF*/0);
return true;
}
void EmuX86_Opcode_CLI()
{
g_bEnableAllInterrupts = false;
@ -1082,12 +1240,58 @@ bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e)
// However, if for any reason, an opcode operand cannot be read from or written to,
// that case may be logged, but it shouldn't fail the opcode handler.
_DInst info;
if (!EmuX86_DecodeOpcode((uint8_t*)e->ContextRecord->Eip, OUT info)) {
// Execute op-codes until we hit an unhandled instruction, or an error occurs
while (true) {
if (!EmuX86_DecodeOpcode((uint8_t*)e->ContextRecord->Eip, info)) {
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "Error decoding opcode at 0x%08X", e->ContextRecord->Eip);
return false;
}
switch (info.opcode) { // Keep these cases alphabetically ordered and condensed
// Exit and branch Opcodes come first, for clarity/visibility
case I_JA:
if (EmuX86_Opcode_JXX(e, info, BITMASK(EMUX86_EFLAG_CF) | BITMASK(EMUX86_EFLAG_ZF), false)) {
continue;
}
break;
case I_JAE:
if (EmuX86_Opcode_JXX(e, info, BITMASK(EMUX86_EFLAG_CF), false)) {
continue;
}
break;
case I_JB:
if (EmuX86_Opcode_JXX(e, info, BITMASK(EMUX86_EFLAG_CF), true)) {
continue;
}
break;
case I_JBE:
if (EmuX86_Opcode_JXX(e, info, BITMASK(EMUX86_EFLAG_CF) | BITMASK(EMUX86_EFLAG_ZF), true)) {
continue;
}
break;
case I_JNZ:
if (EmuX86_Opcode_JXX(e, info, BITMASK(EMUX86_EFLAG_ZF), false)) {
continue;
}
break;
case I_JMP:
if (EmuX86_Opcode_JMP(e, info)) {
continue;
}
break;
case I_JZ:
if (EmuX86_Opcode_JXX(e, info, BITMASK(EMUX86_EFLAG_ZF), true)) {
continue;
}
break;
case I_CALL: case I_RET:
// RET and CALL always signify the end of a code block
return true;
case I_PUSH: case I_POP:
// TODO: Implement these instructions
// currently stubbed to prevent firing the unimplemented instruction handler
return true;
case I_ADD:
if (EmuX86_Opcode_ADD(e, info)) break;
goto opcode_error;
@ -1133,6 +1337,9 @@ bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e)
case I_MOVZX:
if (EmuX86_Opcode_MOVZX(e, info)) break;
goto opcode_error;
case I_NOT:
if (EmuX86_Opcode_NOT(e, info)) break;
goto opcode_error;
case I_OR:
if (EmuX86_Opcode_OR(e, info)) break;
goto opcode_error;
@ -1144,6 +1351,12 @@ bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e)
EmuX86_Opcode_STI();
break;
}
case I_SBB:
if (EmuX86_Opcode_SBB(e, info)) break;
goto opcode_error;
case I_SHR:
if (EmuX86_Opcode_SHR(e, info)) break;
goto opcode_error;
case I_SUB:
if (EmuX86_Opcode_SUB(e, info)) break;
goto opcode_error;
@ -1159,20 +1372,19 @@ bool EmuX86_DecodeException(LPEXCEPTION_POINTERS e)
// Chase: Hollywood Stunt Driver hits this
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "WRMSR instruction ignored");
break;
case I_XOR:
if (EmuX86_Opcode_XOR(e, info)) break;
goto opcode_error;
default:
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "Unhandled instruction : %u", info.opcode);
e->ContextRecord->Eip += info.size;
return false;
return true;
}
// When falling through here, the instruction was handled correctly,
// skip over the instruction and continue execution :
e->ContextRecord->Eip += info.size;
return true;
}
opcode_error:
EmuLog(LOG_PREFIX, LOG_LEVEL::WARNING, "0x%08X: Error while handling instruction %u", e->ContextRecord->Eip, info.opcode); // TODO : format decodedInstructions[0]
e->ContextRecord->Eip += info.size;
return false;
}