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