Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 1 | From c4eaf83548eed4ed6194ff9e1368d6ae65f4ebf9 Mon Sep 17 00:00:00 2001 |
| 2 | From: Julian Hall <julian.hall@arm.com> |
| 3 | Date: Thu, 2 Dec 2021 17:27:55 +0000 |
| 4 | Subject: [PATCH] Add UEFI variable support for QueryVariableInfo |
| 5 | |
| 6 | Adds support for the UEFI QueryVariableInfo operation. The total |
| 7 | store size currently relies on pre-configured values, set for a |
| 8 | particular deployment. Ideally, this information would be read |
| 9 | from the storage backend. This facility is not however yet |
| 10 | supported by the storage backend interface or by any PSA |
| 11 | storage backend storage providers. |
| 12 | |
| 13 | Signed-off-by: Julian Hall <julian.hall@arm.com> |
| 14 | Change-Id: I971252831f7e478914d736c672d184a371e64502 |
| 15 | |
| 16 | Upstream-Status: Pending [Not submitted to upstream yet] |
| 17 | Signed-off-by: Vishnu Banavath <vishnu.banavath@arm.com> |
| 18 | |
| 19 | |
| 20 | --- |
| 21 | .../backend/test/variable_store_tests.cpp | 89 +++++++- |
| 22 | .../backend/uefi_variable_store.c | 213 ++++++++++++++---- |
| 23 | .../backend/uefi_variable_store.h | 39 +++- |
| 24 | .../client/cpp/smm_variable_client.cpp | 66 ++++++ |
| 25 | .../client/cpp/smm_variable_client.h | 7 + |
| 26 | .../provider/smm_variable_provider.c | 31 ++- |
| 27 | .../service/smm_variable_service_tests.cpp | 55 ++++- |
| 28 | 7 files changed, 445 insertions(+), 55 deletions(-) |
| 29 | |
| 30 | diff --git a/components/service/smm_variable/backend/test/variable_store_tests.cpp b/components/service/smm_variable/backend/test/variable_store_tests.cpp |
| 31 | index 578f118f..e90c1067 100644 |
| 32 | --- a/components/service/smm_variable/backend/test/variable_store_tests.cpp |
| 33 | +++ b/components/service/smm_variable/backend/test/variable_store_tests.cpp |
| 34 | @@ -27,6 +27,18 @@ TEST_GROUP(UefiVariableStoreTests) |
| 35 | |
| 36 | UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status); |
| 37 | |
| 38 | + uefi_variable_store_set_storage_limits( |
| 39 | + &m_uefi_variable_store, |
| 40 | + EFI_VARIABLE_NON_VOLATILE, |
| 41 | + STORE_CAPACITY, |
| 42 | + MAX_VARIABLE_SIZE); |
| 43 | + |
| 44 | + uefi_variable_store_set_storage_limits( |
| 45 | + &m_uefi_variable_store, |
| 46 | + 0, |
| 47 | + STORE_CAPACITY, |
| 48 | + MAX_VARIABLE_SIZE); |
| 49 | + |
| 50 | setup_common_guid(); |
| 51 | } |
| 52 | |
| 53 | @@ -152,6 +164,33 @@ TEST_GROUP(UefiVariableStoreTests) |
| 54 | return status; |
| 55 | } |
| 56 | |
| 57 | + efi_status_t query_variable_info( |
| 58 | + uint32_t attributes, |
| 59 | + size_t *max_variable_storage_size, |
| 60 | + size_t *remaining_variable_storage_size, |
| 61 | + size_t *max_variable_size) |
| 62 | + { |
| 63 | + SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO query; |
| 64 | + |
| 65 | + query.MaximumVariableStorageSize = 0; |
| 66 | + query.RemainingVariableStorageSize = 0; |
| 67 | + query.MaximumVariableSize = 0; |
| 68 | + query.Attributes = attributes; |
| 69 | + |
| 70 | + efi_status_t status = uefi_variable_store_query_variable_info( |
| 71 | + &m_uefi_variable_store, |
| 72 | + &query); |
| 73 | + |
| 74 | + if (status == EFI_SUCCESS) { |
| 75 | + |
| 76 | + *max_variable_storage_size = query.MaximumVariableStorageSize; |
| 77 | + *remaining_variable_storage_size = query.RemainingVariableStorageSize; |
| 78 | + *max_variable_size = query.MaximumVariableSize; |
| 79 | + } |
| 80 | + |
| 81 | + return status; |
| 82 | + } |
| 83 | + |
| 84 | efi_status_t set_check_var_property( |
| 85 | const std::wstring &name, |
| 86 | const VAR_CHECK_VARIABLE_PROPERTY &check_property) |
| 87 | @@ -195,7 +234,8 @@ TEST_GROUP(UefiVariableStoreTests) |
| 88 | |
| 89 | if (info && (info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE)) { |
| 90 | |
| 91 | - struct storage_backend *storage_backend = m_uefi_variable_store.persistent_store; |
| 92 | + struct storage_backend *storage_backend = |
| 93 | + m_uefi_variable_store.persistent_store.storage_backend; |
| 94 | |
| 95 | storage_backend->interface->remove( |
| 96 | storage_backend->context, |
| 97 | @@ -220,9 +260,24 @@ TEST_GROUP(UefiVariableStoreTests) |
| 98 | m_volatile_backend); |
| 99 | |
| 100 | UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status); |
| 101 | + |
| 102 | + uefi_variable_store_set_storage_limits( |
| 103 | + &m_uefi_variable_store, |
| 104 | + EFI_VARIABLE_NON_VOLATILE, |
| 105 | + STORE_CAPACITY, |
| 106 | + MAX_VARIABLE_SIZE); |
| 107 | + |
| 108 | + uefi_variable_store_set_storage_limits( |
| 109 | + &m_uefi_variable_store, |
| 110 | + 0, |
| 111 | + STORE_CAPACITY, |
| 112 | + MAX_VARIABLE_SIZE); |
| 113 | } |
| 114 | |
| 115 | static const size_t MAX_VARIABLES = 10; |
| 116 | + static const size_t MAX_VARIABLE_SIZE = 100; |
| 117 | + static const size_t STORE_CAPACITY = 1000; |
| 118 | + |
| 119 | static const uint32_t OWNER_ID = 100; |
| 120 | static const size_t VARIABLE_BUFFER_SIZE = 1024; |
| 121 | |
| 122 | @@ -265,6 +320,22 @@ TEST(UefiVariableStoreTests, setGetRoundtrip) |
| 123 | /* Expect the append write operation to have extended the variable */ |
| 124 | UNSIGNED_LONGLONGS_EQUAL(expected_output.size(), output_data.size()); |
| 125 | LONGS_EQUAL(0, expected_output.compare(output_data)); |
| 126 | + |
| 127 | + /* Expect query_variable_info to return consistent values */ |
| 128 | + size_t max_variable_storage_size = 0; |
| 129 | + size_t remaining_variable_storage_size = 0; |
| 130 | + size_t max_variable_size = 0; |
| 131 | + |
| 132 | + status = query_variable_info( |
| 133 | + 0, |
| 134 | + &max_variable_storage_size, |
| 135 | + &remaining_variable_storage_size, |
| 136 | + &max_variable_size); |
| 137 | + UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status); |
| 138 | + |
| 139 | + UNSIGNED_LONGLONGS_EQUAL(STORE_CAPACITY, max_variable_storage_size); |
| 140 | + UNSIGNED_LONGLONGS_EQUAL(MAX_VARIABLE_SIZE, max_variable_size); |
| 141 | + UNSIGNED_LONGLONGS_EQUAL(STORE_CAPACITY - expected_output.size(), remaining_variable_storage_size); |
| 142 | } |
| 143 | |
| 144 | TEST(UefiVariableStoreTests, persistentSetGet) |
| 145 | @@ -311,6 +382,22 @@ TEST(UefiVariableStoreTests, persistentSetGet) |
| 146 | /* Still expect got variable data to be the same as the set value */ |
| 147 | UNSIGNED_LONGLONGS_EQUAL(expected_output.size(), output_data.size()); |
| 148 | LONGS_EQUAL(0, expected_output.compare(output_data)); |
| 149 | + |
| 150 | + /* Expect query_variable_info to return consistent values */ |
| 151 | + size_t max_variable_storage_size = 0; |
| 152 | + size_t remaining_variable_storage_size = 0; |
| 153 | + size_t max_variable_size = 0; |
| 154 | + |
| 155 | + status = query_variable_info( |
| 156 | + EFI_VARIABLE_NON_VOLATILE, |
| 157 | + &max_variable_storage_size, |
| 158 | + &remaining_variable_storage_size, |
| 159 | + &max_variable_size); |
| 160 | + UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, status); |
| 161 | + |
| 162 | + UNSIGNED_LONGLONGS_EQUAL(STORE_CAPACITY, max_variable_storage_size); |
| 163 | + UNSIGNED_LONGLONGS_EQUAL(MAX_VARIABLE_SIZE, max_variable_size); |
| 164 | + UNSIGNED_LONGLONGS_EQUAL(STORE_CAPACITY - expected_output.size(), remaining_variable_storage_size); |
| 165 | } |
| 166 | |
| 167 | TEST(UefiVariableStoreTests, removeVolatile) |
| 168 | diff --git a/components/service/smm_variable/backend/uefi_variable_store.c b/components/service/smm_variable/backend/uefi_variable_store.c |
| 169 | index bcb85995..ed50eaf9 100644 |
| 170 | --- a/components/service/smm_variable/backend/uefi_variable_store.c |
| 171 | +++ b/components/service/smm_variable/backend/uefi_variable_store.c |
| 172 | @@ -46,8 +46,15 @@ static efi_status_t load_variable_data( |
| 173 | SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *var, |
| 174 | size_t max_data_len); |
| 175 | |
| 176 | -static psa_status_t append_write( |
| 177 | - struct storage_backend *storage_backend, |
| 178 | +static psa_status_t store_overwrite( |
| 179 | + struct delegate_variable_store *delegate_store, |
| 180 | + uint32_t client_id, |
| 181 | + uint64_t uid, |
| 182 | + size_t data_length, |
| 183 | + const void *data); |
| 184 | + |
| 185 | +static psa_status_t store_append_write( |
| 186 | + struct delegate_variable_store *delegate_store, |
| 187 | uint32_t client_id, |
| 188 | uint64_t uid, |
| 189 | size_t data_length, |
| 190 | @@ -56,6 +63,15 @@ static psa_status_t append_write( |
| 191 | static void purge_orphan_index_entries( |
| 192 | struct uefi_variable_store *context); |
| 193 | |
| 194 | +static struct delegate_variable_store *select_delegate_store( |
| 195 | + struct uefi_variable_store *context, |
| 196 | + uint32_t attributes); |
| 197 | + |
| 198 | +static size_t space_used( |
| 199 | + struct uefi_variable_store *context, |
| 200 | + uint32_t attributes, |
| 201 | + struct storage_backend *storage_backend); |
| 202 | + |
| 203 | static efi_status_t psa_to_efi_storage_status( |
| 204 | psa_status_t psa_status); |
| 205 | |
| 206 | @@ -66,6 +82,10 @@ static efi_status_t check_name_terminator( |
| 207 | /* Private UID for storing the variable index */ |
| 208 | #define VARIABLE_INDEX_STORAGE_UID (1) |
| 209 | |
| 210 | +/* Default maximum variable size - |
| 211 | + * may be overridden using uefi_variable_store_set_storage_limits() |
| 212 | + */ |
| 213 | +#define DEFAULT_MAX_VARIABLE_SIZE (2048) |
| 214 | |
| 215 | efi_status_t uefi_variable_store_init( |
| 216 | struct uefi_variable_store *context, |
| 217 | @@ -76,8 +96,17 @@ efi_status_t uefi_variable_store_init( |
| 218 | { |
| 219 | efi_status_t status = EFI_SUCCESS; |
| 220 | |
| 221 | - context->persistent_store = persistent_store; |
| 222 | - context->volatile_store = volatile_store; |
| 223 | + /* Initialise persistent store defaults */ |
| 224 | + context->persistent_store.is_nv = true; |
| 225 | + context->persistent_store.max_variable_size = DEFAULT_MAX_VARIABLE_SIZE; |
| 226 | + context->persistent_store.total_capacity = DEFAULT_MAX_VARIABLE_SIZE * max_variables; |
| 227 | + context->persistent_store.storage_backend = persistent_store; |
| 228 | + |
| 229 | + /* Initialise volatile store defaults */ |
| 230 | + context->volatile_store.is_nv = false; |
| 231 | + context->volatile_store.max_variable_size = DEFAULT_MAX_VARIABLE_SIZE; |
| 232 | + context->volatile_store.total_capacity = DEFAULT_MAX_VARIABLE_SIZE * max_variables; |
| 233 | + context->volatile_store.storage_backend = volatile_store; |
| 234 | |
| 235 | context->owner_id = owner_id; |
| 236 | context->is_boot_service = true; |
| 237 | @@ -116,6 +145,20 @@ void uefi_variable_store_deinit( |
| 238 | context->index_sync_buffer = NULL; |
| 239 | } |
| 240 | |
| 241 | +void uefi_variable_store_set_storage_limits( |
| 242 | + struct uefi_variable_store *context, |
| 243 | + uint32_t attributes, |
| 244 | + size_t total_capacity, |
| 245 | + size_t max_variable_size) |
| 246 | +{ |
| 247 | + struct delegate_variable_store *delegate_store = select_delegate_store( |
| 248 | + context, |
| 249 | + attributes); |
| 250 | + |
| 251 | + delegate_store->total_capacity = total_capacity; |
| 252 | + delegate_store->max_variable_size = max_variable_size; |
| 253 | +} |
| 254 | + |
| 255 | efi_status_t uefi_variable_store_set_variable( |
| 256 | struct uefi_variable_store *context, |
| 257 | const SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *var) |
| 258 | @@ -284,12 +327,24 @@ efi_status_t uefi_variable_store_get_next_variable_name( |
| 259 | |
| 260 | efi_status_t uefi_variable_store_query_variable_info( |
| 261 | struct uefi_variable_store *context, |
| 262 | - SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *cur) |
| 263 | + SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *var_info) |
| 264 | { |
| 265 | - efi_status_t status = EFI_UNSUPPORTED; |
| 266 | + struct delegate_variable_store *delegate_store = select_delegate_store( |
| 267 | + context, |
| 268 | + var_info->Attributes); |
| 269 | |
| 270 | + size_t total_used = space_used( |
| 271 | + context, |
| 272 | + var_info->Attributes, |
| 273 | + delegate_store->storage_backend); |
| 274 | |
| 275 | - return status; |
| 276 | + var_info->MaximumVariableSize = delegate_store->max_variable_size; |
| 277 | + var_info->MaximumVariableStorageSize = delegate_store->total_capacity; |
| 278 | + var_info->RemainingVariableStorageSize = (total_used < delegate_store->total_capacity) ? |
| 279 | + delegate_store->total_capacity - total_used : |
| 280 | + 0; |
| 281 | + |
| 282 | + return EFI_SUCCESS; |
| 283 | } |
| 284 | |
| 285 | efi_status_t uefi_variable_store_exit_boot_service( |
| 286 | @@ -375,7 +430,7 @@ efi_status_t uefi_variable_store_get_var_check_property( |
| 287 | static void load_variable_index( |
| 288 | struct uefi_variable_store *context) |
| 289 | { |
| 290 | - struct storage_backend *persistent_store = context->persistent_store; |
| 291 | + struct storage_backend *persistent_store = context->persistent_store.storage_backend; |
| 292 | |
| 293 | if (persistent_store) { |
| 294 | |
| 295 | @@ -413,7 +468,7 @@ static efi_status_t sync_variable_index( |
| 296 | |
| 297 | if (is_dirty) { |
| 298 | |
| 299 | - struct storage_backend *persistent_store = context->persistent_store; |
| 300 | + struct storage_backend *persistent_store = context->persistent_store.storage_backend; |
| 301 | |
| 302 | if (persistent_store) { |
| 303 | |
| 304 | @@ -501,30 +556,27 @@ static efi_status_t store_variable_data( |
| 305 | const uint8_t *data = (const uint8_t*)var + |
| 306 | SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_DATA_OFFSET(var); |
| 307 | |
| 308 | - bool is_nv = (info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE); |
| 309 | - |
| 310 | - struct storage_backend *storage_backend = (is_nv) ? |
| 311 | - context->persistent_store : |
| 312 | - context->volatile_store; |
| 313 | + struct delegate_variable_store *delegate_store = select_delegate_store( |
| 314 | + context, |
| 315 | + info->metadata.attributes); |
| 316 | |
| 317 | - if (storage_backend) { |
| 318 | + if (delegate_store->storage_backend) { |
| 319 | |
| 320 | if (!(var->Attributes & EFI_VARIABLE_APPEND_WRITE)) { |
| 321 | |
| 322 | /* Create or overwrite variable data */ |
| 323 | - psa_status = storage_backend->interface->set( |
| 324 | - storage_backend->context, |
| 325 | + psa_status = store_overwrite( |
| 326 | + delegate_store, |
| 327 | context->owner_id, |
| 328 | info->metadata.uid, |
| 329 | data_len, |
| 330 | - data, |
| 331 | - PSA_STORAGE_FLAG_NONE); |
| 332 | + data); |
| 333 | } |
| 334 | else { |
| 335 | |
| 336 | /* Append new data to existing variable data */ |
| 337 | - psa_status = append_write( |
| 338 | - storage_backend, |
| 339 | + psa_status = store_append_write( |
| 340 | + delegate_store, |
| 341 | context->owner_id, |
| 342 | info->metadata.uid, |
| 343 | data_len, |
| 344 | @@ -532,7 +584,7 @@ static efi_status_t store_variable_data( |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | - if ((psa_status != PSA_SUCCESS) && is_nv) { |
| 349 | + if ((psa_status != PSA_SUCCESS) && delegate_store->is_nv) { |
| 350 | |
| 351 | /* A storage failure has occurred so attempt to fix any |
| 352 | * mismatch between the variable index and stored NV variables. |
| 353 | @@ -551,16 +603,14 @@ static efi_status_t remove_variable_data( |
| 354 | |
| 355 | if (info->is_variable_set) { |
| 356 | |
| 357 | - bool is_nv = (info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE); |
| 358 | + struct delegate_variable_store *delegate_store = select_delegate_store( |
| 359 | + context, |
| 360 | + info->metadata.attributes); |
| 361 | |
| 362 | - struct storage_backend *storage_backend = (is_nv) ? |
| 363 | - context->persistent_store : |
| 364 | - context->volatile_store; |
| 365 | + if (delegate_store->storage_backend) { |
| 366 | |
| 367 | - if (storage_backend) { |
| 368 | - |
| 369 | - psa_status = storage_backend->interface->remove( |
| 370 | - storage_backend->context, |
| 371 | + psa_status = delegate_store->storage_backend->interface->remove( |
| 372 | + delegate_store->storage_backend->context, |
| 373 | context->owner_id, |
| 374 | info->metadata.uid); |
| 375 | } |
| 376 | @@ -580,16 +630,14 @@ static efi_status_t load_variable_data( |
| 377 | uint8_t *data = (uint8_t*)var + |
| 378 | SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_DATA_OFFSET(var); |
| 379 | |
| 380 | - bool is_nv = (info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE); |
| 381 | + struct delegate_variable_store *delegate_store = select_delegate_store( |
| 382 | + context, |
| 383 | + info->metadata.attributes); |
| 384 | |
| 385 | - struct storage_backend *storage_backend = (is_nv) ? |
| 386 | - context->persistent_store : |
| 387 | - context->volatile_store; |
| 388 | + if (delegate_store->storage_backend) { |
| 389 | |
| 390 | - if (storage_backend) { |
| 391 | - |
| 392 | - psa_status = storage_backend->interface->get( |
| 393 | - storage_backend->context, |
| 394 | + psa_status = delegate_store->storage_backend->interface->get( |
| 395 | + delegate_store->storage_backend->context, |
| 396 | context->owner_id, |
| 397 | info->metadata.uid, |
| 398 | 0, |
| 399 | @@ -603,8 +651,29 @@ static efi_status_t load_variable_data( |
| 400 | return psa_to_efi_storage_status(psa_status); |
| 401 | } |
| 402 | |
| 403 | -static psa_status_t append_write( |
| 404 | - struct storage_backend *storage_backend, |
| 405 | +static psa_status_t store_overwrite( |
| 406 | + struct delegate_variable_store *delegate_store, |
| 407 | + uint32_t client_id, |
| 408 | + uint64_t uid, |
| 409 | + size_t data_length, |
| 410 | + const void *data) |
| 411 | +{ |
| 412 | + /* Police maximum variable size limit */ |
| 413 | + if (data_length > delegate_store->max_variable_size) return PSA_ERROR_INVALID_ARGUMENT; |
| 414 | + |
| 415 | + psa_status_t psa_status = delegate_store->storage_backend->interface->set( |
| 416 | + delegate_store->storage_backend->context, |
| 417 | + client_id, |
| 418 | + uid, |
| 419 | + data_length, |
| 420 | + data, |
| 421 | + PSA_STORAGE_FLAG_NONE); |
| 422 | + |
| 423 | + return psa_status; |
| 424 | +} |
| 425 | + |
| 426 | +static psa_status_t store_append_write( |
| 427 | + struct delegate_variable_store *delegate_store, |
| 428 | uint32_t client_id, |
| 429 | uint64_t uid, |
| 430 | size_t data_length, |
| 431 | @@ -614,8 +683,8 @@ static psa_status_t append_write( |
| 432 | |
| 433 | if (data_length == 0) return PSA_SUCCESS; |
| 434 | |
| 435 | - psa_status_t psa_status = storage_backend->interface->get_info( |
| 436 | - storage_backend->context, |
| 437 | + psa_status_t psa_status = delegate_store->storage_backend->interface->get_info( |
| 438 | + delegate_store->storage_backend->context, |
| 439 | client_id, |
| 440 | uid, |
| 441 | &storage_info); |
| 442 | @@ -628,6 +697,9 @@ static psa_status_t append_write( |
| 443 | /* Defend against integer overflow */ |
| 444 | if (new_size < storage_info.size) return PSA_ERROR_INVALID_ARGUMENT; |
| 445 | |
| 446 | + /* Police maximum variable size limit */ |
| 447 | + if (new_size > delegate_store->max_variable_size) return PSA_ERROR_INVALID_ARGUMENT; |
| 448 | + |
| 449 | /* Storage backend doesn't support an append operation so we need |
| 450 | * need to read the current variable data, extend it and write it back. |
| 451 | */ |
| 452 | @@ -635,8 +707,8 @@ static psa_status_t append_write( |
| 453 | if (!rw_buf) return PSA_ERROR_INSUFFICIENT_MEMORY; |
| 454 | |
| 455 | size_t old_size = 0; |
| 456 | - psa_status = storage_backend->interface->get( |
| 457 | - storage_backend->context, |
| 458 | + psa_status = delegate_store->storage_backend->interface->get( |
| 459 | + delegate_store->storage_backend->context, |
| 460 | client_id, |
| 461 | uid, |
| 462 | 0, |
| 463 | @@ -651,8 +723,8 @@ static psa_status_t append_write( |
| 464 | /* Extend the variable data */ |
| 465 | memcpy(&rw_buf[old_size], data, data_length); |
| 466 | |
| 467 | - psa_status = storage_backend->interface->set( |
| 468 | - storage_backend->context, |
| 469 | + psa_status = delegate_store->storage_backend->interface->set( |
| 470 | + delegate_store->storage_backend->context, |
| 471 | client_id, |
| 472 | uid, |
| 473 | old_size + data_length, |
| 474 | @@ -692,7 +764,7 @@ static void purge_orphan_index_entries( |
| 475 | if (info->is_variable_set && (info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE)) { |
| 476 | |
| 477 | struct psa_storage_info_t storage_info; |
| 478 | - struct storage_backend *storage_backend = context->persistent_store; |
| 479 | + struct storage_backend *storage_backend = context->persistent_store.storage_backend; |
| 480 | |
| 481 | psa_status_t psa_status = storage_backend->interface->get_info( |
| 482 | storage_backend->context, |
| 483 | @@ -714,6 +786,53 @@ static void purge_orphan_index_entries( |
| 484 | if (any_orphans) sync_variable_index(context); |
| 485 | } |
| 486 | |
| 487 | +static struct delegate_variable_store *select_delegate_store( |
| 488 | + struct uefi_variable_store *context, |
| 489 | + uint32_t attributes) |
| 490 | +{ |
| 491 | + bool is_nv = (attributes & EFI_VARIABLE_NON_VOLATILE); |
| 492 | + |
| 493 | + return (is_nv) ? |
| 494 | + &context->persistent_store : |
| 495 | + &context->volatile_store; |
| 496 | +} |
| 497 | + |
| 498 | +static size_t space_used( |
| 499 | + struct uefi_variable_store *context, |
| 500 | + uint32_t attributes, |
| 501 | + struct storage_backend *storage_backend) |
| 502 | +{ |
| 503 | + if (!storage_backend) return 0; |
| 504 | + |
| 505 | + size_t total_used = 0; |
| 506 | + struct variable_index_iterator iter; |
| 507 | + variable_index_iterator_first(&iter, &context->variable_index); |
| 508 | + |
| 509 | + while (!variable_index_iterator_is_done(&iter)) { |
| 510 | + |
| 511 | + struct variable_info *info = variable_index_iterator_current(&iter); |
| 512 | + |
| 513 | + if (info->is_variable_set && |
| 514 | + ((info->metadata.attributes & EFI_VARIABLE_NON_VOLATILE) == |
| 515 | + (attributes & EFI_VARIABLE_NON_VOLATILE))) { |
| 516 | + |
| 517 | + struct psa_storage_info_t storage_info; |
| 518 | + |
| 519 | + psa_status_t psa_status = storage_backend->interface->get_info( |
| 520 | + storage_backend->context, |
| 521 | + context->owner_id, |
| 522 | + info->metadata.uid, |
| 523 | + &storage_info); |
| 524 | + |
| 525 | + if (psa_status == PSA_SUCCESS) total_used += storage_info.size; |
| 526 | + } |
| 527 | + |
| 528 | + variable_index_iterator_next(&iter); |
| 529 | + } |
| 530 | + |
| 531 | + return total_used; |
| 532 | +} |
| 533 | + |
| 534 | static efi_status_t psa_to_efi_storage_status( |
| 535 | psa_status_t psa_status) |
| 536 | { |
| 537 | diff --git a/components/service/smm_variable/backend/uefi_variable_store.h b/components/service/smm_variable/backend/uefi_variable_store.h |
| 538 | index fe0f24af..cc992067 100644 |
| 539 | --- a/components/service/smm_variable/backend/uefi_variable_store.h |
| 540 | +++ b/components/service/smm_variable/backend/uefi_variable_store.h |
| 541 | @@ -20,6 +20,20 @@ |
| 542 | extern "C" { |
| 543 | #endif |
| 544 | |
| 545 | +/** |
| 546 | + * \brief delegate_variable_store structure definition |
| 547 | + * |
| 548 | + * A delegate_variable_store combines an association with a concrete |
| 549 | + * storage backend and a set of limits parameters. |
| 550 | + */ |
| 551 | +struct delegate_variable_store |
| 552 | +{ |
| 553 | + bool is_nv; |
| 554 | + size_t total_capacity; |
| 555 | + size_t max_variable_size; |
| 556 | + struct storage_backend *storage_backend; |
| 557 | +}; |
| 558 | + |
| 559 | /** |
| 560 | * \brief uefi_variable_store structure definition |
| 561 | * |
| 562 | @@ -35,8 +49,8 @@ struct uefi_variable_store |
| 563 | uint8_t *index_sync_buffer; |
| 564 | size_t index_sync_buffer_size; |
| 565 | struct variable_index variable_index; |
| 566 | - struct storage_backend *persistent_store; |
| 567 | - struct storage_backend *volatile_store; |
| 568 | + struct delegate_variable_store persistent_store; |
| 569 | + struct delegate_variable_store volatile_store; |
| 570 | }; |
| 571 | |
| 572 | /** |
| 573 | @@ -69,6 +83,23 @@ efi_status_t uefi_variable_store_init( |
| 574 | void uefi_variable_store_deinit( |
| 575 | struct uefi_variable_store *context); |
| 576 | |
| 577 | +/** |
| 578 | + * @brief Set storage limits |
| 579 | + * |
| 580 | + * Overrides the default limits for the specified storage space. These |
| 581 | + * values are reflected in the values returned by QueryVariableInfo. |
| 582 | + * |
| 583 | + * @param[in] context uefi_variable_store instance |
| 584 | + * @param[in] attributes EFI_VARIABLE_NON_VOLATILE or 0 |
| 585 | + * @param[in] total_capacity The total storage capacity in bytes |
| 586 | + * @param[in] max_variable_size Variable size limit |
| 587 | + */ |
| 588 | +void uefi_variable_store_set_storage_limits( |
| 589 | + struct uefi_variable_store *context, |
| 590 | + uint32_t attributes, |
| 591 | + size_t total_capacity, |
| 592 | + size_t max_variable_size); |
| 593 | + |
| 594 | /** |
| 595 | * @brief Set variable |
| 596 | * |
| 597 | @@ -123,13 +154,13 @@ efi_status_t uefi_variable_store_get_next_variable_name( |
| 598 | * @brief Query for variable info |
| 599 | * |
| 600 | * @param[in] context uefi_variable_store instance |
| 601 | - * @param[out] info Returns info |
| 602 | + * @param[inout] var_info Returns info |
| 603 | * |
| 604 | * @return EFI_SUCCESS if succesful |
| 605 | */ |
| 606 | efi_status_t uefi_variable_store_query_variable_info( |
| 607 | struct uefi_variable_store *context, |
| 608 | - SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *cur); |
| 609 | + SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *var_info); |
| 610 | |
| 611 | /** |
| 612 | * @brief Exit boot service |
| 613 | diff --git a/components/service/smm_variable/client/cpp/smm_variable_client.cpp b/components/service/smm_variable/client/cpp/smm_variable_client.cpp |
| 614 | index a68b7ace..8438285b 100644 |
| 615 | --- a/components/service/smm_variable/client/cpp/smm_variable_client.cpp |
| 616 | +++ b/components/service/smm_variable/client/cpp/smm_variable_client.cpp |
| 617 | @@ -219,6 +219,72 @@ efi_status_t smm_variable_client::get_next_variable_name( |
| 618 | 0); |
| 619 | } |
| 620 | |
| 621 | +efi_status_t smm_variable_client::query_variable_info( |
| 622 | + uint32_t attributes, |
| 623 | + size_t *max_variable_storage_size, |
| 624 | + size_t *remaining_variable_storage_size, |
| 625 | + size_t *max_variable_size) |
| 626 | +{ |
| 627 | + efi_status_t efi_status = EFI_NOT_READY; |
| 628 | + |
| 629 | + size_t req_len = sizeof(SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO); |
| 630 | + rpc_call_handle call_handle; |
| 631 | + uint8_t *req_buf; |
| 632 | + |
| 633 | + call_handle = rpc_caller_begin(m_caller, &req_buf, req_len); |
| 634 | + |
| 635 | + if (call_handle) { |
| 636 | + |
| 637 | + uint8_t *resp_buf; |
| 638 | + size_t resp_len; |
| 639 | + rpc_opstatus_t opstatus; |
| 640 | + |
| 641 | + SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *query = |
| 642 | + (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO*)req_buf; |
| 643 | + |
| 644 | + query->Attributes = attributes; |
| 645 | + query->MaximumVariableSize = 0; |
| 646 | + query->MaximumVariableStorageSize = 0; |
| 647 | + query->RemainingVariableStorageSize = 0; |
| 648 | + |
| 649 | + m_err_rpc_status = rpc_caller_invoke(m_caller, call_handle, |
| 650 | + SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO, &opstatus, &resp_buf, &resp_len); |
| 651 | + |
| 652 | + if (m_err_rpc_status == TS_RPC_CALL_ACCEPTED) { |
| 653 | + |
| 654 | + efi_status = opstatus; |
| 655 | + |
| 656 | + if (efi_status == EFI_SUCCESS) { |
| 657 | + |
| 658 | + if (resp_len >= sizeof(SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO)) { |
| 659 | + |
| 660 | + query = (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO*)resp_buf; |
| 661 | + |
| 662 | + *max_variable_storage_size = query->MaximumVariableStorageSize; |
| 663 | + *remaining_variable_storage_size = query->RemainingVariableStorageSize; |
| 664 | + *max_variable_size = query->MaximumVariableSize; |
| 665 | + } |
| 666 | + else { |
| 667 | + |
| 668 | + efi_status = EFI_PROTOCOL_ERROR; |
| 669 | + } |
| 670 | + } |
| 671 | + else { |
| 672 | + |
| 673 | + efi_status = EFI_PROTOCOL_ERROR; |
| 674 | + } |
| 675 | + } |
| 676 | + else { |
| 677 | + |
| 678 | + efi_status = rpc_to_efi_status(); |
| 679 | + } |
| 680 | + |
| 681 | + rpc_caller_end(m_caller, call_handle); |
| 682 | + } |
| 683 | + |
| 684 | + return efi_status; |
| 685 | +} |
| 686 | + |
| 687 | efi_status_t smm_variable_client::get_next_variable_name( |
| 688 | EFI_GUID &guid, |
| 689 | std::wstring &name, |
| 690 | diff --git a/components/service/smm_variable/client/cpp/smm_variable_client.h b/components/service/smm_variable/client/cpp/smm_variable_client.h |
| 691 | index 9c36c4eb..c7973916 100644 |
| 692 | --- a/components/service/smm_variable/client/cpp/smm_variable_client.h |
| 693 | +++ b/components/service/smm_variable/client/cpp/smm_variable_client.h |
| 694 | @@ -63,6 +63,13 @@ public: |
| 695 | const EFI_GUID &guid, |
| 696 | const std::wstring &name); |
| 697 | |
| 698 | + /* Query variable info */ |
| 699 | + efi_status_t query_variable_info( |
| 700 | + uint32_t attributes, |
| 701 | + size_t *max_variable_storage_size, |
| 702 | + size_t *remaining_variable_storage_size, |
| 703 | + size_t *max_variable_size); |
| 704 | + |
| 705 | /* Get the next variable name - for enumerating store contents */ |
| 706 | efi_status_t get_next_variable_name( |
| 707 | EFI_GUID &guid, |
| 708 | diff --git a/components/service/smm_variable/provider/smm_variable_provider.c b/components/service/smm_variable/provider/smm_variable_provider.c |
| 709 | index d239a428..52e68d09 100644 |
| 710 | --- a/components/service/smm_variable/provider/smm_variable_provider.c |
| 711 | +++ b/components/service/smm_variable/provider/smm_variable_provider.c |
| 712 | @@ -252,11 +252,38 @@ static rpc_status_t set_variable_handler(void *context, struct call_req* req) |
| 713 | |
| 714 | static rpc_status_t query_variable_info_handler(void *context, struct call_req* req) |
| 715 | { |
| 716 | + efi_status_t efi_status = EFI_INVALID_PARAMETER; |
| 717 | struct smm_variable_provider *this_instance = (struct smm_variable_provider*)context; |
| 718 | |
| 719 | - /* todo */ |
| 720 | + const struct call_param_buf *req_buf = call_req_get_req_buf(req); |
| 721 | + |
| 722 | + if (req_buf->data_len >= sizeof(SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO)) { |
| 723 | + |
| 724 | + struct call_param_buf *resp_buf = call_req_get_resp_buf(req); |
| 725 | + |
| 726 | + if (resp_buf->size >= req_buf->data_len) { |
| 727 | |
| 728 | - return TS_RPC_ERROR_NOT_READY; |
| 729 | + memmove(resp_buf->data, req_buf->data, req_buf->data_len); |
| 730 | + |
| 731 | + efi_status = uefi_variable_store_query_variable_info( |
| 732 | + &this_instance->variable_store, |
| 733 | + (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO*)resp_buf->data); |
| 734 | + |
| 735 | + if (efi_status == EFI_SUCCESS) { |
| 736 | + |
| 737 | + resp_buf->data_len = sizeof(SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO); |
| 738 | + } |
| 739 | + } |
| 740 | + else { |
| 741 | + |
| 742 | + /* Reponse buffer not big enough */ |
| 743 | + efi_status = EFI_BAD_BUFFER_SIZE; |
| 744 | + } |
| 745 | + } |
| 746 | + |
| 747 | + call_req_set_opstatus(req, efi_status); |
| 748 | + |
| 749 | + return TS_RPC_CALL_ACCEPTED; |
| 750 | } |
| 751 | |
| 752 | static rpc_status_t exit_boot_service_handler(void *context, struct call_req* req) |
| 753 | diff --git a/components/service/smm_variable/test/service/smm_variable_service_tests.cpp b/components/service/smm_variable/test/service/smm_variable_service_tests.cpp |
| 754 | index 088940a8..15556e9d 100644 |
| 755 | --- a/components/service/smm_variable/test/service/smm_variable_service_tests.cpp |
| 756 | +++ b/components/service/smm_variable/test/service/smm_variable_service_tests.cpp |
| 757 | @@ -335,12 +335,38 @@ TEST(SmmVariableServiceTests, setAndGetNv) |
| 758 | TEST(SmmVariableServiceTests, enumerateStoreContents) |
| 759 | { |
| 760 | efi_status_t efi_status = EFI_SUCCESS; |
| 761 | + |
| 762 | + /* Query information about the empty variable store */ |
| 763 | + size_t nv_max_variable_storage_size = 0; |
| 764 | + size_t nv_max_variable_size = 0; |
| 765 | + size_t nv_remaining_variable_storage_size = 0; |
| 766 | + |
| 767 | + efi_status = m_client->query_variable_info( |
| 768 | + EFI_VARIABLE_NON_VOLATILE, |
| 769 | + &nv_max_variable_storage_size, |
| 770 | + &nv_remaining_variable_storage_size, |
| 771 | + &nv_max_variable_size); |
| 772 | + UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status); |
| 773 | + UNSIGNED_LONGLONGS_EQUAL(nv_max_variable_storage_size, nv_remaining_variable_storage_size); |
| 774 | + |
| 775 | + size_t v_max_variable_storage_size = 0; |
| 776 | + size_t v_max_variable_size = 0; |
| 777 | + size_t v_remaining_variable_storage_size = 0; |
| 778 | + |
| 779 | + efi_status = m_client->query_variable_info( |
| 780 | + 0, |
| 781 | + &v_max_variable_storage_size, |
| 782 | + &v_remaining_variable_storage_size, |
| 783 | + &v_max_variable_size); |
| 784 | + UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status); |
| 785 | + UNSIGNED_LONGLONGS_EQUAL(v_max_variable_storage_size, v_remaining_variable_storage_size); |
| 786 | + |
| 787 | + /* Add some variables to the store */ |
| 788 | std::wstring var_name_1 = L"varibale_1"; |
| 789 | std::wstring var_name_2 = L"varibale_2"; |
| 790 | std::wstring var_name_3 = L"varibale_3"; |
| 791 | std::string set_data = "Some variable data"; |
| 792 | |
| 793 | - /* Add some variables to the store */ |
| 794 | efi_status = m_client->set_variable( |
| 795 | m_common_guid, |
| 796 | var_name_1, |
| 797 | @@ -365,6 +391,33 @@ TEST(SmmVariableServiceTests, enumerateStoreContents) |
| 798 | |
| 799 | UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status); |
| 800 | |
| 801 | + /* Query variable info again and check it's as expected */ |
| 802 | + size_t max_variable_storage_size = 0; |
| 803 | + size_t max_variable_size = 0; |
| 804 | + size_t remaining_variable_storage_size = 0; |
| 805 | + |
| 806 | + /* Check non-volatile - two variables have been added */ |
| 807 | + efi_status = m_client->query_variable_info( |
| 808 | + EFI_VARIABLE_NON_VOLATILE, |
| 809 | + &max_variable_storage_size, |
| 810 | + &remaining_variable_storage_size, |
| 811 | + &max_variable_size); |
| 812 | + UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status); |
| 813 | + UNSIGNED_LONGLONGS_EQUAL( |
| 814 | + (nv_remaining_variable_storage_size - set_data.size() * 2), |
| 815 | + remaining_variable_storage_size); |
| 816 | + |
| 817 | + /* Check volatile - one variables have been added */ |
| 818 | + efi_status = m_client->query_variable_info( |
| 819 | + 0, |
| 820 | + &max_variable_storage_size, |
| 821 | + &remaining_variable_storage_size, |
| 822 | + &max_variable_size); |
| 823 | + UNSIGNED_LONGLONGS_EQUAL(EFI_SUCCESS, efi_status); |
| 824 | + UNSIGNED_LONGLONGS_EQUAL( |
| 825 | + (v_remaining_variable_storage_size - set_data.size() * 1), |
| 826 | + remaining_variable_storage_size); |
| 827 | + |
| 828 | /* Enumerate store contents - expect the values we added */ |
| 829 | std::wstring var_name; |
| 830 | EFI_GUID guid = {0}; |