System: Rewrite EXE override/loading

Relies on POST=7 as a kernel initialization indicator, instead of
patching the BIOS.

Fixes EXE loading with OpenBIOS and PS2 BIOS, and fast boot getting
baked into save states.
This commit is contained in:
Stenzek
2024-07-28 16:16:05 +10:00
parent 7b99fcbbf3
commit 6fe0c986fa
13 changed files with 327 additions and 322 deletions

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#include "bus.h"
#include "bios.h"
#include "cdrom.h"
#include "cpu_code_cache.h"
#include "cpu_core.h"
@ -13,6 +14,7 @@
#include "interrupt_controller.h"
#include "mdec.h"
#include "pad.h"
#include "psf_loader.h"
#include "settings.h"
#include "sio.h"
#include "spu.h"
@ -20,14 +22,17 @@
#include "timers.h"
#include "timing_event.h"
#include "util/cd_image.h"
#include "util/state_wrapper.h"
#include "common/align.h"
#include "common/assert.h"
#include "common/error.h"
#include "common/file_system.h"
#include "common/intrin.h"
#include "common/log.h"
#include "common/memmap.h"
#include "common/path.h"
#include <cstdio>
#include <tuple>
@ -146,6 +151,8 @@ static std::vector<std::pair<u8*, size_t>> s_fastmem_ram_views;
static u8** s_fastmem_lut = nullptr;
static bool s_kernel_initialize_hook_run = false;
static void SetRAMSize(bool enable_8mb_ram);
static std::tuple<TickCount, TickCount, TickCount> CalculateMemoryTiming(MEMDELAY mem_delay, COMDELAY common_delay);
@ -155,6 +162,9 @@ static u8* GetLUTFastmemPointer(u32 address, u8* ram_ptr);
static void SetRAMPageWritable(u32 page_index, bool writable);
static void KernelInitializedHook();
static bool SideloadEXE(const std::string& path, Error* error);
static void SetHandlers();
static void UpdateMappedRAMSize();
@ -348,6 +358,7 @@ void Bus::Reset()
s_MEMCTRL.exp2_delay_size.bits = 0x00070777;
s_MEMCTRL.common_delay.bits = 0x00031125;
g_ram_code_bits = {};
s_kernel_initialize_hook_run = false;
RecalculateMemoryTimings();
// Avoid remapping if unchanged.
@ -358,35 +369,6 @@ void Bus::Reset()
}
}
void Bus::AddTTYCharacter(char ch)
{
if (ch == '\r')
{
}
else if (ch == '\n')
{
if (!s_tty_line_buffer.empty())
{
Log::FastWrite("TTY", "", LOGLEVEL_INFO, "\033[1;34m{}\033[0m", s_tty_line_buffer);
#ifdef _DEBUG
if (CPU::IsTraceEnabled())
CPU::WriteToExecutionLog("TTY: %s\n", s_tty_line_buffer.c_str());
#endif
}
s_tty_line_buffer.clear();
}
else
{
s_tty_line_buffer += ch;
}
}
void Bus::AddTTYString(std::string_view str)
{
for (char ch : str)
AddTTYCharacter(ch);
}
bool Bus::DoState(StateWrapper& sw)
{
u32 ram_size = g_ram_size;
@ -420,12 +402,10 @@ bool Bus::DoState(StateWrapper& sw)
UpdateMappedRAMSize();
sw.Do(&s_tty_line_buffer);
return !sw.HasError();
}
void Bus::SetExpansionROM(std::vector<u8> data)
{
s_exp1_rom = std::move(data);
sw.DoEx(&s_kernel_initialize_hook_run, 68, true);
return !sw.HasError();
}
std::tuple<TickCount, TickCount, TickCount> Bus::CalculateMemoryTiming(MEMDELAY mem_delay, COMDELAY common_delay)
@ -863,6 +843,146 @@ std::optional<PhysicalMemoryAddress> Bus::SearchMemory(PhysicalMemoryAddress sta
return std::nullopt;
}
void Bus::SetExpansionROM(std::vector<u8> data)
{
s_exp1_rom = std::move(data);
}
void Bus::AddTTYCharacter(char ch)
{
if (ch == '\r')
{
}
else if (ch == '\n')
{
if (!s_tty_line_buffer.empty())
{
Log::FastWrite("TTY", "", LOGLEVEL_INFO, "\033[1;34m{}\033[0m", s_tty_line_buffer);
#ifdef _DEBUG
if (CPU::IsTraceEnabled())
CPU::WriteToExecutionLog("TTY: %s\n", s_tty_line_buffer.c_str());
#endif
}
s_tty_line_buffer.clear();
}
else
{
s_tty_line_buffer += ch;
}
}
void Bus::AddTTYString(std::string_view str)
{
for (char ch : str)
AddTTYCharacter(ch);
}
bool Bus::InjectExecutable(std::span<const u8> buffer, bool set_pc, Error* error)
{
BIOS::PSEXEHeader header;
if (buffer.size() < sizeof(header))
{
Error::SetStringView(error, "Executable does not contain a header.");
return false;
}
std::memcpy(&header, buffer.data(), sizeof(header));
if (!BIOS::IsValidPSExeHeader(header, buffer.size()))
{
Error::SetStringView(error, "Executable does not contain a valid header.");
return false;
}
if (header.memfill_size > 0)
{
const u32 words_to_write = header.memfill_size / 4;
u32 address = header.memfill_start & ~UINT32_C(3);
for (u32 i = 0; i < words_to_write; i++)
{
CPU::SafeWriteMemoryWord(address, 0);
address += sizeof(u32);
}
}
const u32 data_load_size =
std::min(static_cast<u32>(static_cast<u32>(buffer.size() - sizeof(BIOS::PSEXEHeader))), header.file_size);
if (data_load_size > 0)
{
if (!CPU::SafeWriteMemoryBytes(header.load_address, &buffer[sizeof(header)], data_load_size))
{
Error::SetStringFmt(error, "Failed to upload {} bytes to memory at address 0x{:08X}.", data_load_size,
header.load_address);
}
}
// patch the BIOS to jump to the executable directly
if (set_pc)
{
const u32 r_pc = header.initial_pc;
CPU::g_state.regs.gp = header.initial_gp;
CPU::g_state.regs.sp = header.initial_sp_base + header.initial_sp_offset;
CPU::g_state.regs.fp = header.initial_sp_base + header.initial_sp_offset;
CPU::SetPC(r_pc);
}
return true;
}
void Bus::KernelInitializedHook()
{
if (s_kernel_initialize_hook_run)
return;
INFO_LOG("Kernel initialized.");
s_kernel_initialize_hook_run = true;
const System::BootMode boot_mode = System::GetBootMode();
if (boot_mode == System::BootMode::BootEXE || boot_mode == System::BootMode::BootPSF)
{
Error error;
if (((boot_mode == System::BootMode::BootEXE) ? SideloadEXE(System::GetExeOverride(), &error) :
PSFLoader::Load(System::GetExeOverride(), &error)))
{
// Clear all state, since we're blatently overwriting memory.
CPU::CodeCache::Reset();
CPU::ClearICache();
// Stop executing the current block and shell init, and jump straight to the new code.
DebugAssert(!TimingEvents::IsRunningEvents());
CPU::ExitExecution();
}
else
{
// Shut down system on load failure.
Host::ReportErrorAsync("EXE/PSF Load Failed", error.GetDescription());
System::ShutdownSystem(false);
}
}
}
bool Bus::SideloadEXE(const std::string& path, Error* error)
{
// look for a libps.exe next to the exe, if it exists, load it
bool okay = true;
if (const std::string libps_path = Path::BuildRelativePath(path, "libps.exe");
FileSystem::FileExists(libps_path.c_str()))
{
const std::optional<std::vector<u8>> exe_data = FileSystem::ReadBinaryFile(libps_path.c_str(), error);
okay = (exe_data.has_value() && InjectExecutable(exe_data.value(), false, error));
if (!okay)
Error::AddPrefix(error, "Failed to load libps.exe: ");
}
if (okay)
{
const std::optional<std::vector<u8>> exe_data = FileSystem::ReadBinaryFile(System::GetExeOverride().c_str(), error);
okay = (exe_data.has_value() && InjectExecutable(exe_data.value(), true, error));
if (!okay)
Error::AddPrefixFmt(error, "Failed to load {}: ", Path::GetFileName(path));
}
return okay;
}
#define BUS_CYCLES(n) CPU::g_state.pending_ticks += n
// TODO: Move handlers to own files for better inlining.
@ -1192,7 +1312,10 @@ void Bus::EXP2WriteHandler(VirtualMemoryAddress address, u32 value)
}
else if (offset == 0x41 || offset == 0x42)
{
DEV_LOG("BIOS POST status: {:02X}", value & UINT32_C(0x0F));
const u32 post_code = value & UINT32_C(0x0F);
DEV_LOG("BIOS POST status: {:02X}", post_code);
if (post_code == 0x07)
KernelInitializedHook();
}
else if (offset == 0x70)
{
@ -1233,7 +1356,12 @@ void Bus::EXP3WriteHandler(VirtualMemoryAddress address, u32 value)
{
const u32 offset = address & EXP3_MASK;
if (offset == 0)
WARNING_LOG("BIOS POST3 status: {:02X}", value & UINT32_C(0x0F));
{
const u32 post_code = value & UINT32_C(0x0F);
WARNING_LOG("BIOS POST3 status: {:02X}", post_code);
if (post_code == 0x07)
KernelInitializedHook();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////