Skip to content

Commit

Permalink
BIOS: Add detection of OpenBIOS
Browse files Browse the repository at this point in the history
  • Loading branch information
stenzek committed Nov 15, 2022
1 parent a883652 commit 6db90e8
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 71 deletions.
101 changes: 41 additions & 60 deletions src/core/bios.cpp
Expand Up @@ -11,10 +11,9 @@
#include <cerrno>
Log_SetChannel(BIOS);

namespace BIOS {
static constexpr Hash MakeHashFromString(const char str[])
static constexpr BIOS::Hash MakeHashFromString(const char str[])
{
Hash h{};
BIOS::Hash h{};
for (int i = 0; str[i] != '\0'; i++)
{
u8 nibble = 0;
Expand All @@ -31,7 +30,7 @@ static constexpr Hash MakeHashFromString(const char str[])
return h;
}

std::string Hash::ToString() const
std::string BIOS::Hash::ToString() const
{
char str[33];
std::snprintf(str, sizeof(str), "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", bytes[0],
Expand All @@ -40,7 +39,7 @@ std::string Hash::ToString() const
return str;
}

static constexpr const ImageInfo s_image_infos[] = {
static constexpr const BIOS::ImageInfo s_image_info_by_hash[] = {
{"SCPH-1000, DTL-H1000 (v1.0)", ConsoleRegion::NTSC_J, MakeHashFromString("239665b1a3dade1b5a52c06338011044"), true},
{"SCPH-1001, 5003, DTL-H1201, H3001 (v2.2 12-04-95 A)", ConsoleRegion::NTSC_U,
MakeHashFromString("924e392ed05558ffdb115408c263dccf"), true},
Expand Down Expand Up @@ -85,18 +84,25 @@ static constexpr const ImageInfo s_image_infos[] = {
true},
{"SCPH-1000R (v4.5 05-25-00 J)", ConsoleRegion::NTSC_J, MakeHashFromString("476d68a94ccec3b9c8303bbd1daf2810"), true},
{"PS3 (v5.0 06-23-03 A)", ConsoleRegion::Auto, MakeHashFromString("c02a6fbb1b27359f84e92fae8bc21316"), false},
{"PS3 (v5.0 06-23-03 A)", ConsoleRegion::Auto, MakeHashFromString("81bbe60ba7a3d1cea1d48c14cbcc647b"), false}};
{"PS3 (v5.0 06-23-03 A)", ConsoleRegion::Auto, MakeHashFromString("81bbe60ba7a3d1cea1d48c14cbcc647b"), false},
};

Hash GetHash(const Image& image)
// OpenBIOS is separate, because there's no fixed hash for it. So just in case something collides with a hash of zero...
// which would be unlikely.
static constexpr const BIOS::ImageInfo s_openbios_info = {"OpenBIOS", ConsoleRegion::Auto, {}, false};
static constexpr const char s_openbios_signature[] = {'O', 'p', 'e', 'n', 'B', 'I', 'O', 'S'};
static constexpr u32 s_openbios_signature_offset = 0x78;

static BIOS::Hash GetHash(const BIOS::Image& image)
{
Hash hash;
BIOS::Hash hash;
MD5Digest digest;
digest.Update(image.data(), static_cast<u32>(image.size()));
digest.Final(hash.bytes);
return hash;
}

std::optional<Image> LoadImageFromFile(const char* filename)
std::optional<BIOS::Image> BIOS::LoadImageFromFile(const char* filename)
{
Image ret(BIOS_SIZE);
auto fp = FileSystem::OpenManagedCFile(filename, "rb");
Expand All @@ -123,39 +129,37 @@ std::optional<Image> LoadImageFromFile(const char* filename)
return std::nullopt;
}

Log_DevPrint(fmt::format("Hash for BIOS '{}': {}", FileSystem::GetDisplayNameFromPath(filename), GetHash(ret).ToString()).c_str());
return ret;
}

std::optional<Hash> GetHashForFile(const char* filename)
const BIOS::ImageInfo* BIOS::GetInfoForImage(const Image& image)
{
auto image = LoadImageFromFile(filename);
if (!image)
return std::nullopt;
const Hash hash(GetHash(image));

return GetHash(*image);
}
// check for openbios
if (image.size() >= (s_openbios_signature_offset + std::size(s_openbios_signature)) &&
std::memcmp(&image[s_openbios_signature_offset], s_openbios_signature, std::size(s_openbios_signature)) == 0)
{
return &s_openbios_info;
}

const ImageInfo* GetImageInfoForHash(const Hash& hash)
{
for (const ImageInfo& ii : s_image_infos)
for (const ImageInfo& ii : s_image_info_by_hash)
{
if (ii.hash == hash)
return &ii;
}

Log_WarningPrintf("Unknown BIOS hash: %s", hash.ToString().c_str());
return nullptr;
}

bool IsValidHashForRegion(ConsoleRegion region, const Hash& hash)
bool BIOS::IsValidBIOSForRegion(ConsoleRegion console_region, ConsoleRegion bios_region)
{
const ImageInfo* ii = GetImageInfoForHash(hash);
if (!ii)
return false;

return (ii->region == ConsoleRegion::Auto || ii->region == region);
return (bios_region == ConsoleRegion::Auto || bios_region == console_region);
}

void PatchBIOS(u8* image, u32 image_size, u32 address, u32 value, u32 mask /*= UINT32_C(0xFFFFFFFF)*/)
void BIOS::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 offset = phys_address - BIOS_BASE;
Expand All @@ -173,30 +177,16 @@ void PatchBIOS(u8* image, u32 image_size, u32 address, u32 value, u32 mask /*= U
old_disasm.GetCharArray(), new_value, new_disasm.GetCharArray());
}

bool PatchBIOSEnableTTY(u8* image, u32 image_size, const Hash& hash)
bool BIOS::PatchBIOSEnableTTY(u8* image, u32 image_size)
{
const ImageInfo* ii = GetImageInfoForHash(hash);
if (!ii || !ii->patch_compatible)
{
Log_WarningPrintf("Incompatible version for TTY patch: %s", hash.ToString().c_str());
return false;
}

Log_InfoPrintf("Patching BIOS to enable TTY/printf");
PatchBIOS(image, image_size, 0x1FC06F0C, 0x24010001);
PatchBIOS(image, image_size, 0x1FC06F14, 0xAF81A9C0);
return true;
}

bool PatchBIOSFastBoot(u8* image, u32 image_size, const Hash& hash)
bool BIOS::PatchBIOSFastBoot(u8* image, u32 image_size)
{
const ImageInfo* ii = GetImageInfoForHash(hash);
if (!ii || !ii->patch_compatible)
{
Log_WarningPrintf("Incompatible version for fast-boot patch: %s", hash.ToString().c_str());
return false;
}

// Replace the shell entry point with a return back to the bootstrap.
Log_InfoPrintf("Patching BIOS to skip intro");
PatchBIOS(image, image_size, 0x1FC18000, 0x3C011F80); // lui at, 1f80
Expand All @@ -207,7 +197,7 @@ bool PatchBIOSFastBoot(u8* image, u32 image_size, const Hash& hash)
return true;
}

bool PatchBIOSForEXE(u8* image, u32 image_size, u32 r_pc, u32 r_gp, u32 r_sp, u32 r_fp)
bool BIOS::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))

Expand Down Expand Up @@ -245,7 +235,7 @@ bool PatchBIOSForEXE(u8* image, u32 image_size, u32 r_pc, u32 r_gp, u32 r_sp, u3
return true;
}

bool IsValidPSExeHeader(const PSEXEHeader& header, u32 file_size)
bool BIOS::IsValidPSExeHeader(const PSEXEHeader& header, u32 file_size)
{
static constexpr char expected_id[] = {'P', 'S', '-', 'X', ' ', 'E', 'X', 'E'};
if (std::memcmp(header.id, expected_id, sizeof(expected_id)) != 0)
Expand All @@ -260,7 +250,7 @@ bool IsValidPSExeHeader(const PSEXEHeader& header, u32 file_size)
return true;
}

DiscRegion GetPSExeDiscRegion(const PSEXEHeader& header)
DiscRegion BIOS::GetPSExeDiscRegion(const PSEXEHeader& header)
{
static constexpr char ntsc_u_id[] = "Sony Computer Entertainment Inc. for North America area";
static constexpr char ntsc_j_id[] = "Sony Computer Entertainment Inc. for Japan area";
Expand All @@ -276,8 +266,6 @@ DiscRegion GetPSExeDiscRegion(const PSEXEHeader& header)
return DiscRegion::Other;
}

} // namespace BIOS

std::optional<std::vector<u8>> BIOS::GetBIOSImage(ConsoleRegion region)
{
std::string bios_name;
Expand Down Expand Up @@ -312,11 +300,9 @@ std::optional<std::vector<u8>> BIOS::GetBIOSImage(ConsoleRegion region)
return std::nullopt;
}

Hash found_hash = GetHash(*image);
Log_DevPrintf("Hash for BIOS '%s': %s", bios_name.c_str(), found_hash.ToString().c_str());

if (!IsValidHashForRegion(region, found_hash))
Log_WarningPrintf("Hash for BIOS '%s' does not match region. This may cause issues.", bios_name.c_str());
const ImageInfo* ii = GetInfoForImage(image.value());
if (!ii || !IsValidBIOSForRegion(region, ii->region))
Log_WarningPrintf("BIOS '%s' does not match region. This may cause issues.", bios_name.c_str());

return image;
}
Expand Down Expand Up @@ -346,14 +332,10 @@ std::optional<std::vector<u8>> BIOS::FindBIOSImageInDirectory(ConsoleRegion regi
if (!found_image)
continue;

Hash found_hash = GetHash(*found_image);
Log_DevPrintf("Hash for BIOS '%s': %s", fd.FileName.c_str(), found_hash.ToString().c_str());

const ImageInfo* ii = GetImageInfoForHash(found_hash);

if (IsValidHashForRegion(region, found_hash))
const ImageInfo* ii = GetInfoForImage(found_image.value());
if (ii && IsValidBIOSForRegion(region, ii->region))
{
Log_InfoPrintf("Using BIOS '%s': %s", fd.FileName.c_str(), ii ? ii->description : "");
Log_InfoPrintf("Using BIOS '%s': %s", fd.FileName.c_str(), ii->description);
return found_image;
}

Expand Down Expand Up @@ -405,8 +387,7 @@ std::vector<std::pair<std::string, const BIOS::ImageInfo*>> BIOS::FindBIOSImages
if (!found_image)
continue;

Hash found_hash = GetHash(*found_image);
const ImageInfo* ii = GetImageInfoForHash(found_hash);
const ImageInfo* ii = GetInfoForImage(found_image.value());
results.emplace_back(std::move(fd.FileName), ii);
}

Expand Down
10 changes: 4 additions & 6 deletions src/core/bios.h
Expand Up @@ -55,17 +55,15 @@ struct PSEXEHeader
static_assert(sizeof(PSEXEHeader) == 0x800);
#pragma pack(pop)

Hash GetHash(const Image& image);
std::optional<Image> LoadImageFromFile(const char* filename);
std::optional<Hash> GetHashForFile(const char* filename);

const ImageInfo* GetImageInfoForHash(const Hash& hash);
bool IsValidHashForRegion(ConsoleRegion region, const Hash& hash);
const ImageInfo* GetInfoForImage(const Image& image);
bool IsValidBIOSForRegion(ConsoleRegion console_region, ConsoleRegion bios_region);

void PatchBIOS(u8* image, u32 image_size, u32 address, u32 value, u32 mask = UINT32_C(0xFFFFFFFF));

bool PatchBIOSEnableTTY(u8* image, u32 image_size, const Hash& hash);
bool PatchBIOSFastBoot(u8* image, u32 image_size, const Hash& hash);
bool PatchBIOSEnableTTY(u8* image, u32 image_size);
bool PatchBIOSFastBoot(u8* image, u32 image_size);
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);
Expand Down
15 changes: 10 additions & 5 deletions src/core/system.cpp
Expand Up @@ -1197,16 +1197,18 @@ bool System::BootSystem(SystemBootParameters parameters)
// Allow controller analog mode for EXEs and PSFs.
s_running_bios = s_running_game_path.empty() && !exe_boot && !psf_boot;

Bus::SetBIOS(*bios_image);
Bus::SetBIOS(bios_image.value());
UpdateControllers();
UpdateMemoryCardTypes();
UpdateMultitaps();
InternalReset();

// Enable tty by patching bios.
const BIOS::Hash bios_hash = BIOS::GetHash(*bios_image);
if (g_settings.bios_patch_tty_enable)
BIOS::PatchBIOSEnableTTY(Bus::g_bios, Bus::BIOS_SIZE, bios_hash);
const BIOS::ImageInfo* bios_info = BIOS::GetInfoForImage(bios_image.value());
if (bios_info && bios_info->patch_compatible)
BIOS::PatchBIOSEnableTTY(Bus::g_bios, Bus::BIOS_SIZE);
else
Log_ErrorPrintf("Not patching TTY enable, as BIOS is not patch compatible.");

// Load EXE late after BIOS.
if (exe_boot && !LoadEXE(parameters.filename.c_str()))
Expand All @@ -1228,7 +1230,10 @@ bool System::BootSystem(SystemBootParameters parameters)
if (g_cdrom.HasMedia() && (parameters.override_fast_boot.has_value() ? parameters.override_fast_boot.value() :
g_settings.bios_patch_fast_boot))
{
BIOS::PatchBIOSFastBoot(Bus::g_bios, Bus::BIOS_SIZE, bios_hash);
if (bios_info && bios_info->patch_compatible)
BIOS::PatchBIOSFastBoot(Bus::g_bios, Bus::BIOS_SIZE);
else
Log_ErrorPrintf("Not patching fast boot, as BIOS is not patch compatible.");
}

// Good to go.
Expand Down

0 comments on commit 6db90e8

Please sign in to comment.