mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-02 23:42:43 +00:00
Patch CPUID
This commit is contained in:
parent
28ec489dbe
commit
7d6a01cc29
@ -798,4 +798,50 @@ void PrePatchInstructions(u64 segment_addr, u64 segment_size) {
|
||||
#endif
|
||||
}
|
||||
|
||||
// We patch CPUID to handle it specially and simulate an AMD environment.
|
||||
// Because CPUID is at minimum 2 bytes, a trampoline jump is not possible.
|
||||
// Instead, we replace the instruction with an illegal instruction and handle it in the illegal instruction handler.
|
||||
void PatchCPUID(u64 segment_addr, u64 segment_size) {
|
||||
u8* code = reinterpret_cast<u8*>(segment_addr);
|
||||
u8* end = code + segment_size;
|
||||
bool previous_instruction_ok = false;
|
||||
|
||||
while (code < end) {
|
||||
ZydisDecodedInstruction instruction;
|
||||
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
|
||||
const auto status = ZydisDecoderDecodeFull(&instr_decoder, code, end - code, &instruction, operands);
|
||||
|
||||
if (!ZYAN_SUCCESS(status)) {
|
||||
code++;
|
||||
previous_instruction_ok = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (instruction.mnemonic == ZYDIS_MNEMONIC_CPUID) {
|
||||
if (!previous_instruction_ok) {
|
||||
// It should be highly unlikely that the CPUID instruction is not preceded by a valid instruction.
|
||||
// Instead of patching it we're going to skip it and generate a warning, because we could be in some
|
||||
// data section that must not be changed.
|
||||
LOG_ERROR(Core, "CPUID instruction is not preceded by a valid instruction at: {} while patching", fmt::ptr(code));
|
||||
code++;
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_CRITICAL(Core, "FOUND CPUID INSTRUCTION AT: {}", fmt::ptr(code));
|
||||
|
||||
// Replace any potential prefixes and the 0F byte with single byte NOPs
|
||||
u8 instruction_length = instruction.length;
|
||||
for (u8 i = 0; i < instruction_length - 1; i++) {
|
||||
code[i] = 0x90;
|
||||
}
|
||||
|
||||
// Replace the CPUID instruction with an illegal instruction
|
||||
code[instruction_length - 1] = 0x37; // 0x37 is AAA, illegal in x86-64
|
||||
}
|
||||
|
||||
code += instruction.length;
|
||||
previous_instruction_ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
@ -18,4 +18,7 @@ void RegisterPatchModule(void* module_ptr, u64 module_size, void* trampoline_are
|
||||
/// Applies CPU patches that need to be done before beginning executions.
|
||||
void PrePatchInstructions(u64 segment_addr, u64 segment_size);
|
||||
|
||||
// Patches CPUID instructions with an illegal instruction to handle them specially in the signal handler
|
||||
void PatchCPUID(u64 segment_addr, u64 segment_size);
|
||||
|
||||
} // namespace Core
|
||||
|
@ -137,6 +137,7 @@ void Module::LoadModuleToMemory(u32& max_tls_index) {
|
||||
#ifdef ARCH_X86_64
|
||||
if (elf_pheader[i].p_flags & PF_EXEC) {
|
||||
PrePatchInstructions(segment_addr, segment_file_size);
|
||||
PatchCPUID(segment_addr, segment_file_size);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
@ -7,8 +7,10 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <intrin.h>
|
||||
#else
|
||||
#include <csignal>
|
||||
#include <cpuid.h>
|
||||
#ifdef ARCH_X86_64
|
||||
#include <Zydis/Decoder.h>
|
||||
#include <Zydis/Formatter.h>
|
||||
@ -31,9 +33,16 @@ static LONG WINAPI SignalHandler(EXCEPTION_POINTERS* pExp) noexcept {
|
||||
code_address, reinterpret_cast<void*>(pExp->ExceptionRecord->ExceptionInformation[1]),
|
||||
pExp->ExceptionRecord->ExceptionInformation[0] == 1);
|
||||
break;
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||
handled = signals->DispatchIllegalInstruction(code_address);
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION: {
|
||||
u8 opcode = *reinterpret_cast<u8*>(code_address);
|
||||
if (opcode == 0x37) { // 0x37 is AAA instruction, we use that to patch CPUID
|
||||
handled = EXCEPTION_CONTINUE_EXECUTION;
|
||||
pExp->ContextRecord->Rip += 1; // skip the illegal instruction
|
||||
} else {
|
||||
handled = signals->DispatchIllegalInstruction(code_address);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -87,7 +96,7 @@ static std::string DisassembleInstruction(void* code_address) {
|
||||
}
|
||||
|
||||
static void SignalHandler(int sig, siginfo_t* info, void* raw_context) {
|
||||
const auto* ctx = static_cast<ucontext_t*>(raw_context);
|
||||
auto* ctx = static_cast<ucontext_t*>(raw_context);
|
||||
const auto* signals = Signals::Instance();
|
||||
|
||||
auto* code_address = CODE_ADDRESS(ctx);
|
||||
@ -102,12 +111,25 @@ static void SignalHandler(int sig, siginfo_t* info, void* raw_context) {
|
||||
fmt::ptr(info->si_addr));
|
||||
}
|
||||
break;
|
||||
case SIGILL:
|
||||
if (!signals->DispatchIllegalInstruction(code_address)) {
|
||||
UNREACHABLE_MSG("Unhandled illegal instruction at code address {}: {}",
|
||||
fmt::ptr(code_address), DisassembleInstruction(code_address));
|
||||
case SIGILL: {
|
||||
u8 opcode = *reinterpret_cast<u8*>(code_address);
|
||||
if (opcode == 0x37) { // 0x37 is AAA instruction, we use that to patch CPUID
|
||||
u64 results[4];
|
||||
u64 eax = ctx->uc_mcontext.gregs[REG_RAX];
|
||||
u64 ecx = ctx->uc_mcontext.gregs[REG_RCX];
|
||||
LOG_WARNING(Core, "CPUID: eax=%lx ecx=%lx\n", eax, ecx);
|
||||
__cpuid_count(eax, ecx, results[0], results[1], results[2], results[3]);
|
||||
LOG_WARNING(Core, "RESULTS: %lx %lx %lx %lx\n", results[0], results[1], results[2], results[3]);
|
||||
ctx->uc_mcontext.gregs[REG_RIP] += 1; // skip the illegal instruction
|
||||
break;
|
||||
} else {
|
||||
if (!signals->DispatchIllegalInstruction(code_address)) {
|
||||
UNREACHABLE_MSG("Unhandled illegal instruction at code address {}: {}",
|
||||
fmt::ptr(code_address), DisassembleInstruction(code_address));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user