7.1. Storage controllers — UFS, ICE, and SDCC

The boot medium and the two SD/eMMC controllers, from the device tree, the XBL uefiplat.cfg config, and the kernel config. This is the standing subsystem view — the controller, the PHY, the inline crypto engine, and the two SDCC instances — with the clock and regulator wiring a replacement bootloader would have to reproduce, and what XBL hands ABL under Where the boot loader touches storage below.

There are three storage controllers on the die and one boot device:

Controller

Node / base

Role here

State in the shipped tree

UFS host

ufshc@1d84000

the boot device (SK Hynix UFS)

base disabled → enabled by dtbo fragment; live from XBL onward

SDCC1

sdhci@7c4000

8-bit eMMC-class + CQE + ICE

present, non-removable; no eMMC populated (boot is UFS)

SDCC2

sdhci@8804000

4-bit removable microSD

enabled by dtbo with card-detect on GPIO 94

7.1.1. UFS host controller

ufshc@1d84000, compatible = "qcom,ufshc" — the Qualcomm UFS host wrapping a standard UFSHCI core.

Register windows:

two, named ufs_mem and ufs_ice:

  • ufs_mem 0x1D84000 size 0x3000 — the UFSHCI register block (UTRLBA/UTRLDBR doorbells, UPIU host/response, capability registers).

  • ufs_ice 0x1D90000 size 0x8000 — the inline crypto register block (see Inline crypto (ICE) below).

The XBL MMAP names the whole span "UFS UFS REGS" 0x01D80000 size 0x20000, so both windows live inside one 128 KiB device page (UNCACHEABLE / NS_DEVICE in uefiplat.cfg). ICE is a sub-block of the UFS controller on this SoC, not a separate crypto core.

Interrupt:

SPI 0x109 (265), level.

Link:

lanes-per-direction = 2 — a 2-lane M-PHY. dev-ref-clk-freq = 0 selects the 19.2 MHz device reference clock. spm-level = 5 is the deepest UFS low-power (hibern8 + clock-gating) level allowed.

Reset:

resets = <gcc 0x04>, core_reset — the GCC UFS BCR. A dev-reset-assert / dev-reset-deassert pinctrl pair drives the device HW-reset line.

Clocks (nine, resolved against the GCC / RPMh providers in Clock tree — GCC, domain CCs, RPMh):

clock-names entry

Provider : id

freq-table-hz

What it feeds

core_clk

gcc : 0x70

50 – 200 MHz

UFS host core (SYS clock)

bus_aggr_clk

gcc : 0x0b

RPMh-managed

aggregate NoC bus clock

iface_clk

gcc : 0x6f

RPMh-managed

AHB config interface

core_clk_unipro

gcc : 0x79

37.5 – 150 MHz

UniPro link core

core_clk_ice

gcc : 0x72

75 – 300 MHz

inline-crypto core clock

ref_clk

rpmh : 0x14

RPMh-managed

M-PHY reference (ln_bb_clk / rf_clk)

tx_lane0_sync_clk

gcc : 0x78

symbol clock

TX lane-0 symbol

rx_lane0_sync_clk

gcc : 0x76

symbol clock

RX lane-0 symbol

rx_lane1_sync_clk

gcc : 0x77

symbol clock

RX lane-1 symbol

The freq-table-hz pairs are {min, max} per clock; the four with {0, 0} are not scaled by the host driver (they are gated on/off or voted through RPMh). core_clk_ice topping out at 300 MHz matches the ICE clock rate tables on SDCC1 (below).

Bus / power-mode scaling. qcom,msm-bus,num-cases = 0x16 (22) named vectors run MINPWM_G1_L1PWM_G4_L2HS_RA_G1_L1HS_RB_G3_L2MAX. That is the full UniPro power-mode ladder: PWM (slow mode) gears 1–4 and HS (high-speed) Rate-A / Rate-B gears 1–3, each at 1 or 2 lanes, each mapping to a NoC bandwidth vote (paths 0x7b→0x200 and 0x01→0x2f5, i.e. the two UFS master ports). The controller renegotiates the UniPro mode and re-votes bus bandwidth as gear changes.

7.1.2. UFS PHY

ufsphy_mem@1d87000 — the M-PHY, register window phy_mem 0x1D87000 size 0xE00, #phy-cells = 0, lanes-per-direction = 2. The base node carries no compatible; the dtbo overlay adds it and its supplies:

  • compatible = "qcom,ufs-phy-qmp-v3" — a QMP V3 UFS PHY.

  • PHY reference clocks: ref_clk_src = rpmh 0x14, ref_clk = gcc 0x6e, ref_aux_clk = gcc 0x74.

  • Supplies (overlay): vdda-phy (max 62 900 µA) and vdda-pll (max 18 300 µA). vdd-hba (host-controller rail), vcc (2.95 V, 0x2D03700x2D2A80) and vccq2 (1.8 V) come in on the controller fragment, with vccq2-pwr-collapse-sup and a vddp-ref-clk rail (1.15–1.2 V) for the reference clock.

Bring-up order for a from-scratch bootloader would therefore be: vote the GCC UFS gates + RPMh ref_clk, raise vdda-phy / vdda-pll and vcc / vccq2, release core_reset, initialise the QMP V3 PHY, bring UniPro up in PWM G1 and gear-switch to HS (an engineering inference from the DTB/regulator map, not a sequence tested against actual from-scratch bootloader code). XBL already did all of this — the UFS link is up at ABL hand-off (Below UEFI — execution state and hardware register surface).

7.1.3. Inline crypto (ICE)

The ufs_ice window at 0x1D90000 (0x8000) is the UFS Inline Crypto Engine: AES-XTS applied to data on the DMA path between the controller and the UFS device, per-request keyed from a keyslot, so file-based encryption costs no extra copy. It is the storage arm of the three-way crypto split — distinct from the general-purpose Crypto Engine (QCE) at 0x1de0000 and from KeyMaster/QSEE key storage in TrustZone (Crypto engine (QCE) and hardware RNG, Secure world and coprocessors (TZ, HYP, trustlets)).

Kernel wiring (from kernel_config.txt):

  • CONFIG_SCSI_UFSHCD=y + CONFIG_SCSI_UFSHCD_PLATFORM=y + CONFIG_SCSI_UFS_QCOM=y — the platform UFS host driver.

  • CONFIG_SCSI_UFS_CRYPTO=y and CONFIG_SCSI_UFS_CRYPTO_QTI=y — the UFSHCI crypto extension plus Qualcomm’s QTI variant, which programs keys through the vendor keyslot / hardware-key-manager (HWKM) path rather than the generic standard-register path.

  • CONFIG_BLK_INLINE_ENCRYPTION=y + CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=y — the block-layer inline-crypto framework that FBE plugs into, with a software fallback when a keyslot is unavailable.

  • The old standalone CONFIG_CRYPTO_DEV_QCOM_ICE is not set — storage crypto is done through the UFS-integrated ICE, not the legacy separate driver. (# CONFIG_ICE is not set in the config is the unrelated Intel Ethernet driver, not this ICE.)

So the encryption stack is: fscrypt (FBE)blk-cryptoSCSI_UFS_CRYPTO(_QTI) → the ufs_ice register block; metadata encryption uses DM_DEFAULT_KEY on the same engine (Boot image: kernel, DTB, ramdisk, config). Assumption, inferred from the HWKM/ICE wrapped-key design described above, not directly observed: the plaintext key material should not leave the secure world, since HWKM wraps it and ICE consumes only the wrapped key in a slot. What was actually checked is narrower — no plaintext key material was found in any partition in the dump — which is consistent with, but does not itself prove, that design holding at runtime.

Static end. The register offsets inside ufs_ice (the CCAP capability register that reports algorithm/keysize support, the number of keyslots, the config-enable registers) follow the UFSHCI-crypto spec and Qualcomm’s HWKM addendum; the slot count and installed capabilities are runtime values in a region that is XPU-limited and not dumped through Firehose. Confirming them needs a live register read (Open questions and limits of analysis). The design — UFS-integrated ICE, QTI/HWKM key path, AES-XTS FBE — is fully pinned from the tree and config.

7.1.4. SDCC1 — sdhci@7c4000

An 8-bit eMMC-class controller with command-queueing and its own ICE, present in the tree but with no device populated (the product’s mass storage is UFS).

Compatible:

qcom,sdhci-msm-v5, qcom,sdhci-msm-cqe — SDHCI-MSM v5 with the command-queue engine (CQE / CQHCI).

Register windows:

hc_mem 0x7C4000 (0x1000), cqhci_mem 0x7C5000 (0x1000), cqhci_ice 0x7C8000 (0x8000) — a second integrated ICE, on the SD/eMMC side. XBL MMAP: "SDC1_REG" 0x007C0000 size 0x20000.

Interrupts:

hc_irq 0x281 (641), pwr_irq 0x284 (644).

Bus / modes:

qcom,bus-width = 8; HS400_1p8v, HS200_1p8v, DDR_1p8v — 1.8 V eMMC HS400. qcom,clk-rates 400 kHz → 384 MHz; qcom,ice-clk-rates = {300 MHz, 100 MHz}.

Clocks:

iface_clk gcc 0x65, core_clk gcc 0x66, ice_core_clk gcc 0x68.

State:

qcom,nonremovable; base DTB disabled. A dtbo fragment sets a 2.95 V vdd + 1.8 V vdd-io and status = "ok", but the fixed 1.8 V HS400 profile and non-removable flag mark this as the internal-eMMC slot of the reference design, unused on a UFS-boot device.

7.1.5. SDCC2 — sdhci@8804000 (removable microSD)

Compatible:

qcom,sdhci-msm-v5 (no CQE, no ICE window).

Register window:

hc_mem 0x8804000 (0x1000). Sits in the PERIPH_SS MMAP span (0x08800000).

Interrupts:

hc_irq 0xCC (204), pwr_irq 0xDE (222).

Bus / modes:

qcom,bus-width = 4; SDR12, SDR25, SDR50, DDR50, SDR104 — a UHS-I SD card. qcom,clk-rates 400 kHz → ~202 MHz (SDR104).

Clocks:

iface_clk gcc 0x6a, core_clk gcc 0x6b.

Enablement:

base DTB disabled; a dtbo fragment sets a 2.95 V vdd, a switchable 1.8–2.95 V vdd-io (the UHS voltage switch), status = "ok", and ``cd-gpios = <tlmm 94 active-low>`` — a card-detect line on TLMM GPIO 94. The switchable I/O rail plus the card-detect GPIO are the signature of a real removable SD socket (contrast SDCC1’s fixed 1.8 V, non-removable).

XBL SDCC configuration (uefiplat.cfg): EnableSDHCSwitch = 0x1 (SDHC mode, not legacy), and per-slot GPIO drive configs Sdc1GpioConfigOn = 0x1E92 / Sdc2GpioConfigOn = 0x1E92 (active) and …Off = 0xA00 (parked). Whether the XBL SD path is exercised depends on boot config; the boot device is UFS.

7.1.6. Where the boot loader touches storage

  • Reads its own images from UFS. XBL brings up the UFS PHY + link and selects a boot LUN (UFS Boot LUN: %d in xbl_a.bin); the images live on the boot LUNs and the general partitions on the data LUNs (Partition map and checksums).

  • Hands the live UFS host to ABL. EFI_BLOCK_IO stays functional until ExitBootServices (Below UEFI — execution state and hardware register surface), so ABL and fastboot read/write through the same initialised controller.

  • A replacement bootloader must either inherit XBL’s initialised UFS (stay in the ABL slot) or redo PHY + UniPro + clock/regulator bring-up (the sequence above) to reach the boot LUNs. SDCC is not on the boot path.

7.1.7. Provenance

Source:

artifacts/boot_a/board.dts (ufshc@1d84000, ufsphy_mem@1d87000, sdhci@7c4000, sdhci@8804000 — registers, IRQs, clock indices, freq/bus tables), artifacts/dtbo_a/overlay_0.dts (UFS PHY/HBA + SDCC enable fragments, cd-gpios), _READONLY/lun1/xbl_a.bin (uefiplat.cfg MMAP + ## UFS ## / ## SDCC ## config keys), and artifacts/boot_a/kernel_config.txt (UFS / ICE / SDHCI / inline-encryption selection).

Method:

DTB + overlay node reading, config-text carve, kernel-config cross-check. Clock ids resolved against the GCC (phandle 0x1f) and RPMh (phandle 0x1e) providers. No code executed.

Cross-refs:

Below UEFI — execution state and hardware register surface (execution state at hand-off), Base kernel device tree (the SoC boot contract) (apps-SMMU / DMA path), Boot image: kernel, DTB, ramdisk, config (FBE / DM_DEFAULT_KEY / metadata), Crypto engine (QCE) and hardware RNG (QCE vs ICE vs KeyMaster), Secure world and coprocessors (TZ, HYP, trustlets) (HWKM / KeyMaster), Clock tree — GCC, domain CCs, RPMh (GCC/RPMh clock ids), Pin control — TLMM and the PMIC GPIOs (SDCC and UFS-reset pin states), Partition map and checksums (LUN layout).