Skip to content

Commit

Permalink
Merge pull request #34 from brad-lin/bios_11_and_20
Browse files Browse the repository at this point in the history
Support BIOS 1.1, and flush icache for BIOS 2.0
  • Loading branch information
brad-lin authored Apr 21, 2021
2 parents 197157e + 2d16ac7 commit 7174142
Show file tree
Hide file tree
Showing 14 changed files with 92 additions and 61 deletions.
51 changes: 29 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ To use it, you will need a way to copy **full memory card images** (not individu
* [Memcarduino](https://github.com/ShendoXT/memcarduino). Requires soldering wires to the memory card.
* Using a [Memcard Pro](https://8bitmods.com/memcard-pro-for-playstation-1/), which lets you create your own virtual memory cards on an sdcard. Simply drop the card image file you want to use as Memory Card 1, Channel 1.
* Using [Unirom](https://github.com/JonathanDotCel/unirom8_bootdisc_and_firmware_for_ps1) and [NOTPSXserial](https://github.com/JonathanDotCel/NOTPSXSerial) with a serial/USB cable, using the command : `nops /fast /mcup 0 FILE.mcd COMPORT` where `FILE` is the mcd file corresponding to your model, and `COMPORT` corresponds to your computer serial port.
* Memcarduino with MemcardRex (success not guaranteed)
* A DexDrive with PSXGameEdit (success not guaranteed)


# WARNING
Expand All @@ -24,37 +26,39 @@ To use it, you will need a way to copy **full memory card images** (not individu

* Copy the full memory card image corresponding to your model/BIOS to a memory card.
* Insert it **in slot 1**.
* **If you have a SCPH-1002 with BIOS version 2.0: insert another memory card in slot 2 (its content doesn't matter)**.
* Power up your PlayStation with the lid open, and go to the memory card manager.
* After a few seconds, the screen will flash orange. Wait ~30 seconds for the [Unirom](https://github.com/JonathanDotCel/unirom8_bootdisc_and_firmware_for_ps1) welcome screen to appear.
* If the screen doesn't flash orange after 20 seconds, you have either used a wrong memory card image, or your model uses an exploit which is not 100% reliable. In that case, power off your PlayStation, wait for 1 minute, and try again.
* Once Unirom is loaded, you can insert a CD, close the lid, and press **R1** to load the game.
* After a few seconds, the screen will be filled with cyan. Wait ~30 seconds for the [Unirom](https://github.com/JonathanDotCel/unirom8_bootdisc_and_firmware_for_ps1) welcome screen to appear.
* If the cyan screen doesn't appear, you have either used a wrong memory card image, or the memory card image was not written properly (the mcd file must be written as raw data to the memory card), or something else went wrong. If you are 100% certain that the memory card image was written properly, and that you are using the correct image, please open an issue.
* Once Unirom is loaded, you can insert a CD, close the lid, and press **R1** to load the game. Note: Japanese PlayStation cannot have their CD drive unlocked by Unirom, and thus cannot load backups.
* Don't forget to remove your memory card, as its exploit will trigger into games as well. This isn't an issue when using the Memcard Pro, as it will automatically change the virtual card to the game you're booting.

# Restoring the memory card

* The most reliable way is to use [Memcarduino](https://github.com/ShendoXT/memcarduino) and its FORMAT option.
* Some games that have a save file manager (shows the contents of the memory card before saving) built into them, like *OddWorld: Abe's Oddysee* and *Cool Boarders 4 (suffers from a caveat that keeps the game from loading the memory card with certain exploit versions)* for example, can be used to overwrite FreePSXBoot when saving progress.
* Some tools and games crash when attempting to format a memory card loaded with FreePSXBoot, but may be able to format it by first inserting a normal memory card, and switching it with the FreePSXBoot memory card just before the format operation starts.
* We plan to bundle a complete version of Unirom in the memory card images in the future, with the ability to format memory cards.

# Supported models

* All models are supported except SCPH-3000, which will probably be supported in the future.
* As of version 20210419, the exploit is 100% reliable on all supported models.
* Certain PSOne consoles appear to not support the exploit in its entirety. We are currently checking on the issue to ensure that it is fully exploitable.
* All models are supported and tested on emulator or real hardware, except the debug models (DTL-H) and Net Yaroze.
* As of version 20210419, the exploit is 100% reliable on all supported models. Nevertheless, some exploit images were only tested on emulators and may not work on real hardware; feedback is welcome.
* See the table below for more details and download links.

## Changelog

* 2021-04-21: Added support for BIOS 1.1, and fixed BIOS 2.0 exploit (needs icache flush to work)
* 2021-04-21: Progress bar added in stage2 payload (thanks Nicolas Noble)
* 2021-04-20: Added support for BIOS 3.0 1996-09-09 (SCPH-5500) (thanks sickle)
* 2021-04-19: Added support for BIOS 1.0 and 4.3 (SCPH-1000 and SCPH-100 respectively)
* 2021-04-19: Exploit 100% reliable for every supported BIOS; now hooks an ISR (thanks sickle)
* 2021-04-19: Unirom version updated to 8.0.F
* 2021-04-14: Exploit uses fastload, which reads the memory card much faster than Sony's code (thanks Nicolas Noble)
* 2021-04-12: New version of Unirom, able to load games. Huge thanks to the psxdev contributors.
* 2021-04-11: 100% reliable exploit for the SCPH-7002, SCPH-7502 and SCPH-9002.

## Caveat

The earlier version of this exploit relies on uninitialized memory in kernel space to be at 0 in order to work properly. The SDRAM chips have a fairly slow decay rate, and this exploit will only be reliable if the machine has been powered off for long enough.
## Information

[Technical details](exploit/EXPLOIT.md)

Expand All @@ -63,23 +67,26 @@ The earlier version of this exploit relies on uninitialized memory in kernel spa
## Downloads
These images are pre-built with Unirom.

There are different downloads for different console versions. Please download the correct ROM for your model and BIOS version. If a model or BIOS version is missing, it means it is not supported yet.
There are different downloads for different BIOS versions. Please download the correct ROM for your BIOS version. If a model or BIOS version is missing, it means it is not supported yet.

As more reliable versions of the exploit are developed, the images are updated. Older versions can be found in the `images` directory.
As more reliable or faster versions of the exploit are developed, the images are updated. Older versions can be found in the `images` directory.

| BIOS version/date | Models | 100% reliable exploit? | Download Link |
|-------------------|--------|------------------------|---------------|
| 1.0 (1994-09-22) | SCPH-1000 | **Yes** | [20210419](images/freepsxboot-unirom-fastload-20210419-bios-1.0.mcd) |
| 2.0 (1995-05-10) | SCPH-1002 | **Yes** | [20210419](images/freepsxboot-unirom-fastload-20210419-bios-2.0.mcd) |
| 2.1 (1995-07-17) | SCPH-1002<br/>SCPH-3500 | **Yes** | [20210419](images/freepsxboot-unirom-fastload-20210419-bios-2.1.mcd) |
| 2.2 (1995-12-04) | SCPH-1001<br/>SCPH-1002<br/>SCPH-5000<br/>SCPH-5903 | **Yes** | [20210419](images/freepsxboot-unirom-fastload-20210419-bios-2.2.mcd) |
| 3.0 (1996-09-09) | SCPH-5500 | **Yes** | [20210420](images/freepsxboot-unirom-fastload-20210420-bios-3.0-19960909.mcd) |
| 3.0 (1996-11-18) | SCPH-5001<br/>SCPH-5501<br/>SCPH-5503<br/>SCPH-7003 | **Yes** | [20210419](images/freepsxboot-unirom-fastload-20210419-bios-3.0.mcd) |
| 3.0 (1997-01-06) | SCPH-5502<br/>SCPH-5552 | **Yes** | [20210419](images/freepsxboot-unirom-fastload-20210419-bios-3.0-19970106.mcd) |
| 4.1 (1997-12-16) | SCPH-7001<br/>SCPH-7002<br/>SCPH-7500<br/>SCPH-7501<br/>SCPH-7502<br/>SCPH-7503<br/>SCPH-9001<br/>SCPH-9002<br/>SCPH-9003 | **Yes** | [20210419](images/freepsxboot-unirom-fastload-20210419-bios-4.1.mcd) |
| 4.3 (2000-03-11) | SCPH-100 | **Yes** | [20210419](images/freepsxboot-unirom-fastload-20210419-bios-4.3.mcd) |
| 4.4 (2000-03-24) | SCPH-101<br/>SCPH-102 | **Yes** | [20210419](images/freepsxboot-unirom-fastload-20210419-bios-4.4.mcd) |
| 4.5 (2000-05-25) | SCPH-101<br/>SCPH-102 | **Yes** | [20210419](images/freepsxboot-unirom-fastload-20210419-bios-4.5.mcd) |
| 1.0 (1994-09-22) | SCPH-1000 | **Yes** | [20210421](images/freepsxboot-unirom-fastload-20210421-bios-1.0.mcd) |
| 1.1 (1995-01-22) | SCPH-3000 | **Yes** | [20210421](images/freepsxboot-unirom-fastload-20210421-bios-1.1.mcd) |
| 2.0 (1995-05-10) | SCPH-1002 | **Yes; see note below** | [20210421](images/freepsxboot-unirom-fastload-20210421-bios-2.0.mcd) |
| 2.1 (1995-07-17) | SCPH-1002<br/>SCPH-3500 | **Yes** | [20210421](images/freepsxboot-unirom-fastload-20210421-bios-2.1.mcd) |
| 2.2 (1995-12-04) | SCPH-1001<br/>SCPH-1002<br/>SCPH-5000<br/>SCPH-5903 | **Yes** | [20210421](images/freepsxboot-unirom-fastload-20210421-bios-2.2.mcd) |
| 3.0 (1996-09-09) | SCPH-5500 | **Yes** | [20210421](images/freepsxboot-unirom-fastload-20210421-bios-3.0.mcd) |
| 3.0 (1996-11-18) | SCPH-5001<br/>SCPH-5501<br/>SCPH-5503<br/>SCPH-7003 | **Yes** | [20210421](images/freepsxboot-unirom-fastload-20210421-bios-3.0-19961118.mcd) |
| 3.0 (1997-01-06) | SCPH-5502<br/>SCPH-5552 | **Yes** | [20210421](images/freepsxboot-unirom-fastload-20210421-bios-3.0-19970106.mcd) |
| 4.1 (1997-12-16) | SCPH-7001<br/>SCPH-7002<br/>SCPH-7500<br/>SCPH-7501<br/>SCPH-7502<br/>SCPH-7503<br/>SCPH-9001<br/>SCPH-9002<br/>SCPH-9003 | **Yes** | [20210421](images/freepsxboot-unirom-fastload-20210421-bios-4.1.mcd) |
| 4.3 (2000-03-11) | SCPH-100 | **Yes** | [20210421](images/freepsxboot-unirom-fastload-20210421-bios-4.3.mcd) |
| 4.4 (2000-03-24) | SCPH-101<br/>SCPH-102 | **Yes** | [20210421](images/freepsxboot-unirom-fastload-20210421-bios-4.4.mcd) |
| 4.5 (2000-05-25) | SCPH-101<br/>SCPH-102 | **Yes** | [20210421](images/freepsxboot-unirom-fastload-20210421-bios-4.5.mcd) |

**Note for BIOS 2.0 (SCPH-1002)**: the memory card containing FreePSXBoot must be inserted in slot 1, and **another memory card must be present in slot 2**. The memory card in slot 2 can have any content.

See the folder [builder](builder) for a tool that can be used to generate your own payloads and memory cards.

Expand Down
102 changes: 63 additions & 39 deletions builder/builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

#include <array>
#include <map>
#include <sstream>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <vector>
#include <stdexcept>
#include <sstream>

#include "flags.h"
#include "stage2-bin.h"
Expand All @@ -18,6 +18,17 @@ using namespace Mips::Encoder;

constexpr uint32_t loaderAddr = 0xbe48;

enum class ExploitType {
Standard, // One or more directory entries, which change an instruction. The payload loads and runs stage2.
MemcardISR, // Hooks an instruction of the memcard ISR, while it is being read (BIOS 1.1). The payload must be
// stored in the last directory entry (with the correct checksum), and in the first broken sector (with
// a bad checksum). The exploit must complete the current read command before loading stage 2.
ICacheFlush, // The exploit is sensible to icache (BIOS 2.0). To force the icache to be flushed, the reading of the
// directory of the first memory card must be complete, and another memory card must be present in
// slot 2. As a result, the payload must be duplicated in every broken sector entry, with a correct
// checksum. It will trigger when the second memory card starts being read.
};

struct ExploitSettings {
uint32_t stackBase;
uint32_t addressToModify;
Expand All @@ -26,6 +37,8 @@ struct ExploitSettings {
bool inIsr = false;
uint32_t loopsPerIncrement = 2;
uint32_t memCardDirEntryIndex = 0;
ExploitType type = ExploitType::Standard;
int16_t isrBeqDistance = 0;

constexpr static uint32_t maxFileSizeToUse = 0x7fffc000;

Expand Down Expand Up @@ -68,16 +81,13 @@ struct ExploitSettings {
// This is used as a key with the format: 41, 19971216.
typedef std::pair<uint32_t, uint32_t> BIOSKey;

static std::map<BIOSKey, ExploitSettings> biosExploitSettings {
static std::map<BIOSKey, ExploitSettings> biosExploitSettings{
// Exploit settings in order:
// base of stack array, address to modify, original value, new value,
// calls payload from ISR, number of loops per increment, index of directory entry.
// calls payload from ISR, number of loops per increment, index of directory entry, exploit type.
{{10, 19940922}, {0x801ffcb0, 0x80204f04, 0x0c0006c1, 0x0c002f92, true, 3}},
// Disable exploit for BIOS 1.1 for now.
// It works, but triggers the payload in the middle of a read of the memcard.
// Needs some adaptation of the payload to work.
// {{11, 19950122}, {0x801ffcc0, 0x80204d6c, 0x10000004, 0x10001c36, true, 3}},
{{20, 19950510}, {0x801ffcb8, 0x80204ef4, 0x0c001ab0, 0x0c002f92, true, 3}},
{{11, 19950122}, {0x801ffcc0, 0x80204d6c, 0x10000004, 0x10001c36, true, 3, 0, ExploitType::MemcardISR, -0x70cc}},
{{20, 19950510}, {0x801ffcb8, 0x80204ef4, 0x0c001ab0, 0x0c002f92, true, 3, 0, ExploitType::ICacheFlush}},
{{21, 19950717}, {0x801ffcc0, 0x80204f64, 0x0c001acc, 0x0c002f92, true, 2, 8}},
{{22, 19951204}, {0x801ffcc0, 0x80204f64, 0x0c001acc, 0x0c002f92, true, 2, 8}},
{{30, 19960909}, {0x801ffcc8, 0x80204f64, 0x0c001acc, 0x0c002f92, true, 2}},
Expand All @@ -90,24 +100,12 @@ static std::map<BIOSKey, ExploitSettings> biosExploitSettings {
};

// Maps model version (e.g. 9002) to its BIOS version.
static std::unordered_map<uint32_t, BIOSKey> modelToBios {
{5001, {30, 19961118}},
{5500, {30, 19960909}},
{5501, {30, 19961118}},
{5502, {30, 19970106}},
{5503, {30, 19961118}},
{5552, {30, 19970106}},
{7001, {41, 19971216}},
{7002, {41, 19971216}},
{7003, {30, 19961118}},
{7500, {41, 19971216}},
{7501, {41, 19971216}},
{7502, {41, 19971216}},
{7503, {41, 19971216}},
{9001, {41, 19971216}},
{9002, {41, 19971216}},
{9003, {41, 19971216}},
{ 101, {45, 20000525}},
static std::unordered_map<uint32_t, BIOSKey> modelToBios{
{5001, {30, 19961118}}, {5500, {30, 19960909}}, {5501, {30, 19961118}}, {5502, {30, 19970106}},
{5503, {30, 19961118}}, {5552, {30, 19970106}}, {7001, {41, 19971216}}, {7002, {41, 19971216}},
{7003, {30, 19961118}}, {7500, {41, 19971216}}, {7501, {41, 19971216}}, {7502, {41, 19971216}},
{7503, {41, 19971216}}, {9001, {41, 19971216}}, {9002, {41, 19971216}}, {9003, {41, 19971216}},
{101, {45, 20000525}},
// 102 can be either 4.4 or 4.5
};

Expand Down Expand Up @@ -199,6 +197,13 @@ static void putU32(uint8_t* ptr, uint32_t d) {
*ptr++ = d & 0xff;
}

static void putU32Vector(uint8_t* ptr, const std::vector<uint32_t>& data) {
for (uint32_t word : data) {
putU32(ptr, word);
ptr += sizeof(uint32_t);
}
}

static int16_t getHI(uint32_t v) {
int16_t lo = v & 0xffff;
int16_t hi = v >> 16;
Expand Down Expand Up @@ -376,6 +381,9 @@ static void createImage(ImageSettings settings) {
addiu(Reg::V0, Reg::V0, getLO(exploitSettings.originalValue)),
lui(Reg::V1, getHI(exploitSettings.addressToModify)),
sw(Reg::V0, getLO(exploitSettings.addressToModify), Reg::V1),
// flush cache
jal(0xa0),
addiu(Reg::T1, Reg::R0, 0x44),
};

unsigned lastLoadAddress = stage2_tload + 128 * (stage2_frames - 1);
Expand Down Expand Up @@ -472,6 +480,12 @@ static void createImage(ImageSettings settings) {
std::copy(begin(block), end(block), std::back_inserter(payload));
};

if (settings.exploitSettings.type == ExploitType::MemcardISR) {
// Workaround for BIOS 1.1: return to ISR unless read is complete (v0 != 0)
payload.push_back(beq(Reg::V0, Reg::R0, settings.exploitSettings.isrBeqDistance));
payload.push_back(nop());
}

if (settings.noInterrupts) append(disableInterrupts);
if (returnToShell) append(saveRegisters);
// If the payload can hold the code to flash the screen, it will be placed here
Expand Down Expand Up @@ -525,22 +539,32 @@ static void createImage(ImageSettings settings) {
crc ^= payloadBytes[crcIndex];
crcIndex++;
}
// If exploit type is not standard, the checksum must be controllable
if (payloadSize == 128 && exploitSettings.type != ExploitType::Standard) {
throw std::runtime_error("Payload is 128 bytes but its checksum must be controllable.");
}
// If payload has 128 bytes, check that the checksum is bad
if (payloadSize == 128 && crc == payloadBytes[127]) {
throw std::runtime_error("Payload is 128 bytes and its checksum is not bad.");
}
// Otherwise, make the checksum bad.
out[0x87f] = ~crc;
unsigned o = 0;
for (auto p : payload) {
out[0x800 + o++] = p & 0xff;
p >>= 8;
out[0x800 + o++] = p & 0xff;
p >>= 8;
out[0x800 + o++] = p & 0xff;
p >>= 8;
out[0x800 + o++] = p & 0xff;
p >>= 8;

if (exploitSettings.type == ExploitType::MemcardISR) {
// Store the payload with correct checksum in the last directory entry (frame 15)
putU32Vector(&out[0x780], payload);
out[0x7ff] = crc;
// Also store the payload with bad checksum in the first broken sector entry (frame 16)
putU32Vector(&out[0x800], payload);
out[0x87f] = ~crc;
} else if (exploitSettings.type == ExploitType::ICacheFlush) {
// Store the payload with correct checksum in all broken sector entries (frames 16 to 35 included)
for (int i = 16; i < 36; i++) {
putU32Vector(&out[i * 0x80], payload);
out[i * 0x80 + 0x7f] = crc;
}
} else {
// Store the payload with bad checksum in the first broken sector entry (frame 16)
putU32Vector(&out[0x800], payload);
out[0x87f] = ~crc;
}

FILE* outFile = fopen(settings.outputFileName.c_str(), "wb");
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit 7174142

Please sign in to comment.