========================================= 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: .. list-table:: :header-rows: 1 :widths: 16 20 20 44 * - 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 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 = ``, ``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 :doc:`clocks`): .. list-table:: :header-rows: 1 :widths: 26 20 22 32 * - ``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 ``MIN`` → ``PWM_G1_L1`` … ``PWM_G4_L2`` → ``HS_RA_G1_L1`` … ``HS_RB_G3_L2`` → ``MAX``. 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. 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, ``0x2D0370``–``0x2D2A80``) 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 (:doc:`/boot/below-uefi`). 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 (:doc:`/trustzone/crypto-engine`, :doc:`/trustzone/secure-world`). 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-crypto** → **SCSI_UFS_CRYPTO(_QTI)** → the ``ufs_ice`` register block; ``metadata`` encryption uses ``DM_DEFAULT_KEY`` on the same engine (:doc:`/boot/boot-image`). 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 (:doc:`/open-questions`). The *design* — UFS-integrated ICE, QTI/HWKM key path, AES-XTS FBE — is fully pinned from the tree and config. 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. 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 = ``** — 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. 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 (:doc:`/partition-map`). - **Hands the live UFS host to ABL.** ``EFI_BLOCK_IO`` stays functional until ``ExitBootServices`` (:doc:`/boot/below-uefi`), 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. 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: :doc:`/boot/below-uefi` (execution state at hand-off), :doc:`/boot/base-dtb` (apps-SMMU / DMA path), :doc:`/boot/boot-image` (FBE / ``DM_DEFAULT_KEY`` / metadata), :doc:`/trustzone/crypto-engine` (QCE vs ICE vs KeyMaster), :doc:`/trustzone/secure-world` (HWKM / KeyMaster), :doc:`clocks` (GCC/RPMh clock ids), :doc:`pinctrl` (SDCC and UFS-reset pin states), :doc:`/partition-map` (LUN layout).