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