| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Permission is hereby granted, free of charge, to any person |
| * obtaining a copy of this software and associated documentation |
| * files (the "Software"), to deal in the Software without |
| * restriction, including without limitation the rights to use, copy, |
| * modify, merge, publish, distribute, sublicense, and/or sell copies |
| * of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| |
| #include <trusty/avb.h> |
| #include <trusty/rpmb.h> |
| #include <trusty/trusty_ipc.h> |
| #include <trusty/util.h> |
| |
| #define LOCAL_LOG 0 |
| |
| static bool initialized; |
| static int avb_tipc_version = 1; |
| static struct trusty_ipc_chan avb_chan; |
| |
| static int avb_send_request(struct avb_message *msg, void *req, size_t req_len) |
| { |
| struct trusty_ipc_iovec req_iovs[2] = { |
| { .base = msg, .len = sizeof(*msg) }, |
| { .base = req, .len = req_len }, |
| }; |
| |
| return trusty_ipc_send(&avb_chan, req_iovs, req ? 2 : 1, true); |
| } |
| |
| static int avb_read_response(struct avb_message *msg, uint32_t cmd, void *resp, |
| size_t resp_len) |
| { |
| int rc; |
| struct trusty_ipc_iovec resp_iovs[2] = { |
| { .base = msg, .len = sizeof(*msg) }, |
| { .base = resp, .len = resp_len }, |
| }; |
| |
| rc = trusty_ipc_recv(&avb_chan, resp_iovs, resp ? 2 : 1, true); |
| if (rc < 0) { |
| trusty_error("failed (%d) to recv response\n", rc); |
| return rc; |
| } |
| if (msg->cmd != (cmd | AVB_RESP_BIT)) { |
| trusty_error("malformed response\n"); |
| return TRUSTY_ERR_GENERIC; |
| } |
| /* return payload size */ |
| return rc - sizeof(*msg); |
| } |
| |
| /* |
| * Convenience function to send a request to the AVB service and read the |
| * response. |
| * |
| * @cmd: the command |
| * @req: the request buffer |
| * @req_size: size of the request buffer |
| * @resp: the response buffer |
| * @resp_size_p: pointer to the size of the response buffer. changed to the |
| actual size of the response read from the secure side |
| * @handle_rpmb: true if the request is expected to invoke RPMB callbacks |
| */ |
| static int avb_do_tipc(uint32_t cmd, void *req, uint32_t req_size, void *resp, |
| uint32_t *resp_size_p, bool handle_rpmb) |
| { |
| int rc; |
| struct avb_message msg = { .cmd = cmd }; |
| |
| if (!initialized && cmd != AVB_GET_VERSION) { |
| trusty_error("%s: AVB TIPC client not initialized\n", __func__); |
| return TRUSTY_ERR_GENERIC; |
| } |
| |
| rc = avb_send_request(&msg, req, req_size); |
| if (rc < 0) { |
| trusty_error("%s: failed (%d) to send AVB request\n", __func__, rc); |
| return rc; |
| } |
| |
| if (handle_rpmb) { |
| /* handle any incoming RPMB requests */ |
| rc = rpmb_storage_proxy_poll(); |
| if (rc < 0) { |
| trusty_error("%s: failed (%d) to get RPMB requests\n", __func__, |
| rc); |
| return rc; |
| } |
| } |
| |
| uint32_t resp_size = resp_size_p ? *resp_size_p : 0; |
| rc = avb_read_response(&msg, cmd, resp, resp_size); |
| if (rc < 0) { |
| trusty_error("%s: failed (%d) to read AVB response\n", __func__, rc); |
| return rc; |
| } |
| /* change response size to actual response size */ |
| if (resp_size_p && rc != *resp_size_p) { |
| *resp_size_p = rc; |
| } |
| if (msg.result != AVB_ERROR_NONE) { |
| trusty_error("%s: AVB service returned error (%d)\n", __func__, |
| msg.result); |
| return TRUSTY_ERR_GENERIC; |
| } |
| return TRUSTY_ERR_NONE; |
| } |
| |
| static int avb_get_version(uint32_t *version) |
| { |
| int rc; |
| struct avb_get_version_resp resp; |
| uint32_t resp_size = sizeof(resp); |
| |
| rc = avb_do_tipc(AVB_GET_VERSION, NULL, 0, &resp, &resp_size, false); |
| |
| *version = resp.version; |
| return rc; |
| } |
| |
| |
| int avb_tipc_init(struct trusty_ipc_dev *dev) |
| { |
| int rc; |
| uint32_t version = 0; |
| |
| trusty_assert(dev); |
| trusty_assert(!initialized); |
| |
| trusty_ipc_chan_init(&avb_chan, dev); |
| trusty_debug("Connecting to AVB service\n"); |
| |
| /* connect to AVB service and wait for connect to complete */ |
| rc = trusty_ipc_connect(&avb_chan, AVB_PORT, true); |
| if (rc < 0) { |
| trusty_error("failed (%d) to connect to '%s'\n", rc, AVB_PORT); |
| return rc; |
| } |
| |
| /* check for version mismatch */ |
| rc = avb_get_version(&version); |
| if (rc != 0) { |
| trusty_error("Error getting version"); |
| return TRUSTY_ERR_GENERIC; |
| } |
| if (version != avb_tipc_version) { |
| trusty_error("AVB TIPC version mismatch. Expected %u, received %u\n", |
| avb_tipc_version, version); |
| return TRUSTY_ERR_GENERIC; |
| } |
| |
| /* mark as initialized */ |
| initialized = true; |
| |
| return TRUSTY_ERR_NONE; |
| } |
| |
| void avb_tipc_shutdown(struct trusty_ipc_dev *dev) |
| { |
| if (!initialized) |
| return; /* nothing to do */ |
| |
| /* close channel */ |
| trusty_ipc_close(&avb_chan); |
| |
| initialized = false; |
| } |
| |
| int trusty_read_rollback_index(uint32_t slot, uint64_t *value) |
| { |
| int rc; |
| struct avb_rollback_req req = { .slot = slot, .value = 0 }; |
| struct avb_rollback_resp resp; |
| uint32_t resp_size = sizeof(resp); |
| |
| rc = avb_do_tipc(READ_ROLLBACK_INDEX, &req, sizeof(req), &resp, |
| &resp_size, true); |
| |
| *value = resp.value; |
| return rc; |
| } |
| |
| int trusty_write_rollback_index(uint32_t slot, uint64_t value) |
| { |
| int rc; |
| struct avb_rollback_req req = { .slot = slot, .value = value }; |
| struct avb_rollback_resp resp; |
| uint32_t resp_size = sizeof(resp); |
| |
| rc = avb_do_tipc(WRITE_ROLLBACK_INDEX, &req, sizeof(req), &resp, |
| &resp_size, true); |
| return rc; |
| } |
| |
| int trusty_read_permanent_attributes(uint8_t *attributes, uint32_t size) |
| { |
| uint8_t resp_buf[AVB_MAX_BUFFER_LENGTH]; |
| uint32_t resp_size = AVB_MAX_BUFFER_LENGTH; |
| int rc = avb_do_tipc(READ_PERMANENT_ATTRIBUTES, NULL, 0, resp_buf, |
| &resp_size, true); |
| if (rc != 0) { |
| return rc; |
| } |
| /* ensure caller passed size matches size returned by Trusty */ |
| if (size != resp_size) { |
| return TRUSTY_ERR_INVALID_ARGS; |
| } |
| trusty_memcpy(attributes, resp_buf, resp_size); |
| return rc; |
| } |
| |
| int trusty_write_permanent_attributes(uint8_t *attributes, uint32_t size) |
| { |
| return avb_do_tipc(WRITE_PERMANENT_ATTRIBUTES, attributes, size, NULL, NULL, |
| true); |
| } |
| |
| int trusty_read_lock_state(uint8_t *lock_state) |
| { |
| uint32_t resp_size = sizeof(*lock_state); |
| return avb_do_tipc(READ_LOCK_STATE, NULL, 0, lock_state, |
| &resp_size, true); |
| } |
| |
| int trusty_write_lock_state(uint8_t lock_state) |
| { |
| return avb_do_tipc(WRITE_LOCK_STATE, &lock_state, sizeof(lock_state), NULL, |
| NULL, true); |
| } |
| |
| int trusty_lock_boot_state(void) |
| { |
| return avb_do_tipc(LOCK_BOOT_STATE, NULL, 0, NULL, NULL, false); |
| } |