System: Set BIOS before resetting

Fixes incorrect first instruction executing in interpreter mode.
This commit is contained in:
Connor McLaughlin 2020-10-29 22:05:09 +10:00
parent e081ee4b8e
commit b2d224abfc
3 changed files with 47 additions and 45 deletions

View file

@ -143,16 +143,16 @@ bool IsValidHashForRegion(ConsoleRegion region, const Hash& hash)
return (ii->region == ConsoleRegion::Auto || ii->region == region); return (ii->region == ConsoleRegion::Auto || ii->region == region);
} }
void PatchBIOS(Image& bios, u32 address, u32 value, u32 mask /*= UINT32_C(0xFFFFFFFF)*/) void PatchBIOS(u8* image, u32 image_size, u32 address, u32 value, u32 mask /*= UINT32_C(0xFFFFFFFF)*/)
{ {
const u32 phys_address = address & UINT32_C(0x1FFFFFFF); const u32 phys_address = address & UINT32_C(0x1FFFFFFF);
const u32 offset = phys_address - BIOS_BASE; const u32 offset = phys_address - BIOS_BASE;
Assert(phys_address >= BIOS_BASE && offset < BIOS_SIZE); Assert(phys_address >= BIOS_BASE && (offset + sizeof(u32)) <= image_size);
u32 existing_value; u32 existing_value;
std::memcpy(&existing_value, &bios[offset], sizeof(existing_value)); std::memcpy(&existing_value, &image[offset], sizeof(existing_value));
u32 new_value = (existing_value & ~mask) | value; u32 new_value = (existing_value & ~mask) | value;
std::memcpy(&bios[offset], &new_value, sizeof(new_value)); std::memcpy(&image[offset], &new_value, sizeof(new_value));
SmallString old_disasm, new_disasm; SmallString old_disasm, new_disasm;
CPU::DisassembleInstruction(&old_disasm, address, existing_value); CPU::DisassembleInstruction(&old_disasm, address, existing_value);
@ -161,7 +161,7 @@ void PatchBIOS(Image& bios, u32 address, u32 value, u32 mask /*= UINT32_C(0xFFFF
old_disasm.GetCharArray(), new_value, new_disasm.GetCharArray()); old_disasm.GetCharArray(), new_value, new_disasm.GetCharArray());
} }
bool PatchBIOSEnableTTY(Image& image, const Hash& hash) bool PatchBIOSEnableTTY(u8* image, u32 image_size, const Hash& hash)
{ {
const ImageInfo* ii = GetImageInfoForHash(hash); const ImageInfo* ii = GetImageInfoForHash(hash);
if (!ii) if (!ii)
@ -171,12 +171,12 @@ bool PatchBIOSEnableTTY(Image& image, const Hash& hash)
} }
Log_InfoPrintf("Patching BIOS to enable TTY/printf"); Log_InfoPrintf("Patching BIOS to enable TTY/printf");
PatchBIOS(image, 0x1FC06F0C, 0x24010001); PatchBIOS(image, image_size, 0x1FC06F0C, 0x24010001);
PatchBIOS(image, 0x1FC06F14, 0xAF81A9C0); PatchBIOS(image, image_size, 0x1FC06F14, 0xAF81A9C0);
return true; return true;
} }
bool PatchBIOSFastBoot(Image& image, const Hash& hash) bool PatchBIOSFastBoot(u8* image, u32 image_size, const Hash& hash)
{ {
const ImageInfo* ii = GetImageInfoForHash(hash); const ImageInfo* ii = GetImageInfoForHash(hash);
if (!ii) if (!ii)
@ -187,42 +187,46 @@ bool PatchBIOSFastBoot(Image& image, const Hash& hash)
// Replace the shell entry point with a return back to the bootstrap. // Replace the shell entry point with a return back to the bootstrap.
Log_InfoPrintf("Patching BIOS to skip intro"); Log_InfoPrintf("Patching BIOS to skip intro");
PatchBIOS(image, 0x1FC18000, 0x03E00008); PatchBIOS(image, image_size, 0x1FC18000, 0x03E00008);
PatchBIOS(image, 0x1FC18004, 0x00000000); PatchBIOS(image, image_size, 0x1FC18004, 0x00000000);
return true; return true;
} }
bool PatchBIOSForEXE(Image& image, u32 r_pc, u32 r_gp, u32 r_sp, u32 r_fp) bool PatchBIOSForEXE(u8* image, u32 image_size, u32 r_pc, u32 r_gp, u32 r_sp, u32 r_fp)
{ {
#define PATCH(offset, value) PatchBIOS(image, image_size, (offset), (value))
// pc has to be done first because we can't load it in the delay slot // pc has to be done first because we can't load it in the delay slot
PatchBIOS(image, 0xBFC06FF0, UINT32_C(0x3C080000) | r_pc >> 16); // lui $t0, (r_pc >> 16) PATCH(0xBFC06FF0, UINT32_C(0x3C080000) | r_pc >> 16); // lui $t0, (r_pc >> 16)
PatchBIOS(image, 0xBFC06FF4, UINT32_C(0x35080000) | (r_pc & UINT32_C(0xFFFF))); // ori $t0, $t0, (r_pc & 0xFFFF) PATCH(0xBFC06FF4, UINT32_C(0x35080000) | (r_pc & UINT32_C(0xFFFF))); // ori $t0, $t0, (r_pc & 0xFFFF)
PatchBIOS(image, 0xBFC06FF8, UINT32_C(0x3C1C0000) | r_gp >> 16); // lui $gp, (r_gp >> 16) PATCH(0xBFC06FF8, UINT32_C(0x3C1C0000) | r_gp >> 16); // lui $gp, (r_gp >> 16)
PatchBIOS(image, 0xBFC06FFC, UINT32_C(0x379C0000) | (r_gp & UINT32_C(0xFFFF))); // ori $gp, $gp, (r_gp & 0xFFFF) PATCH(0xBFC06FFC, UINT32_C(0x379C0000) | (r_gp & UINT32_C(0xFFFF))); // ori $gp, $gp, (r_gp & 0xFFFF)
if (r_sp != 0) if (r_sp != 0)
{ {
PatchBIOS(image, 0xBFC07000, UINT32_C(0x3C1D0000) | r_sp >> 16); // lui $sp, (r_sp >> 16) PATCH(0xBFC07000, UINT32_C(0x3C1D0000) | r_sp >> 16); // lui $sp, (r_sp >> 16)
PatchBIOS(image, 0xBFC07004, UINT32_C(0x37BD0000) | (r_sp & UINT32_C(0xFFFF))); // ori $sp, $sp, (r_sp & 0xFFFF) PATCH(0xBFC07004, UINT32_C(0x37BD0000) | (r_sp & UINT32_C(0xFFFF))); // ori $sp, $sp, (r_sp & 0xFFFF)
} }
else else
{ {
PatchBIOS(image, 0xBFC07000, UINT32_C(0x00000000)); // nop PATCH(0xBFC07000, UINT32_C(0x00000000)); // nop
PatchBIOS(image, 0xBFC07004, UINT32_C(0x00000000)); // nop PATCH(0xBFC07004, UINT32_C(0x00000000)); // nop
} }
if (r_fp != 0) if (r_fp != 0)
{ {
PatchBIOS(image, 0xBFC07008, UINT32_C(0x3C1E0000) | r_fp >> 16); // lui $fp, (r_fp >> 16) PATCH(0xBFC07008, UINT32_C(0x3C1E0000) | r_fp >> 16); // lui $fp, (r_fp >> 16)
PatchBIOS(image, 0xBFC0700C, UINT32_C(0x01000008)); // jr $t0 PATCH(0xBFC0700C, UINT32_C(0x01000008)); // jr $t0
PatchBIOS(image, 0xBFC07010, UINT32_C(0x37DE0000) | (r_fp & UINT32_C(0xFFFF))); // ori $fp, $fp, (r_fp & 0xFFFF) PATCH(0xBFC07010, UINT32_C(0x37DE0000) | (r_fp & UINT32_C(0xFFFF))); // ori $fp, $fp, (r_fp & 0xFFFF)
} }
else else
{ {
PatchBIOS(image, 0xBFC07008, UINT32_C(0x00000000)); // nop PATCH(0xBFC07008, UINT32_C(0x00000000)); // nop
PatchBIOS(image, 0xBFC0700C, UINT32_C(0x01000008)); // jr $t0 PATCH(0xBFC0700C, UINT32_C(0x01000008)); // jr $t0
PatchBIOS(image, 0xBFC07010, UINT32_C(0x00000000)); // nop PATCH(0xBFC07010, UINT32_C(0x00000000)); // nop
} }
#undef PATCH
return true; return true;
} }

View file

@ -59,11 +59,11 @@ std::optional<Hash> GetHashForFile(const char* filename);
const ImageInfo* GetImageInfoForHash(const Hash& hash); const ImageInfo* GetImageInfoForHash(const Hash& hash);
bool IsValidHashForRegion(ConsoleRegion region, const Hash& hash); bool IsValidHashForRegion(ConsoleRegion region, const Hash& hash);
void PatchBIOS(Image& image, u32 address, u32 value, u32 mask = UINT32_C(0xFFFFFFFF)); void PatchBIOS(u8* image, u32 image_size, u32 address, u32 value, u32 mask = UINT32_C(0xFFFFFFFF));
bool PatchBIOSEnableTTY(Image& image, const Hash& hash); bool PatchBIOSEnableTTY(u8* image, u32 image_size, const Hash& hash);
bool PatchBIOSFastBoot(Image& image, const Hash& hash); bool PatchBIOSFastBoot(u8* image, u32 image_size, const Hash& hash);
bool PatchBIOSForEXE(Image& image, u32 r_pc, u32 r_gp, u32 r_sp, u32 r_fp); bool PatchBIOSForEXE(u8* image, u32 image_size, u32 r_pc, u32 r_gp, u32 r_sp, u32 r_fp);
bool IsValidPSExeHeader(const PSEXEHeader& header, u32 file_size); bool IsValidPSExeHeader(const PSEXEHeader& header, u32 file_size);
} // namespace BIOS } // namespace BIOS

View file

@ -49,9 +49,9 @@ SystemBootParameters::~SystemBootParameters() = default;
namespace System { namespace System {
static bool LoadEXE(const char* filename, std::vector<u8>& bios_image); static bool LoadEXE(const char* filename);
static bool LoadEXEFromBuffer(const void* buffer, u32 buffer_size, std::vector<u8>& bios_image); static bool LoadEXEFromBuffer(const void* buffer, u32 buffer_size);
static bool LoadPSF(const char* filename, std::vector<u8>& bios_image); static bool LoadPSF(const char* filename);
static bool SetExpansionROM(const char* filename); static bool SetExpansionROM(const char* filename);
/// Opens CD image, preloading if needed. /// Opens CD image, preloading if needed.
@ -661,6 +661,7 @@ bool Boot(const SystemBootParameters& params)
return false; return false;
} }
Bus::SetBIOS(*bios_image);
UpdateControllers(); UpdateControllers();
UpdateMemoryCards(); UpdateMemoryCards();
Reset(); Reset();
@ -668,16 +669,16 @@ bool Boot(const SystemBootParameters& params)
// Enable tty by patching bios. // Enable tty by patching bios.
const BIOS::Hash bios_hash = BIOS::GetHash(*bios_image); const BIOS::Hash bios_hash = BIOS::GetHash(*bios_image);
if (g_settings.bios_patch_tty_enable) if (g_settings.bios_patch_tty_enable)
BIOS::PatchBIOSEnableTTY(*bios_image, bios_hash); BIOS::PatchBIOSEnableTTY(Bus::g_bios, Bus::BIOS_SIZE, bios_hash);
// Load EXE late after BIOS. // Load EXE late after BIOS.
if (exe_boot && !LoadEXE(params.filename.c_str(), *bios_image)) if (exe_boot && !LoadEXE(params.filename.c_str()))
{ {
g_host_interface->ReportFormattedError("Failed to load EXE file '%s'", params.filename.c_str()); g_host_interface->ReportFormattedError("Failed to load EXE file '%s'", params.filename.c_str());
Shutdown(); Shutdown();
return false; return false;
} }
else if (psf_boot && !LoadPSF(params.filename.c_str(), *bios_image)) else if (psf_boot && !LoadPSF(params.filename.c_str()))
{ {
g_host_interface->ReportFormattedError("Failed to load PSF file '%s'", params.filename.c_str()); g_host_interface->ReportFormattedError("Failed to load PSF file '%s'", params.filename.c_str());
Shutdown(); Shutdown();
@ -690,12 +691,9 @@ bool Boot(const SystemBootParameters& params)
if (g_cdrom.HasMedia() && if (g_cdrom.HasMedia() &&
(params.override_fast_boot.has_value() ? params.override_fast_boot.value() : g_settings.bios_patch_fast_boot)) (params.override_fast_boot.has_value() ? params.override_fast_boot.value() : g_settings.bios_patch_fast_boot))
{ {
BIOS::PatchBIOSFastBoot(*bios_image, bios_hash); BIOS::PatchBIOSFastBoot(Bus::g_bios, Bus::BIOS_SIZE, bios_hash);
} }
// Load the patched BIOS up.
Bus::SetBIOS(*bios_image);
// Good to go. // Good to go.
s_state = State::Running; s_state = State::Running;
return true; return true;
@ -1266,7 +1264,7 @@ void ResetPerformanceCounters()
s_last_throttle_time = 0; s_last_throttle_time = 0;
} }
bool LoadEXE(const char* filename, std::vector<u8>& bios_image) bool LoadEXE(const char* filename)
{ {
std::FILE* fp = FileSystem::OpenCFile(filename, "rb"); std::FILE* fp = FileSystem::OpenCFile(filename, "rb");
if (!fp) if (!fp)
@ -1319,10 +1317,10 @@ bool LoadEXE(const char* filename, std::vector<u8>& bios_image)
const u32 r_gp = header.initial_gp; const u32 r_gp = header.initial_gp;
const u32 r_sp = header.initial_sp_base + header.initial_sp_offset; const u32 r_sp = header.initial_sp_base + header.initial_sp_offset;
const u32 r_fp = header.initial_sp_base + header.initial_sp_offset; const u32 r_fp = header.initial_sp_base + header.initial_sp_offset;
return BIOS::PatchBIOSForEXE(bios_image, r_pc, r_gp, r_sp, r_fp); return BIOS::PatchBIOSForEXE(Bus::g_bios, Bus::BIOS_SIZE, r_pc, r_gp, r_sp, r_fp);
} }
bool LoadEXEFromBuffer(const void* buffer, u32 buffer_size, std::vector<u8>& bios_image) bool LoadEXEFromBuffer(const void* buffer, u32 buffer_size)
{ {
const u8* buffer_ptr = static_cast<const u8*>(buffer); const u8* buffer_ptr = static_cast<const u8*>(buffer);
const u8* buffer_end = static_cast<const u8*>(buffer) + buffer_size; const u8* buffer_end = static_cast<const u8*>(buffer) + buffer_size;
@ -1370,10 +1368,10 @@ bool LoadEXEFromBuffer(const void* buffer, u32 buffer_size, std::vector<u8>& bio
const u32 r_gp = header.initial_gp; const u32 r_gp = header.initial_gp;
const u32 r_sp = header.initial_sp_base + header.initial_sp_offset; const u32 r_sp = header.initial_sp_base + header.initial_sp_offset;
const u32 r_fp = header.initial_sp_base + header.initial_sp_offset; const u32 r_fp = header.initial_sp_base + header.initial_sp_offset;
return BIOS::PatchBIOSForEXE(bios_image, r_pc, r_gp, r_sp, r_fp); return BIOS::PatchBIOSForEXE(Bus::g_bios, Bus::BIOS_SIZE, r_pc, r_gp, r_sp, r_fp);
} }
bool LoadPSF(const char* filename, std::vector<u8>& bios_image) bool LoadPSF(const char* filename)
{ {
Log_InfoPrintf("Loading PSF file from '%s'", filename); Log_InfoPrintf("Loading PSF file from '%s'", filename);
@ -1382,7 +1380,7 @@ bool LoadPSF(const char* filename, std::vector<u8>& bios_image)
return false; return false;
const std::vector<u8>& exe_data = psf.GetProgramData(); const std::vector<u8>& exe_data = psf.GetProgramData();
return LoadEXEFromBuffer(exe_data.data(), static_cast<u32>(exe_data.size()), bios_image); return LoadEXEFromBuffer(exe_data.data(), static_cast<u32>(exe_data.size()));
} }
bool SetExpansionROM(const char* filename) bool SetExpansionROM(const char* filename)