Julian Hall | 1751ce7 | 2022-08-30 13:54:13 +0100 | [diff] [blame] | 1 | Block Storage Service |
| 2 | ===================== |
| 3 | Overview |
| 4 | -------- |
| 5 | The Block Storage service can be used to share a block-oriented storage device |
| 6 | such as a QSPI flash between a set of independent secure world clients. A block |
| 7 | storage service provider presents a block level interface for accessing an |
| 8 | underlying block storage device. To allow multiple higher layer filesystems to |
| 9 | share the same storage device, logical block addresses are partitioned, based on |
| 10 | configuration data provided by a system integrator. The partition configuration |
| 11 | data may be read from a GUID Partition Table (GPT) or from the block storage SP |
| 12 | manifest. The configuration data restricts access to a storage partition to a |
| 13 | defined owner. Each owner is allocated a maximum number of blocks and is given |
| 14 | exclusive access to its own blocks, based on the client ID of the calling client. |
| 15 | |
| 16 | The following diagram illustrates a firmware integration that uses a single block |
| 17 | storage service provider to control access to a dedicated flash device. In this |
| 18 | example StMM, OP-TEE, Update Agent and the Protected Storage SP act as clients of |
| 19 | the service. Each client independently manages its own filesystem and is presented |
| 20 | with its own logical partition, starting with a logical block address (LBA) of zero. |
| 21 | |
| 22 | .. image:: image/block-storage-example-usage.svg |
| 23 | |
| 24 | Project Directories |
| 25 | ------------------- |
| 26 | Components within the Trusted Services project related to the Block Storage service |
| 27 | are located under the following directories: |
| 28 | |
| 29 | .. list-table:: |
| 30 | :header-rows: 1 |
| 31 | |
| 32 | * - Directory |
| 33 | - Contains |
| 34 | * - ``components/service/block_storage`` |
| 35 | - Service specific code components. |
| 36 | * - ``deployments/block-storage`` |
| 37 | - Build files and deployment specific code for building alternative configurations |
| 38 | of the block storage service provider. |
| 39 | * - ``protocols/service/block_storage`` |
| 40 | - Service access protocol definitions. |
| 41 | |
| 42 | Service Interface |
| 43 | ----------------- |
| 44 | The Block Storage service supports a conventional set of block-level operations that |
| 45 | can be adapted to different driver models by clients. The following table summarizes |
| 46 | supported operations: |
| 47 | |
| 48 | .. list-table:: |
| 49 | :header-rows: 1 |
| 50 | |
| 51 | * - Operation |
| 52 | - Description |
| 53 | * - Open |
| 54 | - Open a session - take the required *UniquePartitionGUID* as a parameter. Returns |
| 55 | a handle to be used as a qualifier for other requests made by a client. |
| 56 | * - Close |
| 57 | - Close a previously opened session. |
| 58 | * - GetInfo |
| 59 | - Returns information about the partition associated with an open session. Includes |
| 60 | the block size and the total number of blocks assigned to the partition. |
| 61 | * - Read |
| 62 | - Read data from the specified block. |
| 63 | * - Write |
| 64 | - Write data to the specified block. |
| 65 | * - Erase |
| 66 | - Erase a set of one or more blocks. |
| 67 | |
| 68 | Protocol definitions live under: ``protocols/service/block_storage``. |
| 69 | |
| 70 | The service interface is realized by the block storage service provider. It delegates |
| 71 | storage operations to a backend *block_store*. The *block_store* defines a common |
| 72 | interface for components that realize block storage operations. Where an underlying storage |
| 73 | technology does not support an explicit erase operation (e.g. RPMB), the corresponding |
| 74 | concrete *block_store* should return success for a call to erase but perform no actual |
| 75 | operation (if the partition is writable and the LBA falls within the limits of the |
| 76 | partition). |
| 77 | |
| 78 | Service Provider Configuration |
| 79 | ------------------------------ |
| 80 | A platform integrator must provide a set of configuration data to configure how the block |
| 81 | storage service provider presents block storage to clients. Configuration data relates to |
| 82 | the following: |
| 83 | |
| 84 | - **Storage partition configuration** - determines how storage is divided into separate partitions |
| 85 | - **Block device configuration** - determines how the backed storage device is configured |
| 86 | |
| 87 | Storage Partition Configuration |
| 88 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 89 | The block storage service allows a block storage device to be presented as a single storage |
| 90 | partition or as a set of smaller storage partitions. The way that storage is presented is |
| 91 | determined by configuration data prepared by a platform integrator. Each storage partition |
| 92 | presented by a block storage service provider starts at LBA zero. The number of partitions |
| 93 | and their size are defined by configuration data. Configuration data assigns partitions |
| 94 | to owners to enable access to be controlled. If no partition configuration exists for a |
| 95 | requesting client or if an attempt is made to access a block outside of the configured LBA |
| 96 | range, access is denied. The set of storage partitions used for secure block storage will |
| 97 | not necessarily span the entire underlying storage device. A platform integrator is free to |
| 98 | limit the area used for secure block storage to allow the storage device to be used for other |
| 99 | purposes e.g. as a boot source, read by the boot loader during early boot stages. |
| 100 | |
| 101 | Two partition configuration methods will be supported; one where partition configuration data |
| 102 | is read from an SP manifest and the other where configuration is defined by a GUID Partition |
| 103 | Table. Both methods may be used in combination if necessary. Initial implementations will |
| 104 | use the SP manifest configuration method. |
| 105 | |
| 106 | Each partition configuration entry includes an attributes bitmap that conforms to the UEFI |
| 107 | GPT Partition Entry attributes definition (see section 5.3 of the UEFI specification). Bits |
| 108 | 48-63 are reserved for GUID specific use. For partitions labelled with the Secure Block Store |
| 109 | GUID, bits will be defined for: |
| 110 | |
| 111 | - **Read-only** - write and erase operations are forbidden. |
| 112 | |
| 113 | A GPT partition entry includes the PartitionName property which normally holds a human readable |
| 114 | name for the partition. For secure block store partitions, the PartitionName property will |
| 115 | hold the canonical UUID string identifying the owner. An empty string is interpreted as |
| 116 | 'no specific owner' and any client will be granted access. |
| 117 | |
| 118 | Configuration via SP Manifest |
| 119 | """"""""""""""""""""""""""""" |
| 120 | For an SP deployment, the partition configuration may be read from a device tree blob (DTB), |
| 121 | passed as an initialization parameter. Per-partition configuration data comprises the following: |
| 122 | |
| 123 | .. list-table:: |
| 124 | :header-rows: 1 |
| 125 | |
| 126 | * - Config Value |
| 127 | - Description |
| 128 | * - UniquePartitionGUID |
| 129 | - GUID that is unique for a partition entry. |
| 130 | * - StartingLBA |
| 131 | - The storage block address corresponding to LBA zero. |
| 132 | * - EndingLBA |
| 133 | - The last storage block in the contiguous range of blocks. |
| 134 | * - Attributes |
| 135 | - See UEFI specification |
| 136 | * - Owner |
| 137 | - Holds canonical UUID string for owner. |
| 138 | |
| 139 | The partition configuration is included as a sub-node of the block-dev node that includes |
| 140 | configuration values related to the block device. The following is an example of how a block |
| 141 | device and related partitions are defined within a DT based SP manifest:: |
| 142 | |
| 143 | block-dev { |
| 144 | compatible = "tforg,ts-block-dev" |
| 145 | disk-guid = "af9f72de-d71f-4492-b44b-a4b4d96000bf" |
| 146 | |
| 147 | partitions { |
| 148 | compatible = "tforg,ts-block-partitions" |
| 149 | |
| 150 | fwu-meta { |
| 151 | guid = "a6f99e90-7a75-4384-847a-29c9a86c6279" |
| 152 | start-lba = <0x00000000> |
| 153 | end-lba = <0x00000003> |
| 154 | attr = <0x00000000> |
| 155 | owner = "afb995cd-9354-4333-9ea2-bd62ccaedb22" |
| 156 | }; |
| 157 | |
| 158 | fip { |
| 159 | guid = "1eccc9bc-9a5f-43d0-bcd3-466fd21c9a92" |
| 160 | start-lba = <0x00000004> |
| 161 | end-lba = <0x00040003> |
| 162 | attr = <0x00000000> |
| 163 | owner = "afb995cd-9354-4333-9ea2-bd62ccaedb22" |
| 164 | }; |
| 165 | |
| 166 | uefi-var { |
| 167 | guid = "1022a92b-4b4a-47b4-94cb-35faf5a45dc2" |
| 168 | start-lba = <0x00040004> |
| 169 | end-lba = <0x00080003> |
| 170 | attr = <0x00000000> |
| 171 | owner = "ed32d533-99e6-4209-9cc0-2d72cdd998a7" |
| 172 | }; |
| 173 | }; |
| 174 | }; |
| 175 | |
| 176 | Configuration via GUID Partition Table (GPT) |
| 177 | """""""""""""""""""""""""""""""""""""""""""" |
| 178 | The UEFI specification defines a standard layout for physical storage devices where storage |
| 179 | partitions are described by partition entries within the GUID Partition Table. During |
| 180 | initialization, the Block Storage SP will read the GPT and iterate over partition entries, |
| 181 | identifying those with the secure block store partition type GUID. Each entry contains the |
| 182 | following: |
| 183 | |
| 184 | .. list-table:: |
| 185 | :header-rows: 1 |
| 186 | |
| 187 | * - Offset |
| 188 | - Length |
| 189 | - contents |
| 190 | * - 0 |
| 191 | - 16 bytes |
| 192 | - PartitionTypeGUID - Secure Block Store GUID |
| 193 | * - 16 |
| 194 | - 16 bytes |
| 195 | - UniquePartitionGUID |
| 196 | * - 32 |
| 197 | - 8 bytes |
| 198 | - Starting LBA |
| 199 | * - 40 |
| 200 | - 8 bytes |
| 201 | - Ending LBA |
| 202 | * - 48 |
| 203 | - 8 bytes |
| 204 | - Attributes (e.g. read-only) |
| 205 | * - 56 |
| 206 | - 72 bytes |
| 207 | - PartitionName - Holds canonical UUID string for owner. |
| 208 | |
| 209 | Design Description |
| 210 | ------------------ |
| 211 | The block storage service provider conforms to the same model as other service providers |
| 212 | within the TS project. Service requests from clients are received by a service provider |
| 213 | that is responsible for parameter deserialization/serialization and service level access |
| 214 | control. Block storage operations are delegated to a backend *block_store* that provides |
| 215 | block-level storage in some way. There is much flexibility to realize the backend block-level |
| 216 | storage in different ways, allowing platform integrators to use alternative *block_store* |
| 217 | realizations to provide storage solutions that meet specific product requirements. |
| 218 | |
| 219 | The following class diagram illustrates the block storage service provider model: |
| 220 | |
| 221 | .. uml:: uml/BlockStorageProvider.puml |
| 222 | |
| 223 | Block Store |
| 224 | ^^^^^^^^^^^ |
| 225 | The *block_store* component defines a virtual interface for block IO operations. Alternative |
| 226 | concrete *block_store* implementations are supported. Some *block_store* components are stackable |
| 227 | over other *block_store* components to add features such as store partitioning or block |
| 228 | authentication. Separation of functionality into stackable *block_store* components gives |
| 229 | platform integrators the flexibility to create alternative storage solutions with different |
| 230 | security/cost tradeoffs. The base *block_store* interface is defined in:: |
| 231 | |
| 232 | components/service/block_storage/block_store/block_store.h |
| 233 | |
| 234 | Components that implement the *block_store* interface are located in subdirectories beneath |
| 235 | ``components/service/block_storage/block_store``. A *block_device* is class of *block_store* |
| 236 | that actually provides block-level storage. In a stack of *block_store* components, a |
| 237 | *block_device* will always live at the bottom. The following layer diagram illustrates a |
| 238 | typical block storage deployment where storage is provided by a stack of *block_store* components: |
| 239 | |
| 240 | .. image:: image/block-storage-layers.svg |
| 241 | |
| 242 | Some block devices supported in the TS project (located under: |
| 243 | ``components/service/block_storage/block_store/block_device``) are: |
| 244 | |
| 245 | - **ram_block_store** - stores blocks in RAM. Intended for test purposes. |
| 246 | - **null_block_store** - a store with no real storage. Always accepts legal writes and returns |
| 247 | zeros for reads. |
| 248 | - **fvb_block_store** - an adapter that uses a UEFI firmware volume block driver to access |
| 249 | storage. Can be used with drivers from the EDK2 project. |
| 250 | |
| 251 | Other supported block_store components: |
| 252 | |
| 253 | - **partitioned_block_store** - a stackable *block_store* that presents an underlying *block_store* |
| 254 | as a set of configurable storage partitions. |
| 255 | - **block_storage_client** - communicates with a remote block storage service provider to provide |
| 256 | storage. |
| 257 | |
| 258 | -------------- |
| 259 | |
| 260 | *Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.* |
| 261 | |
| 262 | SPDX-License-Identifier: BSD-3-Clause |