# Introduction **What this page covers:** Why UBI exists, how it compares to other Zephyr storage options, what it provides, what it does not, and its resource footprint. **Prerequisites:** [Overview](overview.md) for the mental model. ## What is UBI? UBI (Unsorted Block Images) is a volume management layer for raw flash devices on Zephyr RTOS. It maps Logical Erase Blocks (LEBs) to Physical Erase Blocks (PEBs), solving three fundamental problems of raw flash: 1. **Wear-leveling** — distributes writes across all PEBs so no single block wears out prematurely. 2. **Bad block management** — detects and isolates failed blocks transparently. 3. **Logical volumes** — partitions a single flash region into multiple named volumes, each independently readable, writable, and resizable. UBI is analogous to the Logical Volume Manager (LVM) in Linux, but operates on erase blocks instead of sectors. ## Why UBI on Zephyr? Zephyr provides several flash abstractions, but none offer a general-purpose volume manager with wear-leveling for raw flash: | Existing Solution | What It Does | What It Lacks | |-------------------|--------------|---------------| | Flash Map (flash_area) | Maps named partitions to fixed flash regions | No wear-leveling, no volumes, static layout | | NVS | Key-value store with wear-leveling | Single key-value namespace, not a volume manager | | LittleFS | Filesystem with wear-leveling | File-grained, heavier footprint, no raw block access | | FCB | Flash circular buffer | Append-only, no random-access volumes | UBI fills this gap as a **thin, low-overhead volume manager** providing: - Multiple named volumes on a single flash partition - Transparent wear-leveling across all volumes - Raw block-level read/write — ideal for firmware images, configuration blobs, or structured binary data - Bad block isolation without application awareness ## Features - Dynamic volume creation, removal, and resizing (dynamic volumes) - Global wear-leveling across the entire flash partition - Transparent bad block detection and isolation - Dual-bank metadata headers for crash resilience (configurable 2–4 reserved PEB copies) - Crash recovery via sequence-number-based conflict resolution - Thread-safe operations via per-device Zephyr mutex - Heap-allocated device and volume state; a small static partition guard (mutex + bitfield) enforces one open handle per `partition_id` ## Non-Goals UBI intentionally does **not** provide: | Non-goal | Rationale | |----------|-----------| | Filesystem (files, directories, POSIX API) | UBI is a block-level volume manager. Use LittleFS or FAT on top if you need a filesystem. | | FTL replacement for eMMC / SD | Managed flash has its own translation layer. UBI adds no value. | | Power-loss atomicity for user data | UBI protects metadata (dual-bank + sqnum). User data writes are not journaled — a power loss mid-write may leave a LEB partially written. | | Authenticated encryption | Optional `CONFIG_UBI_CRYPTO` backend (AES-128-CCM via PSA Crypto). See the [Secure Architecture Guide](secure_architecture.md). | ## Resource Usage UBI is designed for resource-constrained embedded systems. The following measurements were taken with `west build -b b_u585i_iot02a ./sample` (STM32U5, Cortex-M33): `CONFIG_UBI_ENABLE=y`, `CONFIG_SIZE_OPTIMIZATIONS=y`, and no test-only options. Library footprint comes from `arm-none-eabi-size build/stm32u5/sample/modules/ubi/lib/lib..__ubi__lib.a` (sum of `.text` + `.data` for flash, `.data` + `.bss` for static RAM in that archive). The CI pipeline also records flash usage (see the `flash-usage` build artifact). Actual numbers vary with board, toolchain, and Kconfig. ### Flash and Static RAM | Metric | Value | Notes | |------------|----------|-------| | Flash (plain) | ~9.2 KB | `.text` + `.data` in `lib..__ubi__lib.a`, Cortex-M33, `-Os` | | Flash (secure) | ~59.3 KB | Includes PSA Crypto (Mbed TLS) pulled in by `CONFIG_UBI_CRYPTO=y` | | Static RAM (BSS) | Depends on Kconfig | Proportional to `MAX_NR_OF_DEVICES`, `MAX_NR_OF_DATA_PEBS`, `MAX_NR_OF_VOLUMES` under `CONFIG_UBI_MEM_BACKEND_STATIC` (see [Memory Sizing Guide](configuration.md#memory-sizing-guide)) | With `CONFIG_UBI_MEM_BACKEND_STATIC` (default), runtime RAM is fully determined at compile time and isolated from the application heap. Under `CONFIG_UBI_MEM_BACKEND_HEAP` (legacy), static RAM is minimal (partition guard only) and all device/volume state is heap-allocated. Enabling `CONFIG_UBI_TEST_API_ENABLE` (Ztest builds) pulls in extra code paths and logging; the same archive on the `tests/` app was approximately **16.2 KiB** flash (`.text` + `.data` only) with `CONFIG_DEBUG_OPTIMIZATIONS=y`. ### Runtime RAM (Dynamic Allocations) | Runtime Object | RAM per instance | |----------------------------|------------------| | Device (`ubi_device`) | 136 B (plain) / 180 B (secure, POSIX) | | Volume (`ubi_volume`) | 44 B (plain) / 48 B (secure) | | PEB (free/dirty/mapped) | 16 B | | Bad PEB | 16 B (shared slab block) | | Volume tree node | 16 B | Under the static backend, all pools are pre-allocated at compile time. Under the heap backend, allocations are dynamic (`k_malloc`). Runtime RAM is proportional to the number of PEBs and volumes. ### Example: Typical Deployment For a device with 16 PEBs (8 KB erase blocks, 128 KB partition) and 2 volumes: - Device: 136 B (plain) / 180 B (secure) - PEB tracking: 14 data PEBs × 16 B = 224 B - Volumes: 2 × 44 B = 88 B (plain) / 2 × 48 B = 96 B (secure) - Volume tree nodes: 2 × 16 B = 32 B - **Total runtime RAM: ~480 B (plain) / ~532 B (secure)**