1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
/**
* @file rspboot_ap.s
*
* This RSP microcode program acts as a loader for other microcodes. This ucode:
* - (Optionally) Waits for the RDP to go idle
* - Loads a ucode's .data section to the start of DMEM
* - Loads a ucode's .text section to RSPBOOT_ENTRYPOINT
* - Jumps to the loaded ucode
*
* Along the way it also checks whether the task scheduler running on the CPU has
* requested a yield and, if so, halts the RSP.
*
* This is a special variant of rspboot that is designed to work alongside IPL3 X105
* and the cic6105 microcode to attempt to detect whether the running game is a pirated
* copy. If it is so determined (in the antipiracy_test routine) cic6105 will not set
* signal 7 in SP_STATUS, causing subsequent runs of rspboot to corrupt random regions
* of memory (see the dpclock_corrupt routine)
*/
.rsp
#include "rcp.h"
#include "sptask.h"
#include "rspboot.h"
#define OSTASK_FIELD(field) (-0x40 + OS_TASK_OFF_##field)($zero)
.create CODE_FILE, IMEM_START
entry:
// This jump is overwritten once rspboot runs for the first time (see set_status_and_patch below)
j antipiracy_test
lw $17, OSTASK_FIELD(FLAGS)
load_ucode_text_and_enter:
li $ra, RSPBOOT_ENTRYPOINT
li $1, (IMEM_SIZE - (RSPBOOT_ENTRYPOINT - IMEM_START))-1
mtc0 $16, SP_DRAM_ADDR
mtc0 $ra, SP_MEM_ADDR
mtc0 $1, SP_RD_LEN
@@while_dma_busy:
mfc0 $1, SP_DMA_BUSY
bnez $1, @@while_dma_busy
check_yield:
mfc0 $1, SP_STATUS
andi $1, $1, SP_STATUS_YIELD
bnez $1, yield_break
li $1, OSTASK_ADDR // $1 is expected to contain the OSTask pointer by many ucodes
jr $ra
yield_break:
li $17, (SP_SET_SIG2 | SP_SET_SIG1 | SP_CLR_SIG0)
mtc0 $17, SP_STATUS
break
antipiracy_test:
// Checks that $4, $5, $6 and $11 all have expected register values set by IPL3 X105
lui $1, (SP_CLR_SIG7 >> 16)
li $3, 0x3D8
bne $11, $3, set_status_and_patch
addi $16, $zero, entry_failure
lui $3, 0x3A0
ori $3, $3, 0x4820
bne $4, $3, set_status_and_patch
lui $3, 0x2529
ori $3, $3, 0x4
bne $6, $3, set_status_and_patch
srl $3, $5, 12
addi $2, $3, -76
bltz $2, set_status_and_patch
addi $2, $3, -79
bgtz $2, set_status_and_patch
vxor $v13, $v13, $v13[1q]
vaddc $v13, $v13, $v13[2h]
vnxor $v13, $v13, $v13[4]
mfc2 $3, $v13[0]
xori $3, $3, 0x1234 // $3 is calculated and then never used before it is repurposed
nop
lui $1, (SP_SET_SIG7 >> 16)
addi $16, $zero, entry_success
set_status_and_patch:
mtc0 $1, SP_STATUS
lw $1, OSTASK_FIELD(UBOOT)
// fallthrough to dma write 8 bytes, patches the first 8 bytes of the ucode in dram
// with a new jump target that avoids the DPC_CLOCK code if SP_STATUS_SIG7 is set
dma_write_8:
@@while_dma_full:
mfc0 $3, SP_DMA_FULL
bne $3, $3, @@while_dma_full
nop
mtc0 $16, SP_MEM_ADDR
mtc0 $1, SP_DRAM_ADDR
j dp_wait
mtc0 $zero, SP_WR_LEN // 0 = 8-byte write to RDRAM
.align 8
entry_success:
// antipiracy check passed, rspboot is patched to check signal 7 instead of performing the check again
j check_sig7
lw $17, OSTASK_FIELD(FLAGS)
.align 8
entry_failure:
// antipiracy check failed, rspboot is patched to run the failure codepath until reset
j dpclock_corrupt
lw $17, OSTASK_FIELD(FLAGS)
check_sig7: // if signal 7 is set, skip DPC_CLOCK sampling + corruption
mfc0 $16, SP_STATUS
andi $16, $16, SP_STATUS_SIG7
bnez $16, dp_wait
dpclock_corrupt:
mfc0 $16, DPC_CLOCK
andi $1, $16, 0x1FFF
bnez $1, @@stepover // if the 13 low-order bits of the sampled clock are 0, set DPC_END to DPC_CLOCK
andi $1, $16, 0x7F
mtc0 $16, DPC_END
@@stepover:
beqz $1, dma_write_8 // if the 7 low-order bits of the sampled clock are 0, corrupt 8 random bytes of DRAM
srl $1, $16, 2
dp_wait:
sll $17, $17, (32 - OS_TASK_DP_WAIT)
bgezal $17, load_ucode_data
lw $16, OSTASK_FIELD(UDATA_SZ)
mfc0 $1, DPC_STATUS
andi $1, $1, DPC_STATUS_DMA_BUSY
bnez $1, check_yield // yield check while waiting for RDP Command FIFO to become idle
load_ucode_data:
@@while_dma_full:
mfc0 $1, SP_DMA_FULL
bnez $1, @@while_dma_full
lw $1, OSTASK_FIELD(UDATA)
addi $16, $16, -1
mtc0 $zero, SP_MEM_ADDR
mtc0 $1, SP_DRAM_ADDR
mtc0 $16, SP_RD_LEN
@@while_dma_busy:
mfc0 $1, SP_DMA_BUSY
bnez $1, @@while_dma_busy
li $ra, load_ucode_text_and_enter
mtc0 $zero, SP_SEMAPHORE // Release the semaphore just in case a prior task or the CPU failed to release it
vxor $v16, $v16, $v16 // set $v16 to 0, some F3DZEX versions rely on this being set to function properly
sw $4, OSTASK_FIELD(UBOOT)
j check_yield
lw $16, OSTASK_FIELD(UCODE)
.align 0x10
.if . > IMEM_END
.error "Not enough room in IMEM"
.endif
.close
|