| Block Storage Service |
| ===================== |
| Overview |
| -------- |
| The Block Storage service can be used to share a block-oriented storage device |
| such as a QSPI flash between a set of independent secure world clients. A block |
| storage service provider presents a block level interface for accessing an |
| underlying block storage device. To allow multiple higher layer filesystems to |
| share the same storage device, logical block addresses are partitioned, based on |
| configuration data provided by a system integrator. The partition configuration |
| data may be read from a GUID Partition Table (GPT) or from the block storage SP |
| manifest. The configuration data restricts access to a storage partition to a |
| defined owner. Each owner is allocated a maximum number of blocks and is given |
| exclusive access to its own blocks, based on the client ID of the calling client. |
| |
| The following diagram illustrates a firmware integration that uses a single block |
| storage service provider to control access to a dedicated flash device. In this |
| example StMM, OP-TEE, Update Agent and the Protected Storage SP act as clients of |
| the service. Each client independently manages its own filesystem and is presented |
| with its own logical partition, starting with a logical block address (LBA) of zero. |
| |
| .. image:: image/block-storage-example-usage.svg |
| |
| Project Directories |
| ------------------- |
| Components within the Trusted Services project related to the Block Storage service |
| are located under the following directories: |
| |
| .. list-table:: |
| :header-rows: 1 |
| |
| * - Directory |
| - Contains |
| * - ``components/service/block_storage`` |
| - Service specific code components. |
| * - ``deployments/block-storage`` |
| - Build files and deployment specific code for building alternative configurations |
| of the block storage service provider. |
| * - ``protocols/service/block_storage`` |
| - Service access protocol definitions. |
| |
| Service Interface |
| ----------------- |
| The Block Storage service supports a conventional set of block-level operations that |
| can be adapted to different driver models by clients. The following table summarizes |
| supported operations: |
| |
| .. list-table:: |
| :header-rows: 1 |
| |
| * - Operation |
| - Description |
| * - Open |
| - Open a session - take the required *UniquePartitionGUID* as a parameter. Returns |
| a handle to be used as a qualifier for other requests made by a client. |
| * - Close |
| - Close a previously opened session. |
| * - GetInfo |
| - Returns information about the partition associated with an open session. Includes |
| the block size and the total number of blocks assigned to the partition. |
| * - Read |
| - Read data from the specified block. |
| * - Write |
| - Write data to the specified block. |
| * - Erase |
| - Erase a set of one or more blocks. |
| |
| Protocol definitions live under: ``protocols/service/block_storage``. |
| |
| The service interface is realized by the block storage service provider. It delegates |
| storage operations to a backend *block_store*. The *block_store* defines a common |
| interface for components that realize block storage operations. Where an underlying storage |
| technology does not support an explicit erase operation (e.g. RPMB), the corresponding |
| concrete *block_store* should return success for a call to erase but perform no actual |
| operation (if the partition is writable and the LBA falls within the limits of the |
| partition). |
| |
| Service Provider Configuration |
| ------------------------------ |
| A platform integrator must provide a set of configuration data to configure how the block |
| storage service provider presents block storage to clients. Configuration data relates to |
| the following: |
| |
| - **Storage partition configuration** - determines how storage is divided into separate partitions |
| - **Block device configuration** - determines how the backed storage device is configured |
| |
| Storage Partition Configuration |
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| The block storage service allows a block storage device to be presented as a single storage |
| partition or as a set of smaller storage partitions. The way that storage is presented is |
| determined by configuration data prepared by a platform integrator. Each storage partition |
| presented by a block storage service provider starts at LBA zero. The number of partitions |
| and their size are defined by configuration data. Configuration data assigns partitions |
| to owners to enable access to be controlled. If no partition configuration exists for a |
| requesting client or if an attempt is made to access a block outside of the configured LBA |
| range, access is denied. The set of storage partitions used for secure block storage will |
| not necessarily span the entire underlying storage device. A platform integrator is free to |
| limit the area used for secure block storage to allow the storage device to be used for other |
| purposes e.g. as a boot source, read by the boot loader during early boot stages. |
| |
| Two partition configuration methods will be supported; one where partition configuration data |
| is read from an SP manifest and the other where configuration is defined by a GUID Partition |
| Table. Both methods may be used in combination if necessary. Initial implementations will |
| use the SP manifest configuration method. |
| |
| Each partition configuration entry includes an attributes bitmap that conforms to the UEFI |
| GPT Partition Entry attributes definition (see section 5.3 of the UEFI specification). Bits |
| 48-63 are reserved for GUID specific use. For partitions labelled with the Secure Block Store |
| GUID, bits will be defined for: |
| |
| - **Read-only** - write and erase operations are forbidden. |
| |
| A GPT partition entry includes the PartitionName property which normally holds a human readable |
| name for the partition. For secure block store partitions, the PartitionName property will |
| hold the canonical UUID string identifying the owner. An empty string is interpreted as |
| 'no specific owner' and any client will be granted access. |
| |
| Configuration via SP Manifest |
| """"""""""""""""""""""""""""" |
| For an SP deployment, the partition configuration may be read from a device tree blob (DTB), |
| passed as an initialization parameter. Per-partition configuration data comprises the following: |
| |
| .. list-table:: |
| :header-rows: 1 |
| |
| * - Config Value |
| - Description |
| * - UniquePartitionGUID |
| - GUID that is unique for a partition entry. |
| * - StartingLBA |
| - The storage block address corresponding to LBA zero. |
| * - EndingLBA |
| - The last storage block in the contiguous range of blocks. |
| * - Attributes |
| - See UEFI specification |
| * - Owner |
| - Holds canonical UUID string for owner. |
| |
| The partition configuration is included as a sub-node of the block-dev node that includes |
| configuration values related to the block device. The following is an example of how a block |
| device and related partitions are defined within a DT based SP manifest:: |
| |
| block-dev { |
| compatible = "tforg,ts-block-dev" |
| disk-guid = "af9f72de-d71f-4492-b44b-a4b4d96000bf" |
| |
| partitions { |
| compatible = "tforg,ts-block-partitions" |
| |
| fwu-meta { |
| guid = "a6f99e90-7a75-4384-847a-29c9a86c6279" |
| start-lba = <0x00000000> |
| end-lba = <0x00000003> |
| attr = <0x00000000> |
| owner = "afb995cd-9354-4333-9ea2-bd62ccaedb22" |
| }; |
| |
| fip { |
| guid = "1eccc9bc-9a5f-43d0-bcd3-466fd21c9a92" |
| start-lba = <0x00000004> |
| end-lba = <0x00040003> |
| attr = <0x00000000> |
| owner = "afb995cd-9354-4333-9ea2-bd62ccaedb22" |
| }; |
| |
| uefi-var { |
| guid = "1022a92b-4b4a-47b4-94cb-35faf5a45dc2" |
| start-lba = <0x00040004> |
| end-lba = <0x00080003> |
| attr = <0x00000000> |
| owner = "ed32d533-99e6-4209-9cc0-2d72cdd998a7" |
| }; |
| }; |
| }; |
| |
| Configuration via GUID Partition Table (GPT) |
| """""""""""""""""""""""""""""""""""""""""""" |
| The UEFI specification defines a standard layout for physical storage devices where storage |
| partitions are described by partition entries within the GUID Partition Table. During |
| initialization, the Block Storage SP will read the GPT and iterate over partition entries, |
| identifying those with the secure block store partition type GUID. Each entry contains the |
| following: |
| |
| .. list-table:: |
| :header-rows: 1 |
| |
| * - Offset |
| - Length |
| - contents |
| * - 0 |
| - 16 bytes |
| - PartitionTypeGUID - Secure Block Store GUID |
| * - 16 |
| - 16 bytes |
| - UniquePartitionGUID |
| * - 32 |
| - 8 bytes |
| - Starting LBA |
| * - 40 |
| - 8 bytes |
| - Ending LBA |
| * - 48 |
| - 8 bytes |
| - Attributes (e.g. read-only) |
| * - 56 |
| - 72 bytes |
| - PartitionName - Holds canonical UUID string for owner. |
| |
| Design Description |
| ------------------ |
| The block storage service provider conforms to the same model as other service providers |
| within the TS project. Service requests from clients are received by a service provider |
| that is responsible for parameter deserialization/serialization and service level access |
| control. Block storage operations are delegated to a backend *block_store* that provides |
| block-level storage in some way. There is much flexibility to realize the backend block-level |
| storage in different ways, allowing platform integrators to use alternative *block_store* |
| realizations to provide storage solutions that meet specific product requirements. |
| |
| The following class diagram illustrates the block storage service provider model: |
| |
| .. uml:: uml/BlockStorageProvider.puml |
| |
| Block Store |
| ^^^^^^^^^^^ |
| The *block_store* component defines a virtual interface for block IO operations. Alternative |
| concrete *block_store* implementations are supported. Some *block_store* components are stackable |
| over other *block_store* components to add features such as store partitioning or block |
| authentication. Separation of functionality into stackable *block_store* components gives |
| platform integrators the flexibility to create alternative storage solutions with different |
| security/cost tradeoffs. The base *block_store* interface is defined in:: |
| |
| components/service/block_storage/block_store/block_store.h |
| |
| Components that implement the *block_store* interface are located in subdirectories beneath |
| ``components/service/block_storage/block_store``. A *block_device* is class of *block_store* |
| that actually provides block-level storage. In a stack of *block_store* components, a |
| *block_device* will always live at the bottom. The following layer diagram illustrates a |
| typical block storage deployment where storage is provided by a stack of *block_store* components: |
| |
| .. image:: image/block-storage-layers.svg |
| |
| Some block devices supported in the TS project (located under: |
| ``components/service/block_storage/block_store/block_device``) are: |
| |
| - **ram_block_store** - stores blocks in RAM. Intended for test purposes. |
| - **null_block_store** - a store with no real storage. Always accepts legal writes and returns |
| zeros for reads. |
| - **fvb_block_store** - an adapter that uses a UEFI firmware volume block driver to access |
| storage. Can be used with drivers from the EDK2 project. |
| |
| Other supported block_store components: |
| |
| - **partitioned_block_store** - a stackable *block_store* that presents an underlying *block_store* |
| as a set of configurable storage partitions. |
| - **block_storage_client** - communicates with a remote block storage service provider to provide |
| storage. |
| |
| -------------- |
| |
| *Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.* |
| |
| SPDX-License-Identifier: BSD-3-Clause |