3.6. Below UEFI — execution state and hardware register surface

XBL → ABL hand-off contract (bounded) describes the hand-off as seen through the UEFI/DXE protocol layer (BlockIo, RamPartition, PON, ChipInfo, …). This page describes the same moment one level lower: the machine state the SoC is in when XBL’s BDS transfers control to the application in the ABOOT FV — the execution state, reserved memory, and (below) which parts of the picture below the DXE service layer are still knowable only with a live device. The register-level map of the peripheral blocks themselves lives on their own standing pages (pointed to below), not here.

All addresses/IRQs/clock indices are taken from this unit’s own artifacts: the board device tree (artifacts/boot_a/board.dts, i.e. the boot_a DTB), XBL strings/memory-map (_READONLY/lun1/xbl_a.bin), and the parsed GPT. Where a value is a standard programming model rather than a device-specific datum, that is called out.

3.6.1. Execution state at ABL dispatch

Exception level

EL1, non-secure. XBLCore/UEFI runs at EL1: XBL’s own exception handlers print ELR-EL1, ESR-EL1, SCTLR, TTBR0 and log SVC executed in AArch64 (strings in xbl_a.bin). EL2 is occupied by the Qualcomm hypervisor (hyp_a, Open questions and limits of analysis) and EL3 by TZ (tz_a), both established by PBL/SBL before UEFI. So the application in the ABOOT FV is entered at EL1 with EL2/EL3 already owned by Qualcomm firmware — an OS kernel launched from here is likewise entered at EL1 (this SoC does not hand the kernel EL2).

MMU and caches

On. UEFI configures the MMU through EDK2 ArmMmuLib (ArmMmuLibCore.c, ArmConfigureMmu in xbl_a.bin) with a flat, mostly cached mapping; the translation tables live in the XBL working set at MMU PageTables ``0x9FF8D000`` (size ``0x3000``) (XBL memory-map entry, WRITE_BACK_XN). SCTLR_EL1/TTBR0_EL1 are live. Device MMIO windows are mapped UNCACHEABLE/NS_DEVICE (e.g. SMMU 0x15000000); RAM is mapped WRITE_BACK.

Already initialised by XBL (not re-done downstream)

DDR training, the clock trees, PMIC sequencing and the USB/UFS PHYs are all completed inside XBL and are deliberately out of scope of this doc set — XBL stays untouched by anything running in the ABL slot. The UFS host controller is likewise already live (below); GIC-v3 and the architected timer are running too (next).

Interrupt/DAIF and GIC

The GIC-v3 is initialised and the architected timer runs; UEFI services interrupts at EL1. A payload that leaves the UEFI environment inherits this state and is responsible for masking (DAIF) and for any GIC teardown it needs before entering an OS.

Hand-off to an OS kernel (arm64 boot protocol)

The standard contract from this state is: place the FDT and images in the kernel window (below), disable the MMU (SCTLR_EL1.M=0) and clean+invalidate the D-cache, mask interrupts, and branch to the kernel entry with x0 = physical FDT, x1=x2=x3=0, at EL1. Inside UEFI this is what ExitBootServices + the loader’s PreparePlatformHardware perform; the same teardown is required of anything that reaches the kernel without them. The kernel-side view of this ABI is in Kernel/DTB/ramdisk load and the kernel hand-off.

3.6.2. Reserved memory

The DRAM carve-outs a post-XBL payload must not overwrite are enumerated (with exact base/size) from XBL’s own platform memory-map in XBL → ABL hand-off contract (bounded) Q4; the DTB’s own /reserved-memory carve-out map (per-coprocessor windows, QSEE/secure pools, display framebuffers) is decoded in Base kernel device tree (the SoC boot contract). The load window for an OS is Kernel ``0xA2400000`` size ``0x8000000`` (128 MB); the ABOOT FV itself is ``0x9FA00000`` size ``0x200000``. How the actual DRAM base/size is discovered at runtime (the base DTB /memory node is only a placeholder) is under “DRAM discovery without UEFI” in What is fully mapped vs. what still needs a live device below.

3.6.3. Peripheral register surface — delegated

The register windows, clocks, pins and interrupt/IOMMU details of the SoC blocks that stay live at this hand-off point are documented on their own standing pages rather than repeated here, each noting the “state at hand-off” fact where it matters: the UFS host controller in Storage controllers — UFS, ICE, and SDCC, the GENI/QUP serial engines and console UART in QUP/GENI serial-engine bus map (what is wired where) (pin/clock detail also in Pin control — TLMM and the PMIC GPIOs and Clock tree — GCC, domain CCs, RPMh), the GCC/RPMh clock tree in Clock tree — GCC, domain CCs, RPMh, the TLMM/PMIC pin control in Pin control — TLMM and the PMIC GPIOs, and the GIC-v3/PDC/architected -timer and apps-SMMU/GPU-SMMU register windows in Base kernel device tree (the SoC boot contract). What follows is the boundary those pages don’t cover: which parts of that picture are only knowable with a live device.

3.6.4. What is fully mapped vs. what still needs a live device

For operating below the DXE layer, the static-analysis boundary falls in a specific place. Mapped to register fidelity from the dump: the execution state (EL1, MMU/cache, page-table base), every block’s register windows / IRQs / clocks / pins, the reserved-memory map, the partition/LBA layout, and the kernel-entry ABI. What a single static dump cannot fix — the items that separate “have the map” from “have working code”:

Storage read path (UFS)

The register windows are known and the controller is live at hand-off (UEFI keeps EFI_BLOCK_IO functional until ExitBootServices, so the link is up and the device is initialised). But the device-specific init/link-startup sequence is not recoverable statically: XBL does not reference the UFS base 0x1D84000 as a literal (it is formed via ADRP/ADD, so the driver code is not constant-addressable) and the UFS driver is string-sparse (only NumGears). So a register-level reader has two routes, both needing on-device validation — reuse the live controller (submit your own UTRD to the running host controller) or re-run UFS init (the sequence would come from a UFSHCI/ufs-qcom model, not from this device’s images). The PHY itself is already trained by XBL and is not the implementer’s problem.

UFS DMA vs. the SMMU

UFS transfer descriptors are physical/IOVA addresses. The UFS DT node has no iommus property (unlike the QUP nodes), and XBL logs UfsSmmuConfigForOtherBootDev = 1 — so XBL sets up the UFS stream on the apps-SMMU (0x15000000) itself. Whether that stream is left in bypass / identity or an active translation context is not extractable, and a structural decode of devcfg pins down why. devcfg_a is an ELF64/AArch64 image (entry 0x1c003000) carrying a DALProp-style pointer-linked node tree across two domains — 0x1c003000 (the QSEE address space) and 0x805fd000 — its first word is already an absolute node pointer (0x1c003040). The access-control item names are present as a string pool: /ac/smmu + disable_smmu_ac, /ac/xpu + disable_xpu_ac / xpu_silent_log / tlmm_xpu_protection, disable_security_stream_check, OEM_disable_rpmb_autoprovisioning — but each item’s boolean value is reached only through the tree’s relocated node pointers, not stored beside the name (no DALP container signature and no static offset/pointer resolves to a name string). So the values are genuinely not recoverable by static matching — this remains the single most load-bearing unknown for a bare-metal read, settled only by a live probe or a full DALProp graph walk (the container is now identified; Secure world and coprocessors (TZ, HYP, trustlets)).

DRAM discovery without UEFI

The base DTB /memory is empty, so RAM base/size must be read at runtime. Inside UEFI: EFI_RAMPARTITION_Protocol (XBL → ABL hand-off contract (bounded)). Without UEFI: the SMEM region at 0x80900000 (size 0x200000) holds the RAM partition table in the standard Qualcomm SMEM TOC format (XBL validates SMEM_PARTITION_HEADER_ID, toc->version == 1, num_entriesSMEM_MAX_PARTITIONS). The format is known; the values are runtime (SMEM is SRAM/DDR, not in any flash dump).

Left-state of non-boot blocks

The exact clock-gating XBL leaves each non-boot block in (which GCC gates are on; whether the console SE’s se-clk is running), whether the console SE physically drives TX before the OS (the boot_logger_uart buffer may not be flushed to the pad), and the SMMU stream state for non-UFS masters — all runtime, not in the dump.

Readiness. Operating inside the UEFI/DXE environment (reusing BlockIo, RamPartition, etc.) is specified down to register-level fidelity by this dump — the execution state above plus XBL → ABL hand-off contract (bounded) leaves no known static gap for that mode of operation. Operating bare-metal (register only, no DXE) is mapped but not turnkey: the UFS submission path and its SMMU stream mode are the two items that a static single-dump analysis structurally cannot close, and would be settled only by live bring-up.

Source:

artifacts/boot_a/board.dts (board DTB) for all node addresses/IRQs/clocks/pins; _READONLY/lun1/xbl_a.bin for EL/MMU evidence, the platform memory-map, and UFS/console log strings; parsed GPT and Partition map and checksums for the UFS LUN/LBA layout.

Cross-refs:

XBL → ABL hand-off contract (bounded) (UEFI-protocol view of the same hand-off), Kernel/DTB/ramdisk load and the kernel hand-off (kernel-entry ABI), Partition map and checksums, Open questions and limits of analysis, Storage controllers — UFS, ICE, and SDCC (UFS register surface), QUP/GENI serial-engine bus map (what is wired where) (GENI/QUP console), Clock tree — GCC, domain CCs, RPMh (GCC/RPMh), Pin control — TLMM and the PMIC GPIOs (TLMM), Base kernel device tree (the SoC boot contract) (GIC/PDC/timer, apps-SMMU).