Add RE'ed preipl

This commit is contained in:
Arthur Blot 2023-01-28 17:05:17 +01:00
parent ab65c38782
commit 20b4eb8293
6 changed files with 1822 additions and 835 deletions

View File

@ -11,6 +11,19 @@
#define RAM_TYPE_32_MB (1)
#define RAM_TYPE_64_MB (2)
#define HW_SYS_NMI_ENABLE_MASK 0xBC100000
#define HW_SYS_PLL_FREQ 0xBC100068
#define HW_SYS_IO_ENABLE 0xBC100078
#define HW_SYS_IO_ENABLE_AUDIOCLKOUT 0x800
#define HW_SYS_GPIO_ENABLE 0xBC10007C
// Pin used for Jigkick
#define HW_SYS_GPIO_ENABLE_PIN4 0x10
#define HW_RAM_SIZE 0xBC100040
#define HW_TIMER_0 0xBC500000
@ -18,10 +31,75 @@
#define HW_TIMER_2 0xBC500020
#define HW_TIMER_3 0xBC500030
#define HW_NAND_CONTROL 0xBD101000
#define HW_NAND_STATUS 0xBD101004
#define HW_NAND_COMMAND 0xBD101008
#define HW_NAND_COMMAND_RESET 0xFF
#define HW_NAND_ADDRESS 0xBD10100C
#define HW_NAND_RESET 0xBD101014
#define HW_NAND_DMA_ADDRESS 0xBD101020
#define HW_NAND_DMA_CONTROL 0xBD101024
#define HW_NAND_DMA_STATUS 0xBD101028
#define HW_NAND_DMA_BUFFER 0xBFF00000
#define HW_NAND_DMA_ECC 0xBFF00800
#define HW_NAND_DMA_SPARE0 0xBFF00900
#define HW_NAND_DMA_SPARE1 0xBFF00904
#define HW_NAND_DMA_SPARE2 0xBFF00908
#define HW_MEMORYSTICK_START_TPC 0xBD200030
#define HW_MEMORYSTICK_TPC 0xBD200034
#define HW_MEMORYSTICK_STATUS 0xBD200038
#define HW_MEMORYSTICK_STATUS_TIMEOUT 0x100
#define HW_MEMORYSTICK_STATUS_CRC_ERROR 0x200
#define HW_MEMORYSTICK_STATUS_READY 0x1000
#define HW_MEMORYSTICK_STATUS_UNK13 0x2000
#define HW_MEMORYSTICK_STATUS_FIFO_RW 0x4000
#define HW_MEMORYSTICK_SYS 0xBD20003C
#define HW_MEMORYSTICK_SYS_RESET 0x8000
#define HW_KIRK_SIGNATURE 0xBDE00000
#define HW_KIRK_VERSION 0xBDE00004
#define HW_KIRK_ERROR 0xBDE00008
#define HW_KIRK_START 0xBDE0000C
#define HW_KIRK_START_PHASE1 1
#define HW_KIRK_START_PHASE2 2
#define HW_KIRK_COMMAND 0xBDE00010
#define HW_KIRK_COMMAND_PRIV_DEC 0x01
#define HW_KIRK_COMMAND_ENC_2 0x02
#define HW_KIRK_COMMAND_DEC_2 0x03
#define HW_KIRK_COMMAND_ENC_3_IV_ZERO 0x04
#define HW_KIRK_COMMAND_ENC_3_IV_FUSEID 0x05
#define HW_KIRK_COMMAND_ENC_3_IV_USER 0x06
#define HW_KIRK_COMMAND_DEC_3_IV_ZERO 0x07
#define HW_KIRK_COMMAND_DEC_3_IV_FUSEID 0x08
#define HW_KIRK_COMMAND_DEC_3_IV_USER 0x09
#define HW_KIRK_COMMAND_PRIV_SIGNCHECK 0x0A
#define HW_KIRK_COMMAND_SHA1 0x0B
#define HW_KIRK_COMMAND_ECDSA_KEYGEN 0x0C
#define HW_KIRK_COMMAND_ECDSA_MULT 0x0D
#define HW_KIRK_COMMAND_PRNG 0x0E
#define HW_KIRK_COMMAND_PRNG_SEED 0x0F
#define HW_KIRK_COMMAND_ECDSA_SIGN 0x10
#define HW_KIRK_COMMAND_ECDSA_SIGNCHECK 0x11
#define HW_KIRK_COMMAND_RESULT 0xBDE00014
#define HW_KIRK_STATUS 0xBDE0001C
#define HW_KIRK_STATUS_PHASE1_FINISH 0x01
#define HW_KIRK_STATUS_PHASE2_FINISH 0x02
#define HW_KIRK_STATUS_NEEDPHASE2 0x10
#define HW_KIRK_COMMAND_END 0xBDE00028
#define HW_KIRK_SRC_BUF 0xBDE0002C
#define HW_KIRK_DST_BUF 0xBDE00030
#define HW_GPIO_READ 0xBE240004
#define HW_GPIO_READ_PIN4 0x00000010
#define HW_RESET_VECTOR 0xBFC00000
#define HW_RESET_VECTOR_SIZE (0x1000)
/*
* GE hardware registers
*/

View File

@ -11,6 +11,7 @@
#include "common/common.S"
#include "common/errors.h"
#include "common/hardware.h"
#endif

View File

@ -163,7 +163,7 @@ void sceKernelDmaSoftRequest(u32 arg0, u32 arg1, u32 arg2, u32 arg3)
} else if (arg2) {
HW(baseaddr + 32) = (arg2 << arg1);
} else {
HW(baseaddr + 34) = (1 << arg1);
HW(baseaddr + 34) = (1 << arg1); // XXX
}
return SCE_ERROR_OK;
@ -1029,4 +1029,4 @@ static void _sceKernelDmacClkEnable(s32 arg0)
}
HW(addr + 8) = 0x100;
HW(addr + 16) = 0x100;
}
}

File diff suppressed because it is too large Load Diff

120
src/preipl/loader.S Normal file
View File

@ -0,0 +1,120 @@
#include "common_asm.h"
# Code running at 0xBFC00000
# This is the beginning of the Pre-IPL, which is stored in a ROM and mapped at 0xBFC00000 on reset.
# This first stage, called the "loader", will just handle exceptions if they are enabled (ie it's not on boot),
# and otherwise load the second stage of the Pre-IPL, the "payload", in the scratchpad, in order to run it
_start:
# save $v0 in cop0
ctc0 $v0, COP0_CTRL_V0
# check NMI interrupt mask
lui $v0, %hi(HW_SYS_NMI_ENABLE_MASK)
lw $v0, %lo(HW_SYS_NMI_ENABLE_MASK)($v0)
# in case interrupts are enabled, it means we're not at boot time but recovering from an interrupt
bnez $v0, handle_nmi
nop
# in case NMI are not enabled, unload the handler
ctc0 $zr, COP0_CTRL_NMI_HANDLER
jal sceKernelL1IcacheInvalidateAll
nop
jal sceKernelL1DcacheInvalidateAll
nop
# copy preipl payload to 0xA0010000 (scratchpad with kernel & uncached flags)
lui $a0, 0xA001
lui $a1, %hi(preipl_payload)
addiu $a1, $a1, %lo(preipl_payload)
li $a2, (preipl_payload_end - preipl_payload)
copy_preipl:
lw $t0, 0($a1)
addiu $a1, $a1, 4
sw $t0, 0($a0)
addiu $a2, $a2, -4
bgtz $a2, copy_preipl
addiu $a0, $a0, 4
# jump to the copied payload
lui $sp, 0x8001
ori $sp, $sp, 0x3FF0
lui $t9, 0x8001
jr $t9
sync
handle_nmi:
cfc0 $v0, COP0_CTRL_NMI_HANDLER
# in case no NMI handler is defined, use the default exception handler
beqz $v0, handle_exception
nop
jr $v0
nop
handle_exception:
# restore $v0
cfc0 $v0, COP0_CTRL_V0
# set BEV = 0 (ie exception vectors switches from 0xBFC00200 to 0x
mfc0 $k1, COP0_STATE_STATUS
lui $k0, 0xFFBF
ori $k0, $k0, 0xFFFF
and $k1, $k1, $k0
mtc0 $k1, COP0_STATE_STATUS
# get the address of the exception vector and jump there
mfc0 $k0, COP0_STATE_EBASE
jr $k0
nop
# TODO add enough nop's to get to 0xbfc00200
reset_vector_2: # exception vector for some exceptions (everything except reset, NMI & soft reset)
b _start
# ======================================================
sceKernelL1IcacheInvalidateAll: # at 0xBFC00208
# get IC, where primary I-cache size is 2^(12 + IC)
mfc0 $t0, COP0_STATE_CONFIG
li $t1, 4096
ext $t0, $t0, 9, 3
# t1 is the primary I-cache size (in bytes)
sllv $t1, $t1, $t0
mtc0 $zr, COP0_STATE_TAG_LO
mtc0 $zr, COP0_STATE_TAG_HI
move $t0, $zr
icache_loop:
cache 0x1, 0($t0)
cache 0x3, 0($t0)
addiu $t0, $t0, 64
bne $t0, $t1, loc_BFC00224
nop
jr $ra
nop
# ======================================================
sceKernelL1DcacheInvalidateAll: # at 0xBFC00240
mfc0 $t0, COP0_STATE_CONFIG
li $t1, 4096
ext $t0, $t0, 6, 3
sllv $t1, $t1, $t0
mtc0 $zr, COP0_STATE_TAG_LO
mtc0 $zr, COP0_STATE_TAG_HI
move $t0, $zr
dcache_loop:
cache 0x11, 0($t0)
cache 0x13, 0($t0)
addiu $t0, $t0, 64
bne $t0, $t1, loc_BFC0025C
nop
jr $ra
nop
# 2 nop's until 0xBFC00280 (due to compilation?)
preipl_payload:
# insert stage2 here
preipl_payload_end:
# end of stage2
# TODO nop's until 0xBFC00FB0
.string "Copyright (C) 2004 Sony Computer Entertainment Inc. All rights reserved."
# TODO 0xBFC0FFC
.word 0x20040420

788
src/preipl/payload.S Normal file
View File

@ -0,0 +1,788 @@
#include "common_asm.h"
# PRE-IPL payload
# This code runs at 0x80010000
# Its role is to initialize just enough hardware to decrypt and run the IPL
_start:
# Run an init script which initializes AW (GE) Edram, KIRK, NAND and Syscon
lui $a0, %hi(init_script)
addiu $a0, $a0, %lo(init_script)
jal script_exec
nop
# TODO: the 16 MSB of this register are unknown
lui $t0, %hi(HW_SYS_PLL_FREQ)
lw $t1, %lo(HW_SYS_PLL_FREQ)($t0)
srl $t1, $t1, 16
beqz $t1, enable_gpio_pin
nop
# Enable audio clock out I/O (???)
lw $t1, %lo(HW_SYS_IO_ENABLE)($t0)
ori $t1, $t1, HW_SYS_IO_ENABLE_AUDIOCLKOUT
b check_jigkick
sw $t1, %lo(HW_SYS_IO_ENABLE)($t0)
enable_gpio_pin:
# Enable GPIO pin 4
lw $t1, %lo(HW_SYS_GPIO_ENABLE)($t0)
ori $t1, $t1, HW_SYS_GPIO_ENABLE_PIN4
sw $t1, %lo(HW_SYS_GPIO_ENABLE)($t0)
check_jigkick:
# sleep for 10 "cycles"
li $a0, 10
jal sleep
sync
# read GPIO pin 4
lui $t0, %hi(HW_GPIO_READ)
lw $t0, %lo(HW_GPIO_READ)($t0)
andi $t0, $t0, HW_GPIO_READ_PIN4
# if the pin is zero, boot from NAND
lui $t1, %hi(nand_read_block)
addiu $t1, $t1, %lo(nand_read_block)
lui $t2, %hi(nand_init)
addiu $t2, $t2, %lo(nand_init)
beqz $t0, start_decrypt
nop
# otherwise, boot from MS
lui $t1, %hi(memorystick_read_block)
addiu $t1, $t1, %lo(memorystick_read_block)
lui $t2, %hi(memorystick_init)
addiu $t2, $t2, %lo(memorystick_init)
# start decrypting the IPL
start_decrypt:
lui $at, %hi(read_block)
sw $t1, %lo(read_block)($at)
sw $zr, %lo(page_counter)($at)
jalr $t2
nop
move $s7, $zr
decrypt_loop:
# read one page from NAND or MS to 0xBFD00000
lui $t9, %hi(read_block)
lw $t9, %lo(read_block)($t9)
lui $a0, %hi(page_counter)
lw $a0, %lo(page_counter)($a0)
jalr $t9
lui $a1, 0xBFD0
bltz $v0, infinite_loop
nop
# decrypt the page in-place at 0xBFD00000
lui $a0, 0xBFD0
jal kirk_priv_decrypt
move $a1, $a0
bnez $v0, infinite_loop
nop
# each decrypted page starts with four words:
# - one giving a destination address, which must be non-zero (otherwise no copy is performed)
# - one giving the length of the decrypted data
# - one which is non-zero if it is the last block
# - one which must be equal to the 'checksum' of the previous block, returned by memcpy, which is just the sum of the copied words (or 0 for the first loop)
# first check the checksum
lui $s0, 0xBFD0
lw $v0, 12($s0)
bne $v0, $s7, infinite_loop
lw $a0, 0($s0)
# then, check if the destination address is non-zero (otherwise just skip the block)
beqz $a0, after_copy
lw $a2, 4($s0)
# copy the block from 0xBFD00010 to the specified address
jal memcpy
addiu $a1, $s0, 16
move $s7, $v0
after_copy:
# check if this is the last block
lw $t9, 8($s0)
beqz $t9, decrypt_another
nop
# it was the last block, now flush everything and jump to the IPL
jal dcacheWritebackInvalidateAll
nop
jal icacheWritebackInvalidateAll
nop
jalr $t9
nop
decrypt_another:
lui $at, %hi(page_counter)
lw $t0, %lo(page_counter)($at)
addiu $t0, $t0, 1
b decrypt_loop
sw $t0, %lo(page_counter)($at)
infinite_loop:
b infinite_loop
nop
# ======================================================
# Initialize the NAND and find a valid page containing the IPL block indices
nand_init:
move $s0, $ra
jal nand_reset
nop
# Starting from 128 (and increasing by 32 each time), find if there is a valid page (based on spare data)
li $s1, 128
nand_init_valid_page:
move $a0, $s1
lui $a1, %hi(nand_ipl_block_tbl)
addiu $a1, $a1, %lo(nand_ipl_block_tbl)
lui $a2, %lo(nand_spare_data)
addiu $a2, $a2, %lo(nand_spare_data)
jal nand_read_page
nop
bltz $v0, nand_init_invalid_spare
nop
# check spare data; third word must be equal to 0x6DC64A38 (TODO find what this implies exactly)
lw $t0, 0($a2)
lw $t1, 4($a2)
lw $t2, 8($a2)
lui $t3, 0x6DC6
ori $t3, $t3, 0x4A38
xor $t0, $t1, $t3
bnez $t0, nand_init_invalid_spare
nop
jr $s0
nop
nand_init_invalid_spare:
b nand_init_valid_page
addiu $s1, $s1, 32
# ======================================================
# Read an IPL block from the NAND. Arguments: block index, and destination address
nand_read_block:
lui $at, %hi(save_ra)
sw $ra, %lo(save_ra)($at)
sw $a0, %lo(save_a0)($at) # note: the value is not restored so it's actually useless
move $s1, $a1
# Compute in $s0 the page address from the block index, which is given by:
# ((((u16*)nand_ipl_block_tbl)[a0 / 4] * 4) | (a0 & 3)) * 8
# Basically an IPL block is composed of 8 pages, and 4 IPL blocks are stored consecutively in NAND,
# and the table indicates the starting page index (divided by 32) for each group of 4 IPL blocks
addiu $t0, $at, %lo(nand_ipl_block_tbl)
srl $t1, $a0, 2
sll $t1, $t1, 1
addu $t0, $t0, $t1
lhu $t1, 0($t0)
andi $t0, $a0, 0x3
sll $t1, $t1, 2
or $t0, $t1, $t0
sll $s0, $t0, 3
move $s2, $zr
# read 8 pages
nand_read_block_get_page:
# read a page in the output buffer
addu $a0, $s0, $s2
sll $a1, $s2, 9
addu $a1, $s1, $a1
lui $a2, %hi(nand_spare_data)
addiu $a2, $a2, %lo(nand_spare_data)
jal nand_read_page
nop
bltz $v0, nand_read_block_error
nop
# check if spare data is valid
lw $t0, 0($a2)
lw $t1, 4($a2)
lw $t2, 8($a2)
lui $t3, 0x6DC6
ori $t3, $t3, 0x4A38
xor $t0, $t1, $t3
bnez $t0, nand_read_block_error
nop
# continue reading until we read 8 pages
addiu $s2, $s2, 1
sltiu $t0, $s2, 8
bnez $t0, nand_read_block_get_page
nop
lui $ra, %hi(save_ra)
lw $ra, %lo(save_ra)($ra)
jr $ra
move $v0, $zr
nand_read_block_error:
lui $ra, %hi(save_ra)
lw $ra, %lo(save_ra)($ra)
jr $ra
li $v0, -1
# ======================================================
memorystick_init:
j memorystick_init_2
nop
# ======================================================
memorystick_read_block:
lui $at, %hi(save_ra)
sw $ra, %lo(save_ra)($at)
move $s0, $a0
move $s1, $a1
move $s2, $zr
memorystick_read_block_loop:
sll $a0, $s0, 3
addu $a0, $a0, $s2
addiu $a0, $a0, 16
sll $a1, $s2, 9
addu $a1, $a1, $s1
jal memorystick_read_page
nop
bltz $v0, memorystick_read_block_loop
nop
addiu $s2, $s2, 1
sltiu $t0, $s2, 8
bnez $t0, memorystick_read_block_loop
nop
lui $ra, %hi(save_ra)
lw $ra, %lo(save_ra)($ra)
jr $ra
move $v0, $zr
# ======================================================
icacheWritebackInvalidateAll:
mfc0 $t0, Config
li $t1, 4096
ext $t0, $t0, 9, 3
sllv $t1, $t1, $t0
mtc0 $zr, TagLo
mtc0 $zr, TagHi
move $t0, $zr
icacheWritebackInvalidateAll_sub:
cache 0x1, 0($t0)
cache 0x3, 0($t0)
addiu $t0, $t0, 64
bne $t0, $t1, icacheWritebackInvalidateAll_sub
nop
jr $ra
nop
# ======================================================
dcacheWritebackInvalidateAll:
mfc0 $t0, Config
li $t1, 2048
ext $t0, $t0, 6, 3
sllv $t1, $t1, $t0
move $t0, $zr
dcacheWritebackInvalidateAll_sub:
cache 0x14, 0($t0)
cache 0x14, 0($t0)
addiu $t0, $t0, 64
bne $t0, $t1, dcacheWritebackInvalidateAll_sub
nop
jr $ra
sync
# ======================================================
nand_reset:
# run a reset command on the NAND
lui $t0, %hi(HW_NAND_COMMAND)
li $t1, HW_NAND_COMMAND_RESET
sw $t1, %lo(HW_NAND_COMMAND)($t0)
# wait for the command to finish
nand_reset_wait:
lw $t1, %lo(HW_NAND_STATUS)($t0)
andi $t1, $t1, 0x1
beqz $t1, nand_reset_wait
nop
# reset NAND (?)
li $t1, 1
sw $t1, %lo(HW_NAND_RESET)($t0)
jr $ra
sync
# ======================================================
# Read a NAND page.
# Takes three arguments: page number, output address, and spare data
nand_read_page:
lui $t0, %hi(HW_NAND_STATUS)
# wait until NAND is ready
nand_read_page_wait_busy:
lw $t1, %lo(HW_NAND_STATUS)($t0)
andi $t1, $t1, 0x1
beqz $t1, nand_read_page_wait_busy
sll $t1, $a0, 10
# set the page number
sw $t1, %lo(HW_NAND_DMA_ADDRESS)($t0)
# enable DMA transfer with both page data & spare data transfer enabled
li $t1, 0x301
sw $t1, %lo(HW_NAND_DMA_CONTROL)($t0)
# wait until DMA transfer is ready
nand_read_page_wait_dma:
lw $t1, %lo(HW_NAND_DMA_CONTROL)($t0)
andi $t1, $t1, 0x1
bnez $t1, nand_read_page_wait_dma
nop
# check if the status is zero (ie no error), otherwise return -1
lw $t1, %lo(HW_NAND_DMA_STATUS)($t0)
bnez $t1, nand_read_page_exit
li $v0, -1
lui $t0, %hi(HW_NAND_DMA_BUFFER)
# copy the spare data to the last argument
lw $t1, %lo(HW_NAND_DMA_SPARE0)($t0)
lw $t2, %lo(HW_NAND_DMA_SPARE1)($t0)
lw $v0, %lo(HW_NAND_DMA_SPARE2)($t0)
sw $t1, 0($a2)
sw $t2, 4($a2)
sw $v0, 8($a2)
# copy 512 bytes of data to the output buffer
move $t1, $a1
li $v0, 512
nand_read_page_copy:
lw $t2, 0($t0)
addiu $v0, $v0, -4
addiu $t0, $t0, 4
sw $t2, 0($t1)
bnez $v0, nand_read_page_copy
addiu $t1, $t1, 4
# return zero
nand_read_page_exit:
jr $ra
nop
# ======================================================
memorystick_init_2:
move $t8, $ra
# enable memorystick clock, bus clock, I/O, and reset
lui $a0, %hi(memorystick_init_script)
addiu $a0, $a0, %lo(memorystick_init_script)
jal script_exec
nop
# reset memory stick
lui $t9, %hi(HW_MEMORYSTICK_SYS)
li $t1, HW_MEMORYSTICK_SYS_RESET
sw $t1, %lo(HW_MEMORYSTICK_SYS)($t9)
# wait for the memorystick interface to have reset
memorystick_init_wait_reset:
lw $t1, %lo(HW_MEMORYSTICK_SYS)($t9)
andi $t2, $t1, HW_MEMORYSTICK_SYS_RESET
bnez $t2, memorystick_init_wait_reset
nop
jal memorystick_check_register
nop
jal memorystick_check_status
nop
memorystick_wait_interrupt:
jal memorystick_get_interrupt
nop
bltz $v0, memorystick_wait_interrupt
nop
andi $v0, $v0, 0x80 # TODO find what this means
beqz $v0, memorystick_wait_interrupt
nop
jr $t8
move $v0, $zr
# ======================================================
memorystick_read_page:
move $t6, $a1
move $t8, $ra
lui $t9, %hi(HW_MEMORYSTICK_START_TPC)
li $at, 0x9007 # ex set cmd, size 7
sw $at, %lo(HW_MEMORYSTICK_START_TPC)($t9)
wsbw $a1, $a0
srl $a1, $a1, 8
srl $t1, $a0, 24
sll $t1, $t1, 24
lui $a0, 0x1
ori $a0, $a0, 0x20
jal memorystick_run_tpc # TODO run TPC with a0 = 0x10020 | ((a0 >> 24) << 24), a1 = wsbw(a0) >> 8
or $a0, $a0, $t1
bltz $v0, memorystick_read_page_error
nop
jal memorystick_wait
nop
memorystick_read_page_wait_interrupt:
jal memorystick_get_interrupt
nop
bltz $v0, memorystick_read_page_error
andi $t0, $v0, 0x20 # TODO
beqz $t0, memorystick_read_page_wait_interrupt
andi $t0, $v0, 0x40 # TODO
bnez $t0, memorystick_read_page_error
nop
li $at, 8704
sw $at, %lo(HW_MEMORYSTICK_START_TPC)($t9)
move $a0, $t6
jal memorystick_read_tpc
li $a1, 512
bltz $v0, memorystick_read_page_error
nop
jal memorystick_check_status
nop
bltz $v0, memorystick_read_page_error
nop
jal memorystick_wait
nop
b memorystick_wait_interrupt
nop
memorystick_read_page_error:
jr $t8
li $v0, -1
# ======================================================
memorystick_run_tpc:
sw $a0, %lo(HW_MEMORYSTICK_TPC)($t9)
b memorystick_check_status
sw $a1, %lo(HW_MEMORYSTICK_TPC)($t9)
# ======================================================
memorystick_read_tpc:
lw $t1, %lo(HW_MEMORYSTICK_STATUS)($t9)
andi $t2, $t1, HW_MEMORYSTICK_STATUS_TIMEOUT
bnez $t2, memorystick_read_tpc_error
andi $t2, $t1, HW_MEMORYSTICK_STATUS_FIFO_RW
beqz $t2, memorystick_read_tpc
nop
lw $v0, %lo(HW_MEMORYSTICK_TPC)($t9)
addiu $a1, $a1, -4
sw $v0, 0($a0)
bgtz $a1, memorystick_read_tpc
addiu $a0, $a0, 4
jr $ra
move $v0, $zr
memorystick_read_tpc_error:
jr $ra
li $v0, -1
# ======================================================
# Wait until MemoryStick is ready, then check if it did not timeout or receive a CRC error
# Return -1 in case of error, 0 otherwise
memorystick_check_status:
lw $t1, %lo(HW_MEMORYSTICK_STATUS)($t9)
andi $v0, $t1, HW_MEMORYSTICK_STATUS_READY
beqz $v0, memorystick_check_status
andi $v0, $t1, (HW_MEMORYSTICK_STATUS_TIMEOUT & HW_MEMORYSTICK_CRC_ERROR)
bnez $v0, memorystick_check_status_error
nop
jr $ra
move $v0, $zr
memorystick_check_status_error:
jr $ra
li $v0, -1
# ======================================================
memorystick_check_register:
move $t7, $ra
# Set the r/w register address
lui $t9, %hi(HW_MEMORYSTICK_START_TPC)
li $at, 0x8004 # rw register address (0x8000), size 4
sw $at, %lo(HW_MEMORYSTICK_START_TPC)($t9)
lui $at, 0x610 # 0x06100800 (TODO what does this value mean?)
ori $at, $at, 0x800
sw $at, %lo(HW_MEMORYSTICK_TPC)($t9)
sw $zr, %lo(HW_MEMORYSTICK_TPC)($t9)
jal memorystick_check_status
nop
bltz $v0, memorystick_check_register
nop
lui $a0, %hi(memorystick_reg_0)
addiu $a0, $a0, %lo(memorystick_reg_0)
li $a1, 8
jal memorystick_read_reg
nop
lui $a0, %hi(memorystick_reg_0)
lw $a0, %lo(memorystick_reg_0)($a0)
lui $a1, %hi(memorystick_reg_1)
lw $a1, %lo(memorystick_reg_1)($a1)
srl $at, $a0, 16
andi $at, $at, 0x15
# TODO meaning of (a0 >> 16) & 0x15
bnez $at, memorystick_check_register_error
nop
jr $t7
nop
memorystick_check_register_error:
jr $t7
li $v0, -1
# ======================================================
memorystick_read_reg:
li $at, 0x4000 # TPC command to read reg, size 0
or $at, $at, $a1
sw $at, %lo(HW_MEMORYSTICK_START_TPC)($t9)
b memorystick_read_tpc
nop
# ======================================================
memorystick_get_interrupt:
li $at, 0x7001 # get interrupt, size 1
sw $at, %lo(HW_MEMORYSTICK_START_TPC)($t9)
memorystick_get_interrupt_wait_fifo:
lw $t1, %lo(HW_MEMORYSTICK_STATUS)($t9)
andi $t2, $t1, HW_MEMORYSTICK_STATUS_TIMEOUT
bnez $t2, memorystick_get_interrupt_error
andi $t2, $t1, HW_MEMORYSTICK_STATUS_FIFO_RW
beqz $t2, memorystick_get_interrupt_wait_fifo
nop
lw $v0, %lo(HW_MEMORYSTICK_TPC)($t9)
lw $zr, %lo(HW_MEMORYSTICK_TPC)($t9)
memorystick_get_interrupt_wait_ready:
lw $t1, %lo(HW_MEMORYSTICK_STATUS)($t9)
andi $t2, $t1, HW_MEMORYSTICK_STATUS_TIMEOUT
bnez $t2, memorystick_get_interrupt_error
andi $t2, $t1, HW_MEMORYSTICK_STATUS_READY
beqz $t2, memorystick_get_interrupt_wait_ready
nop
jr $ra
andi $v0, $v0, 0xFF
memorystick_get_interrupt_error:
jr $ra
li $v0, -1
# ======================================================
# Wait for some something related to the memory stick
memorystick_wait:
lw $t1, %lo(HW_MEMORYSTICK_STATUS)($t9)
andi $t2, $t1, HW_MEMORYSTICK_STATUS_UNK13 # TODO
beqz $t2, memorystick_wait
nop
jr $ra
nop
# ======================================================
# Execute KIRK command 0x01 on given source & destination buffers
kirk_priv_decrypt:
lui $t9, %hi(HW_KIRK_COMMAND)
li $t0, HW_KIRK_COMMAND_PRIV_DEC
sw $t0, %lo(HW_KIRK_COMMAND)($t9)
ext $t0, $a1, 0, 29
sw $t0, %lo(HW_KIRK_SRC_BUF)($t9)
ext $t0, $a0, 0, 29
sw $t0, %lo(HW_KIRK_DST_BUF)($t9)
li $t0, HW_KIRK_START_PHASE1
sw $t0, %lo(HW_KIRK_START)($t9)
# wait for the operation to finish
kirk_priv_decrypt_wait:
lw $t0, %lo(HW_KIRK_STATUS)($t9)
andi $t0, $t0, 0x11
beqz $t0, kirk_priv_decrypt_wait
andi $t1, $t0, 0x10
# if it needs phase2, go there
bnez $t1, kirk_priv_decrypt_phase2
sw $t0, %lo(HW_KIRK_COMMAND_END)($t9)
jr $ra
lw $v0, %lo(HW_KIRK_COMMAND_RESULT)($t9)
kirk_priv_decrypt_phase2:
li $t0, HW_KIRK_START_PHASE2
sw $t0, %lo(HW_KIRK_START)($t9)
kirk_priv_decrypt_phase2_wait:
lw $t0, %lo(HW_KIRK_STATUS)($t9)
andi $t0, $t0, HW_KIRK_STATUS_PHASE2_FINISH
beqz $t0, kirk_priv_decrypt_phase2_wait
li $v0, -1
sw $t0, %lo(HW_KIRK_COMMAND_END)($t9)
jr $ra
sync
# ======================================================
# Memcpy returning a 'checksum' which is just the XOR of the copied words
_memcpy:
move $v0, $zr
_memcpy_inner:
lw $v1, 0($a1)
addiu $a1, $a1, 4
addiu $a2, $a2, -4
sw $v1, 0($a0)
addu $v0, $v0, $v1
bgtz $a2, _memcpy_inner
addiu $a0, $a0, 4
jr $ra
nop
# ======================================================
# Script execution: store hardware commands as data to save some code space
# Each command is composed of two words (unless the command is 'stop'): an
# address whose 4 MSB have been replaced by the command op (and will be
# replaced by 0xB as the destination address), and a value
# The op can be 0 (store value at destination), 1 (enable flags), 2 (disable
# flags), 3 (wait while flag is set), 4 (wait while flag is not set), 5 (sleep)
# Any other operand interrupts script execution.
script_exec:
move $t0, $a0
move $t9, $ra
cmd_loop:
lw $a0, 0($t0)
# t1 = a0 >> 28: this is the operand
srl $t1, $a0, 28
# a0 = 0xB0000000 | (a0 & 0x0FFFFFFF): this is the destination address
sll $a0, $a0, 4
srl $a0, $a0, 4
lui $at, 0xB000
or $a0, $a0, $at
lw $a1, 4($t0)
# t1 == 0: *a0 = a1
beqz $t1, cmd_store
addiu $t2, $t1, -1
# t1 == 1: *a0 |= a1
beqz $t2, cmd_or
addiu $t2, $t1, -2
# t1 == 2: *a0 &= a1
beqz $t2, cmd_and
addiu $t2, $t1, -3
# t1 == 3: while (*a0 & a1 != 0) {}
beqz $t2, cmd_wait
move $at, $zr
addiu $t2, $t2, -4
# t1 == 4: while (*a0 & a1 == 0) {}
beqz $t2, cmd_wait
not $at, $zr
addiu $t2, $t1, -5
# t1 == 5: sleep(a1)
beqz $t2, cmd_sleep
nop
# otherwise, exit
jr $t9
nop
cmd_sleep:
# sleep(a1)
jal sleep
move $a0, $a1
cmd_continue:
b cmd_loop
addiu $t0, $t0, 8
cmd_store:
# *a0 = a1
b cmd_continue
sw $a1, 0($a0)
cmd_or:
# *a0 |= a1
lw $t1, 0($a0)
or $t1, $t1, $a1
b cmd_continue
sw $t1, 0($a0)
cmd_and:
# *a0 &= a1
lw $t1, 0($a0)
and $t1, $t1, $a1
b cmd_continue
sw $t1, 0($a0)
cmd_wait:
# while ((*a0 ^ at) & a1 != 0) {}
lw $t1, 0($a0)
xor $t1, $t1, $at
and $t1, $t1, $a1
bnez $t1, cmd_wait
nop
b cmd_continue
nop
# ======================================================
# wait for about 100 * (first argument) CPU cycles (make 96 loops)
sleep:
# at = a0 * 15
sll $at, $a0, 1
addu $at, $at, $a0
sll $at, $at, 5
sleep_loop:
bnez $at, sleep_loop
addiu $at, $at, -1
jr $ra
nop
# note: nothing at 0x80010784
save_ra: # at 0x80010800
.word 0
save_a0: # at 0x80010804
.word 0
read_block: # at 0x80010808
.word 0
page_counter: # at 0x8001080C
.word 0
nand_spare_data: # at 0x80010810
.word 0
.word 0
.word 0
nand_ipl_block_tbl: # at 0x8001081C
.skip 512
memorystick_reg_0: # at 0x80010A1C
.word 0
memorystick_reg_1: # at 0x80010A20
.word 0
# note: nothing at 0x80010A24
init_script: # at 0x80010A80
# 0xBC100058 |= 0x00800000 Enable GPIO clock
.word 0x1C100058
.word 0x00800000
# 0xBC100050 |= 0x0000608E Enable bus clock for AW (which is GE) (RegA, RegB, Edram), KIRK, NAND (EMCSM) and APB (for syscon?)
.word 0x1C100050
.word 0x0000608E
# 0xBC10004C &= ~0x408 Clear reset for AW and KIRK
.word 0x2C10004C
.word 0xFFFFFBF7
# 0xBC100078 |= 2 IO enable NAND (EMCSM)
.word 0x1C100078
.word 0x00000002
# 0xBE240000 &= ~0x10 Disable GPIO pin 4 output
.word 0x2E240000
.word 0xFFFFFFEF
# 0xBE240040 |= 0x10 Enable GPIO pin 4 input
.word 0x1E240040
.word 0x00000010
# sleep(1) Wait
.word 0x50000000
.word 0x00000001
# 0xBD500010 = 1 Initialize GE Edram
.word 0x0D500010
.word 0x00000001
# while (*0xBD500010 & 1 != 0) {} Wait for the Edram to be initialized
.word 0x3D500010
.word 0x00000001
# 0xBD500040 = 1 Finish initializing GE Edram
.word 0x0D500040
.word 0x00000001
# end
.word 0xF0000000
memorystick_init_script: # at 0x80010AD4
# 0xBC100054 |= 0x100 Enable clock for memorystick interface 1
.word 0x1C100054
.word 0x00000100
# 0xBC100050 |= 0x400 Enable bus clock for memorystick interface 1
.word 0x1C100050
.word 0x00000400
# 0xBC100078 |= 0x10 Enable I/O for memorystick interface 1
.word 0x1C100078
.word 0x00000010
# 0xBC10004C &= ~0x100 Reset memorystick interface 1
.word 0x2C10004C
.word 0xFFFFFEFF
# end
.word 0xF0000000