-
-
Save yifanlu/70064ba39381a92ca5674bc45c2c55d3 to your computer and use it in GitHub Desktop.
PlayStation Classic bootrom dumper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// arm-none-eabi-gcc -o dump_brom.elf -nostdlib -nostartfiles -nodefaultlibs -pie -Os -fPIE -std=gnu99 -T linker.x -N -fdelete-null-pointer-checks dump_brom.c | |
// arm-none-eabi-objcopy -O binary dump_brom.elf dump_brom.bin | |
#define REG32(reg) ((volatile unsigned int *)(reg)) | |
static inline void magic_setup_uart(void) | |
{ | |
*REG32(0x11005008) |= 7; | |
*REG32(0x11005024) = 3; | |
*REG32(0x11005000) = 1; | |
*REG32(0x11005004) = 0; | |
*REG32(0x11005028) = 27; | |
*REG32(0x1100502C) = 13; | |
volatile int x = *REG32(0x11005000); | |
*REG32(0x1100500C) = 3; | |
} | |
static inline void writech(char c) { | |
while (!(*REG32(0x11005014) & 0x20)); | |
if (c == '\n') | |
*REG32(0x11005000) = '\r'; | |
*REG32(0x11005000) = c; | |
while (!(*REG32(0x11005014) & 0x40)); | |
} | |
static inline char nibble2char(unsigned int c) { | |
return (c > 9) ? 'A' + (c - 10) : '0' + c; | |
} | |
static inline void printhex(unsigned char ch) { | |
unsigned char ch1, ch2; | |
ch1 = ((ch & 0xF0) >> 4); | |
ch2 = ch & 0xF; | |
writech(nibble2char(ch1)); | |
writech(nibble2char(ch2)); | |
} | |
void __attribute__((section(".text.start"))) _start(void) { | |
magic_setup_uart(); | |
for (unsigned addr = 0; addr < 0x14000; addr += 4) { | |
unsigned dat = *(volatile unsigned *)addr; | |
printhex((dat >> 0) & 0xFF); | |
printhex((dat >> 8) & 0xFF); | |
printhex((dat >> 16) & 0xFF); | |
printhex((dat >> 24) & 0xFF); | |
for (volatile int i = 0; i < 5000; i++); | |
} | |
writech('\r'); | |
writech('\n'); | |
while (1); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") | |
OUTPUT_ARCH(arm) | |
SECTIONS | |
{ | |
.text : { *(.text.start) *(.text .text.* .gnu.linkonce.t.*) *(.sceStub.text.*) } | |
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } | |
.data : { *(.data .data.* .gnu.linkonce.d.*) } | |
.bss : { *(.bss .bss.* .gnu.linkonce.b.*) *(COMMON) } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" Taken from https://gitlab.com/zeroepoch/aftv2-tools | |
Usage: python3 mtk-download-payload.py payload.bin | |
Where `payload.bin` is a raw ARM payload. Then plug in | |
your USB cable and enter bootrom download mode. | |
On the PlayStation Classic, this requires shorting the | |
two large pads on the top left of the board (under the | |
left USB port) while plugging in the cable. | |
""" | |
import sys | |
import time | |
import serial | |
import glob | |
import struct | |
BAUD = 115200 | |
def serial_ports (): | |
""" Lists available serial ports | |
:raises EnvironmentError: | |
On unsupported or unknown platforms | |
:returns: | |
A set containing the serial ports available on the system | |
""" | |
if sys.platform.startswith("win"): | |
ports = [ "COM{0:d}".format(i + 1) for i in range(10) ] | |
elif sys.platform.startswith("linux"): | |
ports = glob.glob("/dev/ttyACM*") | |
elif sys.platform.startswith("darwin"): | |
ports = glob.glob("/dev/cu.usbmodem*") | |
else: | |
raise EnvironmentError("Unsupported platform") | |
result = set() | |
for port in ports: | |
try: | |
s = serial.Serial(port) | |
s.close() | |
result.add(port) | |
except (OSError, serial.SerialException): | |
pass | |
return result | |
# serial checker | |
def check (test, gold): | |
if test != gold: | |
sys.stderr.write("ERROR: Serial protocol mismatch\n") | |
sys.exit(1) | |
# write then read 1 byte | |
def write8 (out_str): | |
dev.write(out_str) | |
in_str = dev.read() | |
return in_str | |
def print_hex_byte (data): | |
print(len(data), end=": ") | |
print(" ".join("0x{:02x}".format(x) for x in data)) | |
def print_hex_word (data): | |
print(len(data), end=": ") | |
print(" ".join("0x{:08x}".format(x) for x in data)) | |
# initiate mtk preloader handshake | |
def handshake (): | |
# look for start byte | |
while True: | |
c = write8(b'\xa0') | |
if c == b'\x5f': | |
break | |
dev.flushInput() | |
# complete sequence | |
check(write8(b'\x0a'), b'\xf5') | |
check(write8(b'\x50'), b'\xaf') | |
check(write8(b'\x05'), b'\xfa') | |
def send_da (addr, data): | |
result = [] | |
size = len(data) | |
dev.write(b'\xd7') | |
print_hex_byte(dev.read(1)) | |
dev.write(struct.pack('>I', addr)) | |
print_hex_byte(dev.read(4)) | |
dev.write(struct.pack('>I', size)) | |
print_hex_byte(dev.read(4)) | |
dev.write(struct.pack('>I', 0)) # sig len | |
print_hex_byte(dev.read(4)) | |
status = dev.read(2) | |
print_hex_byte(status) # status | |
int_status = struct.unpack('>H', status)[0] | |
if int_status == 0: | |
print("<===") | |
while data: | |
dev.write(data[:1024]) | |
data = data[1024:] | |
print("<===") | |
chksum = dev.read(2) | |
print_hex_byte(chksum) | |
status = dev.read(2) | |
int_status = struct.unpack('>H', status)[0] | |
print_hex_byte(status) # status | |
print() | |
return int_status | |
def jump_da (addr): | |
dev.write(b'\xd5') | |
print_hex_byte(dev.read(1)) | |
dev.write(struct.pack('>I', addr)) | |
print_hex_byte(dev.read(4)) | |
status = dev.read(2) | |
print_hex_byte(status) # status | |
int_status = struct.unpack('>H', status)[0] | |
return int_status | |
if __name__ == "__main__": | |
if len(sys.argv) != 2: | |
print("Usage: " + os.path.basename(sys.argv[0]) + " payload.bin") | |
sys.exit(1) | |
port = None | |
print("Waiting for boot rom...") | |
# detect boot rom port | |
old = serial_ports() | |
while True: | |
new = serial_ports() | |
# port added | |
if new > old: | |
port = (new - old).pop() | |
break | |
# port removed | |
elif old > new: | |
old = new | |
time.sleep(0.25) | |
print("Found port = " + port) | |
dev = serial.Serial(port, BAUD, timeout=5) | |
handshake() | |
print("Handshake complete!") | |
with open(sys.argv[1], "rb") as f: | |
data = f.read() | |
print("Sending payload...") | |
send_da(0x00200000, data) | |
print("Jumping to payload...") | |
jump_da(0x00200000) | |
print("Done!") | |
# vim: ai et ts=4 sts=4 sw=4 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment