diff options
author | Mattias Nissler <mnissler@chromium.org> | 2016-01-28 23:38:01 +0100 |
---|---|---|
committer | Mattias Nissler <mnissler@google.com> | 2016-02-15 10:07:59 +0100 |
commit | b07c8946c00857d2e843d2ec5d49f9a753d451e0 (patch) | |
tree | f9c305c539e53ed6c1a5a53613fe3e213f152dc6 /core | |
parent | 8748d72a31f800e83bcf48cf7ecf828ef9601fa9 (diff) | |
download | nvram-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.mk | 1 | ||||
-rw-r--r-- | core/include/nvram/core/nvram_manager.h | 65 | ||||
-rw-r--r-- | core/nvram_manager.cpp | 169 |
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 |