diff --git a/garnet/bin/tee_manager/BUILD.gn b/garnet/bin/tee_manager/BUILD.gn index ea853ffacac9a9379e678a611723487cf3d9b485..79cfeec435810f2be7a79a71076e1b9464fb44fd 100644 --- a/garnet/bin/tee_manager/BUILD.gn +++ b/garnet/bin/tee_manager/BUILD.gn @@ -5,6 +5,8 @@ import("//build/config.gni") import("//build/package.gni") import("//build/rust/rustc_binary.gni") +import("//build/test/test_package.gni") +import("//build/testing/environments.gni") package("tee_manager") { meta = [ @@ -52,3 +54,39 @@ config_data("config") { "tee_manager.config", ] } + +test_package("optee_test") { + deps = [ + ":optee_test_bin", + ] + + binaries = [ + { + name = "optee_test" + }, + ] + + tests = [ + { + name = "optee_test" + environments = [ astro_env ] + }, + ] +} + +executable("optee_test_bin") { + testonly = true + output_name = "optee_test" + + sources = [ + "test/optee_test.cc", + ] + + deps = [ + "//garnet/public/lib/fxl/test:gtest_main", + "//sdk/lib/sys/cpp/testing:integration", + "//src/lib/fxl", + "//zircon/public/fidl/fuchsia-tee", + "//zircon/public/lib/tee-client-api", + ] +} diff --git a/garnet/bin/tee_manager/meta/optee_test.cmx b/garnet/bin/tee_manager/meta/optee_test.cmx new file mode 100644 index 0000000000000000000000000000000000000000..bd8f166c2ecf56224dda56cc692b6852b3e2b6eb --- /dev/null +++ b/garnet/bin/tee_manager/meta/optee_test.cmx @@ -0,0 +1,21 @@ +{ + "facets": { + "fuchsia.test": { + "injected-services": { + "fuchsia.tee.Device": "fuchsia-pkg://fuchsia.com/tee_manager#meta/tee_manager.cmx" + } + } + }, + "program": { + "binary": "bin/optee_test" + }, + "sandbox": { + "dev": [], + "features": [], + "services": [ + "fuchsia.sys.Environment", + "fuchsia.sys.Loader", + "fuchsia.tee.Device" + ] + } +} diff --git a/garnet/bin/tee_manager/test/optee_test.cc b/garnet/bin/tee_manager/test/optee_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..1e162f8c04f64c0145b33d3d0cbf278e36d60896 --- /dev/null +++ b/garnet/bin/tee_manager/test/optee_test.cc @@ -0,0 +1,564 @@ +// Copyright 2019 The Fuchsia Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <fuchsia/tee/cpp/fidl.h> +#include <gtest/gtest.h> +#include <lib/sys/cpp/testing/test_with_environment.h> +#include <src/security/tee/third_party/optee_test/ta_storage.h> +#include <tee-client-api/tee_client_api.h> +#include <zircon/syscalls.h> +#include <zircon/types.h> + +#include <iostream> +#include <limits> +#include <memory> +#include <string> +#include <tuple> +#include <vector> + +namespace optee { + +namespace test { + +namespace { +constexpr uint32_t kPrivateStorage = 0x1; +constexpr uint32_t kFlagRead = 0x1; +constexpr uint32_t kFlagWrite = 0x2; +constexpr uint32_t kFlagWriteMetadata = 0x4; + +class OpteeFileHandleGuard { + public: + OpteeFileHandleGuard() = default; + + constexpr OpteeFileHandleGuard(TEEC_Session* session, uint32_t handle) + : session_(session), handle_(handle) {} + + ~OpteeFileHandleGuard() { Close(); } + + OpteeFileHandleGuard(OpteeFileHandleGuard&& other) + : session_(other.session_), handle_(std::move(other.handle_)) { + other.session_ = nullptr; + other.handle_ = std::nullopt; + } + + OpteeFileHandleGuard& operator=(OpteeFileHandleGuard&& other) { + if (&other == this) { + return *this; + } + + Close(); + if (other.IsValid()) { + session_ = other.session_; + handle_ = std::make_optional(other.Release()); + } + return *this; + } + + OpteeFileHandleGuard(const OpteeFileHandleGuard&) = delete; + OpteeFileHandleGuard& operator=(const OpteeFileHandleGuard&) = delete; + + bool IsValid() const { return session_ != nullptr && handle_.has_value(); } + + uint32_t GetHandle() const { + EXPECT_TRUE(IsValid()); + return *handle_; + } + + void Close(); // Forward-declare and implement after `CloseFile` definition + + uint32_t Release() { + EXPECT_TRUE(IsValid()); + + uint32_t released = *handle_; + session_ = nullptr; + handle_ = std::nullopt; + return released; + } + + private: + TEEC_Session* session_; + std::optional<uint32_t> handle_; +}; + +struct OperationResult { + TEEC_Result result; + uint32_t return_origin; +}; + +// Helper class to print numeric values in hex for gtest +template <typename NumericType> +class Hex { + public: + constexpr explicit Hex(NumericType number) : number_(number) {} + + friend std::ostream& operator<<(std::ostream& os, + const Hex<NumericType>& obj) { + return os << "0x" << std::hex << obj.number_; + } + + private: + NumericType number_; +}; + +::testing::AssertionResult IsTeecSuccess(TEEC_Result result) { + if (result == TEEC_SUCCESS) { + return ::testing::AssertionSuccess(); + } else { + return ::testing::AssertionFailure() << "result: " << Hex(result); + } +} + +::testing::AssertionResult IsTeecSuccess(const OperationResult& op_result) { + if (op_result.result == TEEC_SUCCESS) { + return ::testing::AssertionSuccess(); + } else { + return ::testing::AssertionFailure() + << "result: " << Hex(op_result.result) + << ", return origin: " << Hex(op_result.return_origin); + } +} + +static std::vector<uint8_t> StringToBuffer(const std::string& s) { + return std::vector<uint8_t>(s.cbegin(), s.cend()); +} + +static std::string BufferToString(const std::vector<uint8_t>& buf) { + return std::string(buf.cbegin(), buf.cend()); +} + +enum class SeekFrom : uint32_t { kBeginning = 0x0, kCurrent = 0x1, kEnd = 0x2 }; + +// Invokes the storage TA to create a file. Returns an object handle, if +// successful. +static void CreateFile(TEEC_Session* session, std::string name, + std::vector<uint8_t>* init_data, uint32_t flags, + OpteeFileHandleGuard* out_handle_guard) { + ASSERT_NE(session, nullptr); + ASSERT_NE(init_data, nullptr); + ASSERT_GT(init_data->size(), 0u) + << "the trusted application does not support zero-sized initial data"; + ASSERT_NE(out_handle_guard, nullptr); + + TEEC_Operation op = {}; + op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT, TEEC_VALUE_INOUT, + TEEC_VALUE_INPUT, TEEC_MEMREF_TEMP_INPUT); + op.params[2].value.b = kPrivateStorage; + + op.params[0].tmpref.buffer = static_cast<void*>(name.data()); + op.params[0].tmpref.size = name.size(); + + op.params[1].value.a = flags; + + constexpr uint32_t kNullHandle = 0x0; + op.params[2].value.a = kNullHandle; + + op.params[3].tmpref.buffer = static_cast<void*>(init_data->data()); + op.params[3].tmpref.size = static_cast<uint32_t>(init_data->size()); + + OperationResult op_result; + op_result.result = TEEC_InvokeCommand(session, TA_STORAGE_CMD_CREATE, &op, + &op_result.return_origin); + ASSERT_TRUE(IsTeecSuccess(op_result)); + + *out_handle_guard = OpteeFileHandleGuard(session, op.params[1].value.b); +} + +// Invokes the storage TA to open a file. Returns an object handle, if +// successful. +static void OpenFile(TEEC_Session* session, std::string name, uint32_t flags, + OpteeFileHandleGuard* out_handle_guard) { + ASSERT_NE(session, nullptr); + ASSERT_NE(out_handle_guard, nullptr); + + TEEC_Operation op = {}; + op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT, TEEC_VALUE_INOUT, + TEEC_VALUE_INPUT, TEEC_NONE); + op.params[2].value.a = kPrivateStorage; + + op.params[0].tmpref.buffer = static_cast<void*>(name.data()); + op.params[0].tmpref.size = name.size(); + + op.params[1].value.a = flags; + + OperationResult op_result; + op_result.result = TEEC_InvokeCommand(session, TA_STORAGE_CMD_OPEN, &op, + &op_result.return_origin); + ASSERT_TRUE(IsTeecSuccess(op_result)); + + *out_handle_guard = OpteeFileHandleGuard(session, op.params[1].value.b); +} + +// Invokes the storage TA to close a file. +static void CloseFile(TEEC_Session* session, + OpteeFileHandleGuard* handle_guard) { + ASSERT_NE(session, nullptr); + ASSERT_NE(handle_guard, nullptr); + + TEEC_Operation op = {}; + op.paramTypes = + TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_NONE, TEEC_NONE, TEEC_NONE); + + op.params[0].value.a = handle_guard->GetHandle(); + + OperationResult op_result; + op_result.result = TEEC_InvokeCommand(session, TA_STORAGE_CMD_CLOSE, &op, + &op_result.return_origin); + EXPECT_TRUE(IsTeecSuccess(op_result)); // Okay to continue on failure + + handle_guard->Release(); +} + +// Invokes the storage TA to read from a file. Returns the number of bytes read, +// if successful. +static void ReadFile(TEEC_Session* session, + const OpteeFileHandleGuard& handle_guard, + std::vector<uint8_t>* buffer) { + ASSERT_NE(session, nullptr); + ASSERT_NE(buffer, nullptr); + + TEEC_Operation op = {}; + op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_OUTPUT, TEEC_VALUE_INOUT, + TEEC_NONE, TEEC_NONE); + op.params[0].tmpref.buffer = static_cast<void*>(buffer->data()); + op.params[0].tmpref.size = static_cast<uint32_t>(buffer->size()); + + op.params[1].value.a = handle_guard.GetHandle(); + + OperationResult op_result; + op_result.result = TEEC_InvokeCommand(session, TA_STORAGE_CMD_READ, &op, + &op_result.return_origin); + ASSERT_TRUE(IsTeecSuccess(op_result)); + + size_t bytes = static_cast<size_t>(op.params[1].value.b); + EXPECT_LE(bytes, buffer->size()); + + buffer->resize(bytes); +} + +// Invokes the storage TA to write to a file. Returns the number of bytes +// written, if successful. +static void WriteFile(TEEC_Session* session, + const OpteeFileHandleGuard& handle_guard, + std::vector<uint8_t>* buffer) { + ASSERT_NE(session, nullptr); + ASSERT_NE(buffer, nullptr); + + TEEC_Operation op = {}; + op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT, TEEC_VALUE_INPUT, + TEEC_NONE, TEEC_NONE); + op.params[0].tmpref.buffer = static_cast<void*>(buffer->data()); + op.params[0].tmpref.size = static_cast<uint32_t>(buffer->size()); + + op.params[1].value.a = handle_guard.GetHandle(); + + OperationResult op_result; + op_result.result = TEEC_InvokeCommand(session, TA_STORAGE_CMD_WRITE, &op, + &op_result.return_origin); + EXPECT_TRUE(IsTeecSuccess(op_result)); // Okay to continue on failure +} + +// Invokes the storage TA to seek in a file. If successful, returns the offset +// from the beginning of the file. +static void SeekFile(TEEC_Session* session, + const OpteeFileHandleGuard& handle_guard, int32_t offset, + SeekFrom whence, uint32_t* out_absolute_offset) { + ASSERT_NE(session, nullptr); + + TEEC_Operation op = {}; + op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_VALUE_INOUT, + TEEC_NONE, TEEC_NONE); + op.params[0].value.a = handle_guard.GetHandle(); + + // Intentionally copy this int32_t to an uint32_t field, as the TA + // reinterprets these bits as an int32_t + static_assert(sizeof(offset) == sizeof(op.params[0].value.b)); + std::memcpy(&op.params[0].value.b, &offset, sizeof(uint32_t)); + + op.params[1].value.a = static_cast<uint32_t>(whence); + + OperationResult op_result; + op_result.result = TEEC_InvokeCommand(session, TA_STORAGE_CMD_SEEK, &op, + &op_result.return_origin); + ASSERT_TRUE(IsTeecSuccess(op_result)); + + // Since there are scenarios where the client may ignore this, check if null + if (out_absolute_offset != nullptr) { + *out_absolute_offset = op.params[1].value.b; + } +} + +// Invokes the storage TA to unlink a file. +static void UnlinkFile(TEEC_Session* session, + OpteeFileHandleGuard* handle_guard) { + ASSERT_NE(session, nullptr); + ASSERT_NE(handle_guard, nullptr); + + TEEC_Operation op = {}; + op.paramTypes = + TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_NONE, TEEC_NONE, TEEC_NONE); + op.params[0].value.a = handle_guard->GetHandle(); + + OperationResult op_result; + op_result.result = TEEC_InvokeCommand(session, TA_STORAGE_CMD_UNLINK, &op, + &op_result.return_origin); + EXPECT_TRUE(IsTeecSuccess(op_result)); // Okay to continue on failure + + handle_guard->Release(); +} + +void OpteeFileHandleGuard::Close() { + if (IsValid()) { + CloseFile(session_, this); + session_ = nullptr; + handle_ = std::nullopt; + } +} + +class ContextGuard { + public: + ContextGuard() : context_(nullptr) {} + + explicit ContextGuard(TEEC_Context* context) : context_(context) {} + + ~ContextGuard() { Close(); } + + ContextGuard(ContextGuard&& other) : context_(other.context_) { + other.context_ = nullptr; + } + + ContextGuard& operator=(ContextGuard&& other) { + if (&other == this) { + return *this; + } + + Close(); + context_ = other.Release(); + return *this; + } + + ContextGuard(const ContextGuard&) = delete; + ContextGuard& operator=(const ContextGuard&) = delete; + + constexpr bool IsValid() const { return context_ != nullptr; } + + TEEC_Context* Get() const { return context_; } + + void Close() { + if (IsValid()) { + TEEC_FinalizeContext(context_); + context_ = nullptr; + } + } + + TEEC_Context* Release() { + TEEC_Context* released = context_; + context_ = nullptr; + return released; + } + + private: + TEEC_Context* context_; +}; + +class SessionGuard { + public: + constexpr SessionGuard() : session_(nullptr) {} + + constexpr explicit SessionGuard(TEEC_Session* session) : session_(session) {} + + ~SessionGuard() { Close(); } + + SessionGuard(SessionGuard&& other) : session_(other.session_) { + other.session_ = nullptr; + } + + SessionGuard& operator=(SessionGuard&& other) { + if (&other == this) { + return *this; + } + + Close(); + session_ = other.Release(); + return *this; + } + + SessionGuard(const SessionGuard&) = delete; + SessionGuard& operator=(const SessionGuard&) = delete; + + constexpr bool IsValid() const { return session_ != nullptr; } + + TEEC_Session* Get() const { return session_; } + + void Close() { + if (IsValid()) { + TEEC_CloseSession(session_); + session_ = nullptr; + } + } + + TEEC_Session* Release() { + TEEC_Session* released = session_; + session_ = nullptr; + return released; + } + + private: + TEEC_Session* session_; +}; + +} // namespace + +class OpteeTest : public sys::testing::TestWithEnvironment { + protected: + void SetUp() override { + auto services = CreateServices(); + + fuchsia::sys::LaunchInfo launch_info{ + "fuchsia-pkg://fuchsia.com/tee_manager#meta/tee_manager.cmx"}; + zx_status_t status = services->AddServiceWithLaunchInfo( + std::move(launch_info), fuchsia::tee::Device::Name_); + ASSERT_EQ(status, ZX_OK); + + environment_ = + CreateNewEnclosingEnvironment("optee_test", std::move(services)); + WaitForEnclosingEnvToStart(environment_.get()); + + TEEC_Result result = TEEC_InitializeContext(nullptr, &context_); + ASSERT_TRUE(IsTeecSuccess(result)); + context_guard_ = ContextGuard(&context_); + + OperationResult op_result; + op_result.result = + TEEC_OpenSession(&context_, &session_, &kStorageUuid, TEEC_LOGIN_PUBLIC, + nullptr, nullptr, &op_result.return_origin); + ASSERT_TRUE(IsTeecSuccess(op_result)); + session_guard_ = SessionGuard(&session_); + + std::vector<uint8_t> buffer = StringToBuffer(GetInitialFileContents()); + OpteeFileHandleGuard handle_guard; + CreateFile(&session_, GetFileName(), &buffer, kFlagRead, &handle_guard); + } + + void TearDown() override { + constexpr uint32_t kOpenFlags = kFlagRead | kFlagWrite | kFlagWriteMetadata; + OpteeFileHandleGuard handle_guard; + OpenFile(&session_, GetFileName(), kOpenFlags, &handle_guard); + + UnlinkFile(&session_, &handle_guard); + } + + static std::string GetFileName() { + static const std::string kFileName = "optee_test_file"; + return kFileName; + } + + static std::string GetInitialFileContents() { + static const std::string kInitialContents = + "the quick brown fox jumped over the lazy dog"; + return kInitialContents; + } + + TEEC_Session* GetSession() { return &session_; } + + private: + static constexpr TEEC_UUID kStorageUuid = TA_STORAGE_UUID; + + std::unique_ptr<sys::testing::EnclosingEnvironment> environment_; + TEEC_Context context_; + ContextGuard context_guard_; + TEEC_Session session_; + SessionGuard session_guard_; +}; + +TEST_F(OpteeTest, OpenFile) { + OpteeFileHandleGuard handle_guard; + OpenFile(GetSession(), GetFileName(), kFlagRead, &handle_guard); +} + +TEST_F(OpteeTest, ReadFile) { + OpteeFileHandleGuard handle_guard; + OpenFile(GetSession(), GetFileName(), kFlagRead, &handle_guard); + + constexpr size_t kBufferSize = 128; + std::vector<uint8_t> buffer(kBufferSize, 0); + ReadFile(GetSession(), handle_guard, &buffer); + + std::string read_contents = BufferToString(buffer); + EXPECT_EQ(read_contents, GetInitialFileContents()); +} + +TEST_F(OpteeTest, WriteFile) { + constexpr uint32_t kOpenFlags = kFlagRead | kFlagWrite | kFlagWriteMetadata; + OpteeFileHandleGuard handle_guard; + OpenFile(GetSession(), GetFileName(), kOpenFlags, &handle_guard); + + static const std::string kNewFileContents( + "how much wood would a woodchuck chuck if a woodchuck could chuck wood?"); + ASSERT_GE(kNewFileContents.size(), GetInitialFileContents().size()); + + std::vector<uint8_t> buffer = StringToBuffer(kNewFileContents); + WriteFile(GetSession(), handle_guard, &buffer); +} + +TEST_F(OpteeTest, WriteAndReadFile) { + constexpr uint32_t kOpenFlags = kFlagRead | kFlagWrite | kFlagWriteMetadata; + static const std::string kNewFileContents( + "how much wood would a woodchuck chuck if a woodchuck could chuck wood?"); + ASSERT_GE(kNewFileContents.size(), GetInitialFileContents().size()); + std::vector<uint8_t> buffer; + + { + OpteeFileHandleGuard handle_guard; + OpenFile(GetSession(), GetFileName(), kOpenFlags, &handle_guard); + + buffer = StringToBuffer(kNewFileContents); + WriteFile(GetSession(), handle_guard, &buffer); + } + + { + OpteeFileHandleGuard handle_guard; + OpenFile(GetSession(), GetFileName(), kOpenFlags, &handle_guard); + constexpr size_t kBufferSize = 128; + buffer.assign(kBufferSize, 0); + ReadFile(GetSession(), handle_guard, &buffer); + + std::string read_contents = BufferToString(buffer); + EXPECT_EQ(read_contents, kNewFileContents); + } +} + +TEST_F(OpteeTest, SeekWriteReadFile) { + constexpr uint32_t kOpenFlags = kFlagRead | kFlagWrite | kFlagWriteMetadata; + static const std::string kStringToAppend = "!"; + + OpteeFileHandleGuard handle_guard; + OpenFile(GetSession(), GetFileName(), kOpenFlags, &handle_guard); + + // Seek to the end of the file + uint32_t absolute_offset = 0; + SeekFile(GetSession(), handle_guard, 0, SeekFrom::kEnd, &absolute_offset); + EXPECT_EQ(absolute_offset, GetInitialFileContents().size()); + + // Append an exclamation point to the file + std::vector<uint8_t> buffer = StringToBuffer(kStringToAppend); + WriteFile(GetSession(), handle_guard, &buffer); + + // Seek to the beginning of the file + absolute_offset = std::numeric_limits<decltype(absolute_offset)>::max(); + SeekFile(GetSession(), handle_guard, 0, SeekFrom::kBeginning, + &absolute_offset); + EXPECT_EQ(absolute_offset, 0u); + + // Check the new string + const std::string kNewFileContents = + GetInitialFileContents() + kStringToAppend; + constexpr size_t kBufferSize = 128; + buffer.assign(kBufferSize, 0); // Zero out and resize the buffer + ReadFile(GetSession(), handle_guard, &buffer); + std::string read_contents = BufferToString(buffer); + EXPECT_EQ(read_contents, kNewFileContents); +} + +} // namespace test +} // namespace optee \ No newline at end of file diff --git a/zircon/system/dev/tee/optee/optee-client.cpp b/zircon/system/dev/tee/optee/optee-client.cpp index 1aee96a79e9a4c6184bb812e8a29c870d137b1d6..003eb15e38956bee6e25d3fedb0dee28f272edf4 100644 --- a/zircon/system/dev/tee/optee/optee-client.cpp +++ b/zircon/system/dev/tee/optee/optee-client.cpp @@ -2,13 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <algorithm> +#include <libgen.h> +#include <string> #include <utility> #include <ddk/debug.h> #include <fbl/string_buffer.h> -#include <fbl/string_piece.h> #include <fbl/vector.h> +#include <fuchsia/io/c/fidl.h> +#include <fuchsia/tee/manager/c/fidl.h> #include <lib/fidl-utils/bind.h> +#include <lib/fidl/coding.h> +#include <lib/zx/handle.h> #include <lib/zx/time.h> #include <lib/zx/vmo.h> #include <tee-client-api/tee-client-types.h> @@ -122,6 +128,227 @@ static zx_status_t ConvertOpteeToZxResult(uint32_t optee_return_code, uint32_t o return ZX_OK; } +static std::filesystem::path GetPathFromRawMemory(void* mem, size_t max_size) { + ZX_DEBUG_ASSERT(mem != nullptr); + ZX_DEBUG_ASSERT(max_size > 0); + + auto path = static_cast<char*>(mem); + + // Copy the string out from raw memory first + std::string result(path, max_size); + + // Trim string to first null terminating character + auto null_pos = result.find('\0'); + if (null_pos != std::string::npos) { + result.resize(null_pos); + } + + return std::filesystem::path(std::move(result)).lexically_relative("/"); +} + +// Awaits the `fuchsia.io.Node/OnOpen` event that is fired when opening with +// `fuchsia.io.OPEN_FLAG_DESCRIBE` flag and returns the status contained in the event. +// +// This is useful for synchronously awaiting the result of an `Open` request. +// +// TODO(godtamit): Transition to receiving an event normally once this client code can use FIDL C++ +// generated bindings that support events directly. +static zx_status_t AwaitIoOnOpenStatus(const zx::channel& channel) { + zx_signals_t observed_signals = 0; + zx_status_t status = channel.wait_one(ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED, + zx::time::infinite(), &observed_signals); + if (status != ZX_OK) { + zxlogf(ERROR, "optee::%s: failed to wait on channel (status: %d)\n", __FUNCTION__, status); + return status; + } + + // Intentionally allow `ZX_CHANNEL_PEER_CLOSED` to take precedence over `ZX_CHANNEL_READABLE` + // since it indicates an error occurred. + if ((observed_signals & ZX_CHANNEL_PEER_CLOSED) == ZX_CHANNEL_PEER_CLOSED) { + zxlogf(ERROR, "optee::%s: channel closed\n", __FUNCTION__); + + // TODO(godtamit): check for an epitaph here once `fuchsia.io` supports it + return ZX_ERR_NOT_FOUND; + } + + // Sanity check to make sure `ZX_CHANNEL_READABLE` was the signal observed + ZX_DEBUG_ASSERT((observed_signals & ZX_CHANNEL_READABLE) == ZX_CHANNEL_READABLE); + + // Test to see how big the message is + uint32_t actual_bytes; + zx::handle handle; + status = channel.read(0, // flags + nullptr, // bytes + nullptr, // handles + 0, // num_bytes + 0, // num_handles + &actual_bytes, // actual_bytes + nullptr); // actual_handles + if (status != ZX_ERR_BUFFER_TOO_SMALL) { + zxlogf(ERROR, + "optee::%s: received unexpecting error while testing for channel message size " + "(status: %d)\n", + __FUNCTION__, status); + return status == ZX_OK ? ZX_ERR_INTERNAL : status; + } + + uint32_t buffer_size = actual_bytes; + std::unique_ptr<uint8_t[]> buffer(new uint8_t[static_cast<size_t>(buffer_size)]); + status = channel.read(0, // flags + buffer.get(), // bytes + handle.reset_and_get_address(), // handles + buffer_size, // num_bytes + 1, // num_handles + &actual_bytes, // actual_bytes + nullptr); // actual_handles + if (status != ZX_OK) { + zxlogf(ERROR, + "optee::%s: received unexpecting error while reading channel message (status: %d)\n", + __FUNCTION__, status); + return status; + } + + auto header = reinterpret_cast<fidl_message_header_t*>(buffer.get()); + if (header->ordinal != fuchsia_io_NodeOnOpenOrdinal) { + // The `OnOpen` event should be the first event fired. See the function description for + // preconditions and details. + zxlogf(ERROR, "optee::%s: received unexpected message ordinal %x\n", + __FUNCTION__, header->ordinal); + return ZX_ERR_PROTOCOL_NOT_SUPPORTED; + } + + const char* err = nullptr; + status = + fidl_decode(&fuchsia_io_NodeOnOpenEventTable, buffer.get(), actual_bytes, nullptr, 0, &err); + if (status != ZX_OK) { + zxlogf(ERROR, "optee::%s: failed to decode fuchsia.io.Node/OnOpen event: %s (status: %d)\n", + __FUNCTION__, err != nullptr ? err : "", status); + return status; + } + + auto on_open_event = reinterpret_cast<fuchsia_io_NodeOnOpenEvent*>(buffer.get()); + return on_open_event->s; +} + +// Calls `fuchsia.io.Directory/Open` on a channel and awaits the result. +static zx_status_t OpenObjectInDirectory(const zx::channel& root_channel, + uint32_t flags, + uint32_t mode, + std::string path, + zx::channel* out_channel_node) { + ZX_DEBUG_ASSERT(out_channel_node != nullptr); + + // Ensure `OPEN_FLAG_DESCRIBE` is passed + flags |= fuchsia_io_OPEN_FLAG_DESCRIBE; + + // Create temporary channel ends to make FIDL call + zx::channel channel_client_end; + zx::channel channel_server_end; + zx_status_t status = zx::channel::create(0, &channel_client_end, &channel_server_end); + if (status != ZX_OK) { + zxlogf(ERROR, "optee::%s: failed to create channel pair (status: %d)\n", + __FUNCTION__, status); + return status; + } + + status = fuchsia_io_DirectoryOpen(root_channel.get(), // _channel + flags, // flags + mode, // mode + path.data(), // path_data + path.size(), // path_size + channel_server_end.release()); // object + if (status != ZX_OK) { + zxlogf(ERROR, "optee::%s: could not call fuchsia.io.Directory/Open (status: %d)\n", + __FUNCTION__, status); + return status; + } + + status = AwaitIoOnOpenStatus(channel_client_end); + if (status != ZX_OK) { + return status; + } + + *out_channel_node = std::move(channel_client_end); + return ZX_OK; +} + +// Recursively walks down a multi-part path, opening and outputting the final destination. +// +// Template Parameters: +// * kOpenFlags: The flags to call `fuchsia.io.Directory/Open` with. This must not contain +// `OPEN_FLAG_NOT_DIRECTORY`. +// Parameters: +// * root_channel: The channel to the directory to start the walk from. +// * path: The path relative to `root_channel` to open. +// * out_node_channel: Where to store the resulting `fuchsia.io.Node` channel opened. +template <uint32_t kOpenFlags> +static zx_status_t RecursivelyWalkPath(const zx::unowned_channel& root_channel, + std::filesystem::path path, + zx::channel* out_node_channel) { + static_assert((kOpenFlags & fuchsia_io_OPEN_FLAG_NOT_DIRECTORY) == 0, + "kOpenFlags must not include fuchsia_io_OPEN_FLAG_NOT_DIRECTORY"); + ZX_DEBUG_ASSERT(root_channel->is_valid()); + ZX_DEBUG_ASSERT(!path.empty()); + ZX_DEBUG_ASSERT(out_node_channel != nullptr); + + zx_status_t status; + zx::channel result_channel; + + if (path == std::filesystem::path(".")) { + // If the path is lexicographically equivalent to the (relative) root directory, clone the + // root channel instead of opening the path + zx::channel server_channel; + status = zx::channel::create(0, &result_channel, &server_channel); + if (status != ZX_OK) { + return status; + } + + status = fuchsia_io_DirectoryClone(root_channel->get(), + fuchsia_io_CLONE_FLAG_SAME_RIGHTS, + server_channel.release()); + if (status != ZX_OK) { + return status; + } + } else { + zx::unowned_channel current_channel(root_channel); + for (const auto& component : path) { + zx::channel temporary_channel; + static constexpr uint32_t kOpenMode = fuchsia_io_MODE_TYPE_DIRECTORY; + status = OpenObjectInDirectory(*current_channel, + kOpenFlags, + kOpenMode, + component.string(), + &temporary_channel); + if (status != ZX_OK) { + return status; + } + + result_channel = std::move(temporary_channel); + current_channel = zx::unowned(result_channel); + } + } + + *out_node_channel = std::move(result_channel); + return ZX_OK; +} + +template <typename... Args> +static inline zx_status_t CreateDirectory(Args&&... args) { + static constexpr uint32_t kCreateFlags = fuchsia_io_OPEN_RIGHT_READABLE | + fuchsia_io_OPEN_RIGHT_WRITABLE | + fuchsia_io_OPEN_FLAG_CREATE | + fuchsia_io_OPEN_FLAG_DIRECTORY; + return RecursivelyWalkPath<kCreateFlags>(std::forward<Args>(args)...); +} + +template <typename... Args> +static inline zx_status_t OpenDirectory(Args&&... args) { + static constexpr uint32_t kOpenFlags = fuchsia_io_OPEN_RIGHT_READABLE | + fuchsia_io_OPEN_RIGHT_WRITABLE | + fuchsia_io_OPEN_FLAG_DIRECTORY; + return RecursivelyWalkPath<kOpenFlags>(std::forward<Args>(args)...); +} + } // namespace namespace optee { @@ -373,6 +600,97 @@ OpteeClient::SharedMemoryList::iterator OpteeClient::FindSharedMemory(uint64_t m }); } +std::optional<SharedMemoryView> OpteeClient::GetMemoryReference(SharedMemoryList::iterator mem_iter, + zx_paddr_t base_paddr, + size_t size) { + + std::optional<SharedMemoryView> result; + if (!mem_iter.IsValid() || + !(result = mem_iter->SliceByPaddr(base_paddr, base_paddr + size)).has_value()) { + zxlogf(ERROR, "optee: received invalid shared memory region reference\n"); + } + return result; +} + +zx_status_t OpteeClient::GetRootStorageChannel(zx::unowned_channel* out_root_channel) { + ZX_DEBUG_ASSERT(out_root_channel != nullptr); + + if (!service_provider_channel_.is_valid()) { + return ZX_ERR_UNAVAILABLE; + } + if (root_storage_channel_.is_valid()) { + *out_root_channel = zx::unowned_channel(root_storage_channel_); + return ZX_OK; + } + + zx::channel client_channel; + zx::channel server_channel; + zx_status_t status = zx::channel::create(0, &client_channel, &server_channel); + if (status != ZX_OK) { + return status; + } + + status = fuchsia_tee_manager_ServiceProviderRequestPersistentStorage( + service_provider_channel_.get(), + server_channel.release()); + if (status != ZX_OK) { + return status; + } + + root_storage_channel_ = std::move(client_channel); + *out_root_channel = zx::unowned_channel(root_storage_channel_); + return ZX_OK; +} + +zx_status_t OpteeClient::GetStorageDirectory(std::filesystem::path path, + bool create, + zx::channel* out_storage_channel) { + ZX_DEBUG_ASSERT(out_storage_channel != nullptr); + + zx::unowned_channel root_channel; + zx_status_t status = GetRootStorageChannel(&root_channel); + if (status != ZX_OK) { + return status; + } + + zx::channel storage_channel; + + if (create) { + status = CreateDirectory(root_channel, path, &storage_channel); + } else { + status = OpenDirectory(root_channel, path, &storage_channel); + } + if (status != ZX_OK) { + return status; + } + + *out_storage_channel = std::move(storage_channel); + return ZX_OK; +} + +uint64_t OpteeClient::TrackFileSystemObject(zx::channel io_node_channel) { + uint64_t object_id = next_file_system_object_id_.fetch_add(1, std::memory_order_relaxed); + // TODO(godtamit): Move to `std::unordered_map` when BLD-413 is complete + open_file_system_objects_.insert( + std::make_unique<FileSystemObject>(object_id, std::move(io_node_channel))); + + return object_id; +} + +std::optional<zx::unowned_channel> +OpteeClient::GetFileSystemObjectChannel(uint64_t identifier) { + auto iter = open_file_system_objects_.find(identifier); + if (iter == open_file_system_objects_.end()) { + return std::nullopt; + } + return zx::unowned_channel(iter->channel); +} + +bool OpteeClient::UntrackFileSystemObject(uint64_t identifier) { + std::unique_ptr<FileSystemObject> erased_object = open_file_system_objects_.erase(identifier); + return erased_object != nullptr; +} + zx_status_t OpteeClient::HandleRpc(const RpcFunctionArgs& args, RpcFunctionResult* out_result) { zx_status_t status; uint32_t func_code = GetRpcFunctionCode(args.generic.status); @@ -474,7 +792,7 @@ zx_status_t OpteeClient::HandleRpcCommand(const RpcFunctionExecuteCommandsArgs& if (!fs_msg.is_valid()) { return ZX_ERR_INVALID_ARGS; } - return HandleRpcCommandFileSystem(&fs_msg); + return HandleRpcCommandFileSystem(std::move(fs_msg)); } case RpcMessage::Command::kGetTime: { GetTimeRpcMessage get_time_msg(std::move(message)); @@ -530,12 +848,10 @@ zx_status_t OpteeClient::HandleRpcCommandLoadTa(LoadTaRpcMessage* message) { std::optional<SharedMemoryView> out_ta_mem; // Where to write the TA in memory if (message->memory_reference_id() != 0) { - SharedMemoryList::iterator mem_iter = FindSharedMemory(message->memory_reference_id()); - zx_paddr_t lower_paddr = message->memory_reference_paddr(); - zx_paddr_t upper_paddr = lower_paddr + message->memory_reference_size(); - if (!mem_iter.IsValid() || - !(out_ta_mem = mem_iter->SliceByPaddr(lower_paddr, upper_paddr)).has_value()) { - zxlogf(ERROR, "optee: received invalid shared memory region reference\n"); + out_ta_mem = GetMemoryReference(FindSharedMemory(message->memory_reference_id()), + message->memory_reference_paddr(), + message->memory_reference_size()); + if (!out_ta_mem.has_value()) { message->set_return_code(TEEC_ERROR_BAD_PARAMETERS); return ZX_ERR_INVALID_ARGS; } @@ -614,8 +930,8 @@ zx_status_t OpteeClient::HandleRpcCommandGetTime(GetTimeRpcMessage* message) { return status; } - static constexpr const zx::duration kDurationSecond = zx::duration(zx_duration_from_sec(1)); - static constexpr const zx::time_utc kUtcEpoch = zx::time_utc(0); + static constexpr zx::duration kDurationSecond = zx::sec(1); + static constexpr zx::time_utc kUtcEpoch = zx::time_utc(0); zx::duration now_since_epoch = now - kUtcEpoch; auto seconds = static_cast<uint64_t>(now_since_epoch / kDurationSecond); @@ -686,35 +1002,75 @@ zx_status_t OpteeClient::HandleRpcCommandFreeMemory(FreeMemoryRpcMessage* messag return status; } -zx_status_t OpteeClient::HandleRpcCommandFileSystem(FileSystemRpcMessage* message) { - ZX_DEBUG_ASSERT(message != nullptr); - ZX_DEBUG_ASSERT(message->is_valid()); +zx_status_t OpteeClient::HandleRpcCommandFileSystem(FileSystemRpcMessage&& message) { + ZX_DEBUG_ASSERT(message.is_valid()); - switch (message->command()) { - case FileSystemRpcMessage::FileSystemCommand::kOpenFile: - zxlogf(ERROR, "optee: RPC command to open file recognized but not implemented\n"); - break; - case FileSystemRpcMessage::FileSystemCommand::kCreateFile: - zxlogf(ERROR, "optee: RPC command to create file recognized but not implemented\n"); - break; - case FileSystemRpcMessage::FileSystemCommand::kCloseFile: - zxlogf(ERROR, "optee: RPC command to close file recognized but not implemented\n"); - break; - case FileSystemRpcMessage::FileSystemCommand::kReadFile: - zxlogf(ERROR, "optee: RPC command to read file recognized but not implemented\n"); - break; - case FileSystemRpcMessage::FileSystemCommand::kWriteFile: - zxlogf(ERROR, "optee: RPC command to write file recognized but not implemented\n"); - break; - case FileSystemRpcMessage::FileSystemCommand::kTruncateFile: - zxlogf(ERROR, "optee: RPC command to truncate file recognized but not implemented\n"); - break; - case FileSystemRpcMessage::FileSystemCommand::kRemoveFile: - zxlogf(ERROR, "optee: RPC command to remove file recognized but not implemented\n"); - break; - case FileSystemRpcMessage::FileSystemCommand::kRenameFile: - zxlogf(ERROR, "optee: RPC command to rename file recognized but not implemented\n"); - break; + // Mark that the return code will originate from driver + message.set_return_origin(TEEC_ORIGIN_COMMS); + + if (!service_provider_channel_.is_valid()) { + // Client did not connect with a ServiceProvider so none of these RPCs can be serviced + message.set_return_code(TEEC_ERROR_BAD_STATE); + return ZX_ERR_UNAVAILABLE; + } + + switch (message.file_system_command()) { + case FileSystemRpcMessage::FileSystemCommand::kOpenFile: { + OpenFileFileSystemRpcMessage open_file_msg(std::move(message)); + if (!open_file_msg.is_valid()) { + return ZX_ERR_INVALID_ARGS; + } + return HandleRpcCommandFileSystemOpenFile(&open_file_msg); + } + case FileSystemRpcMessage::FileSystemCommand::kCreateFile: { + CreateFileFileSystemRpcMessage create_file_msg(std::move(message)); + if (!create_file_msg.is_valid()) { + return ZX_ERR_INVALID_ARGS; + } + return HandleRpcCommandFileSystemCreateFile(&create_file_msg); + } + case FileSystemRpcMessage::FileSystemCommand::kCloseFile: { + CloseFileFileSystemRpcMessage close_file_msg(std::move(message)); + if (!close_file_msg.is_valid()) { + return ZX_ERR_INVALID_ARGS; + } + return HandleRpcCommandFileSystemCloseFile(&close_file_msg); + } + case FileSystemRpcMessage::FileSystemCommand::kReadFile: { + ReadFileFileSystemRpcMessage read_file_msg(std::move(message)); + if (!read_file_msg.is_valid()) { + return ZX_ERR_INVALID_ARGS; + } + return HandleRpcCommandFileSystemReadFile(&read_file_msg); + } + case FileSystemRpcMessage::FileSystemCommand::kWriteFile: { + WriteFileFileSystemRpcMessage write_file_msg(std::move(message)); + if (!write_file_msg.is_valid()) { + return ZX_ERR_INVALID_ARGS; + } + return HandleRpcCommandFileSystemWriteFile(&write_file_msg); + } + case FileSystemRpcMessage::FileSystemCommand::kTruncateFile: { + TruncateFileFileSystemRpcMessage truncate_file_msg(std::move(message)); + if (!truncate_file_msg.is_valid()) { + return ZX_ERR_INVALID_ARGS; + } + return HandleRpcCommandFileSystemTruncateFile(&truncate_file_msg); + } + case FileSystemRpcMessage::FileSystemCommand::kRemoveFile: { + RemoveFileFileSystemRpcMessage remove_file_msg(std::move(message)); + if (!remove_file_msg.is_valid()) { + return ZX_ERR_INVALID_ARGS; + } + return HandleRpcCommandFileSystemRemoveFile(&remove_file_msg); + } + case FileSystemRpcMessage::FileSystemCommand::kRenameFile: { + RenameFileFileSystemRpcMessage rename_file_msg(std::move(message)); + if (!rename_file_msg.is_valid()) { + return ZX_ERR_INVALID_ARGS; + } + return HandleRpcCommandFileSystemRenameFile(&rename_file_msg); + } case FileSystemRpcMessage::FileSystemCommand::kOpenDirectory: zxlogf(ERROR, "optee: RPC command to open directory recognized but not implemented\n"); break; @@ -727,7 +1083,419 @@ zx_status_t OpteeClient::HandleRpcCommandFileSystem(FileSystemRpcMessage* messag break; } - message->set_return_code(TEEC_ERROR_NOT_SUPPORTED); + message.set_return_code(TEEC_ERROR_NOT_SUPPORTED); + return ZX_OK; +} + +zx_status_t OpteeClient::HandleRpcCommandFileSystemOpenFile(OpenFileFileSystemRpcMessage* message) { + ZX_DEBUG_ASSERT(message != nullptr); + ZX_DEBUG_ASSERT(message->is_valid()); + ZX_DEBUG_ASSERT(service_provider_channel_.is_valid()); + + zxlogf(SPEW, "optee: received RPC to open file\n"); + + SharedMemoryList::iterator mem_iter = FindSharedMemory(message->path_memory_identifier()); + std::optional<SharedMemoryView> path_mem = GetMemoryReference(mem_iter, + message->path_memory_paddr(), + message->path_memory_size()); + if (!path_mem.has_value()) { + message->set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return ZX_ERR_INVALID_ARGS; + } + + std::filesystem::path path = GetPathFromRawMemory(reinterpret_cast<void*>(path_mem->vaddr()), + message->path_memory_size()); + + zx::channel storage_channel; + constexpr bool kNoCreate = false; + zx_status_t status = GetStorageDirectory(path.parent_path(), kNoCreate, &storage_channel); + if (status != ZX_OK) { + message->set_return_code(TEEC_ERROR_BAD_STATE); + return status; + } + + zx::channel file_channel; + static constexpr uint32_t kOpenFlags = fuchsia_io_OPEN_RIGHT_READABLE | + fuchsia_io_OPEN_RIGHT_WRITABLE | + fuchsia_io_OPEN_FLAG_NOT_DIRECTORY | + fuchsia_io_OPEN_FLAG_DESCRIBE; + static constexpr uint32_t kOpenMode = fuchsia_io_MODE_TYPE_FILE; + status = OpenObjectInDirectory(storage_channel, kOpenFlags, kOpenMode, path.filename().string(), + &file_channel); + if (status != ZX_OK) { + zxlogf(ERROR, "optee::%s: unable to open file (status: %d)\n", __FUNCTION__, status); + message->set_return_code(TEEC_ERROR_GENERIC); + return status; + } + + uint64_t object_id = TrackFileSystemObject(std::move(file_channel)); + + message->set_output_file_system_object_identifier(object_id); + message->set_return_code(TEEC_SUCCESS); + return ZX_OK; +} + +zx_status_t OpteeClient::HandleRpcCommandFileSystemCreateFile( + CreateFileFileSystemRpcMessage* message) { + ZX_DEBUG_ASSERT(message != nullptr); + ZX_DEBUG_ASSERT(message->is_valid()); + + zxlogf(SPEW, "optee: received RPC to create file\n"); + + std::optional<SharedMemoryView> path_mem = GetMemoryReference( + FindSharedMemory(message->path_memory_identifier()), + message->path_memory_paddr(), + message->path_memory_size()); + if (!path_mem.has_value()) { + message->set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return ZX_ERR_INVALID_ARGS; + } + + std::filesystem::path path = GetPathFromRawMemory(reinterpret_cast<void*>(path_mem->vaddr()), + message->path_memory_size()); + + zx::channel storage_channel; + constexpr bool kCreate = true; + zx_status_t status = GetStorageDirectory(path.parent_path(), kCreate, &storage_channel); + if (status != ZX_OK) { + message->set_return_code(TEEC_ERROR_BAD_STATE); + return status; + } + + zx::channel file_channel; + static constexpr uint32_t kCreateFlags = fuchsia_io_OPEN_RIGHT_READABLE | + fuchsia_io_OPEN_RIGHT_WRITABLE | + fuchsia_io_OPEN_FLAG_CREATE | + fuchsia_io_OPEN_FLAG_CREATE_IF_ABSENT | + fuchsia_io_OPEN_FLAG_DESCRIBE; + static constexpr uint32_t kCreateMode = fuchsia_io_MODE_TYPE_FILE; + status = OpenObjectInDirectory(storage_channel, kCreateFlags, kCreateMode, + path.filename().string(), &file_channel); + if (status != ZX_OK) { + zxlogf(ERROR, "optee::%s: unable to create file (status: %d)\n", __FUNCTION__, status); + message->set_return_code(status == ZX_ERR_ALREADY_EXISTS ? TEEC_ERROR_ACCESS_CONFLICT + : TEEC_ERROR_GENERIC); + return status; + } + + uint64_t object_id = TrackFileSystemObject(std::move(file_channel)); + + message->set_output_file_system_object_identifier(object_id); + message->set_return_code(TEEC_SUCCESS); + return ZX_OK; +} + +zx_status_t OpteeClient::HandleRpcCommandFileSystemCloseFile( + CloseFileFileSystemRpcMessage* message) { + ZX_DEBUG_ASSERT(message != nullptr); + ZX_DEBUG_ASSERT(message->is_valid()); + + zxlogf(SPEW, "optee: received RPC to close file\n"); + + if (!UntrackFileSystemObject(message->file_system_object_identifier())) { + zxlogf(ERROR, "optee: could not find the requested file to close\n"); + message->set_return_code(TEEC_ERROR_ITEM_NOT_FOUND); + return ZX_ERR_NOT_FOUND; + } + + message->set_return_code(TEEC_SUCCESS); + return ZX_OK; +} + +zx_status_t OpteeClient::HandleRpcCommandFileSystemReadFile(ReadFileFileSystemRpcMessage* message) { + ZX_DEBUG_ASSERT(message != nullptr); + ZX_DEBUG_ASSERT(message->is_valid()); + + zxlogf(SPEW, "optee: received RPC to read from file\n"); + + auto maybe_file_channel = GetFileSystemObjectChannel(message->file_system_object_identifier()); + if (!maybe_file_channel.has_value()) { + message->set_return_code(TEEC_ERROR_ITEM_NOT_FOUND); + return ZX_ERR_NOT_FOUND; + } + + zx::unowned_channel file_channel(std::move(*maybe_file_channel)); + + std::optional<SharedMemoryView> buffer_mem = GetMemoryReference( + FindSharedMemory(message->file_contents_memory_identifier()), + message->file_contents_memory_paddr(), + message->file_contents_memory_size()); + if (!buffer_mem.has_value()) { + message->set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return ZX_ERR_INVALID_ARGS; + } + + zx_status_t status = ZX_OK; + zx_status_t io_status = ZX_OK; + uint8_t* buffer = reinterpret_cast<uint8_t*>(buffer_mem->vaddr()); + uint64_t offset = message->file_offset(); + size_t bytes_left = buffer_mem->size(); + size_t bytes_read = 0; + while (bytes_left > 0) { + uint64_t read_chunk_request = std::min(bytes_left, fuchsia_io_MAX_BUF); + uint64_t read_chunk_actual = 0; + status = fuchsia_io_FileReadAt(file_channel->get(), // _channel + read_chunk_request, // count + offset, // offset + &io_status, // out_s + buffer, // data_buffer + read_chunk_request, // data_capacity + &read_chunk_actual); // out_actual + buffer += read_chunk_actual; + offset += read_chunk_actual; + bytes_left -= read_chunk_actual; + bytes_read += read_chunk_actual; + + if (status != ZX_OK || io_status != ZX_OK) { + zxlogf(ERROR, "optee::%s failed to read from file (FIDL status: %d, IO status: %d)\n", + __FUNCTION__, status, io_status); + message->set_return_code(TEEC_ERROR_GENERIC); + return status; + } + + if (read_chunk_actual == 0) { + break; + } + } + + message->set_output_file_contents_size(bytes_read); + message->set_return_code(TEEC_SUCCESS); + return ZX_OK; +} + +zx_status_t OpteeClient::HandleRpcCommandFileSystemWriteFile( + WriteFileFileSystemRpcMessage* message) { + ZX_DEBUG_ASSERT(message != nullptr); + ZX_DEBUG_ASSERT(message->is_valid()); + + zxlogf(SPEW, "optee: received RPC to write file\n"); + + auto maybe_file_channel = GetFileSystemObjectChannel(message->file_system_object_identifier()); + if (!maybe_file_channel.has_value()) { + message->set_return_code(TEEC_ERROR_ITEM_NOT_FOUND); + return ZX_ERR_NOT_FOUND; + } + + zx::unowned_channel file_channel(std::move(*maybe_file_channel)); + + std::optional<SharedMemoryView> buffer_mem = GetMemoryReference( + FindSharedMemory(message->file_contents_memory_identifier()), + message->file_contents_memory_paddr(), + message->file_contents_memory_size()); + if (!buffer_mem.has_value()) { + message->set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return ZX_ERR_INVALID_ARGS; + } + + zx_status_t status = ZX_OK; + zx_status_t io_status = ZX_OK; + uint8_t* buffer = reinterpret_cast<uint8_t*>(buffer_mem->vaddr()); + uint64_t offset = message->file_offset(); + size_t bytes_left = message->file_contents_memory_size(); + while (bytes_left > 0) { + uint64_t write_chunk_request = std::min(bytes_left, fuchsia_io_MAX_BUF); + uint64_t write_chunk_actual = 0; + status = fuchsia_io_FileWriteAt(file_channel->get(), // _channel + buffer, // data_data + write_chunk_request, // data_count + offset, // offset + &io_status, // out_s + &write_chunk_actual); // out_actual + buffer += write_chunk_actual; + offset += write_chunk_actual; + bytes_left -= write_chunk_actual; + + if (status != ZX_OK || io_status != ZX_OK) { + zxlogf(ERROR, "optee::%s failed to write to file (FIDL status: %d, IO status: %d)\n", + __FUNCTION__, status, io_status); + message->set_return_code(TEEC_ERROR_GENERIC); + return status; + } + } + + message->set_return_code(TEEC_SUCCESS); + return ZX_OK; +} + +zx_status_t OpteeClient::HandleRpcCommandFileSystemTruncateFile( + TruncateFileFileSystemRpcMessage* message) { + ZX_DEBUG_ASSERT(message != nullptr); + ZX_DEBUG_ASSERT(message->is_valid()); + + zxlogf(SPEW, "optee: received RPC to truncate file\n"); + + auto maybe_file_channel = GetFileSystemObjectChannel(message->file_system_object_identifier()); + if (!maybe_file_channel.has_value()) { + message->set_return_code(TEEC_ERROR_ITEM_NOT_FOUND); + return ZX_ERR_NOT_FOUND; + } + + zx::unowned_channel file_channel(std::move(*maybe_file_channel)); + + zx_status_t io_status = ZX_OK; + zx_status_t status = fuchsia_io_FileTruncate(file_channel->get(), // _channel + message->target_file_size(), // length + &io_status); // out_s + if (status != ZX_OK || io_status != ZX_OK) { + zxlogf(ERROR, "optee::%s failed to truncate file (FIDL status: %d, IO status: %d)\n", + __FUNCTION__, status, io_status); + message->set_return_code(TEEC_ERROR_GENERIC); + return status; + } + + message->set_return_code(TEEC_SUCCESS); + return ZX_OK; +} + +zx_status_t OpteeClient::HandleRpcCommandFileSystemRemoveFile( + RemoveFileFileSystemRpcMessage* message) { + ZX_DEBUG_ASSERT(message != nullptr); + ZX_DEBUG_ASSERT(message->is_valid()); + + zxlogf(SPEW, "optee: received RPC to remove file\n"); + + std::optional<SharedMemoryView> path_mem = GetMemoryReference( + FindSharedMemory(message->path_memory_identifier()), + message->path_memory_paddr(), + message->path_memory_size()); + if (!path_mem.has_value()) { + message->set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return ZX_ERR_INVALID_ARGS; + } + + std::filesystem::path path = GetPathFromRawMemory(reinterpret_cast<void*>(path_mem->vaddr()), + message->path_memory_size()); + + zx::channel storage_channel; + constexpr bool kNoCreate = false; + zx_status_t status = GetStorageDirectory(path.parent_path(), kNoCreate, &storage_channel); + if (status != ZX_OK) { + zxlogf(ERROR, "optee::%s: failed to get storage directory (status %d)\n", + __FUNCTION__, status); + message->set_return_code(TEEC_ERROR_BAD_STATE); + return status; + } + + zx_status_t io_status; + std::string filename = path.filename().string(); + status = fuchsia_io_DirectoryUnlink(storage_channel.get(), filename.data(), filename.length(), + &io_status); + if (status != ZX_OK || io_status != ZX_OK) { + zxlogf(ERROR, "optee::%s failed to remove file (FIDL status: %d, IO status: %d)\n", + __FUNCTION__, status, io_status); + message->set_return_code(TEEC_ERROR_GENERIC); + return status; + } + + message->set_return_code(TEEC_SUCCESS); + return ZX_OK; +} + +zx_status_t OpteeClient::HandleRpcCommandFileSystemRenameFile( + RenameFileFileSystemRpcMessage* message) { + ZX_DEBUG_ASSERT(message != nullptr); + ZX_DEBUG_ASSERT(message->is_valid()); + + zxlogf(SPEW, "optee: received RPC to rename file\n"); + + std::optional<SharedMemoryView> old_path_mem = GetMemoryReference( + FindSharedMemory(message->old_file_name_memory_identifier()), + message->old_file_name_memory_paddr(), + message->old_file_name_memory_size()); + if (!old_path_mem.has_value()) { + message->set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return ZX_ERR_INVALID_ARGS; + } + + std::filesystem::path old_path = GetPathFromRawMemory( + reinterpret_cast<void*>(old_path_mem->vaddr()), message->old_file_name_memory_size()); + std::string old_name = old_path.filename().string(); + + std::optional<SharedMemoryView> new_path_mem = GetMemoryReference( + FindSharedMemory(message->new_file_name_memory_identifier()), + message->new_file_name_memory_paddr(), + message->new_file_name_memory_size()); + if (!new_path_mem.has_value()) { + message->set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return ZX_ERR_INVALID_ARGS; + } + + std::filesystem::path new_path = GetPathFromRawMemory( + reinterpret_cast<void*>(new_path_mem->vaddr()), message->new_file_name_memory_size()); + std::string new_name = new_path.filename().string(); + + zx::channel new_storage_channel; + constexpr bool kNoCreate = false; + zx_status_t status = GetStorageDirectory(new_path.parent_path(), kNoCreate, + &new_storage_channel); + if (status != ZX_OK) { + message->set_return_code(TEEC_ERROR_BAD_STATE); + return status; + } + + if (!message->should_overwrite()) { + zx::channel destination_channel; + static constexpr uint32_t kCheckRenameFlags = fuchsia_io_OPEN_RIGHT_READABLE | + fuchsia_io_OPEN_FLAG_DESCRIBE; + static constexpr uint32_t kCheckRenameMode = fuchsia_io_MODE_TYPE_FILE | + fuchsia_io_MODE_TYPE_DIRECTORY; + status = OpenObjectInDirectory(new_storage_channel, + kCheckRenameFlags, + kCheckRenameMode, + new_name, + &destination_channel); + if (status == ZX_OK) { + // The file exists but shouldn't be overwritten + zxlogf(INFO, + "optee::%s: refusing to rename file to path that already exists with overwrite " + "set to false\n", + __FUNCTION__); + message->set_return_code(TEEC_ERROR_ACCESS_CONFLICT); + return ZX_OK; + } else if (status != ZX_ERR_NOT_FOUND) { + zxlogf(ERROR, "optee::%s: could not check file existence before renaming (status %d)\n", + __FUNCTION__, status); + message->set_return_code(TEEC_ERROR_GENERIC); + return status; + } + } + + zx::channel old_storage_channel; + status = GetStorageDirectory(old_path.parent_path(), kNoCreate, &old_storage_channel); + if (status != ZX_OK) { + message->set_return_code(TEEC_ERROR_BAD_STATE); + return status; + } + + zx_status_t io_status = ZX_OK; + zx::handle new_storage_token; + status = fuchsia_io_DirectoryGetToken(new_storage_channel.get(), + &io_status, + new_storage_token.reset_and_get_address()); + if (status != ZX_OK || io_status != ZX_OK) { + zxlogf(ERROR, + "optee::%s: could not get destination directory's storage token " + "(FIDL status: %d, IO status: %d)\n", + __FUNCTION__, status, io_status); + message->set_return_code(TEEC_ERROR_GENERIC); + return status; + } + + status = fuchsia_io_DirectoryRename(old_storage_channel.get(), // _channel + old_name.data(), // src_data + old_name.length(), // src_size + new_storage_token.release(), // dst_parent_token + new_name.data(), // dst_data + new_name.length(), // dst_size + &io_status); // out_s + if (status != ZX_OK || io_status != ZX_OK) { + zxlogf(ERROR, "optee::%s failed to rename file (FIDL status: %d, IO status: %d)\n", + __FUNCTION__, status, io_status); + message->set_return_code(TEEC_ERROR_GENERIC); + return status; + } + + message->set_return_code(TEEC_SUCCESS); return ZX_OK; } diff --git a/zircon/system/dev/tee/optee/optee-client.h b/zircon/system/dev/tee/optee/optee-client.h index e017a54975022e4d9644fec3632d5e0a273c0cb7..41391445fcf7086a0ceb789f4951414bda39c4ed 100644 --- a/zircon/system/dev/tee/optee/optee-client.h +++ b/zircon/system/dev/tee/optee/optee-client.h @@ -4,7 +4,10 @@ #pragma once +#include <atomic> +#include <filesystem> #include <optional> +#include <string_view> #include <ddktl/device.h> #include <ddktl/protocol/empty-protocol.h> @@ -69,6 +72,17 @@ private: }; using OpenSessionsTable = fbl::HashTable<uint32_t, fbl::unique_ptr<OpteeSession>>; + // TODO(godtamit): Move to `std::unordered_map` when BLD-413 is complete + struct FileSystemObject : fbl::SinglyLinkedListable<std::unique_ptr<FileSystemObject>> { + FileSystemObject(uint64_t id, zx::channel&& channel) + : id(id), channel(std::move(channel)) {} + uint64_t GetKey() const { return id; } + static size_t GetHash(uint64_t key) { return static_cast<size_t>(key); } + uint64_t id; + zx::channel channel; + }; + using FileSystemObjectTable = fbl::HashTable<uint64_t, std::unique_ptr<FileSystemObject>>; + zx_status_t CloseSession(uint32_t session_id); // Attempts to allocate a block of SharedMemory from a designated memory pool. @@ -105,6 +119,75 @@ private: // * Otherwise, an iterator object pointing to the end of allocated_shared_memory_. SharedMemoryList::iterator FindSharedMemory(uint64_t mem_id); + // Attempts to get a slice of `SharedMemory` representing an OP-TEE memory reference. + // + // Parameters: + // * mem_iter: The `SharedMemoryList::iterator` object pointing to the `SharedMemory`. + // This may point to the end of `allocated_shared_memory_`. + // * base_paddr: The starting base physical address of the slice. + // * size: The size of the slice. + // + // Returns: + // * If `mem_iter` is valid and the slice bounds are valid, an initialized `std::optional` with + // the `SharedMemoryView`. + // * Otherwise, an uninitialized `std::optional`. + std::optional<SharedMemoryView> GetMemoryReference(SharedMemoryList::iterator mem_iter, + zx_paddr_t base_paddr, + size_t size); + + // Requests the root storage channel from the `ServiceProvider` and caches it in + // `root_storage_channel_`. + // + // Subsequent calls to the function will return the cached channel. + // + // Returns: + // * ZX_OK: The operation was successful. + // * ZX_ERR_UNAVAILABLE: The current client does not have access to a `ServiceProvider`. + // * `zx_status_t` codes from `zx::channel::create` or requesting the `ServiceProvider` over + // FIDL. + zx_status_t GetRootStorageChannel(zx::unowned_channel* out_root_channel); + + // Requests a connection to the storage directory pointed to by the path. + // + // Parameters: + // * path: The path of the directory, relative to the root storage directory. + // * create: Flag specifying whether to create directories if they don't exist. + // * out_storage_channel: Where to output the `fuchsia.io.Directory` client end channel. + zx_status_t GetStorageDirectory(std::filesystem::path path, bool create, + zx::channel* out_storage_channel); + + // Tracks a new file system object associated with the current client. + // + // This occurs when the trusted world creates or opens a file system object. + // + // Parameters: + // * io_node_channel: The channel to the `fuchsia.io.Node` file system object. + // + // Returns: + // * The identifier for the trusted world to refer to the object. + __WARN_UNUSED_RESULT uint64_t TrackFileSystemObject(zx::channel io_node_channel); + + // Gets the channel to the file system object associated with the given identifier. + // + // Parameters: + // * identifier: The identifier to find the file system object by. + // + // Returns: + // * A `std::optional` containing a reference to the `zx::channel`, if it was found. + std::optional<zx::unowned_channel> + GetFileSystemObjectChannel(uint64_t identifier); + + // Untracks a file system object associated with the current client. + // + // This occurs when the trusted world closes a previously open file system object. + // + // Parameters: + // * identifier: The identifier to refer to the object. + // + // Returns: + // * Whether a file system object associated with the identifier was untracked. + bool UntrackFileSystemObject(uint64_t identifier); + // // OP-TEE RPC Function Handlers // @@ -144,18 +227,35 @@ private: zx_status_t HandleRpcCommandGetTime(GetTimeRpcMessage* message); zx_status_t HandleRpcCommandAllocateMemory(AllocateMemoryRpcMessage* message); zx_status_t HandleRpcCommandFreeMemory(FreeMemoryRpcMessage* message); - zx_status_t HandleRpcCommandFileSystem(FileSystemRpcMessage* message); + // Move in the FileSystemRpcMessage since it'll be moved into a sub-type in this function. + zx_status_t HandleRpcCommandFileSystem(FileSystemRpcMessage&& message); + zx_status_t HandleRpcCommandFileSystemOpenFile(OpenFileFileSystemRpcMessage* message); + zx_status_t HandleRpcCommandFileSystemCreateFile(CreateFileFileSystemRpcMessage* message); + zx_status_t HandleRpcCommandFileSystemCloseFile(CloseFileFileSystemRpcMessage* message); + zx_status_t HandleRpcCommandFileSystemReadFile(ReadFileFileSystemRpcMessage* message); + zx_status_t HandleRpcCommandFileSystemWriteFile(WriteFileFileSystemRpcMessage* message); + zx_status_t HandleRpcCommandFileSystemTruncateFile(TruncateFileFileSystemRpcMessage* message); + zx_status_t HandleRpcCommandFileSystemRemoveFile(RemoveFileFileSystemRpcMessage* message); + zx_status_t HandleRpcCommandFileSystemRenameFile(RenameFileFileSystemRpcMessage* message); static fuchsia_tee_Device_ops_t kFidlOps; OpteeController* controller_; bool needs_to_close_ = false; SharedMemoryList allocated_shared_memory_; OpenSessionsTable open_sessions_; + std::atomic<uint64_t> next_file_system_object_id_{1}; + + // TODO(godtamit): Move to `std::unordered_map` when BLD-413 is complete + FileSystemObjectTable open_file_system_objects_; // The client end of a channel to the `fuchsia.tee.manager.ServiceProvider` protocol. // This may be an invalid channel, which indicates the client has no service provider support. zx::channel service_provider_channel_; + + // A lazily-initialized, cached channel to the root storage channel. + // This may be an invalid channel, which indicates it has not been initialized yet. + zx::channel root_storage_channel_; }; } // namespace optee diff --git a/zircon/system/dev/tee/optee/optee-message.cpp b/zircon/system/dev/tee/optee/optee-message.cpp index 61de9aa64d81236b6d458798b568e67ecae6d813..065ac44cbf43df579e15b723c860f7b885eb9a97 100644 --- a/zircon/system/dev/tee/optee/optee-message.cpp +++ b/zircon/system/dev/tee/optee/optee-message.cpp @@ -630,8 +630,9 @@ bool FreeMemoryRpcMessage::TryInitializeMembers() { bool FileSystemRpcMessage::TryInitializeMembers() { if (header()->num_params < kMinNumParams) { zxlogf(ERROR, - "optee: RPC command to access file system received unexpected number of parameters!" - "\n"); + "optee: RPC command to access file system received unexpected number of parameters " + "(%u)\n", + header()->num_params); set_return_origin(TEEC_ORIGIN_COMMS); set_return_code(TEEC_ERROR_BAD_PARAMETERS); return false; @@ -662,4 +663,347 @@ bool FileSystemRpcMessage::TryInitializeMembers() { return true; } +bool OpenFileFileSystemRpcMessage::TryInitializeMembers() { + if (header()->num_params != kNumParams) { + zxlogf(ERROR, + "optee: RPC command to open file received unexpected number of parameters (%u)\n", + header()->num_params); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return false; + } + + // Parse the file name parameter + MessageParam& path_param = params()[kPathParamIndex]; + switch (path_param.attribute) { + case MessageParam::kAttributeTypeTempMemInput: { + MessageParam::TemporaryMemory& temp_mem = path_param.payload.temporary_memory; + path_mem_id_ = temp_mem.shared_memory_reference; + path_mem_size_ = temp_mem.size; + path_mem_paddr_ = static_cast<zx_paddr_t>(temp_mem.buffer); + break; + } + case MessageParam::kAttributeTypeRegMemInput: + zxlogf(ERROR, + "optee: received unsupported registered memory parameter\n"); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_NOT_IMPLEMENTED); + return false; + default: + zxlogf(ERROR, + "optee: RPC command to open file received unexpected second parameter\n"); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return false; + } + + // Parse the output file identifier parameter + MessageParam& out_fs_object_id_param = params()[kOutFileSystemObjectIdParamIndex]; + if (out_fs_object_id_param.attribute != MessageParam::kAttributeTypeValueOutput) { + zxlogf(ERROR, + "optee: RPC command to open file received unexpected third parameter\n"); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return false; + } + + out_fs_object_id_ = &out_fs_object_id_param.payload.value.file_system_object.identifier; + + return true; +} + +bool CreateFileFileSystemRpcMessage::TryInitializeMembers() { + if (header()->num_params != kNumParams) { + zxlogf(ERROR, + "optee: RPC command to create file received unexpected number of parameters (%u)\n", + header()->num_params); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return false; + } + + // Parse the file name parameter + MessageParam& path_param = params()[kPathParamIndex]; + switch (path_param.attribute) { + case MessageParam::kAttributeTypeTempMemInput: { + MessageParam::TemporaryMemory& temp_mem = path_param.payload.temporary_memory; + path_mem_id_ = temp_mem.shared_memory_reference; + path_mem_size_ = temp_mem.size; + path_mem_paddr_ = static_cast<zx_paddr_t>(temp_mem.buffer); + break; + } + case MessageParam::kAttributeTypeRegMemInput: + zxlogf(ERROR, + "optee: received unsupported registered memory parameter\n"); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_NOT_IMPLEMENTED); + return false; + default: + zxlogf(ERROR, + "optee: RPC command to create file received unexpected second parameter\n"); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return false; + } + + // Parse the output file identifier parameter + MessageParam& out_fs_object_param = params()[kOutFileSystemObjectIdParamIndex]; + if (out_fs_object_param.attribute != MessageParam::kAttributeTypeValueOutput) { + zxlogf(ERROR, + "optee: RPC command to create file received unexpected third parameter\n"); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return false; + } + + out_fs_object_id_ = &out_fs_object_param.payload.value.file_system_object.identifier; + + return true; +} + +bool CloseFileFileSystemRpcMessage::TryInitializeMembers() { + if (header()->num_params != kNumParams) { + zxlogf(ERROR, + "optee: RPC command to close file received unexpected number of parameters (%u)\n", + header()->num_params); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return false; + } + + // Parse the file name parameter + MessageParam& command_param = params()[kFileSystemCommandParamIndex]; + + // The attribute was already validated by FileSystemRpcMessage + ZX_DEBUG_ASSERT(command_param.attribute == MessageParam::kAttributeTypeValueInput || + command_param.attribute == MessageParam::kAttributeTypeValueInOut); + + fs_object_id_ = command_param.payload.value.file_system_command.object_identifier; + + return true; +} + +bool ReadFileFileSystemRpcMessage::TryInitializeMembers() { + if (header()->num_params != kNumParams) { + zxlogf(ERROR, + "optee: RPC command to read file received unexpected number of parameters (%u)\n", + header()->num_params); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return false; + } + + // Parse the file name parameter + MessageParam& command_param = params()[kFileSystemCommandParamIndex]; + + // The attribute was already validated by FileSystemRpcMessage + ZX_DEBUG_ASSERT(command_param.attribute == MessageParam::kAttributeTypeValueInput || + command_param.attribute == MessageParam::kAttributeTypeValueInOut); + + fs_object_id_ = command_param.payload.value.file_system_command.object_identifier; + file_offset_ = command_param.payload.value.file_system_command.object_offset; + + // Parse the output memory parameter + MessageParam& out_mem_param = params()[kOutReadBufferMemoryParamIndex]; + switch (out_mem_param.attribute) { + case MessageParam::kAttributeTypeTempMemOutput: { + MessageParam::TemporaryMemory& temp_mem = out_mem_param.payload.temporary_memory; + file_contents_memory_identifier_ = temp_mem.shared_memory_reference; + file_contents_memory_size_ = static_cast<size_t>(temp_mem.size); + file_contents_memory_paddr_ = static_cast<zx_paddr_t>(temp_mem.buffer); + out_file_contents_size_ = &temp_mem.size; + break; + } + case MessageParam::kAttributeTypeRegMemInput: + zxlogf(ERROR, + "optee: received unsupported registered memory parameter\n"); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_NOT_IMPLEMENTED); + return false; + default: + zxlogf(ERROR, + "optee: RPC command to read file received unexpected second parameter\n"); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return false; + } + + return true; +} + +bool WriteFileFileSystemRpcMessage::TryInitializeMembers() { + if (header()->num_params != kNumParams) { + zxlogf(ERROR, + "optee: RPC command to write file received unexpected number of parameters (%u)\n", + header()->num_params); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return false; + } + + // Parse the file name parameter + MessageParam& command_param = params()[kFileSystemCommandParamIndex]; + + // The attribute was already validated by FileSystemRpcMessage + ZX_DEBUG_ASSERT(command_param.attribute == MessageParam::kAttributeTypeValueInput || + command_param.attribute == MessageParam::kAttributeTypeValueInOut); + + fs_object_id_ = command_param.payload.value.file_system_command.object_identifier; + file_offset_ = command_param.payload.value.file_system_command.object_offset; + + // Parse the write memory parameter + MessageParam& mem_param = params()[kWriteBufferMemoryParam]; + switch (mem_param.attribute) { + case MessageParam::kAttributeTypeTempMemInput: { + MessageParam::TemporaryMemory& temp_mem = mem_param.payload.temporary_memory; + file_contents_memory_identifier_ = temp_mem.shared_memory_reference; + file_contents_memory_size_ = static_cast<size_t>(temp_mem.size); + file_contents_memory_paddr_ = static_cast<zx_paddr_t>(temp_mem.buffer); + break; + } + case MessageParam::kAttributeTypeRegMemInput: + zxlogf(ERROR, + "optee: received unsupported registered memory parameter\n"); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_NOT_IMPLEMENTED); + return false; + default: + zxlogf(ERROR, + "optee: RPC command to write file received unexpected second parameter\n"); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return false; + } + + return true; +} + +bool TruncateFileFileSystemRpcMessage::TryInitializeMembers() { + if (header()->num_params != kNumParams) { + zxlogf(ERROR, + "optee: RPC command to truncate file received unexpected number of parameters " + "(%u)\n", + header()->num_params); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return false; + } + + // Parse the file command parameter + MessageParam& command_param = params()[kFileSystemCommandParamIndex]; + + // The attribute was already validated by FileSystemRpcMessage + ZX_DEBUG_ASSERT(command_param.attribute == MessageParam::kAttributeTypeValueInput || + command_param.attribute == MessageParam::kAttributeTypeValueInOut); + + fs_object_id_ = command_param.payload.value.file_system_command.object_identifier; + target_file_size_ = command_param.payload.value.file_system_command.object_offset; + + return true; +} + +bool RemoveFileFileSystemRpcMessage::TryInitializeMembers() { + if (header()->num_params != kNumParams) { + zxlogf(ERROR, + "optee: RPC command to remove file received unexpected number of parameters (%u)\n", + header()->num_params); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return false; + } + + // Parse the file name parameter + MessageParam& path_param = params()[kFileNameParamIndex]; + switch (path_param.attribute) { + case MessageParam::kAttributeTypeTempMemInput: { + MessageParam::TemporaryMemory& temp_mem = path_param.payload.temporary_memory; + path_mem_id_ = temp_mem.shared_memory_reference; + path_mem_size_ = temp_mem.size; + path_mem_paddr_ = static_cast<zx_paddr_t>(temp_mem.buffer); + break; + } + case MessageParam::kAttributeTypeRegMemInput: + zxlogf(ERROR, + "optee: received unsupported registered memory parameter\n"); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_NOT_IMPLEMENTED); + return false; + default: + zxlogf(ERROR, + "optee: RPC command to remove file received unexpected second parameter\n"); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return false; + } + + return true; +} + +bool RenameFileFileSystemRpcMessage::TryInitializeMembers() { + if (header()->num_params != kNumParams) { + zxlogf(ERROR, + "optee: RPC command to rename file received unexpected number of parameters (%u)\n", + header()->num_params); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return false; + } + + // Parse the overwrite value + MessageParam& command_param = params()[kFileSystemCommandParamIndex]; + + // The attribute was already validated by FileSystemRpcMessage + ZX_DEBUG_ASSERT(command_param.attribute == MessageParam::kAttributeTypeValueInput || + command_param.attribute == MessageParam::kAttributeTypeValueInOut); + + should_overwrite_ = (command_param.payload.value.generic.b != 0); + + // Parse the old file name parameter + MessageParam& old_file_name_param = params()[kOldFileNameParamIndex]; + switch (old_file_name_param.attribute) { + case MessageParam::kAttributeTypeTempMemInput: { + MessageParam::TemporaryMemory& temp_mem = old_file_name_param.payload.temporary_memory; + old_file_name_mem_id_ = temp_mem.shared_memory_reference; + old_file_name_mem_size_ = temp_mem.size; + old_file_name_mem_paddr_ = static_cast<zx_paddr_t>(temp_mem.buffer); + break; + } + case MessageParam::kAttributeTypeRegMemInput: + zxlogf(ERROR, "optee: received unsupported registered memory parameter\n"); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_NOT_IMPLEMENTED); + return false; + default: + zxlogf(ERROR, + "optee: RPC command to rename file received unexpected second parameter\n"); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return false; + } + + // Parse the new file name parameter + MessageParam& new_file_name_param = params()[kNewFileNameParamIndex]; + switch (new_file_name_param.attribute) { + case MessageParam::kAttributeTypeTempMemInput: { + MessageParam::TemporaryMemory& temp_mem = new_file_name_param.payload.temporary_memory; + new_file_name_mem_id_ = temp_mem.shared_memory_reference; + new_file_name_mem_size_ = temp_mem.size; + new_file_name_mem_paddr_ = static_cast<zx_paddr_t>(temp_mem.buffer); + break; + } + case MessageParam::kAttributeTypeRegMemInput: + zxlogf(ERROR, "optee: received unsupported registered memory parameter\n"); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_NOT_IMPLEMENTED); + return false; + default: + zxlogf(ERROR, "optee: RPC command to rename file received unexpected third parameter\n"); + set_return_origin(TEEC_ORIGIN_COMMS); + set_return_code(TEEC_ERROR_BAD_PARAMETERS); + return false; + } + + return true; +} + } // namespace optee diff --git a/zircon/system/dev/tee/optee/optee-message.h b/zircon/system/dev/tee/optee/optee-message.h index 3775b6c5307342b9336ec6715e0359fb4ef264f9..c4132aa38edbaf9bd0d1053cb6517f75ef4eef22 100644 --- a/zircon/system/dev/tee/optee/optee-message.h +++ b/zircon/system/dev/tee/optee/optee-message.h @@ -95,7 +95,12 @@ struct MessageParam { } free_memory_specs; struct { uint64_t command_number; + uint64_t object_identifier; + uint64_t object_offset; } file_system_command; + struct { + uint64_t identifier; + } file_system_object; }; uint64_t attribute; @@ -632,7 +637,7 @@ public: // FileSystemRpcMessage // - // Move constructor for FileSystemRpcMessage. Uses the default implicit implementation. + // Move constructor for `FileSystemRpcMessage`. Uses the default implicit implementation. FileSystemRpcMessage(FileSystemRpcMessage&&) = default; // FileSystemRpcMessage @@ -662,4 +667,465 @@ private: bool TryInitializeMembers(); }; +// OpenFileFileSystemRpcMessage +// +// A `FileSystemRpcMessage` that should be interpreted with the command of opening a file. +// A `FileSystemRpcMessage` can be converted into a `OpenFileFileSystemRpcMessage` via a +// constructor. +class OpenFileFileSystemRpcMessage : public FileSystemRpcMessage { +public: + // OpenFileFileSystemRpcMessage + // + // Move constructor for `OpenFileFileSystemRpcMessage`. Uses the default implicit + // implementation. + OpenFileFileSystemRpcMessage(OpenFileFileSystemRpcMessage&&) = default; + + // OpenFileFileSystemRpcMessage + // + // Constructs a `OpenFileFileSystemRpcMessage` from a moved-in `FileSystemRpcMessage`. + explicit OpenFileFileSystemRpcMessage(FileSystemRpcMessage&& fs_message) + : FileSystemRpcMessage(std::move(fs_message)) { + ZX_DEBUG_ASSERT(is_valid()); // The file system message passed in should've been valid + ZX_DEBUG_ASSERT(file_system_command() == FileSystemCommand::kOpenFile); + + is_valid_ = is_valid_ && TryInitializeMembers(); + } + + uint64_t path_memory_identifier() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return path_mem_id_; + } + + size_t path_memory_size() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return path_mem_size_; + } + + zx_paddr_t path_memory_paddr() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return path_mem_paddr_; + } + + void set_output_file_system_object_identifier(uint64_t object_id) { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + ZX_DEBUG_ASSERT(out_fs_object_id_ != nullptr); + *out_fs_object_id_ = object_id; + } + +protected: + static constexpr size_t kNumParams = 3; + static constexpr size_t kPathParamIndex = 1; + static constexpr size_t kOutFileSystemObjectIdParamIndex = 2; + + uint64_t path_mem_id_; + size_t path_mem_size_; + zx_paddr_t path_mem_paddr_; + uint64_t* out_fs_object_id_; + +private: + bool TryInitializeMembers(); +}; + +// CreateFileFileSystemRpcMessage +// +// A `FileSystemRpcMessage` that should be interpreted with the command of creating a file. +// A `FileSystemRpcMessage` can be converted into a `CreateFileFileSystemRpcMessage` via a +// constructor. +class CreateFileFileSystemRpcMessage : public FileSystemRpcMessage { +public: + // CreateFileFileSystemRpcMessage + // + // Move constructor for `CreateFileFileSystemRpcMessage`. Uses the default implicit + // implementation. + CreateFileFileSystemRpcMessage(CreateFileFileSystemRpcMessage&&) = default; + + // CreateFileFileSystemRpcMessage + // + // Constructs a `CreateFileFileSystemRpcMessage` from a moved-in `FileSystemRpcMessage`. + explicit CreateFileFileSystemRpcMessage(FileSystemRpcMessage&& fs_message) + : FileSystemRpcMessage(std::move(fs_message)) { + ZX_DEBUG_ASSERT(is_valid()); // The file system message passed in should've been valid + ZX_DEBUG_ASSERT(file_system_command() == FileSystemCommand::kCreateFile); + + is_valid_ = is_valid_ && TryInitializeMembers(); + } + + uint64_t path_memory_identifier() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return path_mem_id_; + } + + size_t path_memory_size() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return path_mem_size_; + } + + zx_paddr_t path_memory_paddr() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return path_mem_paddr_; + } + + void set_output_file_system_object_identifier(uint64_t object_id) { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + ZX_DEBUG_ASSERT(out_fs_object_id_ != nullptr); + *out_fs_object_id_ = object_id; + } + +protected: + static constexpr size_t kNumParams = 3; + static constexpr size_t kPathParamIndex = 1; + static constexpr size_t kOutFileSystemObjectIdParamIndex = 2; + + uint64_t path_mem_id_; + size_t path_mem_size_; + zx_paddr_t path_mem_paddr_; + uint64_t* out_fs_object_id_; + +private: + bool TryInitializeMembers(); +}; + +// CloseFileFileSystemRpcMessage +// +// A `FileSystemRpcMessage` that should be interpreted with the command of closing a file. +// A `FileSystemRpcMessage` can be converted into a `CloseFileFileSystemRpcMessage` via a +// constructor. +class CloseFileFileSystemRpcMessage : public FileSystemRpcMessage { +public: + // CloseFileFileSystemRpcMessage + // + // Move constructor for `CloseFileFileSystemRpcMessage`. Uses the default implicit + // implementation. + CloseFileFileSystemRpcMessage(CloseFileFileSystemRpcMessage&&) = default; + + // CloseFileFileSystemRpcMessage + // + // Constructs a `CloseFileFileSystemRpcMessage` from a moved-in `FileSystemRpcMessage`. + explicit CloseFileFileSystemRpcMessage(FileSystemRpcMessage&& fs_message) + : FileSystemRpcMessage(std::move(fs_message)) { + ZX_DEBUG_ASSERT(is_valid()); // The file system message passed in should've been valid + ZX_DEBUG_ASSERT(file_system_command() == FileSystemCommand::kCloseFile); + + is_valid_ = is_valid_ && TryInitializeMembers(); + } + + uint64_t file_system_object_identifier() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return fs_object_id_; + } + +protected: + static constexpr size_t kNumParams = 1; + + uint64_t fs_object_id_; + +private: + bool TryInitializeMembers(); +}; + +// ReadFileFileSystemRpcMessage +// +// A `FileSystemRpcMessage` that should be interpreted with the command of reading an open file. +// A `FileSystemRpcMessage` can be converted into a `ReadFileFileSystemRpcMessage` via a +// constructor. +class ReadFileFileSystemRpcMessage : public FileSystemRpcMessage { +public: + // ReadFileFileSystemRpcMessage + // + // Move constructor for `ReadFileFileSystemRpcMessage`. Uses the default implicit + // implementation. + ReadFileFileSystemRpcMessage(ReadFileFileSystemRpcMessage&&) = default; + + // ReadFileFileSystemRpcMessage + // + // Constructs a `ReadFileFileSystemRpcMessage` from a moved-in `FileSystemRpcMessage`. + explicit ReadFileFileSystemRpcMessage(FileSystemRpcMessage&& fs_message) + : FileSystemRpcMessage(std::move(fs_message)) { + ZX_DEBUG_ASSERT(is_valid()); // The file system message passed in should've been valid + ZX_DEBUG_ASSERT(file_system_command() == FileSystemCommand::kReadFile); + + is_valid_ = is_valid_ && TryInitializeMembers(); + } + + uint64_t file_system_object_identifier() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return fs_object_id_; + } + + uint64_t file_offset() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return file_offset_; + } + + uint64_t file_contents_memory_identifier() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return file_contents_memory_identifier_; + } + + size_t file_contents_memory_size() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return file_contents_memory_size_; + } + + zx_paddr_t file_contents_memory_paddr() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return file_contents_memory_paddr_; + } + + void set_output_file_contents_size(size_t size) const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + ZX_DEBUG_ASSERT(out_file_contents_size_ != nullptr); + + *out_file_contents_size_ = static_cast<uint64_t>(size); + } + +protected: + static constexpr size_t kNumParams = 2; + static constexpr size_t kOutReadBufferMemoryParamIndex = 1; + + uint64_t fs_object_id_; + uint64_t file_offset_; + uint64_t file_contents_memory_identifier_; + size_t file_contents_memory_size_; + zx_paddr_t file_contents_memory_paddr_; + uint64_t* out_file_contents_size_; + +private: + bool TryInitializeMembers(); +}; + +// WriteFileFileSystemRpcMessage +// +// A `FileSystemRpcMessage` that should be interpreted with the command of writing to an open file. +// A `FileSystemRpcMessage` can be converted into a `WriteFileFileSystemRpcMessage` via a +// constructor. +class WriteFileFileSystemRpcMessage : public FileSystemRpcMessage { +public: + // WriteFileFileSystemRpcMessage + // + // Move constructor for `WriteFileFileSystemRpcMessage`. Uses the default implicit + // implementation. + WriteFileFileSystemRpcMessage(WriteFileFileSystemRpcMessage&&) = default; + + // WriteFileFileSystemRpcMessage + // + // Constructs a `WriteFileFileSystemRpcMessage` from a moved-in `FileSystemRpcMessage`. + explicit WriteFileFileSystemRpcMessage(FileSystemRpcMessage&& fs_message) + : FileSystemRpcMessage(std::move(fs_message)) { + ZX_DEBUG_ASSERT(is_valid()); // The file system message passed in should've been valid + ZX_DEBUG_ASSERT(file_system_command() == FileSystemCommand::kWriteFile); + + is_valid_ = is_valid_ && TryInitializeMembers(); + } + + uint64_t file_system_object_identifier() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return fs_object_id_; + } + + zx_off_t file_offset() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return file_offset_; + } + + uint64_t file_contents_memory_identifier() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return file_contents_memory_identifier_; + } + + size_t file_contents_memory_size() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return file_contents_memory_size_; + } + + zx_paddr_t file_contents_memory_paddr() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return file_contents_memory_paddr_; + } + +protected: + static constexpr size_t kNumParams = 2; + static constexpr size_t kWriteBufferMemoryParam = 1; + + uint64_t fs_object_id_; + zx_off_t file_offset_; + uint64_t file_contents_memory_identifier_; + size_t file_contents_memory_size_; + zx_paddr_t file_contents_memory_paddr_; + +private: + bool TryInitializeMembers(); +}; + +// TruncateFileFileSystemRpcMessage +// +// A `FileSystemRpcMessage` that should be interpreted with the command of truncating a file. +// A `FileSystemRpcMessage` can be converted into a `TruncateFileFileSystemRpcMessage` via a +// constructor. +class TruncateFileFileSystemRpcMessage : public FileSystemRpcMessage { +public: + // TruncateFileFileSystemRpcMessage + // + // Move constructor for `TruncateFileFileSystemRpcMessage`. Uses the default implicit + // implementation. + TruncateFileFileSystemRpcMessage(TruncateFileFileSystemRpcMessage&&) = default; + + // TruncateFileFileSystemRpcMessage + // + // Constructs a `TruncateFileFileSystemRpcMessage` from a moved-in `FileSystemRpcMessage`. + explicit TruncateFileFileSystemRpcMessage(FileSystemRpcMessage&& fs_message) + : FileSystemRpcMessage(std::move(fs_message)) { + ZX_DEBUG_ASSERT(is_valid()); // The file system message passed in should've been valid + ZX_DEBUG_ASSERT(file_system_command() == FileSystemCommand::kTruncateFile); + + is_valid_ = is_valid_ && TryInitializeMembers(); + } + + uint64_t file_system_object_identifier() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return fs_object_id_; + } + + uint64_t target_file_size() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return target_file_size_; + } + +protected: + static constexpr size_t kNumParams = 1; + + uint64_t fs_object_id_; + uint64_t target_file_size_; + +private: + bool TryInitializeMembers(); +}; + +// RemoveFileFileSystemRpcMessage +// +// A `FileSystemRpcMessage` that should be interpreted with the command of removing a file. +// A `FileSystemRpcMessage` can be converted into a `RemoveFileFileSystemRpcMessage` via a +// constructor. +class RemoveFileFileSystemRpcMessage : public FileSystemRpcMessage { +public: + // RemoveFileFileSystemRpcMessage + // + // Move constructor for `RemoveFileFileSystemRpcMessage`. Uses the default implicit + // implementation. + RemoveFileFileSystemRpcMessage(RemoveFileFileSystemRpcMessage&&) = default; + + // RemoveFileFileSystemRpcMessage + // + // Constructs a `RemoveFileFileSystemRpcMessage` from a moved-in `FileSystemRpcMessage`. + explicit RemoveFileFileSystemRpcMessage(FileSystemRpcMessage&& fs_message) + : FileSystemRpcMessage(std::move(fs_message)) { + ZX_DEBUG_ASSERT(is_valid()); // The file system message passed in should've been valid + ZX_DEBUG_ASSERT(file_system_command() == FileSystemCommand::kRemoveFile); + + is_valid_ = is_valid_ && TryInitializeMembers(); + } + + uint64_t path_memory_identifier() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return path_mem_id_; + } + + size_t path_memory_size() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return path_mem_size_; + } + + zx_paddr_t path_memory_paddr() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return path_mem_paddr_; + } + +protected: + static constexpr size_t kNumParams = 2; + static constexpr size_t kFileNameParamIndex = 1; + + uint64_t path_mem_id_; + size_t path_mem_size_; + zx_paddr_t path_mem_paddr_; + +private: + bool TryInitializeMembers(); +}; + +// RenameFileFileSystemRpcMessage +// +// A `FileSystemRpcMessage` that should be interpreted with the command of renaming a file. +// A `FileSystemRpcMessage` can be converted into a `RenameFileFileSystemRpcMessage` via a +// constructor. +class RenameFileFileSystemRpcMessage : public FileSystemRpcMessage { +public: + // RenameFileFileSystemRpcMessage + // + // Move constructor for `RenameFileFileSystemRpcMessage`. Uses the default implicit + // implementation. + RenameFileFileSystemRpcMessage(RenameFileFileSystemRpcMessage&&) = default; + + // RenameFileFileSystemRpcMessage + // + // Constructs a `RenameFileFileSystemRpcMessage` from a moved-in `FileSystemRpcMessage`. + explicit RenameFileFileSystemRpcMessage(FileSystemRpcMessage&& fs_message) + : FileSystemRpcMessage(std::move(fs_message)) { + ZX_DEBUG_ASSERT(is_valid()); // The file system message passed in should've been valid + ZX_DEBUG_ASSERT(file_system_command() == FileSystemCommand::kRenameFile); + + is_valid_ = is_valid_ && TryInitializeMembers(); + } + + bool should_overwrite() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return should_overwrite_; + } + + uint64_t old_file_name_memory_identifier() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return old_file_name_mem_id_; + } + + size_t old_file_name_memory_size() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return old_file_name_mem_size_; + } + + zx_paddr_t old_file_name_memory_paddr() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return old_file_name_mem_paddr_; + } + + uint64_t new_file_name_memory_identifier() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return new_file_name_mem_id_; + } + + size_t new_file_name_memory_size() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return new_file_name_mem_size_; + } + + zx_paddr_t new_file_name_memory_paddr() const { + ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); + return new_file_name_mem_paddr_; + } + +protected: + static constexpr size_t kNumParams = 3; + static constexpr size_t kOldFileNameParamIndex = 1; + static constexpr size_t kNewFileNameParamIndex = 2; + + bool should_overwrite_; + uint64_t old_file_name_mem_id_; + size_t old_file_name_mem_size_; + zx_paddr_t old_file_name_mem_paddr_; + uint64_t new_file_name_mem_id_; + size_t new_file_name_mem_size_; + zx_paddr_t new_file_name_mem_paddr_; + +private: + bool TryInitializeMembers(); +}; + } // namespace optee