nvram_manager.cpp revision c27c3f1c1bcc24709b3135fe99b2ee7c656e6480
1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "nvram/core/nvram_manager.h" 18 19extern "C" { 20#include <inttypes.h> 21#include <string.h> 22} // extern "C" 23 24#include <mincrypt/sha256.h> 25#include <nvram/core/logger.h> 26 27using namespace nvram::storage; 28 29namespace nvram { 30 31namespace { 32 33// Maximum size of a single space's contents. 34constexpr size_t kMaxSpaceSize = 1024; 35 36// Maximum authorization blob size; 37constexpr size_t kMaxAuthSize = 32; 38 39// The bitmask of all supported control flags. 40constexpr uint32_t kSupportedControlsMask = 41 (1 << NV_CONTROL_PERSISTENT_WRITE_LOCK) | 42 (1 << NV_CONTROL_BOOT_WRITE_LOCK) | 43 (1 << NV_CONTROL_BOOT_READ_LOCK) | 44 (1 << NV_CONTROL_WRITE_AUTHORIZATION) | 45 (1 << NV_CONTROL_READ_AUTHORIZATION) | 46 (1 << NV_CONTROL_WRITE_EXTEND); 47 48// Convert the |space.controls| bitmask to vector representation. 49nvram_result_t GetControlsVector(const NvramSpace& space, 50 Vector<nvram_control_t>* controls) { 51 for (size_t control = 0; control < sizeof(uint32_t) * 8; ++control) { 52 if (space.HasControl(control)) { 53 if (!controls->Resize(controls->size() + 1)) { 54 NVRAM_LOG_ERR("Allocation failure."); 55 return NV_RESULT_INTERNAL_ERROR; 56 } 57 (*controls)[controls->size() - 1] = static_cast<nvram_control_t>(control); 58 } 59 } 60 return NV_RESULT_SUCCESS; 61} 62 63// Constant time memory block comparison. 64bool ConstantTimeEquals(const Blob& a, const Blob& b) { 65 if (a.size() != b.size()) 66 return false; 67 68 // The volatile qualifiers prevent the compiler from making assumptions that 69 // allow shortcuts: 70 // * The entire array data must be read from memory. 71 // * Marking |result| volatile ensures the subsequent loop iterations must 72 // still store to |result|, thus avoiding the loop to exit early. 73 // This achieves the desired constant-time behavior. 74 volatile const uint8_t* data_a = a.data(); 75 volatile const uint8_t* data_b = b.data(); 76 volatile uint8_t result = 0; 77 for (size_t i = 0; i < a.size(); ++i) { 78 result |= data_a[i] ^ data_b[i]; 79 } 80 81 return result == 0; 82} 83 84// A standard minimum function. 85template <typename Type> 86const Type& min(const Type& a, const Type& b) { 87 return (a < b) ? a : b; 88} 89 90} // namespace 91 92// Looks at |request| to determine the command to execute, then invokes 93// the appropriate handler. 94void NvramManager::Dispatch(const nvram::Request& request, 95 nvram::Response* response) { 96 nvram_result_t result = NV_RESULT_INVALID_PARAMETER; 97 const nvram::RequestUnion& input = request.payload; 98 nvram::ResponseUnion* output = &response->payload; 99 100 switch (input.which()) { 101 case nvram::COMMAND_GET_INFO: 102 result = GetInfo(*input.get<COMMAND_GET_INFO>(), 103 &output->Activate<COMMAND_GET_INFO>()); 104 break; 105 case nvram::COMMAND_CREATE_SPACE: 106 result = CreateSpace(*input.get<COMMAND_CREATE_SPACE>(), 107 &output->Activate<COMMAND_CREATE_SPACE>()); 108 break; 109 case nvram::COMMAND_GET_SPACE_INFO: 110 result = GetSpaceInfo(*input.get<COMMAND_GET_SPACE_INFO>(), 111 &output->Activate<COMMAND_GET_SPACE_INFO>()); 112 break; 113 case nvram::COMMAND_DELETE_SPACE: 114 result = DeleteSpace(*input.get<COMMAND_DELETE_SPACE>(), 115 &output->Activate<COMMAND_DELETE_SPACE>()); 116 break; 117 case nvram::COMMAND_DISABLE_CREATE: 118 result = DisableCreate(*input.get<COMMAND_DISABLE_CREATE>(), 119 &output->Activate<COMMAND_DISABLE_CREATE>()); 120 break; 121 case nvram::COMMAND_WRITE_SPACE: 122 result = WriteSpace(*input.get<COMMAND_WRITE_SPACE>(), 123 &output->Activate<COMMAND_WRITE_SPACE>()); 124 break; 125 case nvram::COMMAND_READ_SPACE: 126 result = ReadSpace(*input.get<COMMAND_READ_SPACE>(), 127 &output->Activate<COMMAND_READ_SPACE>()); 128 break; 129 case nvram::COMMAND_LOCK_SPACE_WRITE: 130 result = LockSpaceWrite(*input.get<COMMAND_LOCK_SPACE_WRITE>(), 131 &output->Activate<COMMAND_LOCK_SPACE_WRITE>()); 132 break; 133 case nvram::COMMAND_LOCK_SPACE_READ: 134 result = LockSpaceRead(*input.get<COMMAND_LOCK_SPACE_READ>(), 135 &output->Activate<COMMAND_LOCK_SPACE_READ>()); 136 break; 137 } 138 139 response->result = result; 140} 141 142nvram_result_t NvramManager::GetInfo(const GetInfoRequest& /* request */, 143 GetInfoResponse* response) { 144 NVRAM_LOG_INFO("GetInfo"); 145 146 if (!Initialize()) 147 return NV_RESULT_INTERNAL_ERROR; 148 149 // TODO: Get better values for total and available size from the storage 150 // layer. 151 response->total_size = kMaxSpaceSize * kMaxSpaces; 152 response->available_size = kMaxSpaceSize * (kMaxSpaces - num_spaces_); 153 response->max_spaces = kMaxSpaces; 154 Vector<uint32_t>& space_list = response->space_list; 155 if (!space_list.Resize(num_spaces_)) { 156 NVRAM_LOG_ERR("Allocation failure."); 157 return NV_RESULT_INTERNAL_ERROR; 158 } 159 for (size_t i = 0; i < num_spaces_; ++i) { 160 space_list[i] = spaces_[i].index; 161 } 162 163 return NV_RESULT_SUCCESS; 164} 165 166nvram_result_t NvramManager::CreateSpace(const CreateSpaceRequest& request, 167 CreateSpaceResponse* /* response */) { 168 const uint32_t index = request.index; 169 NVRAM_LOG_INFO("CreateSpace Ox%" PRIx32, index); 170 171 if (!Initialize()) 172 return NV_RESULT_INTERNAL_ERROR; 173 174 if (disable_create_) { 175 NVRAM_LOG_INFO("Creation of further spaces is disabled."); 176 return NV_RESULT_OPERATION_DISABLED; 177 } 178 179 if (FindSpace(index) != kMaxSpaces) { 180 NVRAM_LOG_INFO("Space 0x%" PRIx32 " already exists.", index); 181 return NV_RESULT_SPACE_ALREADY_EXISTS; 182 } 183 184 if (num_spaces_ + 1 > kMaxSpaces) { 185 NVRAM_LOG_INFO("Too many spaces."); 186 return NV_RESULT_INVALID_PARAMETER; 187 } 188 189 if (request.size > kMaxSpaceSize) { 190 NVRAM_LOG_INFO("Create request exceeds max space size."); 191 return NV_RESULT_INVALID_PARAMETER; 192 } 193 194 if (request.authorization_value.size() > kMaxAuthSize) { 195 NVRAM_LOG_INFO("Authorization blob too large."); 196 return NV_RESULT_INVALID_PARAMETER; 197 } 198 199 uint32_t controls = 0; 200 for (uint32_t control : request.controls) { 201 controls |= (1 << control); 202 } 203 if ((controls & ~kSupportedControlsMask) != 0) { 204 NVRAM_LOG_INFO("Bad controls."); 205 return NV_RESULT_INVALID_PARAMETER; 206 } 207 if ((controls & (1 << NV_CONTROL_PERSISTENT_WRITE_LOCK)) != 0 && 208 (controls & (1 << NV_CONTROL_BOOT_WRITE_LOCK)) != 0) { 209 NVRAM_LOG_INFO("Write lock controls are exclusive."); 210 return NV_RESULT_INVALID_PARAMETER; 211 } 212 if ((controls & (1 << NV_CONTROL_WRITE_EXTEND)) != 0 && 213 request.size != SHA256_DIGEST_SIZE) { 214 NVRAM_LOG_INFO("Write-extended space size must be %d.", SHA256_DIGEST_SIZE); 215 return NV_RESULT_INVALID_PARAMETER; 216 } 217 218 // Mark the index as allocated. 219 spaces_[num_spaces_].index = index; 220 spaces_[num_spaces_].write_locked = false; 221 spaces_[num_spaces_].read_locked = false; 222 ++num_spaces_; 223 224 // Create a space record. 225 NvramSpace space; 226 space.flags = 0; 227 space.controls = controls; 228 229 // Copy the auth blob. 230 if (space.HasControl(NV_CONTROL_WRITE_AUTHORIZATION) || 231 space.HasControl(NV_CONTROL_READ_AUTHORIZATION)) { 232 if (!space.authorization_value.Assign(request.authorization_value.data(), 233 request.authorization_value.size())) { 234 NVRAM_LOG_ERR("Allocation failure."); 235 return NV_RESULT_INTERNAL_ERROR; 236 } 237 } 238 239 // Initialize the space content. 240 if (!space.contents.Resize(request.size)) { 241 NVRAM_LOG_ERR("Allocation failure."); 242 return NV_RESULT_INTERNAL_ERROR; 243 } 244 memset(space.contents.data(), 0, request.size); 245 246 // Write the header before the space data. This ensures that all space 247 // definitions present in storage are also recorded in the header. Thus, the 248 // set of spaces present in the header is always a superset of the set of 249 // spaces that have state in storage. If there's a crash after writing the 250 // header but before writing the space information, the space data will be 251 // missing in storage. The initialization code handles this by checking the 252 // for the space data corresponding to the index marked as provisional in the 253 // header. 254 nvram_result_t result; 255 if ((result = WriteHeader(Optional<uint32_t>(index))) != NV_RESULT_SUCCESS || 256 (result = WriteSpace(index, space)) != NV_RESULT_SUCCESS) { 257 --num_spaces_; 258 } 259 return result; 260} 261 262nvram_result_t NvramManager::GetSpaceInfo(const GetSpaceInfoRequest& request, 263 GetSpaceInfoResponse* response) { 264 const uint32_t index = request.index; 265 NVRAM_LOG_INFO("GetSpaceInfo Ox%" PRIx32, index); 266 267 if (!Initialize()) 268 return NV_RESULT_INTERNAL_ERROR; 269 270 SpaceRecord space_record; 271 nvram_result_t result; 272 if (!LoadSpaceRecord(index, &space_record, &result)) { 273 return result; 274 } 275 276 response->size = space_record.persistent.contents.size(); 277 278 result = GetControlsVector(space_record.persistent, &response->controls); 279 if (result != NV_RESULT_SUCCESS) { 280 return NV_RESULT_INTERNAL_ERROR; 281 } 282 283 if (space_record.persistent.HasControl(NV_CONTROL_BOOT_READ_LOCK)) { 284 response->read_locked = space_record.transient->read_locked; 285 } 286 287 if (space_record.persistent.HasControl(NV_CONTROL_PERSISTENT_WRITE_LOCK)) { 288 response->write_locked = 289 space_record.persistent.HasFlag(NvramSpace::kFlagWriteLocked); 290 } else if (space_record.persistent.HasControl(NV_CONTROL_BOOT_WRITE_LOCK)) { 291 response->write_locked = space_record.transient->write_locked; 292 } 293 294 return NV_RESULT_SUCCESS; 295} 296 297nvram_result_t NvramManager::DeleteSpace(const DeleteSpaceRequest& request, 298 DeleteSpaceResponse* /* response */) { 299 const uint32_t index = request.index; 300 NVRAM_LOG_INFO("DeleteSpace Ox%" PRIx32, index); 301 302 if (!Initialize()) 303 return NV_RESULT_INTERNAL_ERROR; 304 305 SpaceRecord space_record; 306 nvram_result_t result; 307 if (!LoadSpaceRecord(index, &space_record, &result)) { 308 return result; 309 } 310 311 result = space_record.CheckWriteAccess(request.authorization_value); 312 if (result != NV_RESULT_SUCCESS) { 313 return result; 314 } 315 316 // Delete the space. First mark the space as provisionally removed in the 317 // header. Then, delete the space data from storage. This allows orphaned 318 // space data be cleaned up after a crash. 319 SpaceListEntry tmp = spaces_[space_record.array_index]; 320 spaces_[space_record.array_index] = spaces_[num_spaces_ - 1]; 321 --num_spaces_; 322 result = WriteHeader(Optional<uint32_t>(index)); 323 if (result == NV_RESULT_SUCCESS) { 324 switch (persistence::DeleteSpace(index)) { 325 case storage::Status::kStorageError: 326 NVRAM_LOG_ERR("Failed to delete space 0x%" PRIx32 " data.", index); 327 result = NV_RESULT_INTERNAL_ERROR; 328 break; 329 case storage::Status::kNotFound: 330 // The space was missing even if it shouldn't have been. Log an error, 331 // but return success as we're in the desired state. 332 NVRAM_LOG_ERR("Space 0x%" PRIx32 " data missing on deletion.", index); 333 return NV_RESULT_SUCCESS; 334 case storage::Status::kSuccess: 335 return NV_RESULT_SUCCESS; 336 } 337 } 338 339 // Failed to delete, re-add the transient state to |spaces_|. 340 spaces_[num_spaces_] = tmp; 341 ++num_spaces_; 342 return result; 343} 344 345nvram_result_t NvramManager::DisableCreate( 346 const DisableCreateRequest& /* request */, 347 DisableCreateResponse* /* response */) { 348 NVRAM_LOG_INFO("DisableCreate"); 349 350 if (!Initialize()) 351 return NV_RESULT_INTERNAL_ERROR; 352 353 // Set the |disable_create_| flag and call |WriteHeader| to persist the flag 354 // such that it remains effective after a reboot. Make sure to restore the 355 // current value of |disable_create_| if the write call fails, as we return an 356 // error in that case and client code would not expect state changes. 357 bool disable_create_previous = disable_create_; 358 disable_create_ = true; 359 nvram_result_t result = WriteHeader(Optional<uint32_t>()); 360 if (result != NV_RESULT_SUCCESS) { 361 disable_create_ = disable_create_previous; 362 } 363 return result; 364} 365 366nvram_result_t NvramManager::WriteSpace(const WriteSpaceRequest& request, 367 WriteSpaceResponse* /* response */) { 368 const uint32_t index = request.index; 369 NVRAM_LOG_INFO("WriteSpace Ox%" PRIx32, index); 370 371 if (!Initialize()) 372 return NV_RESULT_INTERNAL_ERROR; 373 374 SpaceRecord space_record; 375 nvram_result_t result; 376 if (!LoadSpaceRecord(index, &space_record, &result)) { 377 return result; 378 } 379 380 result = space_record.CheckWriteAccess(request.authorization_value); 381 if (result != NV_RESULT_SUCCESS) { 382 return result; 383 } 384 385 Blob& contents = space_record.persistent.contents; 386 if (space_record.persistent.HasControl(NV_CONTROL_WRITE_EXTEND)) { 387 // Compute the hash of existing contents concatenated with input. 388 SHA256_CTX sha256_context; 389 SHA256_init(&sha256_context); 390 SHA256_update(&sha256_context, contents.data(), contents.size()); 391 SHA256_update(&sha256_context, request.buffer.data(), 392 request.buffer.size()); 393 394 // Make sure to handle both short and long space sizes gracefully, 395 // truncating or extending with 0 bytes as necessary. Even though 396 // |CreateSpace()| rejects |NV_CONTROL_WRITE_EXTEND| spaces that are not of 397 // size |SHA256_DIGEST_SIZE|, it's better to avoid any assumptions about 398 // data read from storage. 399 size_t hash_size = 400 min(contents.size(), static_cast<size_t>(SHA256_DIGEST_SIZE)); 401 memcpy(contents.data(), SHA256_final(&sha256_context), hash_size); 402 memset(contents.data() + hash_size, 0x0, contents.size() - hash_size); 403 } else { 404 if (contents.size() < request.buffer.size()) { 405 return NV_RESULT_INVALID_PARAMETER; 406 } 407 408 memcpy(contents.data(), request.buffer.data(), request.buffer.size()); 409 memset(contents.data() + request.buffer.size(), 0x0, 410 contents.size() - request.buffer.size()); 411 } 412 413 return WriteSpace(index, space_record.persistent); 414} 415 416nvram_result_t NvramManager::ReadSpace(const ReadSpaceRequest& request, 417 ReadSpaceResponse* response) { 418 const uint32_t index = request.index; 419 NVRAM_LOG_INFO("ReadSpace Ox%" PRIx32, index); 420 421 if (!Initialize()) 422 return NV_RESULT_INTERNAL_ERROR; 423 424 SpaceRecord space_record; 425 nvram_result_t result; 426 if (!LoadSpaceRecord(index, &space_record, &result)) { 427 return result; 428 } 429 430 result = space_record.CheckReadAccess(request.authorization_value); 431 if (result != NV_RESULT_SUCCESS) { 432 return result; 433 } 434 435 if (!response->buffer.Assign(space_record.persistent.contents.data(), 436 space_record.persistent.contents.size())) { 437 NVRAM_LOG_ERR("Allocation failure."); 438 return NV_RESULT_INTERNAL_ERROR; 439 } 440 441 return NV_RESULT_SUCCESS; 442} 443 444nvram_result_t NvramManager::LockSpaceWrite( 445 const LockSpaceWriteRequest& request, 446 LockSpaceWriteResponse* /* response */) { 447 const uint32_t index = request.index; 448 NVRAM_LOG_INFO("LockSpaceWrite Ox%" PRIx32, index); 449 450 if (!Initialize()) 451 return NV_RESULT_INTERNAL_ERROR; 452 453 SpaceRecord space_record; 454 nvram_result_t result; 455 if (!LoadSpaceRecord(index, &space_record, &result)) { 456 return result; 457 } 458 459 result = space_record.CheckWriteAccess(request.authorization_value); 460 if (result != NV_RESULT_SUCCESS) { 461 return result; 462 } 463 464 if (space_record.persistent.HasControl(NV_CONTROL_PERSISTENT_WRITE_LOCK)) { 465 space_record.persistent.SetFlag(NvramSpace::kFlagWriteLocked); 466 return WriteSpace(index, space_record.persistent); 467 } else if (space_record.persistent.HasControl(NV_CONTROL_BOOT_WRITE_LOCK)) { 468 space_record.transient->write_locked = true; 469 return NV_RESULT_SUCCESS; 470 } 471 472 NVRAM_LOG_ERR("Space not configured for write locking."); 473 return NV_RESULT_INVALID_PARAMETER; 474} 475 476nvram_result_t NvramManager::LockSpaceRead( 477 const LockSpaceReadRequest& request, 478 LockSpaceReadResponse* /* response */) { 479 const uint32_t index = request.index; 480 NVRAM_LOG_INFO("LockSpaceRead Ox%" PRIx32, index); 481 482 if (!Initialize()) 483 return NV_RESULT_INTERNAL_ERROR; 484 485 SpaceRecord space_record; 486 nvram_result_t result; 487 if (!LoadSpaceRecord(index, &space_record, &result)) { 488 return result; 489 } 490 491 result = space_record.CheckReadAccess(request.authorization_value); 492 if (result != NV_RESULT_SUCCESS) { 493 return result; 494 } 495 496 if (space_record.persistent.HasControl(NV_CONTROL_BOOT_READ_LOCK)) { 497 space_record.transient->read_locked = true; 498 return NV_RESULT_SUCCESS; 499 } 500 501 NVRAM_LOG_ERR("Space not configured for read locking."); 502 return NV_RESULT_INVALID_PARAMETER; 503} 504 505nvram_result_t NvramManager::SpaceRecord::CheckWriteAccess( 506 const Blob& authorization_value) { 507 if (persistent.HasControl(NV_CONTROL_PERSISTENT_WRITE_LOCK)) { 508 if (persistent.HasFlag(NvramSpace::kFlagWriteLocked)) { 509 NVRAM_LOG_INFO("Attempt to write persistently locked space 0x%" PRIx32 510 ".", 511 transient->index); 512 return NV_RESULT_OPERATION_DISABLED; 513 } 514 } else if (persistent.HasControl(NV_CONTROL_BOOT_WRITE_LOCK)) { 515 if (transient->write_locked) { 516 NVRAM_LOG_INFO("Attempt to write per-boot locked space 0x%" PRIx32 ".", 517 transient->index); 518 return NV_RESULT_OPERATION_DISABLED; 519 } 520 } 521 522 if (persistent.HasControl(NV_CONTROL_WRITE_AUTHORIZATION) && 523 !ConstantTimeEquals(persistent.authorization_value, 524 authorization_value)) { 525 NVRAM_LOG_INFO( 526 "Authorization value mismatch for write access to space 0x%" PRIx32 ".", 527 transient->index); 528 return NV_RESULT_ACCESS_DENIED; 529 } 530 531 // All checks passed, allow the write. 532 return NV_RESULT_SUCCESS; 533} 534 535nvram_result_t NvramManager::SpaceRecord::CheckReadAccess( 536 const Blob& authorization_value) { 537 if (persistent.HasControl(NV_CONTROL_BOOT_READ_LOCK)) { 538 if (transient->read_locked) { 539 NVRAM_LOG_INFO("Attempt to read per-boot locked space 0x%" PRIx32 ".", 540 transient->index); 541 return NV_RESULT_OPERATION_DISABLED; 542 } 543 } 544 545 if (persistent.HasControl(NV_CONTROL_READ_AUTHORIZATION) && 546 !ConstantTimeEquals(persistent.authorization_value, 547 authorization_value)) { 548 NVRAM_LOG_INFO( 549 "Authorization value mismatch for read access to space 0x%" PRIx32 ".", 550 transient->index); 551 return NV_RESULT_ACCESS_DENIED; 552 } 553 554 // All checks passed, allow the read. 555 return NV_RESULT_SUCCESS; 556} 557 558bool NvramManager::Initialize() { 559 if (initialized_) 560 return true; 561 562 NvramHeader header; 563 switch (persistence::LoadHeader(&header)) { 564 case storage::Status::kStorageError: 565 NVRAM_LOG_ERR("Init failed to load header."); 566 return false; 567 case storage::Status::kNotFound: 568 // No header in storage. This happens the very first time we initialize 569 // on a fresh device where the header isn't present yet. The first write 570 // will flush the fresh header to storage. 571 initialized_ = true; 572 return true; 573 case storage::Status::kSuccess: 574 if (header.version > NvramHeader::kVersion) { 575 NVRAM_LOG_ERR("Storage format %" PRIu32 " is more recent than %" PRIu32 576 ", aborting.", 577 header.version, NvramHeader::kVersion); 578 return false; 579 } 580 break; 581 } 582 583 // Check the state of the provisional space if applicable. 584 const Optional<uint32_t>& provisional_index = header.provisional_index; 585 bool provisional_space_in_storage = false; 586 if (provisional_index.valid()) { 587 NvramSpace space; 588 switch (persistence::LoadSpace(provisional_index.value(), &space)) { 589 case storage::Status::kStorageError: 590 // Log an error but leave the space marked as allocated. This will allow 591 // initialization to complete, so other spaces can be accessed. 592 // Operations on the bad space will fail however. The choice of keeping 593 // the bad space around (as opposed to dropping it) is intentional: 594 // * Failing noisily reduces the chances of bugs going undetected. 595 // * Keeping the index allocated prevents it from being accidentally 596 // clobbered due to appearing absent after transient storage errors. 597 NVRAM_LOG_ERR("Failed to load provisional space 0x%" PRIx32 ".", 598 provisional_index.value()); 599 provisional_space_in_storage = true; 600 break; 601 case storage::Status::kNotFound: 602 break; 603 case storage::Status::kSuccess: 604 provisional_space_in_storage = true; 605 break; 606 } 607 } 608 609 // If there are more spaces allocated than this build supports, fail 610 // initialization. This may seem a bit drastic, but the alternatives aren't 611 // acceptable: 612 // * If we continued with just a subset of the spaces, that may lead to wrong 613 // conclusions about the system state in consumers. Furthermore, consumers 614 // might delete a space to make room and then create a space that appears 615 // free but is present in storage. This would clobber the existing space 616 // data and potentially violate its access control rules. 617 // * We could just try to allocate more memory to hold the larger number of 618 // spaces. That'd render the memory footprint of the NVRAM implementation 619 // unpredictable. One variation that may work is to allow a maximum number 620 // of existing spaces larger than kMaxSpaces, but still within sane limits. 621 if (header.allocated_indices.size() > kMaxSpaces) { 622 NVRAM_LOG_ERR("Excess spaces %zu in header.", 623 header.allocated_indices.size()); 624 return false; 625 } 626 627 // Initialize the transient space bookkeeping data. 628 bool delete_provisional_space = provisional_index.valid(); 629 for (uint32_t index : header.allocated_indices) { 630 if (provisional_index.valid() && provisional_index.value() == index) { 631 // The provisional space index refers to a created space. If it isn't 632 // valid, pretend it was never created. 633 if (!provisional_space_in_storage) { 634 continue; 635 } 636 637 // The provisional space index corresponds to a created space that is 638 // present in storage. Retain the space. 639 delete_provisional_space = false; 640 } 641 642 spaces_[num_spaces_].index = index; 643 spaces_[num_spaces_].write_locked = false; 644 spaces_[num_spaces_].read_locked = false; 645 ++num_spaces_; 646 } 647 648 // If the provisional space data is present in storage, but the index wasn't 649 // in |header.allocated_indices|, it refers to half-deleted space. Destroy the 650 // space in that case. 651 if (delete_provisional_space) { 652 switch (persistence::DeleteSpace(provisional_index.value())) { 653 case storage::Status::kStorageError: 654 NVRAM_LOG_ERR("Failed to delete provisional space 0x%" PRIx32 " data.", 655 provisional_index.value()); 656 return false; 657 case storage::Status::kNotFound: 658 // The space isn't present in storage. This may happen if the space 659 // deletion succeeded, but the header wasn't written subsequently. 660 break; 661 case storage::Status::kSuccess: 662 break; 663 } 664 } 665 666 disable_create_ = header.HasFlag(NvramHeader::kFlagDisableCreate); 667 initialized_ = true; 668 669 // Write the header to clear the provisional index if necessary. It's actually 670 // not a problem if this fails, because the state is consistent regardless. We 671 // still do this opportunistically in order to avoid loading the provisional 672 // space data for each reboot after a crash. 673 if (provisional_index.valid()) { 674 WriteHeader(Optional<uint32_t>()); 675 } 676 677 return true; 678} 679 680size_t NvramManager::FindSpace(uint32_t space_index) { 681 for (size_t i = 0; i < num_spaces_; ++i) { 682 if (spaces_[i].index == space_index) { 683 return i; 684 } 685 } 686 687 return kMaxSpaces; 688} 689 690bool NvramManager::LoadSpaceRecord(uint32_t index, 691 SpaceRecord* space_record, 692 nvram_result_t* result) { 693 space_record->array_index = FindSpace(index); 694 if (space_record->array_index == kMaxSpaces) { 695 *result = NV_RESULT_SPACE_DOES_NOT_EXIST; 696 return false; 697 } 698 699 space_record->transient = &spaces_[space_record->array_index]; 700 701 switch (persistence::LoadSpace(index, &space_record->persistent)) { 702 case storage::Status::kStorageError: 703 NVRAM_LOG_ERR("Failed to load space 0x%" PRIx32 " data.", index); 704 *result = NV_RESULT_INTERNAL_ERROR; 705 return false; 706 case storage::Status::kNotFound: 707 // This should never happen if the header contains the index. 708 NVRAM_LOG_ERR("Space index 0x%" PRIx32 709 " present in header, but data missing.", 710 index); 711 *result = NV_RESULT_INTERNAL_ERROR; 712 return false; 713 case storage::Status::kSuccess: 714 *result = NV_RESULT_SUCCESS; 715 return true; 716 } 717 718 *result = NV_RESULT_INTERNAL_ERROR; 719 return false; 720} 721 722nvram_result_t NvramManager::WriteHeader(Optional<uint32_t> provisional_index) { 723 NvramHeader header; 724 header.version = NvramHeader::kVersion; 725 if (disable_create_) { 726 header.SetFlag(NvramHeader::kFlagDisableCreate); 727 } 728 729 if (!header.allocated_indices.Resize(num_spaces_)) { 730 NVRAM_LOG_ERR("Allocation failure."); 731 return NV_RESULT_INTERNAL_ERROR; 732 } 733 for (size_t i = 0; i < num_spaces_; ++i) { 734 header.allocated_indices[i] = spaces_[i].index; 735 } 736 737 header.provisional_index = provisional_index; 738 739 if (persistence::StoreHeader(header) != storage::Status::kSuccess) { 740 NVRAM_LOG_ERR("Failed to store header."); 741 return NV_RESULT_INTERNAL_ERROR; 742 } 743 744 return NV_RESULT_SUCCESS; 745} 746 747nvram_result_t NvramManager::WriteSpace(uint32_t index, 748 const NvramSpace& space) { 749 if (persistence::StoreSpace(index, space) != storage::Status::kSuccess) { 750 NVRAM_LOG_ERR("Failed to store space 0x%" PRIx32 ".", index); 751 return NV_RESULT_INTERNAL_ERROR; 752 } 753 754 return NV_RESULT_SUCCESS; 755} 756 757} // namespace nvram 758