TSEC is a nvidia falcon processor with crypto extensions.

Firmware can be disassembled with envytools' envydis:

envydis -i tsec_fw.bin -m falcon -V fuc5 -F crypt

Note that the instruction set has variable length instructions, and the disassembler is not very good at detecting locations it should start disassembling from. One needs to disassemble multiple sub-regions and join them together.

Additional Notes on Crypto Extensions

mwk shared additional info learned from RE of falcon processors over the years, which hasn't made it into envytools documentation yet:

cxset

cxset instruction provides a way to change behavior of a variable amount of successively executed DMA-related instructions.

for example: 000000de: f4 3c 02 cxset 0x2

can be read as: dma_override(type=crypto_reg, count=2)

The argument to cxset specifies the type of behavior change in the top 3 bits, and the number of DMA-related instructions the effect lasts for in the lower 5 bits.

Override Types

Unlisted values are unknown, but probably do something.

Value Effect
0b000 falcon data mem <-> falcon $cX register
0b001 external mem <-> crypto input/output stream

DMA-Related Instructions

At least the following instructions may have changed behavior, and count against the cxset "count" argument: xdwait, xdst, xdld.

For example, if override type=0b000, then the "length" argument to xdst is instead treated as the index of the target $cX register.

Register ACLs

Falcon tracks permission metadata about each crypto reg. Permissions include read/write ability per execution mode, as well as ability to use the reg for encrypt/decrypt, among other permissions. Permissions are propagated when registers are referenced by instructions (e.g. moving a value from read-protected $cX to $cY will result in $cY also being read-protected).

Authenticated Mode Entry/Exit

Entry to Authenticated Mode always sets $pc to the address supplied in $cauth (ie the base of the signature-checked region). This takes effect when trying to branch to any address within the range covered by $cauth. Entry to Authenticated Mode (also called "Secure Mode") computes a MAC over the $cauth region and compares it to $c6 in order to perform the signature check.

Exit from Authenticated Mode must poke a special register (this seems to be I[0x10300] = 0) before leaving authenticated code pages. Failure to do this would result in the Falcon core halting.

Annotated Assembly

Keep in mind the architecture is not officially documented, so some things may be incorrect.

00000000: 4d 00 42              mov $r13 0x4200                     ; UC_CAPS
00000003: cf dd 00              iord $r13 I[$r13]
00000006: b6 d5 09              shr b32 $r13 0x9
00000009: f1 d4 ff 01           and $r13 0x1ff
0000000d: b6 d4 08              shl b32 $r13 0x8                    ; r13 = falcon data segment size
00000010: fe d4 00              mov $sp $r13                        ; put stack top all the way at the end
00000013: 7e 19 00 00           lcall 0x19
00000017: f8 02                 exit

00000019: 0f f0              C  mov $r15 -0x10
0000001b: f5 30 e4 fe           add $sp -0x11c
0000001f: f9 82                 mpush $r8
00000021: fe 49 01              mov $r9 $sp
00000024: 90 99 c4              add b32 $r9 $r9 0xc4
00000027: ff 9f 34              and $r3 $r9 $r15                    ; r3 = (sp + 0xc4) & ~0xf
0000002a: 92 99 8b              sub b32 $r9 $r9 0x8b
0000002d: 85 00 03 00           mov $r5 0x300
00000031: ff 9f 44              and $r4 $r9 $r15                    ; r4 = (sp + 0x39) & ~0xf
00000034: b2 5b                 mov b32 $r11 $r5
00000036: 0c 7c                 mov $r12 0x7c
00000038: b2 3a                 mov b32 $r10 $r3
0000003a: 7e 0b 02 00           lcall 0x20b                         ; memcpy_i2d(dst=r3{local}, src=0x300, len=0x7c)
0000003e: 98 3c 1d              ld b32 $r12 D[$r3+TSecLoaderInfo.secure_loader_len] ; r12 = 0x600
00000041: 95 59 08              shr b32 $r9 $r5 0x8
00000044: 8b 00 04 00           mov $r11 0x400
00000048: b0 91 09              st b32 D[$sp+0x24] $r9
0000004b: bd a4                 clear b32 $r10                      ; temp copy for next step
0000004d: 7e 0b 02 00           lcall 0x20b                         ; memcpy_i2d(dst=0, src=0x400, len=0x600)
00000051: 98 3c 1d              ld b32 $r12 D[$r3+TSecLoaderInfo.secure_loader_len] ; r12 = 0x600
00000054: b2 5a                 mov b32 $r10 $r5
00000056: bd b4                 clear b32 $r11
00000058: b2 5d                 mov b32 $r13 $r5
0000005a: 0e 01                 mov $r14 0x1                        ; copy the code originally @ I:0x400 to I:0x300 and mark secret
0000005c: 7e c2 01 00           lcall 0x1c2                         ; memcpy_d2i_ex(dst_page_offset=0x300, src=0, len=0x600, dst=0x300, is_secret=1)
00000060: 89 00 00 06           mov $r9 0x60000                     ; will be used as size bits for xdst @ 000000e1
00000064: 90 4f 20              add b32 $r15 $r4 0x20               ; TSecLoaderInfo.field_20
00000067: bd 64                 clear b32 $r6
00000069: ff f9 75              or $r7 $r15 $r9
0000006c: bd 24                 clear b32 $r2                       ; loop_count = 0
0000006e: bd 94                 clear b32 $r9
00000070: bd 84                 clear b32 $r8                       ; ext_offset = 0
00000072: 3e 78 00 00           lbra 0x78
loop_set_ignored_cmd:
00000076: b2 19               B mov b32 $r9 $r1                     ; i guess this is because SCRATCH0 retains the old cmd
loop:
00000078: 90 22 01            B add b32 $r2 $r2 0x1
0000007b: 8f 01 09 3d           mov $r15 0x3d0901   ; 4000001
0000007f: a6 2f                 cmp b32 $r2 $r15
00000081: f4 1b 17              bra ne 0x98                         ; for (u32 r2 = 0; r2 < 4000000; r2++) {
set_status_c0_loop_expired:
00000084: df c0 c0 c0 c0        mov $r15 0xc0c0c0c0
00000089: 49 00 11              mov $r9 0x1100
0000008c: fa 9f 00              iowr I[$r9] $r15                    ; SCRATCH1 = 0xc0c0c0c0
0000008f: d0 c0 c0 c0 c0        mov $r0 0xc0c0c0c0
00000094: 3e 0c 01 00           lbra 0x10c                          ; -> writeback_key_and_ret
read_next_command:
00000098: 4f 00 10            B mov $r15 0x1000
0000009b: ff f8 1f              iord $r1 I[$r15+$r8*0x4]            ; r1 = SCRATCH0 (cmd)
0000009e: b3 14 64 18           bra b32 $r1 0x64 ne 0xb6            ; -> dispatch_command
handle_command_0x64:
000000a2: df b0 b0 b0 b0        mov $r15 0xb0b0b0b0
000000a7: 49 00 11              mov $r9 0x1100
000000aa: fa 9f 00              iowr I[$r9] $r15                    ; SCRATCH1 = 0xb0b0b0b0
000000ad: d0 b0 b0 b0 b0        mov $r0 0xb0b0b0b0
000000b2: 3e 0c 01 00           lbra 0x10c                          ; -> writeback_key_and_ret
dispatch_command:
000000b6: a6 19               B cmp b32 $r1 $r9
000000b8: f4 0b c0              bra e 0x78                          ; if (cmd == ignored_cmd) continue;
000000bb: b3 10 00 3b           bra b32 $r1 0x0 e 0xf6              ; if (cmd == 0) set_status_b0_ok_and_continue;
000000bf: b0 16 03              cmp b32 $r1 0x3
000000c2: f4 0c 56              bra a 0x118                         ; if (cmd > 3) return BAD_CMD;
000000c5: b2 4a                 mov b32 $r10 $r4
000000c7: b2 3b                 mov b32 $r11 $r3
000000c9: 0c 7c                 mov $r12 0x7c                       ; make another copy of the ldr_info (secure_loader will zero it before return)
000000cb: 7e 37 02 00           lcall 0x237                         ; memcpy_d2d(dst=r4{local}, src=r3{local}, len=0x7c)
000000cf: 98 49 1d              ld b32 $r9 D[$r4+TSecLoaderInfo.secure_loader_len]  ; code_size = 0x600
000000d2: b4 f0 09              ld b32 $r15 D[$sp+0x24]             ; secure_loader_dst_page = 0x300 >> 8
000000d5: b6 94 10              shl b32 $r9 0x10
000000d8: fd 9f 05              or $r9 $r15
000000db: fe 9a 00              mov $cauth $r9                      ; cauth = (code_size << 16) | secure_loader_dst_page
000000de: f4 3c 02              cxset 0x2                           ; dma_override(type=crypto_reg, count=2)
000000e1: fa 87 06              xdst $r8 $r7                        ; crypto_reg_load(crypto_reg=$c6, local_address=r4+0x20)
000000e4: f8 03                 xdwait
000000e6: b2 6c                 mov b32 $r12 $r6
000000e8: b2 4a                 mov b32 $r10 $r4
000000ea: b2 1b                 mov b32 $r11 $r1
000000ec: 06 01                 mov $r6 0x1                         ; set loader_called=1 for next iterations
000000ee: f9 55                 call $r5                            ; secure_loader_thunk(ldr_info{r4 local}, cmd, skip_copy_and_decrypt=loader_called)
000000f0: b2 a0                 mov b32 $r0 $r10
000000f2: b3 a4 00 09           bra b32 $r10 0x0 ne 0xfb            ; if (rv != 0) { SCRATCH1 = rv; return; }
set_status_b0_ok_and_continue:
000000f6: d0 b0 b0 b0 b0      B mov $r0 0xb0b0b0b0                  ; else { SCRATCH1 = 0xb0b0b0b0; continue; }
write_status_and_key: // returns if status != 0xb0b0b0b0
000000fb: 49 00 11            B mov $r9 0x1100
000000fe: fa 90 00              iowr I[$r9] $r0                     ; SCRATCH1 = rv
00000101: df b0 b0 b0 b0        mov $r15 0xb0b0b0b0
00000106: a6 0f                 cmp b32 $r0 $r15
00000108: f5 0b 6e ff           bra e 0x76                          ; -> loop_set_ignored_cmd (continue)
writeback_key_and_ret:
0000010c: b2 3a               B mov b32 $r10 $r3                    ; somehow secure_payload writes back to this..?
0000010e: 7e 77 01 00           lcall 0x177                         ; write128_via_SOR(r3{local})
00000112: b2 0a                 mov b32 $r10 $r0
00000114: fb 83 1c 01           mpopaddret $r8, 0x11c               ; was: mpopunk [unknown: 00 80 1c 01]
set_status_d0_bad_cmd:
00000118: d0 d0 d0 d0 d0      B mov $r0 0xd0d0d0d0                  ; rv = 0xd0d0d0d0
0000011d: 3e fb 00 00           lbra 0xfb

// wait_1c000()
// 0x1c000 == mmio + 0x700
while (((*(u32 *)0x1c000 >> 12) & 7) == 1) {
    ; // expect it to clear to 0
}
00000121: 8f 00 c0 01        C  mov $r15 0x1c000
00000125: cf f9 00            B iord $r9 I[$r15]
00000128: c7 9a 4c              extr $r10 $r9 0xc:0xe
0000012b: b3 a0 01 fa           bra b32 $r10 0x1 e 0x125
0000012f: f8 00                 ret

// write_1c000_indirect(addr=r10, val=r11)
00000131: f9 12              C  mpush $r1
00000133: b2 a0                 mov b32 $r0 $r10
00000135: b2 b1                 mov b32 $r1 $r11
00000137: 7e 21 01 00           lcall 0x121
0000013b: 09 01                 mov $r9 0x1
0000013d: b3 a4 00 2d           bra b32 $r10 0x0 ne 0x16a           ; if (wait_1c000() != STATUS_OK) return 1;
00000141: 89 00 c1 01           mov $r9 0x1c100
00000145: fa 90 00              iowr I[$r9] $r0                     ; 0x1c100 = addr
00000148: b8 99 00 01 00        add b32 $r9 $r9 0x100
0000014d: fa 91 00              iowr I[$r9] $r1                     ; 0x1c200 = val
00000150: df f2 00 00 80        mov $r15 0x800000f2
00000155: b8 99 00 02 02        sub b32 $r9 $r9 0x200
0000015a: fa 9f 00              iowr I[$r9] $r15                    ; 0x1c000 = 0x800000f2
0000015d: 7e 21 01 00           lcall 0x121                         ; wait_1c000()
00000161: b0 a6 00              cmp b32 $r10 0x0
00000164: f0 9c 0b              xbit $r9 $flags z
00000167: f0 96 01              xor $r9 0x1                         ; return (wait_1c000() == STATUS_OK) ? 0 : 1;
0000016a: b2 9a               B mov b32 $r10 $r9
0000016c: fb 11                 mpopret $r1

// write_1c300(val=r10)
; unused function?
0000016e: 89 00 c3 01           mov $r9 0x1c300
00000172: fa 9a 00              iowr I[$r9] $r10                    ; 0x1c300 = r10
00000175: f8 00                 ret

// throw the key material in "random" HDCP key regs...
void write128_via_SOR(u32 *data) {
    0x1c300 = 0xfff;
    if (write_1c000_indirect(0x545801e8, data[0])) {
        return;
    }
    if (write_1c000_indirect(0x5458021c, data[1])) {
        return;
    }
    if (write_1c000_indirect(0x54580208, data[2])) {
        return;
    }
    if (write_1c000_indirect(0x5458020c, data[3])) {
        return;
    }
}
00000177: f9 02              C  mpush $r0
00000179: 4f ff 0f              mov $r15 0xfff
0000017c: b2 a0                 mov b32 $r0 $r10
0000017e: 89 00 c3 01           mov $r9 0x1c300
00000182: fa 9f 00              iowr I[$r9] $r15
00000185: bf ab                 ld b32 $r11 D[$r10]
00000187: da e8 01 58 54        mov $r10 0x545801e8
0000018c: 7e 31 01 00           lcall 0x131
00000190: b3 a4 00 30           bra b32 $r10 0x0 ne 0x1c0
00000194: 98 0b 01              ld b32 $r11 D[$r0+0x4]
00000197: da 1c 02 58 54        mov $r10 0x5458021c
0000019c: 7e 31 01 00           lcall 0x131
000001a0: b3 a4 00 20           bra b32 $r10 0x0 ne 0x1c0
000001a4: 98 0b 02              ld b32 $r11 D[$r0+0x8]
000001a7: da 08 02 58 54        mov $r10 0x54580208
000001ac: 7e 31 01 00           lcall 0x131
000001b0: b3 a4 00 10           bra b32 $r10 0x0 ne 0x1c0
000001b4: 98 0b 03              ld b32 $r11 D[$r0+0xc]
000001b7: da 0c 02 58 54        mov $r10 0x5458020c
000001bc: 7e 31 01 00           lcall 0x131
000001c0: fb 01               B mpopret $r0

// memcpy_d2i_ex(dst_page_offset=r10, src=r11, len=r12, dst=r13, is_secret=r14)
000001c2: f9 02              C  mpush $r0
000001c4: b2 b0                 mov b32 $r0 $r11                ; src, data
000001c6: b2 cb                 mov b32 $r11 $r12               ; len, bytes
000001c8: b2 dc                 mov b32 $r12 $r13               ; dst, insn
000001ca: 33 00 00 0a           bra b8 $r0 0x0 e 0x1d4          ; assert((src & 0xff) == 0)
000001ce: f8 02                 exit
000001d0: 3e d0 01 00         B lbra 0x1d0
000001d4: 33 b0 00 0a         B bra b8 $r11 0x0 e 0x1de         ; assert((len & 0xff) == 0)
000001d8: f8 02                 exit
000001da: 3e da 01 00         B lbra 0x1da
000001de: 33 c0 00 0a         B bra b8 $r12 0x0 e 0x1e8         ; assert((dst & 0xff) == 0)
000001e2: f8 02                 exit
000001e4: 3e e4 01 00         B lbra 0x1e4
000001e8: b3 e0 00 0d         B bra b32 $r14 0x0 e 0x1f5
000001ec: d9 00 00 00 11        mov $r9 0x11000000
000001f1: 3e fa 01 00           lbra 0x1fa
000001f5: d9 00 00 00 01      B mov $r9 0x1000000
000001fa: ff a9 95            B or $r9 $r10 $r9
000001fd: 4f 00 60              mov $r15 0x6000                 ; 
00000200: fa f9 00              iowr I[$r15] $r9                ; CODE_INDEX = r10 | AUTO_INC_WRITE | ((r14) ? SECRET : 0)
00000203: b2 0a                 mov b32 $r10 $r0
00000205: 7e 4f 02 00           lcall 0x24f                     ; memcpy_d2i(src=r10, len=r11, dst=r12)
00000209: fb 01                 mpopret $r0

// memcpy_i2d(dst=r10, src=r11, len=r12)
CODE_INDEX = r11 | 0x2000000; // AUTO_INC_READ
for (u32 r14 = 0; r14 < r12; r14 += 4) {
    r10[r14] = CODE;
}
0000020b: d9 00 00 00 02     C  mov $r9 0x2000000
00000210: fd b9 05              or $r11 $r9
00000213: 49 00 60              mov $r9 0x6000
00000216: fa 9b 00              iowr I[$r9] $r11                ; CODE_INDEX = r11 | 0x2000000
00000219: bd e4                 clear b32 $r14                  ; r14 = 0
0000021b: 4b 00 61              mov $r11 0x6100
0000021e: bd d4                 clear b32 $r13                  ; r13 = 0
00000220: 3e 30 02 00           lbra 0x230
00000224: ff bd ff            B iord $r15 I[$r11+$r13*0x4]      ; r15 = CODE
00000227: 95 e9 02              shr b32 $r9 $r14 0x2            ; r9 = r14 / 2              
0000022a: 90 ee 04              add b32 $r14 $r14 0x4           ; r14 += 4                  
0000022d: bc af 99              st b32 D[$r10+$r9*0x4] $r15     ; [r10 + r9 * 4] = r15
00000230: a6 ec               B cmp b32 $r14 $r12
00000232: f4 08 f2              bra b 0x224
00000235: f8 00                 ret

// memcpy_d2d(dst=r10, src=r11, len=r12)
00000237: 3e 48 02 00        C  lbra 0x248
0000023b: bf bf               B ld b32 $r15 D[$r11]
0000023d: b6 b0 04              add b32 $r11 0x4
00000240: b6 c2 04              sub b32 $r12 0x4
00000243: a0 af                 st b32 D[$r10] $r15
00000245: b6 a0 04              add b32 $r10 0x4
00000248: b3 c4 00 f3         B bra b32 $r12 0x0 ne 0x23b
0000024c: f8 00                 ret

; for some reason there is this extra byte here
0000024e: 01 

// memcpy_d2i(src=r10, len=r11, dst=r12)
// caller sets CODE_INDEX to initial page offset
0000024f: f9 32                 mpush $r3
00000251: 4d 00 61              mov $r13 0x6100                 ; CODE
00000254: 4e 00 62              mov $r14 0x6200                 ; CODE_VIRT_ADDR
00000257: 3e 82 02 00           lbra 0x282
0000025b: c4 c0 ff            B and $r0 $r12 0xff
0000025e: f4 0b 2a              bra e 0x288
00000261: 98 a0 00            B ld b32 $r0 D[$r10]
00000264: 98 a1 01              ld b32 $r1 D[$r10+0x4]
00000267: 98 a2 02              ld b32 $r2 D[$r10+0x8]
0000026a: 98 a3 03              ld b32 $r3 D[$r10+0xc]
0000026d: f6 d0 00              iowr I[$r13] $r0
00000270: f6 d1 00              iowr I[$r13] $r1
00000273: f6 d2 00              iowr I[$r13] $r2
00000276: f6 d3 00              iowr I[$r13] $r3
00000279: b6 a0 10              add b32 $r10 0x10
0000027c: b6 b2 10              sub b32 $r11 0x10
0000027f: b6 c0 10              add b32 $r12 0x10
00000282: b3 b4 00 d9         B bra b32 $r11 0x0 ne 0x25b
00000286: fb 31                 mpopret $r3
00000288: 95 c0 08            B shr b32 $r0 $r12 0x8
0000028b: f6 e0 00              iowr I[$r14] $r0                ; write next page base and continue
0000028e: 3e 61 02 00           lbra 0x261

; this shit gets copied to 00000019's stack
struct TSecLoaderInfo {
    u8 derived_key[16];         // written by the secure payload and then written back for bpmp to pass to SE
    u8 field_10[16];            // mac of unauthenticated code (which loads secure_loader). checked by secure_loader
    u8 field_20[16];            // used for auth of secure_loader code pages
    u8 field_30[16];            // used for auth of secure_payload code pages
    u8 field_40[16];            // IV to decrypt secure_payload
    u8 key_name_0[16];          // arg0 for secure_payload(cmd=1)
    u8 key_name_1[16];          // arg0 for secure_payload(cmd=2)
    u32 secure_loader_offset;   // points to this info header (consumes 0x100 bytes). secure_loader relocated here
    u32 secure_loader_len;      // size of secure_loader code after this 0x100 byte header
    u32 secure_payload_len;
};
00000300  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000310  f1 40 c7 6e 6c 0c 6d 59  a8 26 44 ca d0 85 2f f1  |.@.nl.mY.&D.../.|
00000320  9c 8b 75 d3 df 0b f0 6c  95 fc 91 c0 76 1e f0 62  |..u....l....v..b|
00000330  89 2a 36 22 8d 49 e0 48  4d 48 0c b0 ac da 02 34  |.*6".I.HMH.....4|
00000340  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000350  48 4f 56 49 5f 45 4b 53  5f 30 31 00 00 00 00 00  |HOVI_EKS_01.....|
00000360  48 4f 56 49 5f 43 4f 4d  4d 4f 4e 5f 30 31 00 00  |HOVI_COMMON_01..|
00000370  00 03 00 00 00 06 00 00  00 05 00 00

// u32 secure_loader_thunk(TSecLoaderInfo *info=r10, cmd=r11, skip_load_and_decrypt=r12)
; this is just a thunk that (tries to) sanitize state before running secure code
; originally @ I:0x400, gets copied twice and winds up executed @ I:0x300
00000300: f4 32 10              bclr $flags ie0                 ; some re-init code since this can be called by attackers!
00000303: f4 32 11              bclr $flags ie1
00000306: f4 32 12              bclr $flags ie2
00000309: f4 3c 80              cxset 0x80                      ; dma_override(type=0b100, count=0) ; clear overrides?
0000030c: fe ae 01              mov $r14 $cauth
0000030f: f0 ea 13              bclr $r14 0x13
00000312: fe ea 00              mov $cauth $r14                 ; cauth &= ~(1 << 19)
00000315: 0e 00                 mov $r14 0x0
00000317: fe eb 00              mov $xtargets $r14              ; xtargets = 0
0000031a: f8 03                 xdwait
0000031c: f8 07                 xcwait
0000031e: f4 3c 02              cxset 0x2                       ; dma_override(type=crypto_reg, count=2)
00000321: 0e 00                 mov $r14 0x0
00000323: fa ee 06              xdst $r14 $r14                  ; $c0 = ?
00000326: f8 03                 xdwait
00000328: f5 3c 00 ac           cxor $c0 $c0
0000032c: f5 3c 01 84           cmov $c1 $c0
00000330: f5 3c 02 84           cmov $c2 $c0
00000334: f5 3c 03 84           cmov $c3 $c0
00000338: f5 3c 04 84           cmov $c4 $c0
0000033c: f5 3c 05 84           cmov $c5 $c0
00000340: f5 3c 07 84           cmov $c7 $c0
00000344: 8e 00 0e 02           mov $r14 0x20e00
00000348: cf ef 00              iord $r15 I[$r14]
0000034b: f0 fa 10              bclr $r15 0x10
0000034e: fa ef 00              iowr I[$r14] $r15               ; 0x20e00 &= ~0x10
00000351: 8e 00 06 01           mov $r14 0x10600
00000355: cf ef 00              iord $r15 I[$r14]
00000358: f0 f9 00              bset $r15 0x0
0000035b: fa ef 00              iowr I[$r14] $r15               ; 0x10600 |= 1
0000035e: cf ef 00            B iord $r15 I[$r14]
00000361: f0 f4 02              and $r15 0x2
00000364: f4 0b fa              bra e 0x35e                     ; while (0x10600 & 2) == 0
00000367: 4e 00 42              mov $r14 0x4200
0000036a: cf ee 00              iord $r14 I[$r14]               ; UC_CAPS
0000036d: b6 e5 09              shr b32 $r14 0x9
00000370: f1 e4 ff 01           and $r14 0x1ff
00000374: b6 e4 08              shl b32 $r14 0x8                ; r14 = falcon data segment size
00000377: fe 4f 01              mov $r15 $sp
0000037a: a4 fe                 cmpu b32 $r15 $r14
0000037c: f4 18 51              bra ae 0x3cd                    ; if (sp >= DATA_SEG_SIZE) exit;
0000037f: b1 f4 00 08           cmpu b32 $r15 0x800
00000383: f4 08 4a              bra b 0x3cd                     ; if (sp < 0x800) exit;
00000386: f9 82                 mpush $r8
00000388: 7e cf 03 00           lcall 0x3cf                     ; call the real entrypoint @ secure_loader
0000038c: fb 80                 mpop $r8
0000038e: f5 3c 00 e0           ??? [unknown: 00 00 00 e0] [unknown instruction] ; this is some "cocmd" insn -> "mwk: chmod IIRC, it modifies the ACLs"
00000392: f5 3c 00 ac           cxor $c0 $c0                    ; we're done... clear some state
00000396: f5 3c 11 ac           cxor $c1 $c1
0000039a: f5 3c 22 ac           cxor $c2 $c2
0000039e: f5 3c 33 ac           cxor $c3 $c3
000003a2: f5 3c 44 ac           cxor $c4 $c4
000003a6: f5 3c 55 ac           cxor $c5 $c5
000003aa: f5 3c 66 ac           cxor $c6 $c6
000003ae: f5 3c 77 ac           cxor $c7 $c7
000003b2: 09 00                 mov $r9 0x0
000003b4: 0b 00                 mov $r11 0x0
000003b6: 0c 00                 mov $r12 0x0
000003b8: 0d 00                 mov $r13 0x0
000003ba: 0e 00                 mov $r14 0x0
000003bc: 0f 00                 mov $r15 0x0
000003be: fc f0                 pop $r15                        ; pop THE RETURN ADDRESS!!
000003c0: 4b 00 03              mov $r11 0x300
000003c3: f0 b3 01              sethi $r11 0x10000              ; confusing disassembler! this is |= (1 << 16)
000003c6: 0c 00                 mov $r12 0x0
000003c8: fa bc 00              iowr I[$r11] $r12               ; 0x10300 = 0
000003cb: f9 f4                 bra $r15                        ; -> return..?
000003cd: f8 02               B exit

// u32 secure_loader(TSecLoaderInfo *info=r10, cmd=r11, skip_load_and_decrypt=r12)
000003cf: f4 30 e0           C  add $sp -0x20
000003d2: f9 42                 mpush $r4                       ; sp -= 0x14
000003d4: b2 c4                 mov b32 $r4 $r12
000003d6: 98 ac 1c              ld b32 $r12 D[$r10+TSecLoaderInfo.secure_loader_offset]
000003d9: f4 30 fc              add $sp -0x4
000003dc: b2 a2                 mov b32 $r2 $r10
000003de: b2 b3                 mov b32 $r3 $r11
000003e0: bd a4                 clear b32 $r10
000003e2: bd b4                 clear b32 $r11                  ; copy all insn memory before the secure_loader to D:0
000003e4: 7e 3f 05 00           lcall 0x53f                     ; memcpy_i2d(dst=0, src=0, len=secure_loader_offset)
000003e8: bd a4                 clear b32 $r10
000003ea: bd b4                 clear b32 $r11
000003ec: 7e 47 06 00           lcall 0x647                     ; key_init(hdr_type=CODE_SIG, key_type=ENCRYPT)
000003f0: 98 21 1c              ld b32 $r1 D[$r2+TSecLoaderInfo.secure_loader_offset]
000003f3: 09 f0                 mov $r9 -0x10
000003f5: fe 40 01              mov $r0 $sp
000003f8: 90 00 28              add b32 $r0 $r0 0x28
000003fb: b2 1b                 mov b32 $r11 $r1
000003fd: fd 09 04              and $r0 $r9
00000400: b2 0a                 mov b32 $r10 $r0                ; unauth_code_mac = (sp + 0x28) & ~0xf
00000402: 7e 88 06 00           lcall 0x688                     ; crypto_688(unauth_code_mac{local}, secure_loader_offset)
00000406: bd 94                 clear b32 $r9
00000408: b2 1b                 mov b32 $r11 $r1
0000040a: b2 0c                 mov b32 $r12 $r0
0000040c: bd a4                 clear b32 $r10
0000040e: b2 0d                 mov b32 $r13 $r0
00000410: 0e 02                 mov $r14 0x2
00000412: b0 91 00              st b32 D[$sp] $r9               ; arg on the stack
; crypto_6b9(buf_in=0, buf_in_len=secure_loader_offset, iv=unauth_code_mac, buf_out=unauth_code_mac, mode=2, some_bool=0)
00000415: 7e b9 06 00           lcall 0x6b9
00000419: b2 0a                 mov b32 $r10 $r0
0000041b: 90 2b 10              add b32 $r11 $r2 0x10           ; TSecLoaderInfo.field_10
0000041e: 0c 10                 mov $r12 0x10
00000420: 7e 9a 05 00           lcall 0x59a                     ; r10 = memcmp(p1=unauth_code_mac{local}, p2=&info->field_10, len=0x10)
00000424: b3 a0 00 0d           bra b32 $r10 0x0 e 0x431
00000428: da ef be ad de        mov $r10 0xdeadbeef
0000042d: 3e f0 04 00           lbra 0x4f0                      ; if mac failure, return 0xdeadbeef
00000431: 49 00 42            B mov $r9 0x4200                  ; UC_CAPS
00000434: cf 99 00              iord $r9 I[$r9]                 ; useless code?
00000437: 98 2f 1d              ld b32 $r15 D[$r2+TSecLoaderInfo.secure_loader_len]
0000043a: 98 29 1c              ld b32 $r9 D[$r2+TSecLoaderInfo.secure_loader_offset]
0000043d: bc f9 10              add b32 $r1 $r15 $r9            ; r1 = 0x600 + 0x300 = 0x900
00000440: b3 44 00 4c           bra b32 $r4 0x0 ne 0x48c        ; skip load_and_decrypt_payload?
00000444: 98 20 1e              ld b32 $r0 D[$r2+TSecLoaderInfo.secure_payload_len] ; 0x500
00000447: fe 49 01              mov $r9 $sp
0000044a: a6 09                 cmp b32 $r0 $r9
0000044c: f4 18 40              bra ae 0x48c                    ; if (secure_payload_len >= sp): skip load_and_decrypt_payload ???
load_and_decrypt_payload:
; copy payload to D:0, decrypt it, copy as secret to I:0x900, then clear the copy at D:0
0000044f: b2 0c                 mov b32 $r12 $r0
00000451: b8 1b 00 01 00        add b32 $r11 $r1 0x100          ; r11 = 0xa00 (payload_addr)
00000456: 7e 3f 05 00           lcall 0x53f                     ; memcpy_i2d(dst=0, src=0xa00, len=secure_payload_len)
0000045a: 0a 01                 mov $r10 0x1
0000045c: b2 ab                 mov b32 $r11 $r10
0000045e: 7e 47 06 00           lcall 0x647                     ; key_init(hdr_type=CODE_ENC, key_type=DECRYPT)
00000462: b2 0b                 mov b32 $r11 $r0
00000464: 90 2c 40              add b32 $r12 $r2 0x40           ; TSecLoaderInfo.field_40
00000467: bd d4                 clear b32 $r13
00000469: bd e4                 clear b32 $r14
0000046b: bd a4                 clear b32 $r10
0000046d: b0 41 00              st b32 D[$sp] $r4               ; always zero. arg on the stack
; crypto_6b9(buf_in=0, buf_in_len=secure_payload_len, iv=info->field_40, buf_out=0, mode=0, some_bool=0)
00000470: 7e b9 06 00           lcall 0x6b9
00000474: b2 1a                 mov b32 $r10 $r1
00000476: bd b4                 clear b32 $r11
00000478: b2 0c                 mov b32 $r12 $r0
0000047a: b2 1d                 mov b32 $r13 $r1
0000047c: 0e 01                 mov $r14 0x1
0000047e: 7e f6 04 00           lcall 0x4f6                     ; memcpy_d2i_ex(dst_page_offset=0x900, src=0, len=secure_payload_len, dst=0x900, is_secret=1)
00000482: b2 0c                 mov b32 $r12 $r0
00000484: bd a4                 clear b32 $r10
00000486: bd b4                 clear b32 $r11
00000488: 7e 6b 05 00           lcall 0x56b                     ; memset(dst=0, val=0, len=secure_payload_len)
auth_and_exec_payload:
0000048c: f4 3c 02            B cxset 0x2                       ; dma_override(type=crypto_reg, count=2)
0000048f: 8f 00 00 06           mov $r15 0x60000
00000493: 90 29 30              add b32 $r9 $r2 0x30            ; TSecLoaderInfo.field_30
00000496: fd 9f 05              or $r9 $r15
00000499: bd f4                 clear b32 $r15
0000049b: fa f9 06              xdst $r15 $r9                   ; crypto_reg_load(crypto_reg=$c6, local_address=&info->field_30)
0000049e: f8 03                 xdwait
000004a0: fe a4 01              mov $r4 $cauth                  ; save cauth
000004a3: 98 29 1e              ld b32 $r9 D[$r2+TSecLoaderInfo.secure_payload_len]
000004a6: 95 1f 08              shr b32 $r15 $r1 0x8
000004a9: b6 94 10              shl b32 $r9 0x10
000004ac: fd f9 05              or $r15 $r9
000004af: fe fa 00              mov $cauth $r15                 ; cauth = (secure_payload_len << 16) | (secure_payload_dst >> 8)
000004b2: b3 34 01 0d           bra b32 $r3 0x1 ne 0x4bf
handle_cmd_1:
000004b6: b2 3b                 mov b32 $r11 $r3
000004b8: 90 2a 50              add b32 $r10 $r2 0x50           ; TSecLoaderInfo.key_name_0
000004bb: 3e dd 04 00           lbra 0x4dd                      ; status = secure_payload(&info->key_name_0, cmd=1)
000004bf: b3 34 02 0d         B bra b32 $r3 0x2 ne 0x4cc
handle_cmd_2:
000004c3: b2 3b                 mov b32 $r11 $r3
000004c5: 90 2a 60              add b32 $r10 $r2 0x60           ; TSecLoaderInfo.key_name_1
000004c8: 3e dd 04 00           lbra 0x4dd                      ; status = secure_payload(&info->key_name_1, cmd=2)
000004cc: b3 30 03 0d         B bra b32 $r3 0x3 e 0x4d9
handle_invalid_cmd:
000004d0: d0 d0 d0 d0 d0        mov $r0 0xd0d0d0d0
000004d5: 3e e1 04 00           lbra 0x4e1                      ; status = 0xd0d0d0d0;
handle_cmd_3:
000004d9: b2 3b               B mov b32 $r11 $r3
000004db: b2 2a                 mov b32 $r10 $r2
000004dd: f9 15               B call $r1                        ; status = secure_payload(info, cmd=3)
000004df: b2 a0                 mov b32 $r0 $r10
000004e1: b2 2a               B mov b32 $r10 $r2
000004e3: bd b4                 clear b32 $r11
000004e5: 0c 7c                 mov $r12 0x7c                   ; clear our copy of the info header
000004e7: 7e 6b 05 00           lcall 0x56b                     ; memset(dst=info, val=0, len=0x7c)
000004eb: fe 4a 00              mov $cauth $r4                  ; restore cauth
000004ee: b2 0a                 mov b32 $r10 $r0                ; return status
000004f0: f4 30 04            B add $sp 0x4
000004f3: fb 45 20              mpopaddret $r4 0x20

// memcpy_d2i_ex(dst_page_offset=r10, src=r11, len=r12, dst=r13, is_secret=r14)
000004f6: f9 02              C  mpush $r0
000004f8: b2 b0                 mov b32 $r0 $r11
000004fa: b2 cb                 mov b32 $r11 $r12
000004fc: b2 dc                 mov b32 $r12 $r13
000004fe: 33 00 00 0a           bra b8 $r0 0x0 e 0x508
00000502: f8 02                 exit
00000504: 3e 04 05 00         B lbra 0x504
00000508: 33 b0 00 0a         B bra b8 $r11 0x0 e 0x512
0000050c: f8 02                 exit
0000050e: 3e 0e 05 00         B lbra 0x50e
00000512: 33 c0 00 0a         B bra b8 $r12 0x0 e 0x51c
00000516: f8 02                 exit
00000518: 3e 18 05 00         B lbra 0x518
0000051c: b3 e0 00 0d         B bra b32 $r14 0x0 e 0x529
00000520: d9 00 00 00 11        mov $r9 0x11000000
00000525: 3e 2e 05 00           lbra 0x52e
00000529: d9 00 00 00 01      B mov $r9 0x1000000
0000052e: ff a9 95            B or $r9 $r10 $r9
00000531: 4f 00 60              mov $r15 0x6000                 ; CODE_INDEX
00000534: fa f9 00              iowr I[$r15] $r9
00000537: b2 0a                 mov b32 $r10 $r0
00000539: 7e 88 08 00           lcall 0x888                     ; memcpy_d2i(src=r10, len=r11, dst=r12)
0000053d: fb 01                 mpopret $r0

// memcpy_i2d(dst=r10, src=r11, len=r12)
0000053f: d9 00 00 00 02     C  mov $r9 0x2000000
00000544: fd b9 05              or $r11 $r9
00000547: 49 00 60              mov $r9 0x6000                  ; CODE_INDEX
0000054a: fa 9b 00              iowr I[$r9] $r11
0000054d: bd e4                 clear b32 $r14
0000054f: 4b 00 61              mov $r11 0x6100                 ; CODE
00000552: bd d4                 clear b32 $r13
00000554: 3e 64 05 00           lbra 0x564
00000558: ff bd ff            B iord $r15 I[$r11+$r13*0x4]
0000055b: 95 e9 02              shr b32 $r9 $r14 0x2
0000055e: 90 ee 04              add b32 $r14 $r14 0x4
00000561: bc af 99              st b32 D[$r10+$r9*0x4] $r15
00000564: a6 ec               B cmp b32 $r14 $r12
00000566: f4 08 f2              bra b 0x558
00000569: f8 00                 ret

// memset(dst=r10, val=r11, len=r12)
0000056b: 3e 81 05 00        C  lbra 0x581
0000056f: b5 ab 00            B st b32 D[$r10] $r11
00000572: b5 ab 01              st b32 D[$r10+0x4] $r11
00000575: b5 ab 02              st b32 D[$r10+0x8] $r11
00000578: b5 ab 03              st b32 D[$r10+0xc] $r11
0000057b: b6 a0 10              add b32 $r10 0x10
0000057e: b6 c2 10              sub b32 $r12 0x10
00000581: b0 c6 10            B cmp b32 $r12 0x10
00000584: f4 18 eb              bra ae 0x56f
00000587: 3e 93 05 00           lbra 0x593
0000058b: a0 ab               B st b32 D[$r10] $r11
0000058d: b6 a0 04              add b32 $r10 0x4
00000590: b6 c2 04              sub b32 $r12 0x4
00000593: b3 c4 00 f8         B bra b32 $r12 0x0 ne 0x58b
00000597: f8 00                 ret

; another extra 0x01 byte following a ret
00000599: 01

// int memcmp(p1=r10, p2=r11, len=r12)
// NOT TIMING INVARIANT
0000059a: 3e b0 05 00           lbra 0x5b0
0000059e: bf ad               B ld b32 $r13 D[$r10]
000005a0: bf be                 ld b32 $r14 D[$r11]
000005a2: a6 de                 cmp b32 $r13 $r14
000005a4: f4 1b 14              bra ne 0x5b8
000005a7: b6 a0 04              add b32 $r10 0x4
000005aa: b6 b0 04              add b32 $r11 0x4
000005ad: b6 c2 04              sub b32 $r12 0x4
000005b0: b3 c4 00 ee         B bra b32 $r12 0x0 ne 0x59e
000005b4: 0a 00                 mov $r10 0x0
000005b6: f8 00                 ret
000005b8: 0a 01               B mov $r10 0x1
000005ba: f8 00                 ret

; again, the floating 0x01 byte...
000005bc: 01

// these xfers are for crypto stuff. size is in bits!

// crypto_reg_store(crypto_reg=r10, local_addr=r11)
000005bd: f4 3c 02           C  cxset 0x2                   ; dma_override(type=crypto_reg, count=2)
000005c0: b6 a4 10              shl b32 $r10 0x10
000005c3: fd ba 05              or $r11 $r10
000005c6: fa bb 05              xdld $r11 $r11
000005c9: f8 03                 xdwait
000005cb: f8 00                 ret

// crypto_reg_load(crypto_reg=r10, local_addr=r11)
// read from local_addr into $cX
000005cd: f4 3c 02           C  cxset 0x2                   ; dma_override(type=crypto_reg, count=2)
000005d0: b6 a4 10              shl b32 $r10 0x10
000005d3: fd ba 05              or $r11 $r10
000005d6: fa bb 06              xdst $r11 $r11
000005d9: f8 03                 xdwait
000005db: f8 00                 ret

// get_type_string(buf=r10, type=r11)
000005dd: c4 a9 0f           C  and $r9 $r10 0xf
000005e0: f4 0b 09              bra e 0x5e9
000005e3: f8 02                 exit                        ; assert r10 16byte aligned
000005e5: 3e e5 05 00         B lbra 0x5e5
000005e9: b3 b0 00 0c         B bra b32 $r11 0x0 e 0x5f5
000005ed: b3 b4 01 3e           bra b32 $r11 0x1 ne 0x62b
000005f1: 3e 10 06 00           lbra 0x610
; if (r11 == 0) { "CODE_SIG_01"
000005f5: d9 43 4f 44 45      B mov $r9 0x45444f43          ; "CODE"
000005fa: a0 a9                 st b32 D[$r10] $r9
000005fc: d9 5f 53 49 47        mov $r9 0x4749535f          ; "_SIG"
00000601: b5 ab 03              st b32 D[$r10+0xc] $r11
00000604: b5 a9 01              st b32 D[$r10+0x4] $r9
00000607: 89 5f 30 31           mov $r9 0x31305f            ; "_01"
0000060b: b5 a9 02              st b32 D[$r10+0x8] $r9
0000060e: f8 00                 ret
; } else if (r11 == 1) { "CODE_ENC_01"
00000610: d9 43 4f 44 45      B mov $r9 0x45444f43          ; "CODE"
00000615: a0 a9                 st b32 D[$r10] $r9
00000617: d9 5f 45 4e 43        mov $r9 0x434e455f          ; "_ENC"
0000061c: b5 a9 01              st b32 D[$r10+0x4] $r9
0000061f: 89 5f 30 31           mov $r9 0x31305f            ; "_01"
00000623: b5 a9 02              st b32 D[$r10+0x8] $r9
00000626: bd 94                 clear b32 $r9
00000628: b5 a9 03              st b32 D[$r10+0xc] $r9
; }
0000062b: f8 00               B ret

; unused function?
0000062d: f9 02                 mpush $r0
0000062f: b2 b0                 mov b32 $r0 $r11
00000631: b2 ab                 mov b32 $r11 $r10
00000633: 0a 04                 mov $r10 0x4
00000635: 7e cd 05 00           lcall 0x5cd                 ; crypto_reg_load(crypto_reg=$c4, local_addr=r11)
00000639: b3 00 00 08           bra b32 $r0 0x0 e 0x641
0000063d: b3 04 03 08           bra b32 $r0 0x3 ne 0x645
00000641: f5 3c 44 c8         B ckexp $c4 $c4
00000645: fb 01               B mpopret $r0

// key_init(hdr_type=r10, key_type=r11)
// hdr_type 0: CODE_SIG, 1: CODE_ENC
// key_type 0: ENCRYPT, 1: DECRYPT
// leaves the key material in c4, then other funcs use that
00000647: 09 f0                 mov $r9 -0x10
00000649: f4 30 e0              add $sp -0x20
0000064c: f9 12                 mpush $r1
0000064e: b2 b1                 mov b32 $r1 $r11
00000650: fe 40 01              mov $r0 $sp
00000653: b2 ab                 mov b32 $r11 $r10           ; r11 = hdr_type
00000655: 90 00 18              add b32 $r0 $r0 0x18
00000658: fd 09 04              and $r0 $r9                 ; r0 = (sp + 0x18) & ~0xf
0000065b: b2 0a                 mov b32 $r10 $r0
0000065d: 7e dd 05 00           lcall 0x5dd                 ; get_type_string(buf=r0{local}, type=hdr_type)
00000661: b2 0b                 mov b32 $r11 $r0
00000663: bd a4                 clear b32 $r10
00000665: 7e cd 05 00           lcall 0x5cd                 ; crypto_reg_load(crypto_reg=$c0, r0{local})
00000669: f5 3c 61 c2           csecret $c1 0x26            ; c1 = hw_secrets[0x26]. whatever that means! kfuse data?
0000066d: f5 3c 01 c4           ckeyreg $c1                 ; ACTIVE_KEY_REG = c1
00000671: f5 3c 01 d0           cenc $c1 $c0                ; c1 = Aes128EcbEncrypt(key=c1, data=c0)
00000675: f5 3c 11 dc           csigenc $c1 $c1             ; c1 = EncryptSig(key=c1) this the sig supplied when our code page was auth'd (info.field_20)
00000679: f5 3c 14 84           cmov $c4 $c1                ; c4 = c1
0000067d: b3 10 00 08           bra b32 $r1 0x0 e 0x685     ; check key_type
00000681: f5 3c 44 c8           ckexp $c4 $c4               ; do key expansion if DEC, else skip
00000685: fb 15 20            B mpopaddret $r1 0x20

// crypto_688(buf=r10, in_word=r11)
00000688: f9 02                 mpush $r0
0000068a: bd 94                 clear b32 $r9
0000068c: b2 a0                 mov b32 $r0 $r10
0000068e: a0 a9                 st b32 D[$r10] $r9
00000690: b5 a9 02              st b32 D[$r10+0x8] $r9
00000693: b5 a9 01              st b32 D[$r10+0x4] $r9
00000696: 7d b3                 hswap b16 $r11
00000698: bd b3                 hswap b32 $r11
0000069a: 7d b3                 hswap b16 $r11
0000069c: b5 ab 03              st b32 D[$r10+0xc] $r11     ; r10 = 0 || 0 || 0 || bswap32(in_word)
0000069f: 0a 03                 mov $r10 0x3                ; input num_bytes = 4
000006a1: b2 0b                 mov b32 $r11 $r0
000006a3: 7e cd 05 00           lcall 0x5cd                 ; crypto_reg_load(crypto_reg=$c3, local_addr=buf)
000006a7: f5 3c 04 c4           ckeyreg $c4
000006ab: f5 3c 35 d0           cenc $c5 $c3                ; c5 = Aes128EcbEncrypt(key=c4, data=c3)
000006af: 0a 05                 mov $r10 0x5                ; output num_bytes = 16
000006b1: b2 0b                 mov b32 $r11 $r0
000006b3: 7e bd 05 00           lcall 0x5bd                 ; crypto_reg_store(crypto_reg=$c5, local_addr=buf)
000006b7: fb 01                 mpopret $r0

// crypto_6b9(buf_in=r10, buf_in_len=r11, iv=r12, buf_out=r13, mode=r14, some_bool=[sp])
000006b9: f9 52                 mpush $r5
000006bb: b2 b3                 mov b32 $r3 $r11
000006bd: b2 d4                 mov b32 $r4 $r13
000006bf: b2 cb                 mov b32 $r11 $r12
000006c1: b2 e2                 mov b32 $r2 $r14
000006c3: b4 50 07              ld b32 $r5 D[$sp+0x1c]
000006c6: b2 a1                 mov b32 $r1 $r10
000006c8: b2 d0                 mov b32 $r0 $r13
000006ca: b3 34 00 0a           bra b32 $r3 0x0 ne 0x6d4    ; assert buf_in_len != 0
000006ce: f8 02                 exit
000006d0: 3e d0 06 00         B lbra 0x6d0
000006d4: c4 39 0f            B and $r9 $r3 0xf
000006d7: f4 0b 09              bra e 0x6e0                 ; assert (buf_in_len & 0xf) == 0
000006da: f8 02                 exit
000006dc: 3e dc 06 00         B lbra 0x6dc
000006e0: c4 a9 0f            B and $r9 $r10 0xf
000006e3: f4 0b 09              bra e 0x6ec                 ; assert (buf_in & 0xf) == 0
000006e6: f8 02                 exit
000006e8: 3e e8 06 00         B lbra 0x6e8
000006ec: c4 49 0f            B and $r9 $r4 0xf
000006ef: f4 0b 09              bra e 0x6f8                 ; assert (buf_out & 0xf) == 0
000006f2: f8 02                 exit
000006f4: 3e f4 06 00         B lbra 0x6f4
000006f8: b3 b0 00 0e         B bra b32 $r11 0x0 e 0x706    ; if present, use 16byte iv
set_iv_to_input:
000006fc: 0a 05                 mov $r10 0x5
000006fe: 7e cd 05 00           lcall 0x5cd                 ; crypto_reg_load(crypto_reg=$c5, local_addr=iv)
00000702: 3e 0a 07 00           lbra 0x70a
set_iv_zero:
00000706: f5 3c 55 ac         B cxor $c5 $c5
set_active_key_c4:
0000070a: f5 3c 04 c4         B ckeyreg $c4
mode_dispatch:
0000070e: b3 20 02 50           bra b32 $r2 0x2 e 0x75e
00000712: b0 26 02              cmp b32 $r2 0x2
00000715: f4 0c 10              bra a 0x725
00000718: b3 20 00 2a           bra b32 $r2 0x0 e 0x742
0000071c: b3 2d 01 6a 01        bra b32 $r2 0x1 ne 0x886
00000721: 3e 32 07 00           lbra 0x732
00000725: b3 20 03 5d         B bra b32 $r2 0x3 e 0x782
00000729: b3 2d 04 5d 01        bra b32 $r2 0x4 ne 0x886
0000072e: 3e 72 07 00           lbra 0x772
00000732: f5 3c 40 94         B cs0begin 0x4
00000736: f5 3c 03 88           cxsin $c3
0000073a: f5 3c 53 ac           cxor $c3 $c5
0000073e: 3e 7a 07 00           lbra 0x77a
00000742: f5 3c 50 94         B cs0begin 0x5
00000746: f5 3c 03 88           cxsin $c3
0000074a: f5 3c 32 d4           cdec $c2 $c3
0000074e: f5 3c 25 ac           cxor $c5 $c2
00000752: f5 3c 05 8c           cxsout $c5
00000756: f5 3c 35 84           cmov $c5 $c3
0000075a: 3e 75 08 00           lbra 0x875
0000075e: f5 3c 30 94         B cs0begin 0x3
00000762: f5 3c 03 88           cxsin $c3
00000766: f5 3c 35 ac           cxor $c5 $c3
0000076a: f5 3c 55 d0           cenc $c5 $c5
0000076e: 3e 75 08 00           lbra 0x875
00000772: f5 3c 30 94         B cs0begin 0x3
00000776: f5 3c 03 88           cxsin $c3
0000077a: f5 3c 35 d0         B cenc $c5 $c3
0000077e: 3e 8e 07 00           lbra 0x78e
00000782: f5 3c 30 94         B cs0begin 0x3
00000786: f5 3c 03 88           cxsin $c3
0000078a: f5 3c 35 d4           cdec $c5 $c3
0000078e: f5 3c 05 8c         B cxsout $c5
00000792: 3e 75 08 00           lbra 0x875
00000796: 95 3f 04            B shr b32 $r15 $r3 0x4
00000799: b0 f6 10              cmp b32 $r15 0x10
0000079c: f4 0d 05              bra be 0x7a1
0000079f: 0f 10                 mov $r15 0x10
000007a1: 92 f9 01            B sub b32 $r9 $r15 0x1
000007a4: fd 9f 04              and $r9 $r15
000007a7: f4 0b 05              bra e 0x7ac
000007aa: 0f 01                 mov $r15 0x1
000007ac: 94 fe 04            B shl b32 $r14 $r15 0x4
000007af: ff 01 f5              or $r15 $r0 $r1
000007b2: 92 e9 01              sub b32 $r9 $r14 0x1
000007b5: fd 9f 04              and $r9 $r15
000007b8: f4 1b 68              bra ne 0x820
000007bb: b3 e0 40 41           bra b32 $r14 0x40 e 0x7fc
000007bf: b1 e6 40 00           cmp b32 $r14 0x40
000007c3: f4 0c 0b              bra a 0x7ce
000007c6: b3 e4 20 5a           bra b32 $r14 0x20 ne 0x820
000007ca: 3e 0e 08 00           lbra 0x80e
000007ce: b3 ea 80 00 1c      B bra b32 $r14 0x80 e 0x7ea
000007d3: b3 ee 00 01 4d        bra b32 $r14 0x100 ne 0x820
000007d8: b2 1f                 mov b32 $r15 $r1
000007da: f0 f3 06              sethi $r15 0x60000
000007dd: b2 09                 mov b32 $r9 $r0
000007df: f0 93 06              sethi $r9 0x60000
000007e2: f5 3c 00 99           cs0exec 0x10
000007e6: 3e 30 08 00           lbra 0x830
000007ea: b2 1f               B mov b32 $r15 $r1
000007ec: f0 f3 05              sethi $r15 0x50000
000007ef: b2 09                 mov b32 $r9 $r0
000007f1: f0 93 05              sethi $r9 0x50000
000007f4: f5 3c 80 98           cs0exec 0x8
000007f8: 3e 30 08 00           lbra 0x830
000007fc: b2 1f               B mov b32 $r15 $r1
000007fe: f0 f3 04              sethi $r15 0x40000
00000801: b2 09                 mov b32 $r9 $r0
00000803: f0 93 04              sethi $r9 0x40000
00000806: f5 3c 40 98           cs0exec 0x4
0000080a: 3e 30 08 00           lbra 0x830
0000080e: b2 1f               B mov b32 $r15 $r1
00000810: f0 f3 03              sethi $r15 0x30000
00000813: b2 09                 mov b32 $r9 $r0
00000815: f0 93 03              sethi $r9 0x30000
00000818: f5 3c 20 98           cs0exec 0x2
0000081c: 3e 30 08 00           lbra 0x830
00000820: b2 1f               B mov b32 $r15 $r1
00000822: f0 f3 02              sethi $r15 0x20000
00000825: b2 09                 mov b32 $r9 $r0
00000827: f0 93 02              sethi $r9 0x20000
0000082a: f5 3c 10 98           cs0exec 0x1
0000082e: 0e 10                 mov $r14 0x10
00000830: b3 54 01 0b         B bra b32 $r5 0x1 ne 0x83b
00000834: f4 3c a1              cxset 0xa1                      ; dma_override(type=0b101, count=1)
00000837: 3e 3e 08 00           lbra 0x83e
0000083b: f4 3c 21            B cxset 0x21                      ; dma_override(type=0b001, count=1)
0000083e: fa ff 06            B xdst $r15 $r15
00000841: b3 20 02 1e           bra b32 $r2 0x2 e 0x85f
00000845: b3 54 01 0b           bra b32 $r5 0x1 ne 0x850
00000849: f4 3c a2              cxset 0xa2                      ; dma_override(type=0b101, count=2)
0000084c: 3e 53 08 00           lbra 0x853
00000850: f4 3c 22            B cxset 0x22                      ; dma_override(type=0b001, count=2)
00000853: fa 99 05            B xdld $r9 $r9
00000856: f8 03                 xdwait
00000858: bc 0e 00              add b32 $r0 $r0 $r14
0000085b: 3e 6f 08 00           lbra 0x86f
0000085f: b3 54 01 0b         B bra b32 $r5 0x1 ne 0x86a
00000863: f4 3c a1              cxset 0xa1                      ; dma_override(type=0b101, count=1)
00000866: 3e 6d 08 00           lbra 0x86d
0000086a: f4 3c 21            B cxset 0x21                      ; dma_override(type=0b001, count=1)
0000086d: f8 03               B xdwait
0000086f: bc 1e 10            B add b32 $r1 $r1 $r14
00000872: bb 3e 02              sub b32 $r3 $r14
00000875: b3 3d 00 21 ff      B bra b32 $r3 0x0 ne 0x796
0000087a: b3 24 02 0c           bra b32 $r2 0x2 ne 0x886
0000087e: b2 4b                 mov b32 $r11 $r4
00000880: 0a 05                 mov $r10 0x5
00000882: 7e bd 05 00           lcall 0x5bd                     ; crypto_reg_store(crypto_reg=$c5, local_addr=r11)
00000886: fb 51               B mpopret $r5

// memcpy_d2i(src=r10, len=r11, dst=r12)
00000888: f9 32                 mpush $r3
0000088a: 4d 00 61              mov $r13 0x6100                 ; CODE
0000088d: 4e 00 62              mov $r14 0x6200                 ; CODE_VIRT_ADDR
00000890: 3e bb 08 00           lbra 0x8bb
00000894: c4 c0 ff            B and $r0 $r12 0xff
00000897: f4 0b 2a              bra e 0x8c1
0000089a: 98 a0 00            B ld b32 $r0 D[$r10]
0000089d: 98 a1 01              ld b32 $r1 D[$r10+0x4]
000008a0: 98 a2 02              ld b32 $r2 D[$r10+0x8]
000008a3: 98 a3 03              ld b32 $r3 D[$r10+0xc]
000008a6: f6 d0 00              iowr I[$r13] $r0
000008a9: f6 d1 00              iowr I[$r13] $r1
000008ac: f6 d2 00              iowr I[$r13] $r2
000008af: f6 d3 00              iowr I[$r13] $r3
000008b2: b6 a0 10              add b32 $r10 0x10
000008b5: b6 b2 10              sub b32 $r11 0x10
000008b8: b6 c0 10              add b32 $r12 0x10
000008bb: b3 b4 00 d9         B bra b32 $r11 0x0 ne 0x894
000008bf: fb 31                 mpopret $r3
000008c1: 95 c0 08            B shr b32 $r0 $r12 0x8
000008c4: f6 e0 00              iowr I[$r14] $r0
000008c7: 3e 9a 08 00           lbra 0x89a


; encrypted secure_payload
00000a00  ca e0 f8 6a d9 72 ba 7b  eb 25 b3 a8 42 98 75 38  |...j.r.{.%..B.u8|
00000a10  25 ed 0c 3f 80 56 22 01  8a 19 87 8a fe 6d 9e 4f  |%..?.V"......m.O|
00000a20  dd 80 11 e9 5a 48 40 c4  1a ed c2 7f ed 7f b3 43  |....ZH@........C|
00000a30  14 bb 58 53 09 2a 45 12  3a b4 b6 44 6a ef 19 1b  |..XS.*E.:..Dj...|
00000a40  55 2a 99 69 68 06 5c 1b  ab df c2 00 ee a9 e1 b0  |U*.ih.\.........|
00000a50  c6 aa 5c 54 3a 5d 7d 7d  79 30 50 69 b5 61 be 1d  |..\T:]}}y0Pi.a..|
00000a60  6f a3 2e ec f1 95 c6 6d  62 22 43 5c 36 a4 0e c4  |o......mb"C\6...|
00000a70  94 e6 37 4e 41 80 d4 6f  27 80 1c 5e 94 cb d6 d4  |..7NA..o'..^....|
00000a80  6d f7 0f 26 68 6c fa d7  b4 63 23 f7 19 2f b5 49  |m..&hl...c#../.I|
00000a90  87 13 59 d0 be 85 00 9e  1e 7e 07 50 1b bc 5c ec  |..Y......~.P..\.|
00000aa0  2b 1a 81 62 02 f3 83 58  cd 9a b4 7a 1e 11 94 3e  |+..b...X...z...>|
00000ab0  bf c6 69 fa 41 91 76 c3  4d a4 4e 1a d5 bf 29 37  |..i.A.v.M.N...)7|
00000ac0  36 74 8d 1c 54 89 ec 47  45 3e 20 f4 69 96 25 fe  |6t..T..GE> .i.%.|
00000ad0  74 83 64 44 7a 4c 83 a9  b3 c2 1a 1c a9 59 e9 99  |t.dDzL.......Y..|
00000ae0  89 ba a3 8f ed 26 b6 f7  a2 f4 b2 a7 f0 ab ea 7f  |.....&..........|
00000af0  ea d2 55 0c 64 36 7b 23  c2 a6 69 86 d4 a3 93 33  |..U.d6{#..i....3|
00000b00  a3 7e 39 ad 6e a3 72 76  b4 b0 82 b4 67 30 8a 48  |.~9.n.rv....g0.H|
00000b10  9e dc 94 b3 29 85 22 ca  7b 55 24 33 9c 01 a2 0d  |....).".{U$3....|
00000b20  10 06 6f f9 01 7a 23 0f  99 37 9f bb 2b a4 3a fc  |..o..z#..7..+.:.|
00000b30  a4 a0 d5 06 b4 e0 61 44  01 48 b0 75 3f ac a1 25  |......aD.H.u?..%|
00000b40  4b 85 bf b7 a4 21 f2 82  09 2e 72 61 c7 69 92 11  |K....!....ra.i..|
00000b50  a6 81 2f e1 c2 e8 aa 41  da 62 a7 a0 6c be 74 a6  |../....A.b..l.t.|
00000b60  cb 9b 20 c4 65 ed fb 3c  3d d0 9a c8 4e b0 91 f5  |.. .e..<=...N...|
00000b70  a9 3f e0 9c 7b 6f 21 78  d2 4f 50 a5 b0 61 05 93  |.?..{o!x.OP..a..|
00000b80  b0 4b 60 98 6d 56 cc 07  a7 d5 61 4c d2 9e 23 0f  |.K`.mV....aL..#.|
00000b90  cd d9 bf c8 ef 05 a9 24  72 6d 50 7f f7 be fd bf  |.......$rmP.....|
00000ba0  7c a5 df 4d bb 8d f3 05  2a 48 40 88 34 6d e7 d0  ||..M....*H@.4m..|
00000bb0  03 b7 4b b1 db df 09 ff  20 94 c0 3b 10 01 ed c8  |..K..... ..;....|
00000bc0  a9 d2 0f cd 1d 06 92 0f  4c cc 01 c0 e8 d3 d3 5b  |........L......[|
00000bd0  34 2e 61 20 dd 2c 09 83  6a e3 3b b4 6d d5 34 99  |4.a .,..j.;.m.4.|
00000be0  7b 22 cd 23 e1 f1 a9 c4  50 07 31 fd 97 18 54 3f  |{".#....P.1...T?|
00000bf0  ba 88 70 27 89 b4 74 5a  36 bf 73 37 d3 bc 00 86  |..p'..tZ6.s7....|
00000c00  14 8d 3d e1 30 ac af 6d  72 df 9a 28 b0 98 e7 89  |..=.0..mr..(....|
00000c10  55 88 60 55 48 a1 ef f9  79 15 6b f0 db 48 4b 9f  |U.`UH...y.k..HK.|
00000c20  6a ca 6c 29 a2 0c 97 e7  12 05 76 e5 a2 8e ed 15  |j.l)......v.....|
00000c30  83 14 28 39 fc 98 fa 5d  1d 76 9b ab 3f 06 ea 43  |..(9...].v..?..C|
00000c40  f8 09 d5 d6 a2 85 ff 5f  5f d0 41 60 68 9a 76 23  |.......__.A`h.v#|
00000c50  af 18 b8 76 16 3a 8c 83  cd bc 06 5a a7 44 8f 86  |...v.:.....Z.D..|
00000c60  54 73 e4 11 d0 2b 34 85  34 b2 ee 07 83 a1 57 e8  |Ts...+4.4.....W.|
00000c70  0b 83 a6 7d bc 4b 2f ca  5e a0 21 ed 82 6a bd 8d  |...}.K/.^.!..j..|
00000c80  91 a6 79 5b 53 d8 09 0d  6a d8 7d f9 2f 0c a2 71  |..y[S...j.}./..q|
00000c90  98 37 c7 93 b9 fc 0c af  24 29 66 cc a9 ed 8a 2b  |.7......$)f....+|
00000ca0  cf e2 5a f1 95 ec 27 95  79 2b b3 e5 7b 14 99 a3  |..Z...'.y+..{...|
00000cb0  ca be 0f fe 37 c6 04 a4  61 6f 2a 0f 90 d3 f9 88  |....7...ao*.....|
00000cc0  c3 0c e2 bc 09 20 47 19  53 89 1e e6 18 01 02 51  |..... G.S......Q|
00000cd0  43 ea b3 c0 04 4f a2 53  74 30 41 2b ec c6 54 4f  |C....O.St0A+..TO|
00000ce0  65 50 fa 6c f7 d9 bf 45  7c 23 8f 38 3a 89 e0 fd  |eP.l...E|#.8:...|
00000cf0  d4 48 71 b8 86 65 87 42  01 00 23 22 b6 e7 08 c3  |.Hq..e.B..#"....|
00000d00  1a c8 c1 4e 69 78 6d 68  c5 9f de 64 85 be 94 52  |...Nixmh...d...R|
00000d10  fb 7c c5 9e 97 4a e3 aa  2c 6c 49 e6 5d af 1e 2c  |.|...J..,lI.]..,|
00000d20  a0 29 3c bb f4 94 5f fb  66 49 e7 30 0e 6b e0 9d  |.)<..._.fI.0.k..|
00000d30  bf 28 25 e8 97 8f 17 c4  87 f3 95 ce be ad 87 b9  |.(%.............|
00000d40  d1 e4 0c 9e 12 cf f9 9c  ac 8c 6f 77 2a 8a b5 b3  |..........ow*...|
00000d50  19 7c ea bf b8 c4 0e e0  0f 73 8a 34 8d a9 73 2d  |.|.......s.4..s-|
00000d60  97 cd 72 51 43 82 e9 bc  af 4c 34 6c b9 3e ec cc  |..rQC....L4l.>..|
00000d70  e3 e5 7d 06 72 a8 8e ee  2c 11 a1 40 eb 20 0f 77  |..}.r...,..@. .w|
00000d80  b1 27 b4 3b 82 b6 ec 85  f6 5b 35 48 e4 46 51 e5  |.'.;.....[5H.FQ.|
00000d90  2a 31 ce e0 e8 04 04 91  d0 db 46 a0 57 19 26 02  |*1........F.W.&.|
00000da0  d6 c6 dd b9 ce c6 cf bf  55 e5 35 96 71 d3 9c d5  |........U.5.q...|
00000db0  e0 a7 ec fe e3 8b 7d 9a  28 32 b5 b0 b7 c8 21 38  |......}.(2....!8|
00000dc0  9e 6e be 08 6f 62 cd ee  b0 a1 79 e2 a0 2b 6e 22  |.n..ob....y..+n"|
00000dd0  6b 2b 00 7f 49 57 4a 23  28 99 75 b5 40 01 c7 9f  |k+..IWJ#(.u.@...|
00000de0  2d e7 85 1b 5a 4e 55 3c  ce d6 c7 7b 85 ec 52 ff  |-...ZNU<...{..R.|
00000df0  23 87 38 cf 44 e1 95 bb  9c e5 44 05 3d f6 f1 85  |#.8.D.....D.=...|
00000e00  78 a3 7d 19 44 43 3b d4  fa db 9a bd 81 85 34 fd  |x.}.DC;.......4.|
00000e10  ba c7 9b f5 96 1a 73 c1  46 7c 39 ec 37 56 81 ca  |......s.F|9.7V..|
00000e20  0e a9 30 7f 82 3c f7 49  85 c7 dc 2e 34 50 78 c1  |..0..<.I....4Px.|
00000e30  da d5 d2 14 b6 d3 05 5c  be b8 d9 ca 96 bd 52 8b  |.......\......R.|
00000e40  c1 40 41 56 14 0f 16 aa  3e 07 bb 52 d5 1a 94 0f  |.@AV....>..R....|
00000e50  5f 53 e5 46 dc 12 b1 74  05 70 6b 16 17 a7 94 ec  |_S.F...t.pk.....|
00000e60  c0 5f 5e 0e 88 b1 45 e2  08 19 63 e4 b9 12 0d ca  |._^...E...c.....|
00000e70  05 8d 42 64 39 37 3d fb  5c d1 d3 27 e7 c5 67 1b  |..Bd97=.\..'..g.|
00000e80  21 61 64 5e a1 5f 6e f6  7b a1 bd 37 35 3a 3a 90  |!ad^._n.{..75::.|
00000e90  8e 65 78 e9 4b 04 87 6f  fa be 82 63 12 5d 46 b8  |.ex.K..o...c.]F.|
00000ea0  32 f8 10 67 f9 4b 1f 40  33 f4 b6 50 2d 76 34 bc  |2..g.K.@3..P-v4.|
00000eb0  e3 49 ee a7 d5 84 31 4e  f3 1e 5b d8 a4 8a f5 12  |.I....1N..[.....|
00000ec0  80 1a 4b c6 ab 16 31 48  23 ea 1b 72 c4 f6 7c 2d  |..K...1H#..r..|-|
00000ed0  ef 7a e7 54 65 cc 19 9f  f6 7a fe d7 35 de d7 ab  |.z.Te....z..5...|
00000ee0  94 7f 7e 5e 47 2a 19 78  95 e2 91 37 bd c9 e9 e9  |..~^G*.x...7....|
00000ef0  9f 55 b8 00 69 c2 b3 50  ea b4 f9 48 f3 d2 78 c6  |.U..i..P...H..x.|