============================================ The EL3 "Sec" monitor and the EL1 hand-off ============================================ PH12 of ``xbl_a.bin`` is not ordinary code. It loads at ``0x14950000`` with the distinctive PT_LOAD flags ``0x05000005``, and its entry at ``0x14953000`` installs an EL3 exception environment. This is the same feature set — vaddr, flags, entry offset, ``SCR_EL3``/``VBAR_EL3`` install — as the Firehose programmer's nested EL3 monitor that :doc:`/edl/firehose` proves is signature-enforced **unconditionally**. The segment also carries the ``XBL Sec Attestation Root CA 4`` string (file ``0x2beb87``). The consequence for replacing XBL (you can substitute the EL1/UEFI payload but not clean-room this EL3 root) is analysed in :doc:`/boot/boot-chain`. The ELF ``e_entry`` (``0x14817908``, in PH2) is *not* this monitor — it is the **EL1** XBLCore reset stub the monitor ``eret``\ s down to. On this SoC XBLCore executes at EL1 and reaches EL3 through a single ``smc #0`` at ``0x14699024`` (PH7). EL3 "Sec" monitor — entry disassembly ======================================= PH12 is not laid out like the other code segments: its **first ``0x3000`` bytes are a literal/data header**, and executable code only begins at ``0x14953000`` (``= 0x14950000 + 0x3000``, file ``0x2c17a0``). A naive *linear* disassembly of the segment from ``0x14950000`` desyncs on that header and never re-aligns to the real instructions — which is why a whole-image sweep can wrongly report "no ``scr_el3``". Disassembled from ``0x14953000`` the monitor's EL3 set-up is explicit (all instructions below are Capstone ``CS_ARCH_ARM64`` output):: 0x14953000: mov x9, x0 ; stash PBL boot-arg 0x14953004: adr x0, #0x14953000 ; self-position check: 0x14953008: ldr x1, #0x149531a8 ; literal[0x149531a8] = 0x14953000 0x1495300c: cmp x0, x1 0x14953010: b.ne #0x14953010 ; hang if not loaded where expected 0x14953014: mov x0, #0xe00 0x14953018: msr scr_el3, x0 ; SCR_EL3 = 0xE00 (RW|ST|SIF, still Secure) 0x1495301c: isb 0x14953020: ldr x0, #0x149531b0 ; literal = 0x14964800 0x14953024: msr vbar_el3, x0 ; VBAR_EL3 = 0x14964800 (EL3 vectors) 0x14953028: mov x0, #0x1c0 0x1495302c: msr daif, x0 ; mask A/I/F 0x14953030: ldr x10, #0x149531b8 ; = 0x14967080 0x14953034: str x9, [x10] ; persist boot-arg 0x1495303c: movk x0, #0x1495, lsl #16 0x14953044: bl #0x14961488 ; relocate/copy (memcpy), sp = 0x14953000 ... 0x14953070: bl #0x1495313c ; sanitise EL1 register file (see below) 0x14953074: bl #0x149563d4 ; loader dispatch -> x0 = EL1 entry 0x14953078: msr elr_el3, x0 ; ELR_EL3 = computed EL1 entry point 0x1495307c: mrs x0, hcr_el2 0x14953084: orr x0, x0, #0x80000000 0x14953088: msr hcr_el2, x0 ; HCR_EL2.RW = 1 (EL1 is AArch64) 0x1495308c: mov x0, #0x1c5 0x14953090: msr spsr_el3, x0 ; SPSR_EL3 = 0x1C5 (return to EL1h, A/I/F masked) 0x14953094: mrs x0, scr_el3 0x14953098: orr x0, x0, #1 0x1495309c: msr scr_el3, x0 ; SCR_EL3 |= NS -> 0xE01 (Non-secure) ... ; x0..x30 cleared to 0 0x14953128: ldr x0, #0x149531d8 ; = 0x14967088 0x1495312c: ldr x0, [x0] ; x0 = boot-info pointer for EL1 0x14953130: dsb sy 0x14953134: isb 0x14953138: eret ; drop to EL1h at ELR_EL3 So the "Sec" monitor is the piece that **owns EL3** and then hands control *down* to EL1: it installs ``VBAR_EL3``, sets ``SCR_EL3`` (Secure ``0xE00`` while it runs, then flips the ``NS`` bit to ``0xE01`` just before returning), masks exceptions via ``DAIF``, then ``eret``\ s with ``SPSR_EL3 = 0x1C5`` (EL1h) and ``HCR_EL2.RW = 1``. The ``eret`` target (``ELR_EL3``) is **computed at run time** by the loader-dispatch routine ``0x149563d4`` (it walks two tables of loader callbacks at ``0x149617e8`` (6) and ``0x149617c0`` (5), tailing into ``0x14958c00``); the XBLCore EL1 entry ``0x14817908`` appears in the whole image **only** at file ``0x18`` (the ELF header ``e_entry`` field), never as an in-code constant, consistent with a computed jump. ``x0`` is loaded with the boot-info pointer ``0x14967088`` per the AArch64 SMCCC/boot convention. Before the ``eret`` the monitor scrubs the EL1 context it is about to enter (``0x1495313c``) — every EL1 system register is written with ``xzr``:: 0x1495313c: msr actlr_el1, xzr 0x14953148: msr contextidr_el1, xzr 0x1495314c: msr amair_el1, xzr 0x14953158: msr mair_el1, xzr 0x14953160: msr sctlr_el1, xzr 0x14953170: msr tcr_el1, xzr 0x14953178: msr ttbr1_el1, xzr 0x1495317c: msr ttbr0_el1, xzr 0x14953184: msr vbar_el1, xzr 0x14953190: msr elr_el1, xzr 0x14953194: msr sp_el1, xzr 0x1495319c: msr spsr_el1, xzr ... (and afsr0/1, far, csselr, esr, par, tpidr*, cntp_ctl/tval/cval, sp_el0) **EL3 state the monitor does establish** (confirmed by disassembly from ``0x14953000``): .. list-table:: :header-rows: 1 :widths: 20 20 60 * - Register - Value / action - Where * - ``SCR_EL3`` - ``0xE00`` then ``|=NS`` → ``0xE01`` - ``0x14953018`` / ``0x1495309c`` (6 writes total) * - ``VBAR_EL3`` - ``0x14964800`` - ``0x14953024`` * - ``SPSR_EL3`` - ``0x1C5`` (EL1h, A/I/F masked) - ``0x14953090`` * - ``ELR_EL3`` - computed EL1 entry - ``0x14953078`` * - ``HCR_EL2.RW`` - ``1`` (EL1 AArch64) - ``0x14953088`` * - ``DAIF`` - ``0x1C0`` (mask A/I/F) - ``0x1495302c`` * - ``ESR_EL3`` / ``TPIDR_EL3`` - read in the EL3 handlers - ``0x14961510`` / ``0x14959f2c`` **What it does *not* touch:** there is no write to ``CPTR_EL3``, ``MDCR_EL3`` or ``SCTLR_EL3`` (and no ``MAIR_EL3``/``TCR_EL3``/``TTBR0_EL3``) in PH12 — the EL3 monitor runs with the **EL3 MMU off / flat** and does not reprogram FP-trap or debug at EL3. (Those EL3-MMU registers *do* appear in the image, but only in the EL-agnostic boot-MMU helpers reached from the EL1 XBLCore path — see `EL1 XBLCore reset stub`_.) This entry is byte-for-byte the same shape as the TZ ``tz_a`` EL3 stub at ``0x14680000`` (``mov x0,#0xe00; msr scr_el3``; ``mov x0,#0x1c0; msr daif``) documented in :doc:`/trustzone/secure-world`. The **EL3 vector table** at ``VBAR_EL3 = 0x14964800`` is a standard 16×``0x80`` AArch64 table. The Current-EL synchronous slots tail into a common handler at ``0x149614a8`` (which builds a ``0x130``-byte frame and saves ``ELR_EL3``); the **Lower-EL AArch64 synchronous** slot (``+0x400``, ``0x14964c00``) is the **SMC/lower-EL dispatcher** — it re-asserts ``SCR_EL3 = 0xe00`` (Secure) and builds a ``0x110``-byte frame including ``SP_EL0``/``SP_EL1`` before branching to ``0x14961558``:: 0x14964c00: stp x0, x1, [sp, #-0x10]! 0x14964c04: mrs x0, scr_el3 0x14964c08: mov x1, #0xe00 0x14964c0c: msr scr_el3, x1 ; handle call in Secure state ... 0x14964c70: b #0x14961558 This is the EL3 end of the SMC path whose EL1 caller is the gateway below. EL1 XBLCore reset stub ======================= The monitor's ``eret`` lands in the **EL1** XBLCore reset stub — the ELF ``e_entry`` ``0x14817908`` (PH2, file ``0x3908``). It manipulates only the **EL1** copies of the control registers:: 0x14817908: b #0x14817910 ; over a udf padding word 0x14817920: mov x7, x0 ; save boot-arg 0x14817928: mov sp, x0 ; sp = 0 0x14817934: tlbi vmalle1 0x14817938: ic iallu 0x14817944: mrs x0, sctlr_el1 0x14817948: and x0, x0, #~1 ; M=0 (MMU off) 0x1481794c: and x0, x0, #~4 ; C=0 (D-cache off) 0x14817950: orr x0, x0, #8 ; SA=1 (SP-align check) 0x14817954: msr sctlr_el1, x0 0x14817960: ldr x0, #0x148179c0 ; literal = 0x14817000 0x14817964: msr vbar_el1, x0 ; VBAR_EL1 = 0x14817000 (SBL1 vector table) 0x14817968: mrs x0, isr_el1 0x1481796c: and x0, x0, #0x100 ; pending SError? 0x14817970: cbnz x0, #0x148179bc ; -> hang if so 0x14817974: msr daifclr, #4 ; unmask SError (A) 0x1481797c: mov x0, #0xb000 0x14817980: movk x0, #0x1486, lsl #16 0x14817984: mov sp, x0 ; sp = 0x1486b000 0x14817988: mov x0, #0x8000 ; zero .bss 0x14868000..0x1486b000 0x1481798c: movk x0, #0x1486, lsl #16 0x148179a0: stp xzr, xzr, [x0], #0x10 0x148179ac: b.ne #0x148179a0 0x148179b0: mov w0, w7 ; boot-arg 0x148179b4: bl #0x148222b0 ; XBLCore C loader (enables FP via CPACR_EL1) Every control-register write here is the **EL1** encoding (``sctlr_el1``, ``vbar_el1``, ``isr_el1``); there is no ``CurrentEL`` check and no EL3 write — the stub assumes it is already at EL1. ``VBAR_EL1`` points at the **SBL1 vector table** at ``0x14817000`` (16×``0x80``); only the Current-EL-``SPx`` slots are live (Synchronous ``+0x200`` saves all GPRs then ``bl 0x1482bec0``; IRQ ``+0x280``, FIQ ``+0x300``, SError ``+0x380``), while the SP0 and Lower-EL slots are ``b .`` hangs. The synchronous handler's C routine prints ``Exception caught by SBL1 vector table!!`` and dumps ``ELR-EL1``/``ESR-EL1`` (strings at file ``0x6c38``/``0x6c60``) — i.e. EL1 exception reporting, corroborating that XBLCore runs at EL1. A boot-MMU library on this EL1 path *can* program the EL3 MMU: ``0x14818280`` writes ``TTBR0_EL3``\ +\ ``TTBR0_EL1``, ``MAIR_EL3``\ /\ ``MAIR_EL1`` = ``0xAAFFF4884A4F4400``, ``TCR_EL3``/``TCR_EL1`` = ``0x30019`` (T0SZ=25 → 39-bit VA, PS=3 → 42-bit PA), and lays a 4-entry 1 GB-block identity map over 0–4 GB — these six ``_el3``/``_el1`` register pairs are written back-to-back, unconditionally, with no ``CurrentEL`` guard on the ``_el3`` half (no direct ``bl`` caller for ``0x14818280`` was found inside PH2, so its actual call site — and thus whether it in fact runs at EL3 when it executes — is not independently confirmed here). Only the *separate* SCTLR accessor pair, ``0x14817a08`` (getter) / ``0x14817a28`` (setter), which the MMU library also calls to enable ``M|C|I``, is ``CurrentEL``-dispatched: each ``msr sctlr_el3`` there is individually guarded by ``cmp x1,#3 ; b.ge`` on ``CurrentEL`` and only runs at EL3 — that guard does not extend to the TTBR0/MAIR/TCR writes above. **EL1 XBLCore C main.** The reset stub's ``bl 0x148179b4`` reaches the C entry ``0x148222b0`` (in PH2), identified by its two panic sites logging the source file ``sbl1_mc.c`` (file ``0x14818a2f``) with a line number and error code through a function pointer at ``0x1486c1b0``, and by the ``SBL1, Start`` banner (file ``0x14818a6e``). It enables FP/SIMD (``0x14818510``), copies a ``0x180``-byte boot-info block from its argument, runs an init/milestone chain, and polls a debug cookie register before continuing — the SBL1 main-control routine of the EL1 loader. EL1 → EL3 SMC gateway (PH7) ============================= PH7 (``0x14699000``, ``0x1230`` bytes, ``R E``) is the ``scm``/SMC trampoline — and it carries the **only** ``smc`` instruction in the entire image (``0x14699024``; the whole-image scan finds ``smc`` = 1, ``hvc``/``eret`` outside PH12 = 0). This is how the dropped-to-EL1 XBLCore calls back up into the EL3 monitor (and TZ):: 0x14699000: stp x7, x8, [sp, #-0x10]! ; save x7,x8 + x15..x30 (x0..x6 = SMCCC args) 0x14699020: stp x15, x16, [sp, #-0x10]! 0x14699024: smc #0 ; trap to EL3 (SMC64) 0x14699028: cmp x0, #1 0x1469902c: b.eq #0x14699024 ; SMCCC busy/interrupted -> call again ... ; restore x15..x30, x7, x8 0x14699054: cmp x7, #0 ; x7 = result-struct pointer 0x14699058: b.eq #0x14699068 0x1469905c: str x1, [x7] ; write back return values x1..x3 0x14699064: str x3, [x7, #0x10] 0x14699068: ret Callers pass Qualcomm SiP function IDs in ``w0`` (e.g. ``0x82000117`` built by the EL1 exception handler at ``0x1482bf0c``). The ``smc #0`` lands in the EL3 monitor's Lower-EL synchronous vector (``0x14964c00`` above). Together the two ends make the EL3/EL1 split concrete: XBLCore is EL1 code that reaches the resident EL3 "Sec" monitor exclusively through this gateway. Function inventory and call graph ==================================== Beyond the entry/monitor stubs above, the EL3 "Sec" segment (PH12) is a small, self-contained runtime. Following the ``bl``/``blr`` targets out of the entry and the vector handlers yields the call graph below (all addresses are PH12 vaddrs; "file" offsets map through the ELF phdrs, ``0x14950000`` → file ``0x2be7a0``). Function *roles* are inferred from structure only — the image is symbol-free, so no Qualcomm names are assigned. **EL3 monitor support routines** (reached from the entry ``0x14953000``):: 0x14961488 DC-ZVA zero-fill: `lsr x1,x1,#6; dc zva,x0; add x0,#0x40; loop`. The monitor's memset-0. Clears the 0x14950000..0x14953000 EL3 stack from the entry, and scrubs the stack in the SMC top-half. 0x14957558 stack-protected reader whose result is stored to global 0x149660b0 (0xDEADDEAD sentinel in the file image); worker 0x149574a4, logs "(%x)" on canary failure. 0x1495f528 memscpy(dst, dst_size, src, src_size): copies min(sizes) via the bytewise loop 0x149575d0. 0x1495f564 is the matching memset. 0x14965078 cache-info helper (reads ``CLIDR_EL1``); 0x14965178/0x14965190 wrap an I-cache invalidate-all (``ic iallu``). 0x14956560 printf-style logger: parses ``%x``/``%X``/``%s`` using the digit table "0123456789ABCDEF" (file 0x14961819) into a 1 KiB character ring at 0x1496c0b8 (index masked ``& 0x3ff``). The monitor's console; called with a level in ``w0`` and a tag in ``w2``. **EL3 exception path.** The ``VBAR_EL3`` Current-EL synchronous slots inject a code (``#1``/``#5``) and fall into the common handler ``0x149614a8``, which saves a ``0x130`` frame (``x0..x30``, ``ELR_EL3``, ``SPSR_EL3``, ``SP_EL0``/``SP_EL1``, ``ESR_EL3``), forces ``SCR_EL3 = 0xE00`` (saving the old value to ``0x14966f04``), then calls the dump routine ``0x149546a4`` and dead-loops. Any non-SMC EL3 exception therefore terminates here — this is the "``Exception caught``"-style panic sink at EL3. **EL3 SMC path.** The Lower-EL AArch64 synchronous vector (``0x14964c00``) saves a ``0x110`` frame and branches to the top-half ``0x14961558``:: 0x1496155c: mrs x4, esr_el3 0x14961560: ubfx x4, x4, #0x1a, #6 ; EC = ESR_EL3[31:26] 0x14961564: cmp x4, #0x17 ; 0x17 = SMC from AArch64 0x14961568: b.ne #0x14961604 ; not an SMC -> re-dispatch (code #9) to 0x149614a8 0x1496156c: mov x0, sp ; x0 = saved-register frame 0x14961570: bl #0x14957614 ; SMC service dispatcher ... ; restore frame; scrub stack (0x14961488) 0x149615f0: msr scr_el3, x19 ; restore saved SCR_EL3 (from 0x14966f04) 0x14961600: eret ; return to the EL1 caller The dispatcher ``0x14957614`` masks the function ID to ``& 0x3FFFFFFF``, rejects IDs with bits ``[23:16]`` set, then **linearly scans a handler table** whose start and end pointers live at ``0x149660b8``/``0x149660c0``. Each table entry is ``0x10`` bytes — ``{u32 funcID; u32 arg_desc; u64 handler}`` — and on a match the handler pointer at ``entry+8`` is called via ``blr`` with ``x0..x7`` taken from the (validated, copied-in) argument buffer. Pointer arguments are bounds-checked against allowed-region tables by ``0x14953bac`` and ``0x14959208`` before the call. On this image the table is **pre-populated in the file** at ``0x14962cf0..0x14962dd0`` — 14 entries, all in the Qualcomm SiP ``0x0200xxxx`` owner range (verified byte-for-byte through the phdr map):: funcID arg_desc handler funcID arg_desc handler 0x02000114 0x00000022 0x149573a8 0x02000119 0x00000001 0x14957050 0x02000117 0x00000022 0x14953728 0x02000214 0x00008017 0x14955d68 0x02000310 0x00000000 0x14956e38 0x02000215 0x00000446 0x14956060 0x02000210 0x00021048 0x14954d0c 0x0200011a 0x00000001 0x1495475c 0x02000211 0x00000083 0x149558fc 0x02000115 0x00000000 0x14956454 0x02000212 0x00000043 0x14954868 0x02000116 0x00000000 0x14958f84 0x02000213 0x00000043 0x149548fc 0x02000118 0x00000001 0x149538a0 This closes the EL1→EL3 loop: the ``0x82000117`` SiP ID that the EL1 exception handler builds (``0x1482bf0c``, in `EL1 → EL3 SMC gateway (PH7)`_) masks to ``0x02000117`` — the table's second row, handler ``0x14953728``. Handler roles are only partially recoverable without symbols; structurally, handler ``0x14956454`` (ID ``0x02000115``) zero-fills the TZ base ``0x14680000`` (the ``tz_a`` EL3-stub base, :doc:`/trustzone/secure-world`) and a ``0x1C000000`` staging window — consistent with a secure-image handoff transfer. :Source: ``_READONLY/lun1/xbl_a.bin`` — Capstone ``CS_ARCH_ARM64`` disasm of the EL3 "Sec" monitor ``0x14953000``, EL1 XBLCore reset stub ``0x14817908``, EL3 vector table ``0x14964800`` and the ``smc`` gateway ``0x14699000``; full listings in ``artifacts/re_static/item3_xbl_disasm.txt``. :Cross-refs: :doc:`/xbl/index` (segment layout table), :doc:`/boot/boot-chain`, :doc:`/trustzone/secure-world`, :doc:`/edl/firehose`.