diff --git a/devicemodel/Makefile b/devicemodel/Makefile index 8842802d2..d3781f9fc 100644 --- a/devicemodel/Makefile +++ b/devicemodel/Makefile @@ -82,6 +82,7 @@ SRCS += hw/pci/virtio/virtio_hyper_dmabuf.c SRCS += hw/pci/virtio/virtio_heci.c SRCS += hw/pci/virtio/virtio_rpmb.c SRCS += hw/pci/virtio/rpmb_sim.c +SRCS += hw/pci/virtio/rpmb_backend.c SRCS += hw/pci/irq.c SRCS += hw/pci/uart.c SRCS += hw/pci/gvt.c diff --git a/devicemodel/hw/pci/virtio/rpmb_backend.c b/devicemodel/hw/pci/virtio/rpmb_backend.c new file mode 100644 index 000000000..e70789a64 --- /dev/null +++ b/devicemodel/hw/pci/virtio/rpmb_backend.c @@ -0,0 +1,535 @@ +/* + * Copyright (C) 2018 Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer in + * this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "rpmb.h" +#include "rpmb_sim.h" +#include "vrpmb.h" +#include "rpmb_backend.h" + +static int virtio_rpmb_debug = 1; +#define DPRINTF(params) do { if (virtio_rpmb_debug) printf params; } while (0) +#define WPRINTF(params) (printf params) + +static uint32_t phy_counter = 0; +static uint32_t virt_counter = 0; +static uint8_t rpmb_key[RPMB_KEY_32_LEN] = {0}; +static uint8_t virt_rpmb_key[RPMB_KEY_32_LEN] = {0}; +static uint16_t g_rpmb_mode = RPMB_SIM_MODE; +static const char READ_DATA_STR[] = "read data"; +static const char WRITE_DATA_STR[] = "write data"; + +//TODO: will be read from config file. +static uint16_t get_uos_count(void) +{ + return 1; +} + +//TODO: will be predefined and read from config file. +static uint16_t get_rpmb_blocks(void) +{ + return rpmb_get_blocks(); +} + +//TODO: hardcode keybox size. It will be read from keybox header from RPMB. +static uint16_t get_common_blocks(void) +{ + uint16_t kb_blocks; + uint32_t kb_size = 15872; + + kb_blocks = (kb_size + (RPMB_BLOCK_SIZE -1)) / RPMB_BLOCK_SIZE; + //reserve for simulated rpmb + KBox header + padding + return kb_blocks + 1 + 1 + 1; +} + +static uint16_t get_accessible_blocks(void) +{ + return (get_rpmb_blocks() - get_common_blocks()) / + get_uos_count() + get_common_blocks(); +} + +/* Todo: To get the uos number, e.g. No.0 or No.1, which is + used for calculating UOS RPMB range address. + But this will be removed after config file is supported. + We plan to predefine such info and save to config file. +*/ +static uint8_t get_uos_id(void) +{ + return (get_uos_count() - 1); +} + +void rpmb_mode_init(uint16_t mode) +{ + g_rpmb_mode = mode; + DPRINTF(("%s: rpmb mode is %d\n", __func__, g_rpmb_mode)); +} + +void rpmb_counter_init(uint32_t counter) +{ + phy_counter = counter; +} + +static bool is_key_programmed(void) +{ + if (g_rpmb_mode == RPMB_PHY_MODE) { + return true; + } else if (g_rpmb_mode == RPMB_SIM_MODE) { + if (is_use_sim_rpmb()) + return true; + } + + DPRINTF(("%s: rpmb mode 0x%x is unsupported\n", __func__, g_rpmb_mode)); + return false; +} + +static uint16_t get_phy_addr(uint8_t uos_id, uint16_t vaddr) +{ + uint16_t common_blocks = get_common_blocks(); + uint16_t accessible_blocks = get_accessible_blocks(); + + if (vaddr < get_common_blocks()) { + return vaddr; + } else { + return (((accessible_blocks - common_blocks) * uos_id) + vaddr); + } +} + +int get_virt_rpmb_key(void) +{ + int rc = -1; + uint8_t key[RPMB_KEY_LEN]; + + rc = get_vrpmb_key(key, sizeof(key)); + if (rc == 0){ + DPRINTF(("%s: get uos key fail\n", __func__)); + } + + memcpy(virt_rpmb_key, key, RPMB_KEY_32_LEN); + memset(key, 0, RPMB_KEY_LEN); + return rc; +} + +static int rpmb_replace_frame(struct rpmb_frame *frames, uint32_t frame_cnt, + const uint8_t *key, const uint8_t *nonce, const uint32_t *write_counter, + const uint16_t *addr, const uint16_t *block_count, const int *result, const int *req_resp) +{ + uint32_t i; + + for (i = 0; i < frame_cnt; i++) { + if (nonce) + memcpy(frames[i].nonce, nonce, sizeof(frames[i].nonce)); + if (write_counter) + frames[i].write_counter = swap32(*write_counter); + if (addr) + frames[i].addr = swap16(*addr); + if (block_count) + frames[i].block_count = swap16(*block_count); + if (result) + frames[i].result = swap16(*result); + if (req_resp) + frames[i].req_resp = swap16(*req_resp); + } + + if (key) { + if (rpmb_mac(key, frames, frame_cnt, frames[frame_cnt - 1].key_mac)) { + DPRINTF(("%s: rpmb_mac failed\n", __func__)); + return -1; + } + } + + return 0; +} + +static int rpmb_check_frame(const char *cmd_str, int *err, + const struct rpmb_frame *frames, uint32_t frame_cnt, + const uint8_t *key, const uint32_t *write_counter, + const uint16_t *addr, const uint16_t *block_count) +{ + uint32_t i; + uint8_t mac[32]; + + for (i = 0; i < frame_cnt; i++) { + if (write_counter && *write_counter != swap32(frames[i].write_counter)) { + *err = RPMB_RES_COUNT_FAILURE; + DPRINTF(("%s: Bad write counter %u\n", cmd_str, *write_counter)); + return -1; + } + } + + if (addr && *addr >= get_accessible_blocks()) { + *err = RPMB_RES_ADDR_FAILURE; + DPRINTF(("%s: Bad addr, got %u, expected %u\n", + cmd_str, swap16(frames[i].addr), *addr)); + return -1; + } + + if (addr && block_count && (*addr + *block_count) > get_accessible_blocks()) { + *err = RPMB_RES_GENERAL_FAILURE; + DPRINTF(("%s: Bad block count %u\n", + cmd_str, *block_count)); + return -1; + } + + if (addr && !memcmp(cmd_str, WRITE_DATA_STR, sizeof(WRITE_DATA_STR))) { + if (*addr < get_common_blocks()) { + *err = RPMB_RES_WRITE_FAILURE; + DPRINTF(("%s: Common block is readed only\n", cmd_str)); + return -1; + } + } + + if (key) { + if (rpmb_mac(key, frames, frame_cnt, mac)) { + *err = RPMB_RES_GENERAL_FAILURE; + DPRINTF(("%s: rpmb_mac failed\n", cmd_str)); + return -1; + } + + if (memcmp(frames[frame_cnt - 1].key_mac, mac, sizeof(mac))) { + *err = RPMB_RES_AUTH_FAILURE; + DPRINTF(("%s: Bad MAC\n", cmd_str)); + return -1; + } + } + + return 0; +} + +static int rpmb_phy_ioctl(uint32_t ioc_cmd, void* seq_data) +{ + int rc = -1; + int fd; + + if (seq_data == NULL) { + DPRINTF(("%s: seq_data is NULL\n", __func__)); + return rc; + } + + /* open rpmb device.*/ + fd = open(RPMB_PHY_PATH_NAME, O_RDWR | O_NONBLOCK); + if (fd < 0) { + DPRINTF(("%s: failed to open %s.\n", __func__, RPMB_PHY_PATH_NAME)); + return fd; + } + + /* send ioctl cmd.*/ + rc = ioctl(fd, ioc_cmd, seq_data); + if (rc) + DPRINTF(("%s: seq ioctl cmd failed(%d).\n", __func__, rc)); + + /* close rpmb device.*/ + close(fd); + + return rc; +} + +static int rpmb_sim_ioctl(uint32_t ioc_cmd, void* seq_data) +{ + if (seq_data == NULL) { + DPRINTF(("%s: seq_data is NULL\n", __func__)); + return -1; + } + + switch (ioc_cmd) { + case RPMB_IOC_REQ_CMD: + case RPMB_IOC_SEQ_CMD: + return rpmb_sim_send(seq_data); + default: + DPRINTF(("%s: ioctl 0x%x is unsupported\n", __func__, ioc_cmd)); + return -1; + } +} + +static int rpmb_virt_ioctl(uint32_t ioc_cmd, void* seq_data) +{ + if (g_rpmb_mode == RPMB_PHY_MODE) { + return rpmb_phy_ioctl(ioc_cmd, seq_data); + } else if (g_rpmb_mode == RPMB_SIM_MODE) { + return rpmb_sim_ioctl(ioc_cmd, seq_data); + } + + DPRINTF(("%s: rpmb mode 0x%x is unsupported\n", __func__, g_rpmb_mode)); + return -1; +} + +static int rpmb_virt_write(uint32_t ioc_cmd, void* seq_data, + struct rpmb_frame* in_frame, uint32_t in_cnt, + struct rpmb_frame* out_frame, uint32_t out_cnt) +{ + int err = RPMB_RES_WRITE_FAILURE; + int resp = RPMB_RESP_DATA_WRITE; + uint16_t vaddr; + uint16_t paddr; + uint16_t block_count; + uint8_t uos_id; + __u16 rpmb_result = 0; + int rc; + + DPRINTF(("enter to %s\n", __func__)); + + if (in_cnt == 0 || in_frame == NULL || seq_data == NULL) { + DPRINTF(("%s: in_frame, in_cnt or seq_data is not available.\n", __func__)); + return -1; + } + + if (out_cnt > 0 && out_frame != NULL) { + memset(out_frame, 0, out_cnt * RPMB_FRAME_SIZE); + } else { + DPRINTF(("%s: vrpmb must be aware of the result in out_frame.\n", __func__)); + return -1; + } + + uos_id = get_uos_id(); + + vaddr = swap16(in_frame->addr); + block_count = in_cnt; + if (rpmb_check_frame(WRITE_DATA_STR, &err, in_frame, in_cnt, + virt_rpmb_key, &virt_counter, &vaddr, &block_count)) + goto out; + + paddr = get_phy_addr(uos_id, vaddr); + + if (rpmb_replace_frame(in_frame, in_cnt, rpmb_key, NULL, + &phy_counter, &paddr, NULL, NULL, NULL)) + { + err = RPMB_RES_GENERAL_FAILURE; + goto out; + } + + if (rpmb_virt_ioctl(ioc_cmd, seq_data)) { + DPRINTF(("%s: rpmb virt ioctl failed.\n", __func__)); + return -1; + } + + if (out_frame->result == swap16(RPMB_RES_COUNT_FAILURE)) { + memset(out_frame, 0, out_cnt * RPMB_FRAME_SIZE); + rc = rpmb_get_counter(g_rpmb_mode, rpmb_key, &phy_counter, &rpmb_result); + if (rc) { + DPRINTF(("%s: rpmb_get_counter failed(0x%x)\n", __func__, rpmb_result)); + return -1; + } + + /* Since phy_counter has changed, so we have to generate mac again*/ + if (rpmb_replace_frame(in_frame, in_cnt, rpmb_key, NULL, + &phy_counter, &paddr, NULL, NULL, NULL)) + { + err = RPMB_RES_GENERAL_FAILURE; + goto out; + } + + if (rpmb_virt_ioctl(ioc_cmd, seq_data)) { + DPRINTF(("%s: rpmb virt retry ioctl failed.\n", __func__)); + return -1; + } + } + + if (out_frame->result == RPMB_RES_OK) { + phy_counter++; + virt_counter++; + DPRINTF(("%s: rpmb virt ioctl result is ok.\n", __func__)); + } + + rpmb_replace_frame(out_frame, out_cnt, virt_rpmb_key, NULL, + &virt_counter, &vaddr, NULL, NULL, NULL); + + return 0; +out: + rpmb_replace_frame(out_frame, out_cnt, virt_rpmb_key, in_frame[0].nonce, + &virt_counter, &vaddr, &block_count, &err, &resp); + + return 0; +} + +static int rpmb_virt_read(uint32_t ioc_cmd, void* seq_data, + struct rpmb_frame* in_frame, uint32_t in_cnt, + struct rpmb_frame* out_frame, uint32_t out_cnt) +{ + int err = RPMB_RES_READ_FAILURE; + int resp = RPMB_RESP_DATA_READ; + uint16_t vaddr; + uint16_t paddr; + uint16_t block_count; + uint8_t uos_id; + + DPRINTF(("enter to %s\n", __func__)); + + if (in_cnt == 0 || in_frame == NULL) { + DPRINTF(("%s: in_frame, in_cnt or seq_data is not available\n", __func__)); + return -1; + } + + if (out_cnt == 0 || out_frame == NULL) { + DPRINTF(("%s: out_frame or out_cnt is not available\n", __func__)); + return -1; + } + + uos_id = get_uos_id(); + + memset(out_frame, 0, out_cnt * RPMB_FRAME_SIZE); + vaddr = swap16(in_frame->addr); + block_count = out_cnt; + + if (rpmb_check_frame(READ_DATA_STR, &err, in_frame, + in_cnt, NULL, NULL, &vaddr, &block_count)) + goto out; + + paddr = get_phy_addr(uos_id, vaddr); + if (rpmb_replace_frame(in_frame, in_cnt, NULL, + NULL, NULL, &paddr, NULL, NULL, NULL )) { + err = RPMB_RES_GENERAL_FAILURE; + goto out; + } + + if (rpmb_virt_ioctl(ioc_cmd, seq_data)) { + DPRINTF(("%s: rpmb ioctl failed\n", __func__)); + return -1; + } + + rpmb_replace_frame(out_frame, out_cnt, virt_rpmb_key, NULL, + NULL, &vaddr, NULL, NULL, NULL); + + return 0; +out: + rpmb_replace_frame(out_frame, out_cnt, virt_rpmb_key, in_frame[0].nonce, + NULL, &vaddr, &block_count, &err, &resp); + + return 0; +} + +static int rpmb_virt_get_counter(struct rpmb_frame* in_frame, uint32_t in_cnt, + struct rpmb_frame* out_frame, uint32_t out_cnt) +{ + int err = RPMB_RES_OK; + int resp = RPMB_RESP_GET_COUNTER; + + DPRINTF(("enter to %s\n", __func__)); + + if (in_cnt == 0 || in_frame == NULL) { + DPRINTF(("%s: in_frame or in_cnt is not available\n", __func__)); + return -1; + } + + if (out_cnt == 0 || out_frame == NULL) { + DPRINTF(("%s: out_frame or out_cnt is not available\n", __func__)); + return -1; + } + + memset(out_frame, 0, out_cnt * RPMB_FRAME_SIZE); + + if (!is_key_programmed()) { + DPRINTF(("%s: rpmb key is not programmed\n", __func__)); + err = RPMB_RES_NO_AUTH_KEY; + goto out; + } + + rpmb_replace_frame(out_frame, out_cnt, virt_rpmb_key, + in_frame[0].nonce, &virt_counter, NULL, NULL, &err, &resp); + + return 0; + +out: + rpmb_replace_frame(out_frame, out_cnt, virt_rpmb_key, + in_frame[0].nonce, NULL, NULL, NULL, &err, &resp); + + return 0; +} + +int rpmb_handler(uint32_t cmd, void *r) +{ + int rc = -1; + uint16_t i; + uint32_t write_cnt = 0; + uint32_t rel_write_cnt = 0; + uint32_t read_cnt = 0; + struct rpmb_frame *frame_write = NULL; + struct rpmb_frame *frame_rel_write = NULL; + struct rpmb_frame *frame_read = NULL; + struct rpmb_ioc_cmd *ioc_cmd = NULL; + struct rpmb_ioc_seq_data *iseq = r; + + if (r == NULL) { + DPRINTF(("%s: rpmb iocctl seq data is NULL\n", __func__)); + goto err_response; + } + + for (i = 0; i < iseq->h.num_of_cmds; i++) { + ioc_cmd = (struct rpmb_ioc_cmd *)(&iseq->cmd[i]); + if (ioc_cmd->flags == 0) { + frame_read = (struct rpmb_frame *)ioc_cmd->frames_ptr; + read_cnt = ioc_cmd->nframes; + } else if (ioc_cmd->flags == RPMB_F_WRITE) { + frame_write = (struct rpmb_frame *)ioc_cmd->frames_ptr; + write_cnt = ioc_cmd->nframes; + } else if (ioc_cmd->flags == (RPMB_F_WRITE | RPMB_F_REL_WRITE)) { + frame_rel_write = (struct rpmb_frame *)ioc_cmd->frames_ptr; + rel_write_cnt = ioc_cmd->nframes; + } else { + DPRINTF(("%s: rpmb_ioc_cmd is invalid in the rpmb_ioc_seq_data\n", __func__)); + goto err_response; + } + } + + if (rel_write_cnt) { + if (frame_rel_write[0].req_resp == swap16(RPMB_REQ_DATA_WRITE)) { + if (write_cnt && (frame_write->req_resp == swap16(RPMB_REQ_RESULT_READ))) + rc = rpmb_virt_write(cmd, r, frame_rel_write, + rel_write_cnt, frame_read, read_cnt); + else + rc = rpmb_virt_write(cmd, r, frame_rel_write, rel_write_cnt, NULL, 0); + } else if (frame_rel_write[0].req_resp == swap16(RPMB_REQ_PROGRAM_KEY)) { + DPRINTF(("%s: rpmb grogram key is unsupported\n", __func__)); + goto err_response; + } else { + DPRINTF(("%s: rpmb ioctl frame is invalid\n", __func__)); + goto err_response; + } + } else if (write_cnt) { + if (frame_write[0].req_resp == swap16(RPMB_REQ_DATA_READ)) { + rc = rpmb_virt_read(cmd, r, frame_write, 1, frame_read, read_cnt); + } else if (frame_write[0].req_resp == swap16(RPMB_REQ_GET_COUNTER)) { + rc = rpmb_virt_get_counter(frame_write, 1, frame_read, 1); + } else { + DPRINTF(("%s: rpmb get counter frame is invalid\n", __func__)); + goto err_response; + } + } else { + DPRINTF(("%s: rpmb ioctl frame is invalid\n", __func__)); + goto err_response; + } + + return rc; + +err_response: + return -1; +} diff --git a/devicemodel/hw/pci/virtio/virtio_rpmb.c b/devicemodel/hw/pci/virtio/virtio_rpmb.c index 44a9c1b0e..65ca48af4 100644 --- a/devicemodel/hw/pci/virtio/virtio_rpmb.c +++ b/devicemodel/hw/pci/virtio/virtio_rpmb.c @@ -19,7 +19,7 @@ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) @@ -31,7 +31,7 @@ * Create virtio rpmb backend VBS-U. This component will work with RPMB FE * driver to provide one communication channel between UOS and SOS. * The message from RPMB daemon in Android will be transferred over the - * channel and finally arrived RPMB physical driver on SOS kernel. + * channel and finally arrived RPMB physical driver on SOS kernel. * */ @@ -56,16 +56,18 @@ #include #include #include -#include +#include "rpmb.h" +#include "rpmb_sim.h" +#include "rpmb_backend.h" #define VIRTIO_RPMB_RINGSZ 64 +#define ADDR_NOT_PRESENT -2 -static int virtio_rpmb_debug; +static int virtio_rpmb_debug = 1; #define DPRINTF(params) do { if (virtio_rpmb_debug) printf params; } while (0) #define WPRINTF(params) (printf params) -#define SEQ_CMD_MAX 3 /*support up to 3 cmds*/ - +static __u16 rpmb_block_count = 0; /* * virtio-rpmb struct */ @@ -236,11 +238,373 @@ virtio_rpmb_map_seq_frames(struct iovec *iov) return 0; } +static int +rpmb_check_mac(__u8 *key, struct rpmb_frame *frames, __u8 frame_cnt) +{ + int rc = -1; + __u8 mac[32]; + + if (!key || !frames) { + DPRINTF(("key or frames is NULL\n")); + return -1; + } + + if (frame_cnt == 0) { + DPRINTF(("frame count is zero\n")); + return -1; + } + + rc = rpmb_mac(key, frames, frame_cnt, mac); + if (rc < 0) { + DPRINTF(("rpmb_calc_mac failed\n")); + return rc; + } + + if (memcmp(mac, frames[frame_cnt - 1].key_mac, 32)) { + DPRINTF(("rpmb mac mismatch:\n")); + return -1; + } + + return rc; +} + +static int +rpmb_check_response(const char *cmd_str, enum rpmb_response response_type, + const struct rpmb_frame *frames, __u32 frame_cnt, + const __u8 *key, const __u8 *nonce, const __u16 *addr) +{ + __u32 i; + __u8 mac[32]; + + for (i = 0; i < frame_cnt; i++) { + if (swap16(frames[i].req_resp) != response_type) { + DPRINTF(("%s: Bad response type, 0x%x, expected 0x%x\n", + cmd_str, swap16(frames[i].req_resp), response_type)); + return -1; + } + + if (swap16(frames[i].result) != RPMB_RES_OK) { + if (swap16(frames[i].result) == RPMB_RES_ADDR_FAILURE) { + DPRINTF(("%s: Addr failure, %u\n", cmd_str, swap16(frames[i].addr))); + return ADDR_NOT_PRESENT; + } + DPRINTF(("%s: Bad result, 0x%x\n", cmd_str, swap16(frames[i].result))); + return -1; + } + + if (nonce && memcmp(frames[i].nonce, nonce, sizeof(frames[i].nonce))) { + DPRINTF(("%s: Bad nonce\n", cmd_str)); + return -1; + } + + if (addr && *addr != swap16(frames[i].addr)) { + DPRINTF(("%s: Bad addr, got %u, expected %u\n", + cmd_str, swap16(frames[i].addr), *addr)); + return -1; + } + } + + if (key) { + if (rpmb_mac(key, frames, frame_cnt, mac)) { + DPRINTF(("%s: rpmb_mac failed\n", cmd_str)); + return -1; + } + + if (memcmp(frames[frame_cnt - 1].key_mac, mac, sizeof(mac))) { + DPRINTF(("%s: Bad MAC\n", cmd_str)); + return -1; + } + } + + return 0; +} + +int +rpmb_get_counter(__u8 mode, __u8 *key, __u32 *counter, __u16 *result) +{ + int rc; + int fd; + struct { + struct rpmb_ioc_seq_cmd h; + struct rpmb_ioc_cmd cmd[3]; + } iseq = {}; + struct rpmb_frame frame_in; + struct rpmb_frame frame_out; + + if (!key || !counter || !result) { + DPRINTF(("key, counter or result is NULL!\n")); + return -1; + } + + frame_in.req_resp = swap16(RPMB_REQ_GET_COUNTER); + + iseq.cmd[0].flags = RPMB_F_WRITE; + iseq.cmd[0].nframes = 1; + iseq.cmd[0].frames_ptr = (__aligned_u64)(intptr_t)(&frame_in); + iseq.cmd[1].flags = 0; + iseq.cmd[1].nframes = 1; + iseq.cmd[1].frames_ptr = (__aligned_u64)(intptr_t)(&frame_out); + iseq.h.num_of_cmds = 2; + + if (mode == RPMB_PHY_MODE) { + /* open rpmb device.*/ + fd = open(RPMB_PHY_PATH_NAME, O_RDWR | O_NONBLOCK); + if (fd < 0) { + DPRINTF(("failed to open %s.\n", RPMB_PHY_PATH_NAME)); + return fd; + } + + /* send ioctl cmd.*/ + rc = ioctl(fd, RPMB_IOC_SEQ_CMD, &iseq); + + /* close rpmb device.*/ + close(fd); + + if (rc) { + DPRINTF(("get counter for physical rpmb failed.\n")); + return rc; + } + } else { + rc = rpmb_sim_send(&iseq); + if (rc) { + DPRINTF(("get counter for simulated rpmb failed.\n")); + return rc; + } + } + + *result = swap16(frame_out.result); + if (*result != RPMB_RES_OK ) { + DPRINTF(("get rpmb counter failed(0x%x).\n", *result)); + return -1; + } + + rc = rpmb_check_mac(key, &frame_out, 1); + if (rc) { + DPRINTF(("rpmb counter check mac failed.\n")); + return rc; + } + + *counter = swap32(frame_out.write_counter); + DPRINTF(("rpmb counter value: 0x%x.\n", *counter)); + + return rc; +} + +/* TODO: this function will be used for AttKB */ +#if 0 +static int +rpmb_write_block(__u8 mode, __u8 *key, __u16 addr, void *buf, __u32 count) +{ + int rc; + int fd; + __u32 i; + __u32 write_counter; + __u16 result; + struct { + struct rpmb_ioc_seq_cmd h; + struct rpmb_ioc_cmd cmd[3]; + } iseq = {}; + struct rpmb_frame frame_write; + struct rpmb_frame frame_rel[count]; + struct rpmb_frame frame_read; + + if (!buf || count == 0) { + DPRINTF(("%s:buf or count is invalid!\n", __func__)); + return -1; + } + + rc = rpmb_get_counter(mode, key, &write_counter, &result); + if (rc) { + DPRINTF(("%s: virtio_rpmb_get_counter failed\n", __func__)); + return -1; + } + + /*fill write frame*/ + frame_write.addr = swap16(addr); + frame_write.req_resp = swap16(RPMB_REQ_RESULT_READ); + frame_write.write_counter = swap32(write_counter); + + /*fill rel write frame*/ + for (i = 0; i < count; i++) { + memset(&frame_rel[i], 0, sizeof(frame_rel[i])); + memcpy(frame_rel[i].data, buf + i * sizeof(frame_rel[i].data), sizeof(frame_rel[i].data)); + frame_rel[i].write_counter = swap32(write_counter); + frame_rel[i].addr = swap16(addr); + frame_rel[i].block_count = swap16(count); + frame_rel[i].req_resp = swap16(RPMB_REQ_DATA_WRITE); + } + + /*generate rel frame mac*/ + if (rpmb_mac(key, frame_rel, count, frame_rel[count - 1].key_mac)) { + DPRINTF(("%s: rel frame rpmb mac failed\n", __func__)); + return -1; + } + + /*fill io cmd*/ + iseq.cmd[0].flags = RPMB_F_WRITE | RPMB_F_REL_WRITE; + iseq.cmd[0].nframes = count; + iseq.cmd[0].frames_ptr = (__aligned_u64)(intptr_t)(frame_rel); + iseq.cmd[1].flags = RPMB_F_WRITE; + iseq.cmd[1].nframes = 1; + iseq.cmd[1].frames_ptr = (__aligned_u64)(intptr_t)(&frame_write); + iseq.cmd[2].flags = 0; + iseq.cmd[2].nframes = 1; + iseq.cmd[2].frames_ptr = (__aligned_u64)(intptr_t)(&frame_read); + iseq.h.num_of_cmds = 3; + + if (mode == RPMB_PHY_MODE) { + /* open rpmb device.*/ + fd = open(RPMB_PHY_PATH_NAME, O_RDWR | O_NONBLOCK); + if (fd < 0) { + DPRINTF(("failed to open %s for read blocks.\n", RPMB_PHY_PATH_NAME)); + return fd; + } + + /* send ioctl cmd.*/ + rc = ioctl(fd, RPMB_IOC_SEQ_CMD, &iseq); + + /* close rpmb device.*/ + close(fd); + + if (rc) { + DPRINTF(("read blocks for physical rpmb failed.\n")); + return rc; + } + } else { + rc = rpmb_sim_send(&iseq); + if (rc) { + DPRINTF(("read blocks for simulated rpmb failed.\n")); + return rc; + } + } + + rc = rpmb_check_response("write blocks", RPMB_RESP_DATA_WRITE, + &frame_read, 1, key, NULL, &addr); + return rc; +} +#endif + +static int +rpmb_read_block(__u8 mode, __u8 *key, __u16 addr, void *buf, __u32 count) +{ + int rc; + int fd; + __u8 *bufp; + __u32 i; + struct { + struct rpmb_ioc_seq_cmd h; + struct rpmb_ioc_cmd cmd[3]; + } iseq = {}; + struct rpmb_frame frame_in; + struct rpmb_frame frame_out[count]; + + if (!buf || count == 0) { + DPRINTF(("buf or count is invalid!.\n")); + return -1; + } + + frame_in.addr = swap16(addr); + frame_in.req_resp = swap16(RPMB_REQ_DATA_READ); + + iseq.cmd[0].flags = RPMB_F_WRITE; + iseq.cmd[0].nframes = 1; + iseq.cmd[0].frames_ptr = (__aligned_u64)(intptr_t)(&frame_in); + iseq.cmd[1].flags = 0; + iseq.cmd[1].nframes = count; + iseq.cmd[1].frames_ptr = (__aligned_u64)(intptr_t)(frame_out); + iseq.h.num_of_cmds = 2; + + if (mode == RPMB_PHY_MODE) { + /* open rpmb device.*/ + fd = open(RPMB_PHY_PATH_NAME, O_RDWR | O_NONBLOCK); + if (fd < 0) { + DPRINTF(("failed to open %s for read blocks.\n", RPMB_PHY_PATH_NAME)); + return fd; + } + + /* send ioctl cmd.*/ + rc = ioctl(fd, RPMB_IOC_SEQ_CMD, &iseq); + + /* close rpmb device.*/ + close(fd); + + if (rc) { + DPRINTF(("read blocks for physical rpmb failed.\n")); + return rc; + } + } else { + rc = rpmb_sim_send(&iseq); + if (rc) { + DPRINTF(("read blocks for simulated rpmb failed.\n")); + return rc; + } + } + + rc = rpmb_check_response("read blocks", RPMB_RESP_DATA_READ, + frame_out, count, key, NULL, &addr); + + if (rc) + return rc; + + for (bufp = buf, i = 0; i < count; i++, bufp += sizeof(frame_out[i].data)) + memcpy(bufp, frame_out[i].data, sizeof(frame_out[i].data)); + + return rc; +} + +static int +rpmb_check(__u8 mode, __u8 *key, __u16 block) +{ + int rc; + __u8 tmp[RPMB_BLOCK_SIZE]; + + rc = rpmb_read_block(mode, key, block, tmp, 1); + DPRINTF(("check rpmb_block %d, ret %d\n", block, rc)); + + return rc; +} + +static __u32 +rpmb_search_size(__u8 mode, __u8 *key, __u16 hint) +{ + int ret; + __u32 low = 0; + __u16 high = ~0; + __u16 curr = hint - 1; + + while (low <= high) { + ret = rpmb_check(mode, key, curr); + switch (ret) { + case 0: + low = curr + 1; + break; + case ADDR_NOT_PRESENT: + high = curr - 1; + break; + default: + return 0; + }; + if (ret || curr != hint) { + curr = low + (high - low) / 2; + hint = curr; + } else { + curr = curr + 1; + } + } + assert ((__u32)high + 1 == low); + return low; +} + +__u16 +rpmb_get_blocks(void) +{ + return rpmb_block_count; +} + static int virtio_rpmb_seq_handler(struct virtio_rpmb *rpmb, struct iovec *iov) { struct virtio_rpmb_ioctl_cmd *ioc = NULL; - int fd; void *pdata; int rc; @@ -268,22 +632,10 @@ virtio_rpmb_seq_handler(struct virtio_rpmb *rpmb, struct iovec *iov) return -1; } - /* open rpmb device.*/ - rc = open("/dev/rpmb0", O_RDWR | O_NONBLOCK); - if (rc < 0) { - DPRINTF(("failed to open /dev/rpmb0.\n")); - return rc; - } - fd = rc; - - /* send ioctl cmd.*/ - rc = ioctl(fd, ioc->cmd, pdata); + rc = rpmb_handler(ioc->cmd, pdata); if (rc) DPRINTF(("seq ioctl cmd failed(%d).\n", rc)); - /* close rpmb device.*/ - close(fd); - return rc; } @@ -292,7 +644,7 @@ virtio_rpmb_notify(void *base, struct virtio_vq_info *vq) { struct iovec iov; int len; - uint16_t idx; + __u16 idx; struct virtio_rpmb *rpmb = (struct virtio_rpmb *)base; struct virtio_rpmb_ioctl_cmd *ioc; @@ -358,6 +710,9 @@ virtio_rpmb_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) struct virtio_rpmb *rpmb; pthread_mutexattr_t attr; int rc; + __u8 key[RPMB_KEY_32_LEN]; + __u32 rpmb_counter = 0; + __u16 rpmb_result = 0; rpmb = calloc(1, sizeof(struct virtio_rpmb)); if (!rpmb) { @@ -411,10 +766,51 @@ virtio_rpmb_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) virtio_set_io_bar(&rpmb->base, 0); + rc = get_virt_rpmb_key(); + if (rc == 0) { + DPRINTF(("%s: get uos key failed!\n", __func__)); + goto out; + } + + // TODO: keep it for self-adaption rpmb mode + /*rc = rpmb_get_counter(RPMB_PHY_MODE, key, &rpmb_counter, &rpmb_result); + if (rc) { + DPRINTF(("rpmb_get_counter failed\n")); + goto out; + }*/ + + memset(key, 0, RPMB_KEY_32_LEN); + /*TODO: hardcode rpmb mode to RPMB_SIM_MODE*/ + rpmb_result = RPMB_RES_GENERAL_FAILURE; + if (rpmb_result == RPMB_RES_OK) { + rpmb_mode_init(RPMB_PHY_MODE); + rpmb_block_count = rpmb_search_size(RPMB_PHY_MODE, key, 0); + } else { + rc = rpmb_sim_key_init(key); + if (rc) { + DPRINTF(("rpmb_sim_key_init failed!\n")); + goto out; + } + + rc = rpmb_get_counter(RPMB_SIM_MODE, key, &rpmb_counter, &rpmb_result); + if (rc) { + DPRINTF(("rpmb_get_counter failed\n")); + goto out; + } + + rpmb_mode_init(RPMB_SIM_MODE); + + rpmb_block_count = rpmb_search_size(RPMB_SIM_MODE, key, 0); + } + + memset(key, 0, RPMB_KEY_32_LEN); + rpmb_counter_init(rpmb_counter); + return 0; out: free(rpmb); + memset(key, 0, RPMB_KEY_32_LEN); return rc; } diff --git a/devicemodel/include/rpmb_backend.h b/devicemodel/include/rpmb_backend.h new file mode 100644 index 000000000..d0fafd9fa --- /dev/null +++ b/devicemodel/include/rpmb_backend.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer in + * this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __RPMB_BACKEND_H__ +#define __RPMB_BACKEND_H__ + +#define RPMB_KEY_32_LEN 32 + +void rpmb_mode_init(uint16_t mode); +void rpmb_counter_init(uint32_t counter); +int rpmb_handler(uint32_t ioc_cmd, void *r); +int get_virt_rpmb_key(void); + +#endif /* __RPMB_BACKEND_H__ */