DM: RPMB virtualization

Full logic to supprot vRPMB:
1. Automatic switch between physical or simulated RPMB.
   But hardcode to use simulated one.
2. Parse RPMB cmd to basic APIs to:
   2.1 check request frame HMAC with uos vkey
   2.2 replace RPMB frame with real values.
3. RPMB partitioning for multiple UOS.
   It's hardcoded for coming config file support.

Signed-off-by: Huang Yang <yang.huang@intel.com>
Signed-off-by: Du Min <minx.du@intel.com>
Acked-by: Zhu Bing <bing.zhu@intel.com>
This commit is contained in:
Huang, Yang 2018-05-22 17:46:40 -04:00 committed by lijinxia
parent e6d57f439f
commit 8db85ecb25
4 changed files with 991 additions and 21 deletions

View File

@ -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

View File

@ -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 <string.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdbool.h>
#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;
}

View File

@ -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 <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <rpmb.h>
#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;
}

View File

@ -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__ */