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