upload android base code part6
This commit is contained in:
parent
421e214c7d
commit
4e516ec6ed
35396 changed files with 9188716 additions and 0 deletions
45
android/system/nvram/core/Android.mk
Normal file
45
android/system/nvram/core/Android.mk
Normal file
|
@ -0,0 +1,45 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libnvram-core
|
||||
LOCAL_SRC_FILES := \
|
||||
crypto_boringssl.cpp \
|
||||
nvram_manager.cpp \
|
||||
persistence.cpp
|
||||
LOCAL_CFLAGS := -Wall -Werror -Wextra
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
|
||||
LOCAL_SHARED_LIBRARIES := libnvram-messages libcrypto
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libnvram-core-host
|
||||
LOCAL_SRC_FILES := \
|
||||
crypto_boringssl.cpp \
|
||||
nvram_manager.cpp \
|
||||
persistence.cpp
|
||||
LOCAL_CFLAGS := -Wall -Werror -Wextra -DNVRAM_WIPE_STORAGE_SUPPORT
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
|
||||
LOCAL_SHARED_LIBRARIES := libnvram-messages libcrypto
|
||||
include $(BUILD_HOST_STATIC_LIBRARY)
|
||||
|
||||
include $(call all-makefiles-under,$(LOCAL_PATH))
|
45
android/system/nvram/core/crypto.h
Normal file
45
android/system/nvram/core/crypto.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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_CRYPTO_H_
|
||||
#define NVRAM_CORE_CRYPTO_H_
|
||||
|
||||
extern "C" {
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
} // extern "C"
|
||||
|
||||
namespace nvram {
|
||||
namespace crypto {
|
||||
|
||||
// Size of a SHA-256 digest in bytes.
|
||||
constexpr size_t kSHA256DigestSize = 32;
|
||||
|
||||
// Computes the SHA-256 digest of the |data_size| input bytes stored at |data|.
|
||||
// The digest is written to |digest|, which is a buffer of size |digest_size|.
|
||||
// Note that |digest_size| doesn't have to match SHA-256's output size of 32
|
||||
// bytes. If it doesn't the digest is truncated or zero-padded as necessary.
|
||||
//
|
||||
// Returns true if the digest was computed successfully, false otherwise.
|
||||
void SHA256(const uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t* digest,
|
||||
size_t digest_size);
|
||||
|
||||
} // namespace crypto
|
||||
} // namespace nvram
|
||||
|
||||
#endif // NVRAM_CORE_CRYPTO_H_
|
48
android/system/nvram/core/crypto_boringssl.cpp
Normal file
48
android/system/nvram/core/crypto_boringssl.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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 "crypto.h"
|
||||
|
||||
extern "C" {
|
||||
#include <string.h>
|
||||
|
||||
#include <openssl/mem.h>
|
||||
#include <openssl/sha.h>
|
||||
} // extern "C"
|
||||
|
||||
namespace nvram {
|
||||
namespace crypto {
|
||||
|
||||
void SHA256(const uint8_t* data,
|
||||
size_t data_size,
|
||||
uint8_t* digest,
|
||||
size_t digest_size) {
|
||||
// SHA256 requires an output buffer of at least SHA256_DIGEST_LENGTH.
|
||||
// |digest_size| might be less, so store the digest in a local buffer.
|
||||
uint8_t buffer[SHA256_DIGEST_LENGTH];
|
||||
::SHA256(data, data_size, buffer);
|
||||
|
||||
// Copy the result to |digest|.
|
||||
if (digest_size < sizeof(buffer)) {
|
||||
memcpy(digest, buffer, digest_size);
|
||||
} else {
|
||||
memcpy(digest, buffer, sizeof(buffer));
|
||||
memset(digest + sizeof(buffer), 0, digest_size - sizeof(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace crypto
|
||||
} // namespace nvram
|
77
android/system/nvram/core/include/nvram/core/logger.h
Normal file
77
android/system/nvram/core/include/nvram/core/logger.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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_LOGGER_H_
|
||||
#define NVRAM_CORE_LOGGER_H_
|
||||
|
||||
// Numeric constants for log levels.
|
||||
#define NVRAM_LOG_LEVEL_ERR 1
|
||||
#define NVRAM_LOG_LEVEL_WARN 2
|
||||
#define NVRAM_LOG_LEVEL_INFO 3
|
||||
#define NVRAM_LOG_LEVEL_DEBUG 4
|
||||
|
||||
#if !defined(NVRAM_LOG_LEVEL)
|
||||
// By default, log only warnings and errors.
|
||||
#define NVRAM_LOG_LEVEL NVRAM_LOG_LEVEL_WARN
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
#define LOG_TAG "NVRAM"
|
||||
#include <log/log.h>
|
||||
|
||||
// Maps NVRAM log levels to Android log priorities.
|
||||
#define NVRAM_ANDROID_LOG_PRI_ERR ANDROID_LOG_ERROR
|
||||
#define NVRAM_ANDROID_LOG_PRI_WARN ANDROID_LOG_WARN
|
||||
#define NVRAM_ANDROID_LOG_PRI_INFO ANDROID_LOG_INFO
|
||||
#define NVRAM_ANDROID_LOG_PRI_DEBUG ANDROID_LOG_DEBUG
|
||||
|
||||
// Send log output to Android's logging system.
|
||||
#define NVRAM_LOG_EMIT(level, fmt, ...) \
|
||||
LOG_PRI(NVRAM_ANDROID_LOG_PRI_##level, LOG_TAG, fmt, ##__VA_ARGS__)
|
||||
|
||||
#else // __ANDROID__
|
||||
|
||||
extern "C" {
|
||||
#include <stdio.h>
|
||||
}
|
||||
|
||||
// By default, send log output to stderr.
|
||||
#define NVRAM_LOG_EMIT(level, fmt, ...) \
|
||||
fprintf(stderr, "NVRAM: " fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
#endif // !__ANDROID__
|
||||
|
||||
// NVRAM_LOG is the central log macro. It checks whether the log level is
|
||||
// effective, adds file and line information and calls the platform-specific
|
||||
// NVRAM_LOG_EMIT.
|
||||
#define NVRAM_STR(arg) #arg
|
||||
#define NVRAM_STRINGIFY(arg) NVRAM_STR(arg)
|
||||
#define NVRAM_LOG(level, fmt, ...) \
|
||||
do { \
|
||||
if (NVRAM_LOG_LEVEL_##level <= NVRAM_LOG_LEVEL) { \
|
||||
NVRAM_LOG_EMIT(level, __FILE__ ":" NVRAM_STRINGIFY(__LINE__) ": " fmt, \
|
||||
##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// Convenience logging macros.
|
||||
#define NVRAM_LOG_ERR(fmt, ...) NVRAM_LOG(ERR, fmt, ##__VA_ARGS__)
|
||||
#define NVRAM_LOG_WARN(fmt, ...) NVRAM_LOG(WARN, fmt, ##__VA_ARGS__)
|
||||
#define NVRAM_LOG_INFO(fmt, ...) NVRAM_LOG(INFO, fmt, ##__VA_ARGS__)
|
||||
#define NVRAM_LOG_DEBUG(fmt, ...) NVRAM_LOG(DEBUG, fmt, ##__VA_ARGS__)
|
||||
|
||||
#endif // NVRAM_CORE_LOGGER_H_
|
155
android/system/nvram/core/include/nvram/core/nvram_manager.h
Normal file
155
android/system/nvram/core/include/nvram/core/nvram_manager.h
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* 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 {
|
||||
public:
|
||||
// Looks at |request| to determine the command to execute, extracts the
|
||||
// request parameters and invokes the correct handler function. Stores status
|
||||
// and output parameters in |response|.
|
||||
void Dispatch(const Request& request, Response* response);
|
||||
|
||||
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 DeleteSpace(const DeleteSpaceRequest& request,
|
||||
DeleteSpaceResponse* response);
|
||||
nvram_result_t DisableCreate(const DisableCreateRequest& request,
|
||||
DisableCreateResponse* response);
|
||||
nvram_result_t WriteSpace(const WriteSpaceRequest& request,
|
||||
WriteSpaceResponse* response);
|
||||
nvram_result_t ReadSpace(const ReadSpaceRequest& request,
|
||||
ReadSpaceResponse* response);
|
||||
nvram_result_t LockSpaceWrite(const LockSpaceWriteRequest& request,
|
||||
LockSpaceWriteResponse* response);
|
||||
nvram_result_t LockSpaceRead(const LockSpaceReadRequest& request,
|
||||
LockSpaceReadResponse* response);
|
||||
|
||||
// The wipe functions are meant for use by firmware after determining the
|
||||
// device's mode of operation. These can be used to clear access-controlled
|
||||
// NVRAM when a user invokes a full hardware reset. Note that in regular
|
||||
// operation, the user *MUST BE PREVENTED* from wiping access-controlled
|
||||
// NVRAM.
|
||||
//
|
||||
// If a full hardware reset can conveniently clear the access-controlled NVRAM
|
||||
// storage area out of band, it's fine to do so. In this case, the
|
||||
// wiping-related commands should not be exposed at all. Note that this is the
|
||||
// default behavior - the reference implementation will ignore all wipe
|
||||
// requests unless compiled with NVRAM_WIPE_STORAGE_SUPPORT=1.
|
||||
//
|
||||
// For devices where firmware doesn't have direct control over the storage
|
||||
// area used by access-controlled NVRAM, the wiping commands are provided to
|
||||
// facilitate clearing storage:
|
||||
// 1. Determine boot mode.
|
||||
// 2. If not in recovery mode, call DisableWipe(). All further wipe requests
|
||||
// will be rejected. A reboot (or TEE restart for that matter) is
|
||||
// required before a new decision can be made.
|
||||
// 3. If operating in recovery mode, forgo calling DisableWipe(). The
|
||||
// recovery process will then be able to invoke WipeStorage() later as
|
||||
// needed.
|
||||
nvram_result_t WipeStorage(const WipeStorageRequest& request,
|
||||
WipeStorageResponse* response);
|
||||
nvram_result_t DisableWipe(const DisableWipeRequest& request,
|
||||
DisableWipeResponse* 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
|
||||
// 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;
|
||||
};
|
||||
|
||||
// |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;
|
||||
|
||||
bool initialized_ = false;
|
||||
bool disable_create_ = false;
|
||||
bool disable_wipe_ = false;
|
||||
|
||||
// Bookkeeping information for allocated spaces.
|
||||
size_t num_spaces_ = 0;
|
||||
SpaceListEntry spaces_[kMaxSpaces];
|
||||
};
|
||||
|
||||
} // namespace nvram
|
||||
|
||||
#endif // NVRAM_CORE_NVRAM_MANAGER_H_
|
149
android/system/nvram/core/include/nvram/core/persistence.h
Normal file
149
android/system/nvram/core/include/nvram/core/persistence.h
Normal file
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* 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_PERSISTENCE_H_
|
||||
#define NVRAM_CORE_PERSISTENCE_H_
|
||||
|
||||
extern "C" {
|
||||
#include <stdint.h>
|
||||
} // extern "C"
|
||||
|
||||
#include <nvram/messages/optional.h>
|
||||
#include <nvram/messages/struct.h>
|
||||
#include <nvram/messages/vector.h>
|
||||
|
||||
#include <nvram/core/storage.h>
|
||||
|
||||
namespace nvram {
|
||||
|
||||
// The NVRAM header data structure, which holds global information used by the
|
||||
// NVRAM service, such as version and a list of defined spaces.
|
||||
struct NvramHeader {
|
||||
// State flags affecting all spaces.
|
||||
enum Flags {
|
||||
kFlagDisableCreate = 1 << 0,
|
||||
};
|
||||
|
||||
// Check whether a flag is present.
|
||||
bool HasFlag(Flags flag) const {
|
||||
return (flags & flag) != 0;
|
||||
}
|
||||
|
||||
// Set a flag.
|
||||
void SetFlag(Flags flag) {
|
||||
flags |= flag;
|
||||
}
|
||||
|
||||
// The current major header version. Bump this upon making
|
||||
// forward-incompatible changes to the storage format. Old versions will
|
||||
// reject the header on load and refuse to operate when they encounter a
|
||||
// version that is larger than the compile-time one.
|
||||
static constexpr uint32_t kVersion = 1;
|
||||
|
||||
// The header version, indicating the data format revision used when the
|
||||
// header was last written. On load, if the version is more recent then what
|
||||
// the code can handle, we bail out. This allows making forward-incompatible
|
||||
// changes to the data format with the guarantee that old code versions won't
|
||||
// clobber new data. This is a last resort kill switch for old code, in
|
||||
// general we should aim for maximum compatibility between versions.
|
||||
uint32_t version = kVersion;
|
||||
|
||||
// Current header flags. Bitwise OR of |NvramHeader::Flags| values.
|
||||
uint32_t flags = 0;
|
||||
|
||||
// A list of allocated indices, in no particular order.
|
||||
Vector<uint32_t> allocated_indices;
|
||||
|
||||
// An index that is in the process of being created or deleted. This field is
|
||||
// used as follows:
|
||||
// * On space creation, we add the new space's index both to
|
||||
// |allocated_indices| and set it as the |provisional_index|. Then, the
|
||||
// header is written, followed by the space data. If we crash in between,
|
||||
// the next load will find |provisional_index| present and will check
|
||||
// whether that space is present on disk or not. If not, it'll clear the
|
||||
// index from |allocated_indices| and reset |provisional_index|, hence the
|
||||
// space is as good as never created.
|
||||
// * On space deletion, the deleted space's index is removed from
|
||||
// |allocated_indices|, but stored in |provisional_index|. The, the header
|
||||
// gets written, followed by the space deletion. If we crash in between ,
|
||||
// the next load will find the provisional index set, but the space is
|
||||
// absent in |allocated_indices|. If this is the case, the initialization
|
||||
// code will make sure to delete the space data if it's still around and
|
||||
// clear |provisional_index| afterwards.
|
||||
Optional<uint32_t> provisional_index;
|
||||
};
|
||||
|
||||
// All data corresponding to a single NVRAM space is held in an NvramSpace
|
||||
// structure. There is one structure per allocated index.
|
||||
struct NvramSpace {
|
||||
// Flags indicating internal status in effect for a space.
|
||||
enum Flags {
|
||||
kFlagWriteLocked = 1 << 0,
|
||||
};
|
||||
|
||||
// Check whether a given flag is set.
|
||||
bool HasFlag(Flags flag) const {
|
||||
return (flags & flag) != 0;
|
||||
}
|
||||
|
||||
// Set a flag.
|
||||
void SetFlag(Flags flag) {
|
||||
flags |= flag;
|
||||
}
|
||||
|
||||
// A helper to simplify checking control flags.
|
||||
bool HasControl(uint32_t control) const {
|
||||
return (controls & (1 << control)) != 0;
|
||||
}
|
||||
|
||||
// Persistent space flags. Bitwise OR of |NvramSpace::Flags| values.
|
||||
uint32_t flags = 0;
|
||||
|
||||
// A bitmask of CONTROL_XYZ values in effect for the space. These are set at
|
||||
// space creation time and generally not touched afterwards.
|
||||
uint32_t controls = 0;
|
||||
|
||||
// The authorization value for the space. This is a shared secret that must be
|
||||
// provided to read and write the space as specified by the appropriate
|
||||
// |controls| flags.
|
||||
Blob authorization_value;
|
||||
|
||||
// The space payload data.
|
||||
Blob contents;
|
||||
};
|
||||
|
||||
namespace persistence {
|
||||
|
||||
// Load NVRAM header from storage.
|
||||
storage::Status LoadHeader(NvramHeader* header);
|
||||
|
||||
// Write the NVRAM header to storage.
|
||||
storage::Status StoreHeader(const NvramHeader& header);
|
||||
|
||||
// Load NVRAM space data for a given index from storage.
|
||||
storage::Status LoadSpace(uint32_t index, NvramSpace* space);
|
||||
|
||||
// Write the NVRAM space data for the given index to storage.
|
||||
storage::Status StoreSpace(uint32_t index, const NvramSpace& space);
|
||||
|
||||
// Delete the stored NVRAM space data for the given index.
|
||||
storage::Status DeleteSpace(uint32_t index);
|
||||
|
||||
} // namespace persistence
|
||||
|
||||
} // namespace nvram
|
||||
|
||||
#endif // NVRAM_CORE_PERSISTENCE_H_
|
96
android/system/nvram/core/include/nvram/core/storage.h
Normal file
96
android/system/nvram/core/include/nvram/core/storage.h
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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_STORAGE_H_
|
||||
#define NVRAM_CORE_STORAGE_H_
|
||||
|
||||
extern "C" {
|
||||
#include <stdint.h>
|
||||
} // extern "C"
|
||||
|
||||
#include <nvram/messages/blob.h>
|
||||
|
||||
namespace nvram {
|
||||
namespace storage {
|
||||
|
||||
// Indicates the result of a storage operation.
|
||||
enum class Status {
|
||||
kSuccess, // Operation successful.
|
||||
kNotFound, // The element to be read could not be found.
|
||||
kStorageError, // Failure on the underlying storage layer.
|
||||
};
|
||||
|
||||
// Load NVRAM header from storage. See the comment on LoadSpace() for details on
|
||||
// semantics and return values.
|
||||
Status LoadHeader(Blob* blob);
|
||||
|
||||
// Write the binary-encoded NVRAM header |blob| to storage. See the comment on
|
||||
// StoreSpace() for details on semantics and return values.
|
||||
Status StoreHeader(const Blob& blob);
|
||||
|
||||
// Load NVRAM space data for a given index from storage.
|
||||
//
|
||||
// This must place the data in |blob| that was provided by the last store
|
||||
// operation If there is evidence that no header data is present in the storage
|
||||
// system, this function must return Status::kNotFound. For all other error
|
||||
// conditions, implementations should return Status::kStorageError.
|
||||
//
|
||||
// It's OK if the data placed in |blob| exceeds the size of the original data,
|
||||
// i.e. contain trailing bytes that haven't actually been written. This allows
|
||||
// implementations to write at block granularity of the underlying storage
|
||||
// system, which may be simpler instead of having to track sizes accurately.
|
||||
Status LoadSpace(uint32_t index, Blob* blob);
|
||||
|
||||
// Write the NVRAM space data for the given index to storage.
|
||||
//
|
||||
// Implementations must atomically replace the current data with the data
|
||||
// provided in |blob|. This must be robust against crashes, i.e. reloading the
|
||||
// data after the crash should either return the previous version of the data,
|
||||
// or the updated data provided in |blob|. In particular, crashes must not
|
||||
// result in any form of data corruption or loss.
|
||||
//
|
||||
// It's OK for the implementation to allocate a larger storage area than
|
||||
// required, i.e. not match blob.size() accurately. This allows implementations
|
||||
// to perform I/O at block granularity of the underlying storage subsystem in
|
||||
// case this simplifies things. There is no requirement as to the values of any
|
||||
// additional trailing bytes.
|
||||
//
|
||||
// This function must make sure that the data actually hits disk before
|
||||
// returning. The return value must be Status::kSuccess if and only if data was
|
||||
// stored successfully, the function should return kStorageError otherwise.
|
||||
Status StoreSpace(uint32_t index, const Blob& blob);
|
||||
|
||||
// Delete the stored NVRAM space data for the given index.
|
||||
//
|
||||
// This function must atomically delete the storage corresponding to the NVRAM
|
||||
// space data associated with index. A subsequent read operation for the same
|
||||
// index should return Status::kNotFound.
|
||||
//
|
||||
// This function must be robust in the event of crashes, i.e. a crash should
|
||||
// leave the system with either the previously existing data still intact and
|
||||
// accessible, or the space data entirely removed.
|
||||
//
|
||||
// This function must return Status::kSuccess upon deleting the space data
|
||||
// successfully and having committed that operation to the underlying storage
|
||||
// medium. Status::kNotFound should be returned in case the space data
|
||||
// positively doesn't exist on the medium, and Status::kStorageError should be
|
||||
// returned for all other error conditions.
|
||||
Status DeleteSpace(uint32_t index);
|
||||
|
||||
} // namespace storage
|
||||
} // namespace nvram
|
||||
|
||||
#endif // NVRAM_CORE_STORAGE_H
|
855
android/system/nvram/core/nvram_manager.cpp
Normal file
855
android/system/nvram/core/nvram_manager.cpp
Normal file
|
@ -0,0 +1,855 @@
|
|||
/*
|
||||
* 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"
|
||||
|
||||
extern "C" {
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
} // extern "C"
|
||||
|
||||
#include <nvram/core/logger.h>
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Constant time memory block comparison.
|
||||
bool ConstantTimeEquals(const Blob& a, const Blob& b) {
|
||||
if (a.size() != b.size())
|
||||
return false;
|
||||
|
||||
// The volatile qualifiers prevent the compiler from making assumptions that
|
||||
// allow shortcuts:
|
||||
// * The entire array data must be read from memory.
|
||||
// * Marking |result| volatile ensures the subsequent loop iterations must
|
||||
// still store to |result|, thus avoiding the loop to exit early.
|
||||
// This achieves the desired constant-time behavior.
|
||||
volatile const uint8_t* data_a = a.data();
|
||||
volatile const uint8_t* data_b = b.data();
|
||||
volatile uint8_t result = 0;
|
||||
for (size_t i = 0; i < a.size(); ++i) {
|
||||
result |= data_a[i] ^ data_b[i];
|
||||
}
|
||||
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
// A standard minimum function.
|
||||
template <typename Type>
|
||||
const Type& min(const Type& a, const Type& b) {
|
||||
return (a < b) ? a : b;
|
||||
}
|
||||
|
||||
// Filter status codes from the storage layer to only include known values.
|
||||
// Anything outside the range will be mapped to the generic |kStorageError|.
|
||||
storage::Status SanitizeStorageStatus(storage::Status status) {
|
||||
switch (status) {
|
||||
case storage::Status::kSuccess:
|
||||
return storage::Status::kSuccess;
|
||||
case storage::Status::kNotFound:
|
||||
return storage::Status::kNotFound;
|
||||
case storage::Status::kStorageError:
|
||||
return storage::Status::kStorageError;
|
||||
}
|
||||
NVRAM_LOG_ERR("Unknown status code %u!", status);
|
||||
return storage::Status::kStorageError;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Looks at |request| to determine the command to execute, then invokes
|
||||
// the appropriate handler.
|
||||
void NvramManager::Dispatch(const nvram::Request& request,
|
||||
nvram::Response* response) {
|
||||
nvram_result_t result = NV_RESULT_INVALID_PARAMETER;
|
||||
const nvram::RequestUnion& input = request.payload;
|
||||
nvram::ResponseUnion* output = &response->payload;
|
||||
|
||||
switch (input.which()) {
|
||||
case nvram::COMMAND_GET_INFO:
|
||||
result = GetInfo(*input.get<COMMAND_GET_INFO>(),
|
||||
&output->Activate<COMMAND_GET_INFO>());
|
||||
break;
|
||||
case nvram::COMMAND_CREATE_SPACE:
|
||||
result = CreateSpace(*input.get<COMMAND_CREATE_SPACE>(),
|
||||
&output->Activate<COMMAND_CREATE_SPACE>());
|
||||
break;
|
||||
case nvram::COMMAND_GET_SPACE_INFO:
|
||||
result = GetSpaceInfo(*input.get<COMMAND_GET_SPACE_INFO>(),
|
||||
&output->Activate<COMMAND_GET_SPACE_INFO>());
|
||||
break;
|
||||
case nvram::COMMAND_DELETE_SPACE:
|
||||
result = DeleteSpace(*input.get<COMMAND_DELETE_SPACE>(),
|
||||
&output->Activate<COMMAND_DELETE_SPACE>());
|
||||
break;
|
||||
case nvram::COMMAND_DISABLE_CREATE:
|
||||
result = DisableCreate(*input.get<COMMAND_DISABLE_CREATE>(),
|
||||
&output->Activate<COMMAND_DISABLE_CREATE>());
|
||||
break;
|
||||
case nvram::COMMAND_WRITE_SPACE:
|
||||
result = WriteSpace(*input.get<COMMAND_WRITE_SPACE>(),
|
||||
&output->Activate<COMMAND_WRITE_SPACE>());
|
||||
break;
|
||||
case nvram::COMMAND_READ_SPACE:
|
||||
result = ReadSpace(*input.get<COMMAND_READ_SPACE>(),
|
||||
&output->Activate<COMMAND_READ_SPACE>());
|
||||
break;
|
||||
case nvram::COMMAND_LOCK_SPACE_WRITE:
|
||||
result = LockSpaceWrite(*input.get<COMMAND_LOCK_SPACE_WRITE>(),
|
||||
&output->Activate<COMMAND_LOCK_SPACE_WRITE>());
|
||||
break;
|
||||
case nvram::COMMAND_LOCK_SPACE_READ:
|
||||
result = LockSpaceRead(*input.get<COMMAND_LOCK_SPACE_READ>(),
|
||||
&output->Activate<COMMAND_LOCK_SPACE_READ>());
|
||||
break;
|
||||
case nvram::COMMAND_WIPE_STORAGE:
|
||||
result = WipeStorage(*input.get<COMMAND_WIPE_STORAGE>(),
|
||||
&output->Activate<COMMAND_WIPE_STORAGE>());
|
||||
break;
|
||||
case nvram::COMMAND_DISABLE_WIPE:
|
||||
result = DisableWipe(*input.get<COMMAND_DISABLE_WIPE>(),
|
||||
&output->Activate<COMMAND_DISABLE_WIPE>());
|
||||
break;
|
||||
}
|
||||
|
||||
response->result = result;
|
||||
}
|
||||
|
||||
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_space_size = kMaxSpaceSize;
|
||||
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;
|
||||
}
|
||||
response->wipe_disabled = disable_wipe_;
|
||||
|
||||
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%" PRIx32, 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%" PRIx32 " 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;
|
||||
}
|
||||
if ((controls & (1 << NV_CONTROL_WRITE_EXTEND)) != 0 &&
|
||||
request.size != crypto::kSHA256DigestSize) {
|
||||
NVRAM_LOG_INFO("Write-extended space size must be %zu.",
|
||||
crypto::kSHA256DigestSize);
|
||||
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%" PRIx32, 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::DeleteSpace(const DeleteSpaceRequest& request,
|
||||
DeleteSpaceResponse* /* response */) {
|
||||
const uint32_t index = request.index;
|
||||
NVRAM_LOG_INFO("DeleteSpace Ox%" PRIx32, index);
|
||||
|
||||
if (!Initialize())
|
||||
return NV_RESULT_INTERNAL_ERROR;
|
||||
|
||||
SpaceRecord space_record;
|
||||
nvram_result_t result;
|
||||
if (!LoadSpaceRecord(index, &space_record, &result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = space_record.CheckWriteAccess(request.authorization_value);
|
||||
if (result != NV_RESULT_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Delete the space. First mark the space as provisionally removed in the
|
||||
// header. Then, delete the space data from storage. This allows orphaned
|
||||
// space data be cleaned up after a crash.
|
||||
SpaceListEntry tmp = spaces_[space_record.array_index];
|
||||
spaces_[space_record.array_index] = spaces_[num_spaces_ - 1];
|
||||
--num_spaces_;
|
||||
result = WriteHeader(Optional<uint32_t>(index));
|
||||
if (result == NV_RESULT_SUCCESS) {
|
||||
switch (SanitizeStorageStatus(persistence::DeleteSpace(index))) {
|
||||
case storage::Status::kStorageError:
|
||||
NVRAM_LOG_ERR("Failed to delete space 0x%" PRIx32 " data.", index);
|
||||
result = NV_RESULT_INTERNAL_ERROR;
|
||||
break;
|
||||
case storage::Status::kNotFound:
|
||||
// The space was missing even if it shouldn't have been. Log an error,
|
||||
// but return success as we're in the desired state.
|
||||
NVRAM_LOG_ERR("Space 0x%" PRIx32 " data missing on deletion.", index);
|
||||
return NV_RESULT_SUCCESS;
|
||||
case storage::Status::kSuccess:
|
||||
return NV_RESULT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
// Failed to delete, re-add the transient state to |spaces_|.
|
||||
spaces_[num_spaces_] = tmp;
|
||||
++num_spaces_;
|
||||
return result;
|
||||
}
|
||||
|
||||
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. Make sure to restore the
|
||||
// current value of |disable_create_| if the write call fails, as we return an
|
||||
// error in that case and client code would not expect state changes.
|
||||
bool disable_create_previous = disable_create_;
|
||||
disable_create_ = true;
|
||||
nvram_result_t result = WriteHeader(Optional<uint32_t>());
|
||||
if (result != NV_RESULT_SUCCESS) {
|
||||
disable_create_ = disable_create_previous;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
nvram_result_t NvramManager::WriteSpace(const WriteSpaceRequest& request,
|
||||
WriteSpaceResponse* /* response */) {
|
||||
const uint32_t index = request.index;
|
||||
NVRAM_LOG_INFO("WriteSpace Ox%" PRIx32, index);
|
||||
|
||||
if (!Initialize())
|
||||
return NV_RESULT_INTERNAL_ERROR;
|
||||
|
||||
SpaceRecord space_record;
|
||||
nvram_result_t result;
|
||||
if (!LoadSpaceRecord(index, &space_record, &result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = space_record.CheckWriteAccess(request.authorization_value);
|
||||
if (result != NV_RESULT_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Blob& contents = space_record.persistent.contents;
|
||||
if (space_record.persistent.HasControl(NV_CONTROL_WRITE_EXTEND)) {
|
||||
// Concatenate the current space |contents| with the input data.
|
||||
Blob sha256_input;
|
||||
if (!sha256_input.Resize(contents.size() + request.buffer.size())) {
|
||||
return NV_RESULT_INTERNAL_ERROR;
|
||||
}
|
||||
memcpy(sha256_input.data(), contents.data(), contents.size());
|
||||
memcpy(sha256_input.data() + contents.size(), request.buffer.data(),
|
||||
request.buffer.size());
|
||||
|
||||
// Compute the SHA-256 digest and write it back to |contents|.
|
||||
crypto::SHA256(sha256_input.data(), sha256_input.size(), contents.data(),
|
||||
contents.size());
|
||||
} else {
|
||||
if (contents.size() < request.buffer.size()) {
|
||||
return NV_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
memcpy(contents.data(), request.buffer.data(), request.buffer.size());
|
||||
memset(contents.data() + request.buffer.size(), 0x0,
|
||||
contents.size() - request.buffer.size());
|
||||
}
|
||||
|
||||
return WriteSpace(index, space_record.persistent);
|
||||
}
|
||||
|
||||
nvram_result_t NvramManager::ReadSpace(const ReadSpaceRequest& request,
|
||||
ReadSpaceResponse* response) {
|
||||
const uint32_t index = request.index;
|
||||
NVRAM_LOG_INFO("ReadSpace Ox%" PRIx32, index);
|
||||
|
||||
if (!Initialize())
|
||||
return NV_RESULT_INTERNAL_ERROR;
|
||||
|
||||
SpaceRecord space_record;
|
||||
nvram_result_t result;
|
||||
if (!LoadSpaceRecord(index, &space_record, &result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = space_record.CheckReadAccess(request.authorization_value);
|
||||
if (result != NV_RESULT_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!response->buffer.Assign(space_record.persistent.contents.data(),
|
||||
space_record.persistent.contents.size())) {
|
||||
NVRAM_LOG_ERR("Allocation failure.");
|
||||
return NV_RESULT_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return NV_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
nvram_result_t NvramManager::LockSpaceWrite(
|
||||
const LockSpaceWriteRequest& request,
|
||||
LockSpaceWriteResponse* /* response */) {
|
||||
const uint32_t index = request.index;
|
||||
NVRAM_LOG_INFO("LockSpaceWrite Ox%" PRIx32, index);
|
||||
|
||||
if (!Initialize())
|
||||
return NV_RESULT_INTERNAL_ERROR;
|
||||
|
||||
SpaceRecord space_record;
|
||||
nvram_result_t result;
|
||||
if (!LoadSpaceRecord(index, &space_record, &result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = space_record.CheckWriteAccess(request.authorization_value);
|
||||
if (result != NV_RESULT_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (space_record.persistent.HasControl(NV_CONTROL_PERSISTENT_WRITE_LOCK)) {
|
||||
space_record.persistent.SetFlag(NvramSpace::kFlagWriteLocked);
|
||||
return WriteSpace(index, space_record.persistent);
|
||||
} else if (space_record.persistent.HasControl(NV_CONTROL_BOOT_WRITE_LOCK)) {
|
||||
space_record.transient->write_locked = true;
|
||||
return NV_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
NVRAM_LOG_ERR("Space not configured for write locking.");
|
||||
return NV_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
nvram_result_t NvramManager::LockSpaceRead(
|
||||
const LockSpaceReadRequest& request,
|
||||
LockSpaceReadResponse* /* response */) {
|
||||
const uint32_t index = request.index;
|
||||
NVRAM_LOG_INFO("LockSpaceRead Ox%" PRIx32, index);
|
||||
|
||||
if (!Initialize())
|
||||
return NV_RESULT_INTERNAL_ERROR;
|
||||
|
||||
SpaceRecord space_record;
|
||||
nvram_result_t result;
|
||||
if (!LoadSpaceRecord(index, &space_record, &result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = space_record.CheckReadAccess(request.authorization_value);
|
||||
if (result != NV_RESULT_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (space_record.persistent.HasControl(NV_CONTROL_BOOT_READ_LOCK)) {
|
||||
space_record.transient->read_locked = true;
|
||||
return NV_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
NVRAM_LOG_ERR("Space not configured for read locking.");
|
||||
return NV_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
nvram_result_t NvramManager::WipeStorage(
|
||||
const WipeStorageRequest& /* request */,
|
||||
WipeStorageResponse* /* response */) {
|
||||
if (!Initialize())
|
||||
return NV_RESULT_INTERNAL_ERROR;
|
||||
|
||||
#ifdef NVRAM_WIPE_STORAGE_SUPPORT
|
||||
if (disable_wipe_) {
|
||||
return NV_RESULT_OPERATION_DISABLED;
|
||||
}
|
||||
|
||||
// Go through all spaces and wipe the corresponding data. Note that the header
|
||||
// is only updated once all space data is gone. This will "break" all spaces
|
||||
// that are left declared but don't have data. This situation can be observed
|
||||
// if we crash somewhere during the wiping process before clearing the header.
|
||||
//
|
||||
// Note that we deliberately choose this wiping sequence so we can never end
|
||||
// up in a state where the header appears clean but existing space data
|
||||
// remains.
|
||||
//
|
||||
// As a final note, the ideal solution would be to atomically clear the header
|
||||
// and delete all space data. While more desirable from an operational point
|
||||
// of view, this would drastically complicate storage layer requirements to
|
||||
// support cross-object atomicity instead of per-object atomicity.
|
||||
for (size_t i = 0; i < num_spaces_; ++i) {
|
||||
const uint32_t index = spaces_[i].index;
|
||||
switch (SanitizeStorageStatus(persistence::DeleteSpace(index))) {
|
||||
case storage::Status::kStorageError:
|
||||
NVRAM_LOG_ERR("Failed to wipe space 0x%" PRIx32 " data.", index);
|
||||
return NV_RESULT_INTERNAL_ERROR;
|
||||
case storage::Status::kNotFound:
|
||||
// The space was missing even if it shouldn't have been. This may occur
|
||||
// if a previous wiping attempt was aborted half-way. Log an error, but
|
||||
// return success as we're in the desired state.
|
||||
NVRAM_LOG_WARN("Space 0x%" PRIx32 " data missing on wipe.", index);
|
||||
break;
|
||||
case storage::Status::kSuccess:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// All spaces are gone, clear the header.
|
||||
num_spaces_ = 0;
|
||||
return WriteHeader(Optional<uint32_t>());
|
||||
#else // NVRAM_WIPE_STORAGE_SUPPORT
|
||||
// We're not accessing the flag member, so prevent a compiler warning. The
|
||||
// alternative of conditionally including the member in the class declaration
|
||||
// looks cleaner at first sight, but comes with the risk of
|
||||
// NVRAM_WIPE_STORAGE_SUPPORT polarity mismatches between compilation units,
|
||||
// which is more subtly dangerous, so we rather keep the member even for the
|
||||
// case in which it is not used.
|
||||
(void)disable_wipe_;
|
||||
return NV_RESULT_OPERATION_DISABLED;
|
||||
#endif // NVRAM_WIPE_STORAGE_SUPPORT
|
||||
}
|
||||
|
||||
nvram_result_t NvramManager::DisableWipe(
|
||||
const DisableWipeRequest& /* request */,
|
||||
DisableWipeResponse* /* response */) {
|
||||
if (!Initialize())
|
||||
return NV_RESULT_INTERNAL_ERROR;
|
||||
|
||||
#ifdef NVRAM_WIPE_STORAGE_SUPPORT
|
||||
disable_wipe_ = true;
|
||||
return NV_RESULT_SUCCESS;
|
||||
#else // NVRAM_WIPE_STORAGE_SUPPORT
|
||||
return NV_RESULT_OPERATION_DISABLED;
|
||||
#endif // NVRAM_WIPE_STORAGE_SUPPORT
|
||||
}
|
||||
|
||||
nvram_result_t NvramManager::SpaceRecord::CheckWriteAccess(
|
||||
const Blob& authorization_value) {
|
||||
if (persistent.HasControl(NV_CONTROL_PERSISTENT_WRITE_LOCK)) {
|
||||
if (persistent.HasFlag(NvramSpace::kFlagWriteLocked)) {
|
||||
NVRAM_LOG_INFO("Attempt to write persistently locked space 0x%" PRIx32
|
||||
".",
|
||||
transient->index);
|
||||
return NV_RESULT_OPERATION_DISABLED;
|
||||
}
|
||||
} else if (persistent.HasControl(NV_CONTROL_BOOT_WRITE_LOCK)) {
|
||||
if (transient->write_locked) {
|
||||
NVRAM_LOG_INFO("Attempt to write per-boot locked space 0x%" PRIx32 ".",
|
||||
transient->index);
|
||||
return NV_RESULT_OPERATION_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
if (persistent.HasControl(NV_CONTROL_WRITE_AUTHORIZATION) &&
|
||||
!ConstantTimeEquals(persistent.authorization_value,
|
||||
authorization_value)) {
|
||||
NVRAM_LOG_INFO(
|
||||
"Authorization value mismatch for write access to space 0x%" PRIx32 ".",
|
||||
transient->index);
|
||||
return NV_RESULT_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
// All checks passed, allow the write.
|
||||
return NV_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
nvram_result_t NvramManager::SpaceRecord::CheckReadAccess(
|
||||
const Blob& authorization_value) {
|
||||
if (persistent.HasControl(NV_CONTROL_BOOT_READ_LOCK)) {
|
||||
if (transient->read_locked) {
|
||||
NVRAM_LOG_INFO("Attempt to read per-boot locked space 0x%" PRIx32 ".",
|
||||
transient->index);
|
||||
return NV_RESULT_OPERATION_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
if (persistent.HasControl(NV_CONTROL_READ_AUTHORIZATION) &&
|
||||
!ConstantTimeEquals(persistent.authorization_value,
|
||||
authorization_value)) {
|
||||
NVRAM_LOG_INFO(
|
||||
"Authorization value mismatch for read access to space 0x%" PRIx32 ".",
|
||||
transient->index);
|
||||
return NV_RESULT_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
// All checks passed, allow the read.
|
||||
return NV_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
bool NvramManager::Initialize() {
|
||||
if (initialized_)
|
||||
return true;
|
||||
|
||||
NvramHeader header;
|
||||
switch (SanitizeStorageStatus(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 %" PRIu32 " is more recent than %" PRIu32
|
||||
", 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 (SanitizeStorageStatus(
|
||||
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%" PRIx32 ".",
|
||||
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 (SanitizeStorageStatus(
|
||||
persistence::DeleteSpace(provisional_index.value()))) {
|
||||
case storage::Status::kStorageError:
|
||||
NVRAM_LOG_ERR("Failed to delete provisional space 0x%" PRIx32 " data.",
|
||||
provisional_index.value());
|
||||
return false;
|
||||
case storage::Status::kNotFound:
|
||||
// The space isn't present in storage. This may happen if the space
|
||||
// deletion succeeded, but the header wasn't written subsequently.
|
||||
break;
|
||||
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;
|
||||
}
|
||||
|
||||
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 (SanitizeStorageStatus(
|
||||
persistence::LoadSpace(index, &space_record->persistent))) {
|
||||
case storage::Status::kStorageError:
|
||||
NVRAM_LOG_ERR("Failed to load space 0x%" PRIx32 " 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%" PRIx32
|
||||
" 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;
|
||||
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 (SanitizeStorageStatus(persistence::StoreHeader(header)) !=
|
||||
storage::Status::kSuccess) {
|
||||
NVRAM_LOG_ERR("Failed to store header.");
|
||||
return NV_RESULT_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return NV_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
nvram_result_t NvramManager::WriteSpace(uint32_t index,
|
||||
const NvramSpace& space) {
|
||||
if (SanitizeStorageStatus(persistence::StoreSpace(index, space)) !=
|
||||
storage::Status::kSuccess) {
|
||||
NVRAM_LOG_ERR("Failed to store space 0x%" PRIx32 ".", index);
|
||||
return NV_RESULT_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return NV_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace nvram
|
140
android/system/nvram/core/persistence.cpp
Normal file
140
android/system/nvram/core/persistence.cpp
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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/persistence.h"
|
||||
|
||||
#include <nvram/messages/io.h>
|
||||
#include <nvram/messages/proto.hpp>
|
||||
|
||||
#include <nvram/core/logger.h>
|
||||
|
||||
namespace nvram {
|
||||
|
||||
namespace {
|
||||
|
||||
// Magic constants that identify encoded |NvramHeader| vs. |NvramSpace| objects.
|
||||
const uint32_t kHeaderMagic = 0x4e5648; // "NVH" in hex
|
||||
const uint32_t kSpaceMagic = 0x4e5653; // "NVS" in hex
|
||||
|
||||
// Encodes an |object| as a protobuf message and writes it to |blob|. Note that
|
||||
// standard protobuf encoding doesn't include information about the overall size
|
||||
// of the encoded object. This is not good enough here, as encoding should
|
||||
// gracefully handle trailing data on decode, e.g. to allow underlying storage
|
||||
// systems that only provide block-granular I/O.
|
||||
//
|
||||
// Not that the code uses |proto::detail::MessageEncoder<Object>::Encode()|
|
||||
// instead of the regular |proto::Encode()| to encode the message. This results
|
||||
// in the message being wrapped in a length-delimited proto field record, so the
|
||||
// length field can be used to determine the actual length of the message. Also,
|
||||
// this gives us the opportunity to encode a magic constant in the field number
|
||||
// bits of the wire tag, thus allowing us to detect situations where we're
|
||||
// attempting to decode a message of wrong type.
|
||||
template <uint32_t magic, typename Object>
|
||||
storage::Status EncodeObject(const Object& object, Blob* blob) {
|
||||
BlobOutputStreamBuffer stream(blob);
|
||||
ProtoWriter writer(&stream);
|
||||
writer.set_field_number(magic);
|
||||
if (!proto::detail::MessageEncoder<Object>::Encode(object, &writer) ||
|
||||
!stream.Truncate()) {
|
||||
NVRAM_LOG_ERR("Failed to encode object.");
|
||||
return storage::Status::kStorageError;
|
||||
}
|
||||
return storage::Status::kSuccess;
|
||||
}
|
||||
|
||||
// Decodes a protobuf-encoded |object| from |blob|. It is OK if the provided
|
||||
// |blob| includes trailing data that doesn't belong to the encoded object.
|
||||
//
|
||||
// Note that the code below reads the wire tag to strip the wrapping proto field
|
||||
// record produced by |EncodeObject|. It then checks the magic field number to
|
||||
// make sure we're decoding a message of correct type. Finally,
|
||||
// |proto::detail::MessageDecoder<Object>::Decode()| takes care of reading the
|
||||
// message payload from the proto field record.
|
||||
template <uint32_t magic, typename Object>
|
||||
storage::Status DecodeObject(const Blob& blob, Object* object) {
|
||||
InputStreamBuffer stream(blob.data(), blob.size());
|
||||
ProtoReader reader(&stream);
|
||||
if (!reader.ReadWireTag() || reader.field_number() != magic ||
|
||||
reader.wire_type() != WireType::kLengthDelimited ||
|
||||
!proto::detail::MessageDecoder<Object>::Decode(*object, &reader)) {
|
||||
NVRAM_LOG_ERR("Failed to decode object of size %zu.", blob.size());
|
||||
return storage::Status::kStorageError;
|
||||
}
|
||||
return storage::Status::kSuccess;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
template <> struct DescriptorForType<NvramHeader> {
|
||||
static constexpr auto kFields =
|
||||
MakeFieldList(MakeField(1, &NvramHeader::version),
|
||||
MakeField(2, &NvramHeader::flags),
|
||||
MakeField(3, &NvramHeader::allocated_indices),
|
||||
MakeField(4, &NvramHeader::provisional_index));
|
||||
};
|
||||
|
||||
template <> struct DescriptorForType<NvramSpace> {
|
||||
static constexpr auto kFields =
|
||||
MakeFieldList(MakeField(1, &NvramSpace::flags),
|
||||
MakeField(2, &NvramSpace::controls),
|
||||
MakeField(3, &NvramSpace::authorization_value),
|
||||
MakeField(4, &NvramSpace::contents));
|
||||
};
|
||||
|
||||
namespace persistence {
|
||||
|
||||
storage::Status LoadHeader(NvramHeader* header) {
|
||||
Blob blob;
|
||||
storage::Status status = storage::LoadHeader(&blob);
|
||||
if (status != storage::Status::kSuccess) {
|
||||
return status;
|
||||
}
|
||||
return DecodeObject<kHeaderMagic>(blob, header);
|
||||
}
|
||||
|
||||
storage::Status StoreHeader(const NvramHeader& header) {
|
||||
Blob blob;
|
||||
storage::Status status = EncodeObject<kHeaderMagic>(header, &blob);
|
||||
if (status != storage::Status::kSuccess) {
|
||||
return status;
|
||||
}
|
||||
return storage::StoreHeader(blob);
|
||||
}
|
||||
|
||||
storage::Status LoadSpace(uint32_t index, NvramSpace* space) {
|
||||
Blob blob;
|
||||
storage::Status status = storage::LoadSpace(index, &blob);
|
||||
if (status != storage::Status::kSuccess) {
|
||||
return status;
|
||||
}
|
||||
return DecodeObject<kSpaceMagic>(blob, space);
|
||||
}
|
||||
|
||||
storage::Status StoreSpace(uint32_t index, const NvramSpace& space) {
|
||||
Blob blob;
|
||||
storage::Status status = EncodeObject<kSpaceMagic>(space, &blob);
|
||||
if (status != storage::Status::kSuccess) {
|
||||
return status;
|
||||
}
|
||||
return storage::StoreSpace(index, blob);
|
||||
}
|
||||
|
||||
storage::Status DeleteSpace(uint32_t index) {
|
||||
return storage::DeleteSpace(index);
|
||||
}
|
||||
|
||||
} // namespace persistence
|
||||
} // namespace nvram
|
47
android/system/nvram/core/rules.mk
Normal file
47
android/system/nvram/core/rules.mk
Normal file
|
@ -0,0 +1,47 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
# A library target that contains the trusty NVRAM implementation, without any
|
||||
# glue to build the app or dependencies to other services. This allows to link
|
||||
# this module into the unittest app with dependencies mocked out.
|
||||
MODULE := $(LOCAL_DIR)
|
||||
|
||||
MODULE_SRCS += \
|
||||
$(LOCAL_DIR)/crypto_boringssl.cpp \
|
||||
$(LOCAL_DIR)/nvram_manager.cpp \
|
||||
$(LOCAL_DIR)/persistence.cpp
|
||||
|
||||
MODULE_CPPFLAGS := -Wall -Werror -Wextra
|
||||
|
||||
MODULE_DEPS += \
|
||||
external/openssl \
|
||||
lib/libc-trusty \
|
||||
lib/libstdc++-trusty \
|
||||
system/nvram/messages
|
||||
|
||||
ifneq ($(NVRAM_LOG_LEVEL),)
|
||||
GLOBAL_DEFINES += NVRAM_LOG_LEVEL=$(NVRAM_LOG_LEVEL)
|
||||
endif
|
||||
|
||||
ifneq ($(NVRAM_WIPE_STORAGE_SUPPORT),)
|
||||
GLOBAL_DEFINES += NVRAM_WIPE_STORAGE_SUPPORT=$(NVRAM_WIPE_STORAGE_SUPPORT)
|
||||
endif
|
||||
|
||||
GLOBAL_INCLUDES += $(LOCAL_DIR)/include/
|
||||
|
||||
include make/module.mk
|
29
android/system/nvram/core/tests/Android.mk
Normal file
29
android/system/nvram/core/tests/Android.mk
Normal file
|
@ -0,0 +1,29 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libnvram-core-tests
|
||||
LOCAL_MODULE_TAGS := debug
|
||||
LOCAL_SRC_FILES := \
|
||||
fake_storage.cpp \
|
||||
nvram_manager_test.cpp
|
||||
LOCAL_CFLAGS := -Wall -Werror -Wextra -DHAS_GTEST
|
||||
LOCAL_CLANG := true
|
||||
LOCAL_STATIC_LIBRARIES := libnvram-core-host
|
||||
LOCAL_SHARED_LIBRARIES := libnvram-messages libcrypto
|
||||
include $(BUILD_HOST_NATIVE_TEST)
|
180
android/system/nvram/core/tests/fake_storage.cpp
Normal file
180
android/system/nvram/core/tests/fake_storage.cpp
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* 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 "fake_storage.h"
|
||||
|
||||
#include <nvram/messages/blob.h>
|
||||
#include <nvram/messages/compiler.h>
|
||||
|
||||
#define countof(a) (sizeof(a) / sizeof((a)[0]))
|
||||
|
||||
namespace nvram {
|
||||
namespace storage {
|
||||
|
||||
namespace {
|
||||
|
||||
class StorageSlot {
|
||||
public:
|
||||
Status Load(Blob* blob) {
|
||||
if (read_error_) {
|
||||
return Status::kStorageError;
|
||||
}
|
||||
|
||||
if (!present_) {
|
||||
return Status::kNotFound;
|
||||
}
|
||||
|
||||
NVRAM_CHECK(blob->Assign(blob_.data(), blob_.size()));
|
||||
return Status::kSuccess;
|
||||
}
|
||||
|
||||
Status Store(const Blob& blob) {
|
||||
if (write_error_) {
|
||||
return Status::kStorageError;
|
||||
}
|
||||
|
||||
NVRAM_CHECK(blob_.Assign(blob.data(), blob.size()));
|
||||
present_ = true;
|
||||
return Status::kSuccess;
|
||||
}
|
||||
|
||||
Status Delete() {
|
||||
if (write_error_) {
|
||||
return Status::kStorageError;
|
||||
}
|
||||
|
||||
NVRAM_CHECK(blob_.Resize(0));
|
||||
present_ = false;
|
||||
return Status::kSuccess;
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
present_ = false;
|
||||
read_error_ = false;
|
||||
write_error_ = false;
|
||||
NVRAM_CHECK(blob_.Resize(0));
|
||||
}
|
||||
|
||||
bool present() const { return present_; }
|
||||
void set_present(bool present) { present_ = present; }
|
||||
void set_read_error(bool error) { read_error_ = error; }
|
||||
void set_write_error(bool error) { write_error_ = error; }
|
||||
|
||||
private:
|
||||
bool present_ = false;
|
||||
bool read_error_ = false;
|
||||
bool write_error_ = false;
|
||||
Blob blob_;
|
||||
};
|
||||
|
||||
// Header storage.
|
||||
StorageSlot g_header;
|
||||
|
||||
// Space blob storage.
|
||||
struct SpaceStorageSlot {
|
||||
uint32_t index;
|
||||
StorageSlot slot;
|
||||
};
|
||||
|
||||
SpaceStorageSlot g_spaces[256];
|
||||
|
||||
// Find the position in |g_spaces| corresponding to a given space |index|.
|
||||
// Returns the slot pointer or |nullptr| if not found.
|
||||
StorageSlot* FindSlotForIndex(uint32_t index) {
|
||||
for (size_t i = 0; i < countof(g_spaces); ++i) {
|
||||
if (g_spaces[i].slot.present() && g_spaces[i].index == index) {
|
||||
return &g_spaces[i].slot;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Finds or creates the slot for |index|. Returns the slot pointer or |nullptr|
|
||||
// if not found.
|
||||
StorageSlot* FindOrCreateSlotForIndex(uint32_t index) {
|
||||
StorageSlot* slot = FindSlotForIndex(index);
|
||||
if (slot) {
|
||||
return slot;
|
||||
}
|
||||
|
||||
|
||||
for (size_t i = 0; i < countof(g_spaces); ++i) {
|
||||
if (!g_spaces[i].slot.present()) {
|
||||
g_spaces[i].index = index;
|
||||
return &g_spaces[i].slot;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Status LoadHeader(Blob* blob) {
|
||||
return g_header.Load(blob);
|
||||
}
|
||||
|
||||
Status StoreHeader(const Blob& blob) {
|
||||
return g_header.Store(blob);
|
||||
}
|
||||
|
||||
void SetHeaderReadError(bool error) {
|
||||
g_header.set_read_error(error);
|
||||
}
|
||||
|
||||
void SetHeaderWriteError(bool error) {
|
||||
g_header.set_write_error(error);
|
||||
}
|
||||
|
||||
Status LoadSpace(uint32_t index, Blob* blob) {
|
||||
StorageSlot* slot = FindSlotForIndex(index);
|
||||
return slot ? slot->Load(blob) : Status::kNotFound;
|
||||
}
|
||||
|
||||
Status StoreSpace(uint32_t index, const Blob& blob) {
|
||||
StorageSlot* slot = FindOrCreateSlotForIndex(index);
|
||||
return slot ? slot->Store(blob) : Status::kStorageError;
|
||||
}
|
||||
|
||||
Status DeleteSpace(uint32_t index) {
|
||||
StorageSlot* slot = FindSlotForIndex(index);
|
||||
return slot ? slot->Delete() : Status::kNotFound;
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
g_header.Clear();
|
||||
for (size_t i = 0; i < countof(g_spaces); ++i) {
|
||||
g_spaces[i].slot.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void SetSpaceReadError(uint32_t index, bool error) {
|
||||
StorageSlot* slot = FindOrCreateSlotForIndex(index);
|
||||
if (slot) {
|
||||
slot->set_read_error(error);
|
||||
}
|
||||
}
|
||||
|
||||
void SetSpaceWriteError(uint32_t index, bool error) {
|
||||
StorageSlot* slot = FindOrCreateSlotForIndex(index);
|
||||
if (slot) {
|
||||
slot->set_write_error(error);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace storage
|
||||
} // namespace nvram
|
45
android/system/nvram/core/tests/fake_storage.h
Normal file
45
android/system/nvram/core/tests/fake_storage.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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_TEST_FAKE_STORAGE_H_
|
||||
#define NVRAM_TEST_FAKE_STORAGE_H_
|
||||
|
||||
#include <nvram/core/storage.h>
|
||||
|
||||
namespace nvram {
|
||||
namespace storage {
|
||||
|
||||
// Setup the header storage read functions to return Status::kStorageError.
|
||||
void SetHeaderReadError(bool error);
|
||||
|
||||
// Setup the header storage write functions to return Status::kStorageError.
|
||||
void SetHeaderWriteError(bool error);
|
||||
|
||||
// Setup the storage read calls for space |index| to return
|
||||
// Status::kStorageError.
|
||||
void SetSpaceReadError(uint32_t index, bool error);
|
||||
|
||||
// Setup the storage write calls for space |index| to return
|
||||
// Status::kStorageError.
|
||||
void SetSpaceWriteError(uint32_t index, bool error);
|
||||
|
||||
// Clears all storage.
|
||||
void Clear();
|
||||
|
||||
} // namespace storage
|
||||
} // namespace nvram
|
||||
|
||||
#endif // NVRAM_TEST_FAKE_STORAGE_H_
|
54
android/system/nvram/core/tests/gtest_stubs.cpp
Normal file
54
android/system/nvram/core/tests/gtest_stubs.cpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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 "gtest_stubs.h"
|
||||
|
||||
namespace testing {
|
||||
|
||||
bool g_test_status = false;
|
||||
|
||||
namespace detail {
|
||||
|
||||
void TestRegistry::RunAllTests() {
|
||||
int test_count = 0;
|
||||
int test_failures = 0;
|
||||
for (TestDeclarationBase* decl = tests_; decl; decl = decl->next) {
|
||||
TestInstanceBase* instance = decl->create_function();
|
||||
fprintf(stderr, "[ %s ] Starting...\n", decl->name);
|
||||
g_test_status = true;
|
||||
instance->Run();
|
||||
test_failures += g_test_status ? 0 : 1;
|
||||
++test_count;
|
||||
fprintf(stderr, "[ %s ] %s\n", decl->name, g_test_status ? "PASS" : "FAIL");
|
||||
delete instance;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Ran %d tests, %d failures.\n", test_count, test_failures);
|
||||
}
|
||||
|
||||
void TestRegistry::Register(TestDeclarationBase* test_declaration) {
|
||||
test_declaration->next = tests_;
|
||||
tests_ = test_declaration;
|
||||
}
|
||||
|
||||
TestRegistry TestRegistry::g_instance;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace testing
|
||||
|
||||
int main() {
|
||||
testing::detail::TestRegistry::instance()->RunAllTests();
|
||||
}
|
129
android/system/nvram/core/tests/gtest_stubs.h
Normal file
129
android/system/nvram/core/tests/gtest_stubs.h
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is a minimal set of stubs to compile nvram_manager_test.cpp in
|
||||
* environments where googletest is not available. The test status output isn't
|
||||
* as pretty, but good enough to follow test progress and pinpoint test
|
||||
* failures.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include <stdio.h>
|
||||
} // extern "C"
|
||||
|
||||
namespace testing {
|
||||
|
||||
// Global test status.
|
||||
extern bool g_test_status;
|
||||
|
||||
#define ASSERT_MSG(cond) \
|
||||
if (!(cond)) { \
|
||||
testing::g_test_status = false; \
|
||||
fprintf(stderr, "Assertion failed: " #cond "\n"); \
|
||||
return; \
|
||||
}
|
||||
#define ASSERT_TRUE(cond) ASSERT_MSG(cond)
|
||||
#define ASSERT_EQ(expected, actual) ASSERT_MSG(expected == actual)
|
||||
|
||||
#define EXPECT_MSG(cond) \
|
||||
if (!(cond)) { \
|
||||
testing::g_test_status = false; \
|
||||
fprintf(stderr, "Expectation failed: " #cond "\n"); \
|
||||
}
|
||||
#define EXPECT_TRUE(cond) EXPECTED_MSG(cond)
|
||||
#define EXPECT_EQ(expected, actual) EXPECT_MSG((expected) == (actual))
|
||||
#define EXPECT_NE(expected, actual) EXPECT_MSG((expected) != (actual))
|
||||
|
||||
// Test fixture base class.
|
||||
class Test {};
|
||||
|
||||
namespace detail {
|
||||
|
||||
// A polymorphic wrapper around test instances. This is the base class that
|
||||
// defines the common interface.
|
||||
class TestInstanceBase {
|
||||
public:
|
||||
virtual ~TestInstanceBase() = default;
|
||||
virtual void Run() = 0;
|
||||
};
|
||||
|
||||
// Test-specific subclass that holds an instance of the test.
|
||||
template<typename TestCase>
|
||||
class TestInstance : public TestInstanceBase {
|
||||
public:
|
||||
~TestInstance() override = default;
|
||||
|
||||
static TestInstanceBase* Create() {
|
||||
return new TestInstance<TestCase>;
|
||||
}
|
||||
|
||||
private:
|
||||
void Run() override {
|
||||
test_.Run();
|
||||
}
|
||||
|
||||
TestCase test_;
|
||||
};
|
||||
|
||||
struct TestDeclarationBase;
|
||||
using CreateTestInstanceFunction = TestInstanceBase*(void);
|
||||
|
||||
// |TestRegistry| keeps track of all registered tests.
|
||||
class TestRegistry {
|
||||
public:
|
||||
static TestRegistry* instance() { return &g_instance; }
|
||||
|
||||
void RunAllTests();
|
||||
void Register(TestDeclarationBase* test_declaration);
|
||||
|
||||
private:
|
||||
TestDeclarationBase* tests_ = nullptr;
|
||||
|
||||
static TestRegistry g_instance;
|
||||
};
|
||||
|
||||
struct TestDeclarationBase {
|
||||
TestDeclarationBase(const char* name,
|
||||
CreateTestInstanceFunction* create_function)
|
||||
: name(name), create_function(create_function) {
|
||||
TestRegistry::instance()->Register(this);
|
||||
}
|
||||
|
||||
const char* name;
|
||||
CreateTestInstanceFunction* create_function;
|
||||
TestDeclarationBase* next;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// Registers |TestCase| with |TestRegistry|.
|
||||
template <typename TestCase>
|
||||
struct TestDeclaration : public detail::TestDeclarationBase {
|
||||
TestDeclaration(const char* name)
|
||||
: TestDeclarationBase(name, &detail::TestInstance<TestCase>::Create) {}
|
||||
};
|
||||
|
||||
#define TEST_F(fixture, name) \
|
||||
class fixture##_##name : public fixture { \
|
||||
public: \
|
||||
void Run(); \
|
||||
}; \
|
||||
static testing::TestDeclaration<fixture##_##name> \
|
||||
g_##fixture##_##name##_declaration(#name); \
|
||||
void fixture##_##name::Run()
|
||||
|
||||
} // namespace testing
|
25
android/system/nvram/core/tests/manifest.c
Normal file
25
android/system/nvram/core/tests/manifest.c
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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 <trusty_app_manifest.h>
|
||||
|
||||
trusty_app_manifest_t TRUSTY_APP_MANIFEST_ATTRS trusty_app_manifest = {
|
||||
// UUID: {879419fa-37a8-44a1-b719-504d03b7502a}
|
||||
{0x879419fa,
|
||||
0x37a8,
|
||||
0x44a1,
|
||||
{0xb7, 0x19, 0x50, 0x4d, 0x03, 0xb7, 0x50, 0x2a}},
|
||||
};
|
1231
android/system/nvram/core/tests/nvram_manager_test.cpp
Normal file
1231
android/system/nvram/core/tests/nvram_manager_test.cpp
Normal file
File diff suppressed because it is too large
Load diff
35
android/system/nvram/core/tests/rules.mk
Normal file
35
android/system/nvram/core/tests/rules.mk
Normal file
|
@ -0,0 +1,35 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
MODULE := $(LOCAL_DIR)
|
||||
|
||||
MODULE_SRCS += \
|
||||
$(LOCAL_DIR)/nvram_manager_test.cpp \
|
||||
$(LOCAL_DIR)/fake_storage.cpp \
|
||||
$(LOCAL_DIR)/gtest_stubs.cpp \
|
||||
$(LOCAL_DIR)/manifest.c
|
||||
|
||||
MODULE_CPPFLAGS := -Wall -Werror -Wextra
|
||||
|
||||
MODULE_DEPS += \
|
||||
app/trusty \
|
||||
lib/libc-trusty \
|
||||
system/nvram/core
|
||||
|
||||
include make/module.mk
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue