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
154
155
156
|
# PSP sOS SVC (syscall) overview — kernel-verified
Every SVC is implemented in sOS (`sos.elf`). Dispatch:
- trampoline (~0x254) reads the immediate -> `sub_587C` (main dispatcher).
- `sub_587C` gates by CALLER PROCESS TYPE (PCB+75 -> off_58EC class), then:
svc <= 0xDF -> `sub_59E8` (switch on svc-81) [bulk of syscalls]
0xE0..0xEF -> `sub_58F0` (require capability bit 0x20) [privileged aliases]
svc >= 0xF0 -> `sub_6450`
Most `sub_59E8` cases validate args (`sub_178C` = 1 arg, `sub_17C0` = ptr+len)
before the real worker. Confidence: [K]=kernel handler read; [I]=inferred.
A recurring gate `sos_proc_type_check(a1)` means "operate on a
resource/process by id, allowed only for the caller's privilege class."
## By category (worker fn in parens)
### Process / scheduling
- 0x54 query current-process field (returns *proc) [K]
- 0x66 get OWN process type/id (returns PCB[75]) [K]
- 0x70 (sub_4238) clear PCB proc-state byte (+1) [K]
- 0x75 (sub_24A4) **TA return / exit** [K]
- 0x81 (sub_2A34) get proc memory region base (&0xFFF00000) [K]
- 0xF2 (sub_22B4) **RPC / process switch** (R0=service id, R1=req) [K]
- 0xF4/0xF5 read PCB fields of a process [K]
### Memory / pages
- 0x68 (sub_1054) align/round a region (a3&~0x1F, a4) [K]
- 0x88 (sub_373C) **map a page** into proc AS; base by type (0xE00000/0xD00000), free slot in 32-entry table -> base+(slot<<12) [K]
- 0x63 (sub_6744) region lookup/validate over 0xD00000/0xE00000 windows [K]
- 0x79 (sub_6D84) address/region validate (rejects <0x200000) [K]
- 0x8A (sub_6ADC) region PROTECT (sub_3F88 over 0x4000000 blocks) [K]
- 0x5A/0x5B (sub_6E40) map/reserve a memory range [K]
### Resource / object table management
- 0x58 (sub_1C90) internal resource GET (backs 0x59/0x5D) [K]
- 0x59 (sub_47E4) **register/get object by id** (a1<=0x85) -> resource id [K]✓verified
- 0x5D (sub_1CE8) **allocate** object slot (0x40-entry table) -> handle [K]✓verified
- 0x5C (sub_54A4) release/free object (a1<0x80, table off_5534) [K]
- 0x57 (sub_553C) object/session op (a1<0x30, 34-byte entries off_55BC) [K]
- 0x5F (sub_4860) find object by (id,type) (a1<0x40) [K]
### Register / MMIO access (sub_3D8C computes the target reg address)
- 0x7C & 0xA0 (sub_6F3A) **write value to an address** (mem/MMIO poke) [K]✓
- 0xA5 (sub_6F74) write 64-bit value to a reg [K]
- 0xA6 (sub_465C) read-modify-write a reg field [K]
- 0x7A (sub_3384) MMIO op at MEMORY[0x32000D8] [K]
- 0x78 (sub_2948) read proc/resource descriptor fields into *a1,*a2 [K]
- 0x92 (sub_4514) HW block config: MMIO 0x3200158/15C/160 + 0xAD004 [K]
- 0x7D (sub_3CFC) map/reserve a 1MB-granular VA region [a1,a1+a2] (a2<=0x100000) [K]✓
- 0x7E (sub_6AA8) UNMAP/release that 1MB VA region (inverse of 0x7D) [K]✓ (was mis-inferred as 'commit')
- 0x96 (sub_21E8) MMIO control R/M/W at MEMORY[0x32000D8] [K]✓
- 0xB0 (sub_2C5C) HW command submit (cache flush + doorbell + WFI; CCP-like) [K]
### Capability / config QUERIES (return boot-filled globals)
- 0x73 off_6424 · 0x87 (*off_642C==0) · 0x8D *off_6430 · 0x9C off_6438
- 0xA1 off_6440 · 0xA3 off_6448 · 0xA8 off_644C · 0xAC off_6434
- 0x9D per-type cap table[v8]
- 0x76 **capability query**: (reqmask & ~cap[PCB_type])==0 -> the ACL gate [K]✓
(cap table off_6428 is BSS, boot-filled -- same bucket as the above)
### Crypto / hash
- 0x6C (sub_6828) crypto/hash session END [K]
- 0x6D crypto/hash session BEGIN / init [B]
### Sync / misc
- 0x56 (sub_662C) **wait-for-event / yield** [K]✓
- 0x65 (sub_2150) proc-gated op · 0x93 (sub_2288) flag query
- 0x83 (sub_6664) write config globals · 0x9A/0xAE nullsub (no-ops)
- 0x51/0x52 arg copy/validate
### Privileged aliases (0xE0..0xEF via sub_58F0, need cap bit 0x20)
Mostly privileged variants of the above: 0xE1->wait, 0xE2->0x57 obj-op,
0xE4->0x5E, 0xE5->0x5F find-obj, 0xE6/E7->0x5A map-range.
### 0xF0+ (sub_6450)
0xF0 wait · 0xF1 (sub_64C4) · 0xF2 RPC/switch · 0xF3 return dword_64BC ·
0xF4/0xF5 read PCB fields.
## Verification status
Fully read & confirmed [K]✓: 0x56, 0x59, 0x5D, 0x76, 0x78, 0x7C, 0x7D, 0x7E, 0x88, 0x96, 0xF2.
CORRECTION from verification: 0x7E is region UNMAP, not 'register commit' (my
emulator inference was wrong); 0x7D maps a general 1MB region, not just HW regs.
[K] = handler read, category solid, exact edge-semantics not exhaustively traced.
[I] rows now largely eliminated by reading the kernel handlers.
[B] = behavioural (crypto session begin/end) from loader/verify usage.
## Note for the cookie hunt
The "return off_64XX" query SVCs and the 0x76 capability table are all
BSS globals filled during BOOT. Whatever early-init routine seeds those is the
same region a per-boot stack-cookie seed would live in (below the TA loader).
---------------------------------------------------------------------------
## Second pass — the remaining 59 SVCs characterized (kernel-read)
### Register / MMIO (the heavy-traffic ones)
- 0x7B & 0x9F (sub_4730) READ value from address (sub_3D8C addr, size 1/2/4/8)->*a2
-- read-counterpart of 0x7C write. SYS_DRV uses 0x7B 117x (most-used). [K]
- 0xA9 (sub_477A) read HW register, extended addressing [K]
- 0xAA (sub_6F94) write HW register, extended addressing + DSB barrier [K]
- 0x97 (sub_25B4) submit HW command + WFI-wait (CCP-like; blocks til status) [K]
### Object / handle / process management
- 0x5E bind / 0xB1 unbind (sub_A38) attach a handle (slot<0x40) to a process [K]
- 0x6B & 0x98 (sub_3A04) create/register a resource object -> handle in *a2 [K]
- 0x6E (sub_44C0) set PCB fields +12/+16, clear state [K]
- 0x80 & 0x6F (sub_4364) read PCB[proc*96+20] [K]
- 0x85 (sub_6504) release ALL sessions owned by caller (cleanup) [K]
- 0x89 (sub_42E4) enumerate processes with pending flag -> caller array [K]
- 0x8E (sub_44EC) OR flag bits into per-process descriptor [K]
- 0x9B (sub_29D8) find process by 16-byte key -> index [K]
- 0x9E (sub_2AF4) find entry by caller process type [K]
- 0xF1 (sub_64C4) set proc return info + release its object (exit path) [K]
### Memory / mapping
- 0x60 (sub_4DC4) yield/RESCHEDULE - pick & switch to next process [K]
- 0x90 (sub_C04) page-align check + map [K]
- 0xA4 (sub_2A5C) map region for process matched by key [K]
- 0xAB (sub_CA8) map per-process region ((pid<<13)+0xD3000) [K]
- 0xE0 & 0xE3 (sub_1908) validate addr in [0xD00000,+0x100000) [K]
### Timers / queues / misc services
- 0x61 (sub_2578) arm/disarm timer task 96 [K]
- 0x99 (sub_25F4) timer/watchdog tick [K]
- 0x64 (sub_1D5C) submit typed command (magic 0x4505) [K]
- 0x91 (sub_2700) iterate resource table (16-byte entries) [K]
- 0xB3 (sub_1F30) enqueue N words into trace/log buffer off_1F84 [K]
- 0xB4 (sub_1FC4) cap-gated multi-op (a1 selects) [K]
- 0xB8 (sub_1F88) enqueue a 3-dword record [K]
- 0xD0 (sub_6FF4) debug/console output [K]
- 0x72 (sub_728) ack/probe (returns 1) [K]
- 0x74 (sub_47C0) set config byte off_47E0[idx<16] [K]
- 0x77 & 0x67 (sub_292C) query region -> size 0x2000 + table ptr [K]
- 0xA2 (sub_2C00) query field (*(global+12)) [K]
- 0xA7 (sub_2C0C) proc-gated setup (sub_434) [K]
### Aliases of already-documented SVCs
- 0x69 -> 0x79 (addr validate) · 0x6A -> 0x7A (MMIO 0x32000D8) ·
0x71 -> 0x81 (proc mem region)
### Stubs (return 9 = unimplemented) and no-ops
- return-9 stubs: 0x8C, 0x8B, 0x8F, 0x7F, 0x94, 0x95, 0x84
- no-ops (nullsub): 0x9A, 0xAE, 0xE8, 0xE9
- 0xAF (inline) calls fptr loc_4700 with a mode bool
### Coverage
All 121 handled SVCs now have a purpose (verified/[K]) except the return-9 stubs
and nullsubs, which are effectively "unimplemented/no-op". Names + descriptions
are in sos_symbols.json (551 names / 350 comments / 294 types).
## CORRECTION (ROP-surface audit): 0xE8/0xE9 are no-ops; E9 is NOT the 0x7C poke
Decoding the 0x58F0 tbb (table 0x590C) against sos2.elf: 0xE8 and 0xE9 both `bl 0x6f38`, which is a
standalone `bx lr` (no-op), not `sub_6F3A`. The `0x7C & 0xA0 -> sub_6F3A` poke is real but reachable
ONLY via 0x7C/0xA0, both `<= 0xDF`, hence denied to type-5 TAs. The E-alias memory primitive is
0xE6/0xE7 -> sub_6E40 (0x5A), a per-object permission-bitmap op. See
`fw/LEAD-GCP-ROP-TA-KERNEL-SURFACE.md`. Also: a type-5 TA CAN issue 0xE0-0xE9 (cap bit 0x20 set in
byte 0xF5), correcting AUDIT-TA-KERNEL-BOUNDARY's "only 0x75 and 0xF0+".
|