aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorMattias Nissler <mnissler@chromium.org>2016-01-28 23:38:01 +0100
committerMattias Nissler <mnissler@google.com>2016-02-15 10:07:59 +0100
commitb07c8946c00857d2e843d2ec5d49f9a753d451e0 (patch)
treef9c305c539e53ed6c1a5a53613fe3e213f152dc6 /core
parent8748d72a31f800e83bcf48cf7ecf828ef9601fa9 (diff)
downloadnvram-b07c8946c00857d2e843d2ec5d49f9a753d451e0.tar.gz
First bits of core NVRAM logic.
This adds TrustyNvram, which will hold the main logic implementing the semantics mandated by the access-controlled NVRAM HAL spec. BUG: 25762540 Change-Id: I40914bd161ca58b4f79d32a1b82c6754db5cfca8
Diffstat (limited to 'core')
-rw-r--r--core/Android.mk1
-rw-r--r--core/include/nvram/core/nvram_manager.h65
-rw-r--r--core/nvram_manager.cpp169
3 files changed, 235 insertions, 0 deletions
diff --git a/core/Android.mk b/core/Android.mk
index 512c14f..4fede2d 100644
--- a/core/Android.mk
+++ b/core/Android.mk
@@ -19,6 +19,7 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libnvram-core
LOCAL_SRC_FILES := \
+ nvram_manager.cpp \
persistence.cpp
LOCAL_SHARED_LIBRARIES := libnvram-messages
LOCAL_CFLAGS := -Wall -Werror -Wextra
diff --git a/core/include/nvram/core/nvram_manager.h b/core/include/nvram/core/nvram_manager.h
new file mode 100644
index 0000000..1cce0a6
--- /dev/null
+++ b/core/include/nvram/core/nvram_manager.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NVRAM_CORE_NVRAM_MANAGER_H_
+#define NVRAM_CORE_NVRAM_MANAGER_H_
+
+#include <nvram/messages/nvram_messages.h>
+
+#include <nvram/core/persistence.h>
+
+namespace nvram {
+
+// |NvramManager| implements the core functionality of the access-controlled
+// NVRAM HAL backend. It keeps track of the allocated spaces and their state,
+// including the transient state that is held per boot. It provides operations
+// for querying, creating, deleting, reading and writing spaces. It deals with
+// persistent storage objects in the form of |NvramHeader| and |NvramSpace|
+// objects and uses the persistence layer to read and write them from persistent
+// storage.
+class NvramManager {
+ private:
+ // Holds transient state corresponding to an allocated NVRAM space, i.e. meta
+ // data valid for a single boot. One instance of this struct is kept in memory
+ // in the |spaces_| array for each of the spaces that are currently allocated.
+ struct SpaceListEntry {
+ uint32_t index;
+ bool write_locked = false;
+ bool read_locked = false;
+ };
+
+ // Initializes |header_| from storage if that hasn't happened already. Returns
+ // true if NvramManager object is initialized and ready to serve requests. May
+ // be called again after failure to attempt initialization again.
+ bool Initialize();
+
+ // Writes the header to storage and returns a suitable status code.
+ nvram_result_t WriteHeader(Optional<uint32_t> provisional_index);
+
+ // Maximum number of NVRAM spaces we're willing to allocate.
+ static constexpr size_t kMaxSpaces = 32;
+
+ bool initialized_ = false;
+ bool disable_create_ = false;
+
+ // Bookkeeping information for allocated spaces.
+ size_t num_spaces_ = 0;
+ SpaceListEntry spaces_[kMaxSpaces];
+};
+
+} // namespace nvram
+
+#endif // NVRAM_CORE_NVRAM_MANAGER_H_
diff --git a/core/nvram_manager.cpp b/core/nvram_manager.cpp
new file mode 100644
index 0000000..dbbe68d
--- /dev/null
+++ b/core/nvram_manager.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "nvram/core/nvram_manager.h"
+
+#include <nvram/core/logger.h>
+
+namespace nvram {
+
+bool NvramManager::Initialize() {
+ if (initialized_)
+ return true;
+
+ NvramHeader header;
+ switch (persistence::LoadHeader(&header)) {
+ case storage::Status::kStorageError:
+ NVRAM_LOG_ERR("Init failed to load header.");
+ return false;
+ case storage::Status::kNotFound:
+ // No header in storage. This happens the very first time we initialize
+ // on a fresh device where the header isn't present yet. The first write
+ // will flush the fresh header to storage.
+ initialized_ = true;
+ return true;
+ case storage::Status::kSuccess:
+ if (header.version > NvramHeader::kVersion) {
+ NVRAM_LOG_ERR("Storage format %u is more recent than %u, aborting.",
+ header.version, NvramHeader::kVersion);
+ return false;
+ }
+ break;
+ }
+
+ // Check the state of the provisional space if applicable.
+ const Optional<uint32_t>& provisional_index = header.provisional_index;
+ bool provisional_space_in_storage = false;
+ if (provisional_index.valid()) {
+ NvramSpace space;
+ switch (persistence::LoadSpace(provisional_index.value(), &space)) {
+ case storage::Status::kStorageError:
+ // Log an error but leave the space marked as allocated. This will allow
+ // initialization to complete, so other spaces can be accessed.
+ // Operations on the bad space will fail however. The choice of keeping
+ // the bad space around (as opposed to dropping it) is intentional:
+ // * Failing noisily reduces the chances of bugs going undetected.
+ // * Keeping the index allocated prevents it from being accidentally
+ // clobbered due to appearing absent after transient storage errors.
+ NVRAM_LOG_ERR("Failed to load provisional space 0x%x.",
+ provisional_index.value());
+ provisional_space_in_storage = true;
+ break;
+ case storage::Status::kNotFound:
+ break;
+ case storage::Status::kSuccess:
+ provisional_space_in_storage = true;
+ break;
+ }
+ }
+
+ // If there are more spaces allocated than this build supports, fail
+ // initialization. This may seem a bit drastic, but the alternatives aren't
+ // acceptable:
+ // * If we continued with just a subset of the spaces, that may lead to wrong
+ // conclusions about the system state in consumers. Furthermore, consumers
+ // might delete a space to make room and then create a space that appears
+ // free but is present in storage. This would clobber the existing space
+ // data and potentially violate its access control rules.
+ // * We could just try to allocate more memory to hold the larger number of
+ // spaces. That'd render the memory footprint of the NVRAM implementation
+ // unpredictable. One variation that may work is to allow a maximum number
+ // of existing spaces larger than kMaxSpaces, but still within sane limits.
+ if (header.allocated_indices.size() > kMaxSpaces) {
+ NVRAM_LOG_ERR("Excess spaces %zu in header.",
+ header.allocated_indices.size());
+ return false;
+ }
+
+ // Initialize the transient space bookkeeping data.
+ bool delete_provisional_space = provisional_index.valid();
+ for (uint32_t index : header.allocated_indices) {
+ if (provisional_index.valid() && provisional_index.value() == index) {
+ // The provisional space index refers to a created space. If it isn't
+ // valid, pretend it was never created.
+ if (!provisional_space_in_storage) {
+ continue;
+ }
+
+ // The provisional space index corresponds to a created space that is
+ // present in storage. Retain the space.
+ delete_provisional_space = false;
+ }
+
+ spaces_[num_spaces_].index = index;
+ spaces_[num_spaces_].write_locked = false;
+ spaces_[num_spaces_].read_locked = false;
+ ++num_spaces_;
+ }
+
+ // If the provisional space data is present in storage, but the index wasn't
+ // in |header.allocated_indices|, it refers to half-deleted space. Destroy the
+ // space in that case.
+ if (delete_provisional_space) {
+ switch (persistence::DeleteSpace(provisional_index.value())) {
+ case storage::Status::kStorageError:
+ NVRAM_LOG_ERR("Failed to delete provisional space 0x%x data.",
+ provisional_index.value());
+ return false;
+ case storage::Status::kNotFound:
+ NVRAM_LOG_ERR("Provisional space 0x%x absent on deletion.",
+ provisional_index.value());
+ return false;
+ case storage::Status::kSuccess:
+ break;
+ }
+ }
+
+ disable_create_ = header.HasFlag(NvramHeader::kFlagDisableCreate);
+ initialized_ = true;
+
+ // Write the header to clear the provisional index if necessary. It's actually
+ // not a problem if this fails, because the state is consistent regardless. We
+ // still do this opportunistically in order to avoid loading the provisional
+ // space data for each reboot after a crash.
+ if (provisional_index.valid()) {
+ WriteHeader(Optional<uint32_t>());
+ }
+
+ return true;
+}
+
+nvram_result_t NvramManager::WriteHeader(Optional<uint32_t> provisional_index) {
+ NvramHeader header;
+ header.version = NvramHeader::kVersion;
+ if (disable_create_) {
+ header.SetFlag(NvramHeader::kFlagDisableCreate);
+ }
+
+ if (!header.allocated_indices.Resize(num_spaces_)) {
+ NVRAM_LOG_ERR("Allocation failure.");
+ return NV_RESULT_INTERNAL_ERROR;
+ }
+ for (size_t i = 0; i < num_spaces_; ++i) {
+ header.allocated_indices[i] = spaces_[i].index;
+ }
+
+ header.provisional_index = provisional_index;
+
+ if (persistence::StoreHeader(header) != storage::Status::kSuccess) {
+ NVRAM_LOG_ERR("Failed to store header.");
+ return NV_RESULT_INTERNAL_ERROR;
+ }
+
+ return NV_RESULT_SUCCESS;
+}
+
+} // namespace nvram