/* dtm reference (dtm2) -- named functions * generated by fw/ta_re.py -- do not edit * reference: dtm2.elf * functions: 14 total in build, 14 named, 0 anonymous * porting tiers: {"DISPATCH": 0, "EXACT": 9, "FUZZY": 0, "MNEM": 0, "ambiguous": 4, "cmd_protos": 0, "comments": 12, "dispatch_conflict": 0, "none": 1, "protos": 9, "relabelled": 0, "types": 1} * * Addresses are omitted and functions are ordered by NAME so that the same * function lines up across versions. See the .index.json sidecar for * name -> address. * * CAUTION on ported names: a name is only an identity claim at tier=EXACT. * At tier=MNEM/FUZZY the bytes differ from the reference, so any ADDRESS or * CONTENT HASH embedded in the name (e.g. *_4025F8, gcp_lib__*) still * refers to the REFERENCE build and does not describe this one. */ /* ==== TA_InvokeCommandEntryPoint [tier=EXACT size=94] ==== */ // GP command 5 (dtm_entry_tbl[4]) - the host command surface. // Requires (paramTypes & 0xF) == 7, params != 0 and params[0].size >= 0x50. // buf = params[0].buffer; command id = *buf; handlers receive buf+16 // (the source is 'v5 + 4' with v5 typed int*, i.e. +16 bytes). // cmd 2 -> dtm_cmd_topology_update // cmd 3 -> dtm_cmd_set_flag // cmd 4 -> dtm_cmd_topology_update_v2 // The handler result is written back to buf+8. int __fastcall TA_InvokeCommandEntryPoint(int a1, int a2, char a3, int a4) { int result; // r0 struct dtm_req *v5; // r4 unsigned int cmd_id; // r0 int updated; // r0 unsigned int v8; // r1 result = dword_4010B8 + 6; if ( (a3 & 0xF) == 7 && a4 != 0 ) { if ( *(_DWORD *)(a4 + 4) < 0x50u ) return dword_4010B8 + 16; v5 = *(struct dtm_req **)a4; if ( *(_DWORD *)a4 == 0 ) return dword_4010B8; cmd_id = v5->cmd_id; if ( v5->cmd_id == 4 ) { updated = dtm_cmd_topology_update_v2(a1: v5->args); } else if ( cmd_id == 3 ) { updated = dtm_cmd_set_flag(a1: v5->args); } else { if ( cmd_id != 2 ) { v8 = 1; result = dword_4010B8; goto LABEL_15; } updated = dtm_cmd_topology_update(a1: (int *)v5->args); } v8 = updated; result = 0; LABEL_15: v5->unk08 = v8; } return result; } /* ==== asd_rpc_100_topology_update [tier=EXACT size=34] ==== */ // SVC 0xF2 -> ASD, opcode 0x100 (asd_topology_update). // Frame {0x100, link_index, descriptor_ptr}; ASD bounds the index to 0x3F and // region-validates the descriptor for 0x40 bytes. int __fastcall asd_rpc_100_topology_update() { _DWORD v5[7]; // [sp+0h] [bp-1Ch] BYREF bzero_core(result: v5, a2: 0x18u); __asm { SVC 0xF2 } return 2; } /* ==== asd_rpc_101_topology_remove [tier=EXACT size=32] ==== */ // SVC 0xF2 -> ASD, opcode 0x101 (asd_topology_remove). Frame {0x101, link_index}. int __fastcall asd_rpc_101_topology_remove() { _DWORD v5[6]; // [sp+0h] [bp-18h] BYREF bzero_core(result: v5, a2: 0x18u); __asm { SVC 0xF2 } return 2; } /* ==== asd_rpc_1C0 [tier=EXACT size=32] ==== */ // SVC 0xF2 -> ASD (target process 2), opcode 0x1C0. 0x18-byte frame {opcode, arg}. int __fastcall asd_rpc_1C0() { _DWORD v5[6]; // [sp+0h] [bp-18h] BYREF bzero_core(result: v5, a2: 0x18u); __asm { SVC 0xF2 } return 2; } /* ==== bzero_core [tier=EXACT size=78] ==== */ // Zero-fill helper (32-byte unrolled tail-handling). Same SDK routine as the // identical function in HDCP, ASD and RAS. _DWORD *__fastcall bzero_core(_DWORD *result, unsigned int a2) { bool v2; // cf unsigned int v3; // r1 _DWORD *v4; // r0 int v5; // r1 int v6; // r1 v2 = a2 >= 0x20; v3 = a2 - 32; do { if ( !v2 ) break; *result = 0; result[1] = 0; result[2] = 0; result[3] = 0; v4 = result + 4; *v4 = 0; v4[1] = 0; v4[2] = 0; v4[3] = 0; result = v4 + 4; v2 = v3 >= 0x20; v3 -= 32; } while ( v2 ); v2 = __CFSHL__(v3, 28); v5 = v3 << 28; if ( v2 ) { *result = 0; result[1] = 0; result[2] = 0; result[3] = 0; result += 4; } if ( v5 < 0 ) { *result = 0; result[1] = 0; result += 2; } v2 = __CFSHL__(v5, 2); v6 = 4 * v5; if ( v2 ) *result++ = 0; if ( v6 != 0 ) { if ( v6 < 0 ) { *(_WORD *)result = 0; result = (_DWORD *)((char *)result + 2); } if ( (v6 & 0x40000000) != 0 ) { *(_BYTE *)result = 0; return (_DWORD *)((char *)result + 1); } } return result; } /* ==== dtm_TA_CloseSessionEntryPoint [tier=ENTRY size=2] ==== */ void dtm_TA_CloseSessionEntryPoint() { ; } /* ==== dtm_TA_CreateEntryPoint [tier=ENTRY size=4] ==== */ int dtm_TA_CreateEntryPoint() { return 0; } /* ==== dtm_TA_DestroyEntryPoint [tier=ENTRY size=2] ==== */ void dtm_TA_DestroyEntryPoint() { ; } /* ==== dtm_TA_OpenSessionEntryPoint [tier=ENTRY size=4] ==== */ int dtm_TA_OpenSessionEntryPoint() { return 0; } /* ==== dtm_cmd_set_flag [tier=EXACT size=22] ==== */ // Host command 3. Null-checks the request then forwards *req to ASD opcode 0x1C0 // via asd_rpc_1C0. Returns 3 on a null request. int __fastcall dtm_cmd_set_flag(_DWORD *a1) { if ( a1 != nullptr ) return asd_rpc_1C0() != 0; else return 3; } /* ==== dtm_cmd_topology_update [tier=EXACT size=94] ==== */ // Host command 2. If req[1] != 0: bounds req[9] <= 0x17, builds a 5-dword topology // descriptor from req[2..6] plus the 64-bit pair at req[7..8], and issues ASD 0x100 // (asd_rpc_100_topology_update). If req[1] == 0 it instead issues ASD 0x101 // (asd_rpc_101_topology_remove) with *req. Deepest read req[9] -> buf+52. int __fastcall dtm_cmd_topology_update(int *a1) { int v2; // r0 _DWORD v3[7]; // [sp+24h] [bp-1Ch] BYREF if ( a1 == nullptr ) return 3; if ( a1[1] != 0 ) { if ( (unsigned int)a1[9] > 0x17 ) return 2; bzero_core(result: v3, a2: 0x1Cu); v2 = asd_rpc_100_topology_update(); } else { v2 = asd_rpc_101_topology_remove(); } return v2 != 0; } /* ==== dtm_cmd_topology_update_v2 [tier=EXACT size=120] ==== */ // Host command 4. Larger form of dtm_cmd_topology_update: bounds req[9] <= 0x17 and // marshals req[2..14] into an 11-dword descriptor, normalising req[13] to 0/1/2. // Deepest read req[14] -> buf+72, +4 = buf+76, against the buf size gate of 0x50 (80). int __fastcall dtm_cmd_topology_update_v2(_DWORD *a1) { int v3; // r0 int v4; // r3 _DWORD v5[5]; // [sp+2Ch] [bp-14h] BYREF if ( a1 == nullptr ) return 3; if ( a1[1] != 0 ) { if ( a1[9] > 0x17u ) return 2; bzero_core(result: v5, a2: 0x14u); v4 = a1[13]; v5[2] = a1[14]; if ( v4 != 1 ) v4 = 2 * (v4 == 2); v5[1] = v4; v3 = asd_rpc_100_topology_update(); } else { v3 = asd_rpc_101_topology_remove(); } return v3 != 0; } /* ==== dtm_ta_main_loop [tier=EXACT size=148] ==== */ // DTM TA entry (header +0x10). Waits on SVC 0xF0, reads the GP command from frame[0], // vectors through dtm_entry_tbl (1..5), calls it, then replies with SVC 0xF1 and loops. // An unknown command id spins forever in a tight self-loop. void __fastcall __noreturn dtm_ta_main_loop(int *a1) { int v6; // r1 void (__cdecl *v7)(); // r12 while ( 1 ) { __asm { SVC 0xF0 } v6 = *a1; if ( *a1 == 1 ) { v7 = (void (__cdecl *)())dtm_TA_CreateEntryPoint; } else { switch ( v6 ) { case 2: v7 = (void (__cdecl *)())off_401250; break; case 3: v7 = (void (__cdecl *)())off_401254; break; case 4: v7 = (void (__cdecl *)())off_401258; break; case 5: v7 = (void (__cdecl *)())off_40125C; break; default: while ( 1 ) ; } } v7(); __asm { SVC 0xF1 } } } /* ==== start [tier=IDA size=1832] ==== */ // DTM TA header. magic 0x4154 'TA' -> host-loadable via LOAD_TA/INVOKE_CMD. // load_base 0x400000, data_base == image_end == 0x402000 (no .data/.bss), // entry 0x4011AC, stack guard VA 0x401240. void __fastcall __noreturn start(int a1, int a2, int a3) { int v3; // r5 int v4; // r6 char v5; // r7 int v6; // r4 v6 = a3 + (v3 >> 6); while ( 1 ) { v4 *= 16; *(_BYTE *)(v6 + 1) = v5; *(_DWORD *)(v4 + 20) = v6; *(_DWORD *)(((__int16)v6 >> 15) + 0x10) = v4; *(_BYTE *)(v4 + 1) = 0; } }