Skip to content

Instantly share code, notes, and snippets.

@yifanlu
Last active June 7, 2019 14:57
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save yifanlu/70064ba39381a92ca5674bc45c2c55d3 to your computer and use it in GitHub Desktop.
Save yifanlu/70064ba39381a92ca5674bc45c2c55d3 to your computer and use it in GitHub Desktop.
PlayStation Classic bootrom dumper
// 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);
}
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) }
}
#!/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