diff options
author | Mattias Nissler <mnissler@chromium.org> | 2016-01-28 23:45:28 +0100 |
---|---|---|
committer | Mattias Nissler <mnissler@google.com> | 2016-02-15 10:07:59 +0100 |
commit | aa4a1a8c35e0b944fbf721ff00fd03b52a59f8aa (patch) | |
tree | 156c7c1866683f31d1742a82852e9122bec05e17 /core | |
parent | b07c8946c00857d2e843d2ec5d49f9a753d451e0 (diff) | |
download | nvram-aa4a1a8c35e0b944fbf721ff00fd03b52a59f8aa.tar.gz |
Implement info operation and space creation.
This implements the first batch of NVRAM operations: GetInfo,
CreateSpace, GetSpaceInfo and DisableCreate. Operations to manipulate
individual spaces will follow in a subsequent CL.
BUG: 25762540
Change-Id: I74c6bcf7804cecbe7630479fbdd6bb0a0a668c7b
Diffstat (limited to 'core')
-rw-r--r-- | core/include/nvram/core/nvram_manager.h | 47 | ||||
-rw-r--r-- | core/nvram_manager.cpp | 255 |
2 files changed, 302 insertions, 0 deletions
diff --git a/core/include/nvram/core/nvram_manager.h b/core/include/nvram/core/nvram_manager.h index 1cce0a6..5e05e3c 100644 --- a/core/include/nvram/core/nvram_manager.h +++ b/core/include/nvram/core/nvram_manager.h @@ -31,6 +31,16 @@ namespace nvram { // objects and uses the persistence layer to read and write them from persistent // storage. class NvramManager { + public: + nvram_result_t GetInfo(const GetInfoRequest& request, + GetInfoResponse* response); + nvram_result_t CreateSpace(const CreateSpaceRequest& request, + CreateSpaceResponse* response); + nvram_result_t GetSpaceInfo(const GetSpaceInfoRequest& request, + GetSpaceInfoResponse* response); + nvram_result_t DisableCreate(const DisableCreateRequest& request, + DisableCreateResponse* response); + 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 @@ -41,14 +51,51 @@ class NvramManager { bool read_locked = false; }; + // |SpaceRecord| holds all information known about a space. It includes both + // an index and pointer to the transient information held in the + // |SpaceListEntry| in the |spaces_| array and the persistent |NvramSpace| + // state held in permanent storage. We only load the persistent space data + // from storage when it is needed for an operation, such as reading and + // writing space contents. + struct SpaceRecord { + // Access control check for write access to the space. The + // |authorization_value| is only relevant if the space was configured to + // require authorization. Returns RESULT_SUCCESS if write access is + // permitted and a suitable result code to return to the client on failure. + nvram_result_t CheckWriteAccess(const Blob& authorization_value); + + // Access control check for read access to the space. The + // |authorization_value| is only relevant if the space was configured to + // require authorization. Returns RESULT_SUCCESS if write access is + // permitted and a suitable result code to return the client on failure. + nvram_result_t CheckReadAccess(const Blob& authorization_value); + + size_t array_index = 0; + SpaceListEntry* transient = nullptr; + NvramSpace persistent; + }; + // 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(); + // Finds the array index in |spaces_| that corresponds to |space_index|. + // Returns |kMaxSpaces| if there is no matching space. + size_t FindSpace(uint32_t space_index); + + // Loads space data for |index|. Fills in |space_record| and returns true if + // successful. Returns false and sets |result| on error. + bool LoadSpaceRecord(uint32_t index, + SpaceRecord* space_record, + nvram_result_t* result); + // Writes the header to storage and returns a suitable status code. nvram_result_t WriteHeader(Optional<uint32_t> provisional_index); + // Write |space| data for |index|. + nvram_result_t WriteSpace(uint32_t index, const NvramSpace& space); + // Maximum number of NVRAM spaces we're willing to allocate. static constexpr size_t kMaxSpaces = 32; diff --git a/core/nvram_manager.cpp b/core/nvram_manager.cpp index dbbe68d..19e23dd 100644 --- a/core/nvram_manager.cpp +++ b/core/nvram_manager.cpp @@ -18,8 +18,212 @@ #include <nvram/core/logger.h> +extern "C" { +#include <string.h> +} // extern "C" + +using namespace nvram::storage; + namespace nvram { +namespace { + +// Maximum size of a single space's contents. +constexpr size_t kMaxSpaceSize = 1024; + +// Maximum authorization blob size; +constexpr size_t kMaxAuthSize = 32; + +// The bitmask of all supported control flags. +constexpr uint32_t kSupportedControlsMask = + (1 << NV_CONTROL_PERSISTENT_WRITE_LOCK) | + (1 << NV_CONTROL_BOOT_WRITE_LOCK) | + (1 << NV_CONTROL_BOOT_READ_LOCK) | + (1 << NV_CONTROL_WRITE_AUTHORIZATION) | + (1 << NV_CONTROL_READ_AUTHORIZATION) | + (1 << NV_CONTROL_WRITE_EXTEND); + +// Convert the |space.controls| bitmask to vector representation. +nvram_result_t GetControlsVector(const NvramSpace& space, + Vector<nvram_control_t>* controls) { + for (size_t control = 0; control < sizeof(uint32_t) * 8; ++control) { + if (space.HasControl(control)) { + if (!controls->Resize(controls->size() + 1)) { + NVRAM_LOG_ERR("Allocation failure."); + return NV_RESULT_INTERNAL_ERROR; + } + (*controls)[controls->size() - 1] = static_cast<nvram_control_t>(control); + } + } + return NV_RESULT_SUCCESS; +} + +} // namespace + +nvram_result_t NvramManager::GetInfo(const GetInfoRequest& /* request */, + GetInfoResponse* response) { + NVRAM_LOG_INFO("GetInfo"); + + if (!Initialize()) + return NV_RESULT_INTERNAL_ERROR; + + // TODO: Get better values for total and available size from the storage + // layer. + response->total_size = kMaxSpaceSize * kMaxSpaces; + response->available_size = kMaxSpaceSize * (kMaxSpaces - num_spaces_); + response->max_spaces = kMaxSpaces; + Vector<uint32_t>& space_list = response->space_list; + if (!space_list.Resize(num_spaces_)) { + NVRAM_LOG_ERR("Allocation failure."); + return NV_RESULT_INTERNAL_ERROR; + } + for (size_t i = 0; i < num_spaces_; ++i) { + space_list[i] = spaces_[i].index; + } + + return NV_RESULT_SUCCESS; +} + +nvram_result_t NvramManager::CreateSpace(const CreateSpaceRequest& request, + CreateSpaceResponse* /* response */) { + const uint32_t index = request.index; + NVRAM_LOG_INFO("CreateSpace Ox%x", index); + + if (!Initialize()) + return NV_RESULT_INTERNAL_ERROR; + + if (disable_create_) { + NVRAM_LOG_INFO("Creation of further spaces is disabled."); + return NV_RESULT_OPERATION_DISABLED; + } + + if (FindSpace(index) != kMaxSpaces) { + NVRAM_LOG_INFO("Space 0x%x already exists.", index); + return NV_RESULT_SPACE_ALREADY_EXISTS; + } + + if (num_spaces_ + 1 > kMaxSpaces) { + NVRAM_LOG_INFO("Too many spaces."); + return NV_RESULT_INVALID_PARAMETER; + } + + if (request.size > kMaxSpaceSize) { + NVRAM_LOG_INFO("Create request exceeds max space size."); + return NV_RESULT_INVALID_PARAMETER; + } + + if (request.authorization_value.size() > kMaxAuthSize) { + NVRAM_LOG_INFO("Authorization blob too large."); + return NV_RESULT_INVALID_PARAMETER; + } + + uint32_t controls = 0; + for (uint32_t control : request.controls) { + controls |= (1 << control); + } + if ((controls & ~kSupportedControlsMask) != 0) { + NVRAM_LOG_INFO("Bad controls."); + return NV_RESULT_INVALID_PARAMETER; + } + if ((controls & (1 << NV_CONTROL_PERSISTENT_WRITE_LOCK)) != 0 && + (controls & (1 << NV_CONTROL_BOOT_WRITE_LOCK)) != 0) { + NVRAM_LOG_INFO("Write lock controls are exclusive."); + return NV_RESULT_INVALID_PARAMETER; + } + + // Mark the index as allocated. + spaces_[num_spaces_].index = index; + spaces_[num_spaces_].write_locked = false; + spaces_[num_spaces_].read_locked = false; + ++num_spaces_; + + // Create a space record. + NvramSpace space; + space.flags = 0; + space.controls = controls; + + // Copy the auth blob. + if (space.HasControl(NV_CONTROL_WRITE_AUTHORIZATION) || + space.HasControl(NV_CONTROL_READ_AUTHORIZATION)) { + if (!space.authorization_value.Assign(request.authorization_value.data(), + request.authorization_value.size())) { + NVRAM_LOG_ERR("Allocation failure."); + return NV_RESULT_INTERNAL_ERROR; + } + } + + // Initialize the space content. + if (!space.contents.Resize(request.size)) { + NVRAM_LOG_ERR("Allocation failure."); + return NV_RESULT_INTERNAL_ERROR; + } + memset(space.contents.data(), 0, request.size); + + // Write the header before the space data. This ensures that all space + // definitions present in storage are also recorded in the header. Thus, the + // set of spaces present in the header is always a superset of the set of + // spaces that have state in storage. If there's a crash after writing the + // header but before writing the space information, the space data will be + // missing in storage. The initialization code handles this by checking the + // for the space data corresponding to the index marked as provisional in the + // header. + nvram_result_t result; + if ((result = WriteHeader(Optional<uint32_t>(index))) != NV_RESULT_SUCCESS || + (result = WriteSpace(index, space)) != NV_RESULT_SUCCESS) { + --num_spaces_; + } + return result; +} + +nvram_result_t NvramManager::GetSpaceInfo(const GetSpaceInfoRequest& request, + GetSpaceInfoResponse* response) { + const uint32_t index = request.index; + NVRAM_LOG_INFO("GetSpaceInfo Ox%x", index); + + if (!Initialize()) + return NV_RESULT_INTERNAL_ERROR; + + SpaceRecord space_record; + nvram_result_t result; + if (!LoadSpaceRecord(index, &space_record, &result)) { + return result; + } + + response->size = space_record.persistent.contents.size(); + + result = GetControlsVector(space_record.persistent, &response->controls); + if (result != NV_RESULT_SUCCESS) { + return NV_RESULT_INTERNAL_ERROR; + } + + if (space_record.persistent.HasControl(NV_CONTROL_BOOT_READ_LOCK)) { + response->read_locked = space_record.transient->read_locked; + } + + if (space_record.persistent.HasControl(NV_CONTROL_PERSISTENT_WRITE_LOCK)) { + response->write_locked = + space_record.persistent.HasFlag(NvramSpace::kFlagWriteLocked); + } else if (space_record.persistent.HasControl(NV_CONTROL_BOOT_WRITE_LOCK)) { + response->write_locked = space_record.transient->write_locked; + } + + return NV_RESULT_SUCCESS; +} + +nvram_result_t NvramManager::DisableCreate( + const DisableCreateRequest& /* request */, + DisableCreateResponse* /* response */) { + NVRAM_LOG_INFO("DisableCreate"); + + if (!Initialize()) + return NV_RESULT_INTERNAL_ERROR; + + // Set the |disable_create_| flag and call |WriteHeader| to persist the flag + // such that it remains effective after a reboot. + disable_create_ = true; + return WriteHeader(Optional<uint32_t>()); +} + bool NvramManager::Initialize() { if (initialized_) return true; @@ -141,6 +345,47 @@ bool NvramManager::Initialize() { return true; } +size_t NvramManager::FindSpace(uint32_t space_index) { + for (size_t i = 0; i < num_spaces_; ++i) { + if (spaces_[i].index == space_index) { + return i; + } + } + + return kMaxSpaces; +} + +bool NvramManager::LoadSpaceRecord(uint32_t index, + SpaceRecord* space_record, + nvram_result_t* result) { + space_record->array_index = FindSpace(index); + if (space_record->array_index == kMaxSpaces) { + *result = NV_RESULT_SPACE_DOES_NOT_EXIST; + return false; + } + + space_record->transient = &spaces_[space_record->array_index]; + + switch (persistence::LoadSpace(index, &space_record->persistent)) { + case storage::Status::kStorageError: + NVRAM_LOG_ERR("Failed to load space 0x%x data.", index); + *result = NV_RESULT_INTERNAL_ERROR; + return false; + case storage::Status::kNotFound: + // This should never happen if the header contains the index. + NVRAM_LOG_ERR("Space index 0x%x present in header, but data missing.", + index); + *result = NV_RESULT_INTERNAL_ERROR; + return false; + case storage::Status::kSuccess: + *result = NV_RESULT_SUCCESS; + return true; + } + + *result = NV_RESULT_INTERNAL_ERROR; + return false; +} + nvram_result_t NvramManager::WriteHeader(Optional<uint32_t> provisional_index) { NvramHeader header; header.version = NvramHeader::kVersion; @@ -166,4 +411,14 @@ nvram_result_t NvramManager::WriteHeader(Optional<uint32_t> provisional_index) { return NV_RESULT_SUCCESS; } +nvram_result_t NvramManager::WriteSpace(uint32_t index, + const NvramSpace& space) { + if (persistence::StoreSpace(index, space) != storage::Status::kSuccess) { + NVRAM_LOG_ERR("Failed to store space 0x%x.", index); + return NV_RESULT_INTERNAL_ERROR; + } + + return NV_RESULT_SUCCESS; +} + } // namespace nvram |