# 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+".