From e26b4baabffb0d06997cc49ec2e70d9fa9b4c911 Mon Sep 17 00:00:00 2001 From: Mattias Nissler Date: Mon, 8 Aug 2016 14:00:07 +0200 Subject: Wire up TrustyNvram with TIPC. This adds glue code that receives NVRAM operations via Trusty IPC, passes them to TrustyNvram for execution and sends the result back via IPC. BUG: 27194378 Change-Id: I0f033a5e4745552570693589c377e7ecfb20a9cc --- ipc/nvram_ipc.cpp | 228 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ ipc/rules.mk | 25 ++++++ 2 files changed, 253 insertions(+) create mode 100644 ipc/nvram_ipc.cpp create mode 100644 ipc/rules.mk diff --git a/ipc/nvram_ipc.cpp b/ipc/nvram_ipc.cpp new file mode 100644 index 0000000..7099500 --- /dev/null +++ b/ipc/nvram_ipc.cpp @@ -0,0 +1,228 @@ +/* + * 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. + */ + +extern "C" { +#include + +#include +#include +} + +#include +#include +#include + +namespace { + +const char kNvramServiceName[] = "com.android.trusty.nvram"; +const int kNvramServiceNumBufs = 1; +const int kNvramServiceBufSize = 4096; +const uint32_t kNvramServiceFlags = + IPC_PORT_ALLOW_TA_CONNECT | IPC_PORT_ALLOW_NS_CONNECT; + +nvram::NvramManager* g_nvram_manager = nullptr; + +// Decodes a command from the binary |request_blob| and dispatches it. The +// serialized response is written to |response_blob|. Returns a Trusty error +// code in case there's a problem with decoding the request or encoding the +// response. Note that NVRAM-specific errors are carried in the serialized +// response and this function will return success. +int ProcessRequest(const nvram::Blob& request_blob, + nvram::Blob* response_blob) { + nvram::Request request; + if (!nvram::Decode(request_blob.data(), request_blob.size(), &request)) { + NVRAM_LOG_INFO("Failed to decode request"); + return ERR_INVALID_ARGS; + } + + nvram::Response response; + g_nvram_manager->Dispatch(request, &response); + + if (!nvram::Encode(response, response_blob)) { + NVRAM_LOG_INFO("Failed to encode response"); + return ERR_GENERIC; + } + + return NO_ERROR; +} + +// Reads the message described by |msg_info| from channel and processes it. +// Writes the response message back to |channel|. Returns a Trusty error code if +// there is any I/O problem. +int ProcessOneMessage(handle_t channel, const ipc_msg_info_t& msg_info) { + if (msg_info.len > kNvramServiceBufSize) { + NVRAM_LOG_ERR("Message too large on channel %x: %d", channel, msg_info.len); + return ERR_TOO_BIG; + } + + nvram::Blob request_blob; + if (!request_blob.Resize(msg_info.len)) { + NVRAM_LOG_ERR("Failed to allocate buffer for message on channel %x\n", + channel); + return ERR_NO_MEMORY; + } + + iovec_t request_iov{request_blob.data(), request_blob.size()}; + ipc_msg_t request_msg{ + 1, // number of iovecs + &request_iov, // iovecs pointer + 0, // number of handles + nullptr, // handles pointer + }; + int rc = read_msg(channel, msg_info.id, 0, &request_msg); + if (rc < 0) { + NVRAM_LOG_ERR("Failed to read_msg on channel %x: %d", channel, rc); + return rc; + } + + nvram::Blob response_blob; + rc = ProcessRequest(request_blob, &response_blob); + if (rc != NO_ERROR) { + NVRAM_LOG_ERR("Failed to process request on channel %x: %d", channel, rc); + return rc; + } + + iovec_t response_iov{response_blob.data(), response_blob.size()}; + ipc_msg_t response_msg{ + 1, // number of iovecs + &response_iov, // iovecs pointer + 0, // number of handles + nullptr // handles pointer + }; + rc = send_msg(channel, &response_msg); + if (rc < 0) { + NVRAM_LOG_ERR("Failed to send_msg on channel %x: %d", channel, rc); + return rc; + } + + return NO_ERROR; +} + +// Receives all pending messages from |channel| and processes them, producing +// responses as appropriate. Returns a Trusty error code if there's a problem +// with message processing or I/O. +int ProcessMessages(handle_t channel) { + while (true) { + struct ipc_msg_info msg_info; + int rc = get_msg(channel, &msg_info); + if (rc == ERR_NO_MSG) { + break; + } + + if (rc != NO_ERROR) { + NVRAM_LOG_ERR("Failed to get_msg on channel %x: %d", channel, rc); + return rc; + } + + rc = ProcessOneMessage(channel, msg_info); + if (rc != NO_ERROR) { + put_msg(channel, msg_info.id); + return rc; + } + + rc = put_msg(channel, msg_info.id); + if (rc != NO_ERROR) { + NVRAM_LOG_ERR("Failed to put_msg on channel %x: %d", channel, rc); + return rc; + } + } + + return NO_ERROR; +} + +// Handles an |event| that has occurred on a connected channel. This may either +// be an incoming message, which is then processed, or the event indicates +// channel teardown or errors, in which case the channel will be closed. +void HandleChannelEvent(const uevent_t& event) { + if (event.event & IPC_HANDLE_POLL_ERROR) { + NVRAM_LOG_ERR("Error on channel %x", event.handle); + close(event.handle); + return; + } + + if (event.event & IPC_HANDLE_POLL_HUP) { + close(event.handle); + return; + } + + if (event.event & IPC_HANDLE_POLL_MSG) { + if (ProcessMessages(event.handle) != NO_ERROR) { + close(event.handle); + return; + } + } else { + NVRAM_LOG_ERR("Unexpected event on channel %x: 0x%x", event.handle, + event.event); + } +} + +// Handles an event on the nvram IPC port. The only relevant event are incoming +// connections, which get accepted and will then be ready to receive requests. +void HandlePortEvent(const uevent_t& event) { + if (event.event & IPC_HANDLE_POLL_READY) { + uuid_t peer_uuid; + int rc = accept(event.handle, &peer_uuid); + if (rc < 0) { + NVRAM_LOG_ERR("Failed to accept connection: %d", rc); + } else { + // The channel is open now. The main loop will detect incoming messages + // and process them. + } + } else { + NVRAM_LOG_ERR("Unexpected event on port: 0x%x", event.event); + } +} + +} // namespace + +int main() { + nvram::NvramManager nvram_manager; + g_nvram_manager = &nvram_manager; + + // Create the IPC port. This publishes the nvram service to IPC clients (both + // on the trusty side and for the main OS). + int rc = port_create(kNvramServiceName, kNvramServiceNumBufs, + kNvramServiceBufSize, kNvramServiceFlags); + if (rc < 0) { + NVRAM_LOG_ERR("Failed to init ipc: %d", rc); + g_nvram_manager = nullptr; + return rc; + } + + handle_t port = static_cast(rc); + + // Process events on the port or any open channels. + while (true) { + uevent_t event; + event.handle = INVALID_IPC_HANDLE; + event.event = 0; + event.cookie = nullptr; + unsigned long timeout = -1; + rc = wait_any(&event, timeout); + if (rc == NO_ERROR) { + if (event.handle == port) { + HandlePortEvent(event); + } else { + HandleChannelEvent(event); + } + } else { + NVRAM_LOG_ERR("wait_any failed: %d", rc); + } + } + + g_nvram_manager = nullptr; + return 0; +} diff --git a/ipc/rules.mk b/ipc/rules.mk new file mode 100644 index 0000000..a240d66 --- /dev/null +++ b/ipc/rules.mk @@ -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. +# + +LOCAL_DIR := $(GET_LOCAL_DIR) + +MODULE_SRCS += \ + $(LOCAL_DIR)/nvram_ipc.cpp + +MODULE_DEPS += \ + app/trusty \ + lib/libc-trusty \ + system/nvram/messages -- cgit v1.2.3