blob: 6d992087930e62c1fae28c8769afab03d9ff17a2 [file] [log] [blame]
/*
* Copyright (C) 2015-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.
*/
#include "block_device_tipc.h"
#include <errno.h>
#include <lk/compiler.h>
#include <stdint.h>
#include <string.h>
#include <trusty_ipc.h>
#include <interface/storage/storage.h>
#include "block_cache.h"
#include "client_tipc.h"
#include "ipc.h"
#include "rpmb.h"
#include "tipc_ns.h"
#ifdef APP_STORAGE_RPMB_BLOCK_SIZE
#define BLOCK_SIZE_RPMB (APP_STORAGE_RPMB_BLOCK_SIZE)
#else
#define BLOCK_SIZE_RPMB (512)
#endif
#ifdef APP_STORAGE_RPMB_BLOCK_COUNT
#define BLOCK_COUNT_RPMB (APP_STORAGE_RPMB_BLOCK_COUNT)
#else
#define BLOCK_COUNT_RPMB (0) /* Auto detect */
#endif
#ifdef APP_STORAGE_MAIN_BLOCK_SIZE
#define BLOCK_SIZE_MAIN (APP_STORAGE_MAIN_BLOCK_SIZE)
#else
#define BLOCK_SIZE_MAIN (2048)
#endif
#ifdef APP_STORAGE_MAIN_BLOCK_COUNT
#define BLOCK_COUNT_MAIN (APP_STORAGE_MAIN_BLOCK_COUNT)
#else
#define BLOCK_COUNT_MAIN (0x10000000000 / BLOCK_SIZE_MAIN)
#endif
#define BLOCK_SIZE_RPMB_BLOCKS (BLOCK_SIZE_RPMB / RPMB_BUF_SIZE)
STATIC_ASSERT(BLOCK_SIZE_RPMB_BLOCKS == 1 || BLOCK_SIZE_RPMB_BLOCKS == 2);
STATIC_ASSERT((BLOCK_SIZE_RPMB_BLOCKS * RPMB_BUF_SIZE) == BLOCK_SIZE_RPMB);
STATIC_ASSERT(BLOCK_COUNT_RPMB == 0 || BLOCK_COUNT_RPMB >= 8);
STATIC_ASSERT(BLOCK_SIZE_MAIN >= 256);
STATIC_ASSERT(BLOCK_COUNT_MAIN >= 8);
STATIC_ASSERT(BLOCK_SIZE_MAIN >= BLOCK_SIZE_RPMB);
#define SS_ERR(args...) fprintf(stderr, "ss: " args)
#define SS_WARN(args...) fprintf(stderr, "ss: " args)
#define SS_DBG_IO(args...) \
do { \
} while (0)
static int rpmb_check(struct block_device_tipc* state, uint16_t block) {
int ret;
uint8_t tmp[RPMB_BUF_SIZE];
ret = rpmb_read(state->rpmb_state, tmp, block, 1);
SS_DBG_IO("%s: check rpmb_block %d, ret %d\n", __func__, block, ret);
return ret;
}
static uint32_t rpmb_search_size(struct block_device_tipc* state,
uint16_t hint) {
int ret;
uint32_t low = 0;
uint16_t high = UINT16_MAX;
uint16_t curr = hint ? hint - 1 : UINT16_MAX;
while (low <= high) {
ret = rpmb_check(state, curr);
switch (ret) {
case 0:
low = curr + 1;
break;
case -ENOENT:
high = curr - 1;
break;
default:
return 0;
};
if (ret || curr != hint) {
curr = (low + high) / 2;
hint = curr;
} else {
curr = curr + 1;
}
}
assert((uint32_t)high + 1 == low);
return low;
}
static struct block_device_rpmb* dev_rpmb_to_state(struct block_device* dev) {
assert(dev);
return containerof(dev, struct block_device_rpmb, dev);
}
static void block_device_tipc_rpmb_start_read(struct block_device* dev,
data_block_t block) {
int ret;
uint8_t tmp[BLOCK_SIZE_RPMB]; /* TODO: pass data in? */
uint16_t rpmb_block;
struct block_device_rpmb* dev_rpmb = dev_rpmb_to_state(dev);
assert(block < dev->block_count);
rpmb_block = block + dev_rpmb->base;
ret = rpmb_read(dev_rpmb->state->rpmb_state, tmp,
rpmb_block * BLOCK_SIZE_RPMB_BLOCKS,
BLOCK_SIZE_RPMB_BLOCKS);
SS_DBG_IO("%s: block %" PRIu64 ", base %d, rpmb_block %d, ret %d\n",
__func__, block, dev_rpmb->base, rpmb_block, ret);
block_cache_complete_read(dev, block, tmp, BLOCK_SIZE_RPMB, !!ret);
}
static void block_device_tipc_rpmb_start_write(struct block_device* dev,
data_block_t block,
const void* data,
size_t data_size) {
int ret;
uint16_t rpmb_block;
struct block_device_rpmb* dev_rpmb = dev_rpmb_to_state(dev);
assert(data_size == BLOCK_SIZE_RPMB);
assert(block < dev->block_count);
rpmb_block = block + dev_rpmb->base;
ret = rpmb_write(dev_rpmb->state->rpmb_state, data,
rpmb_block * BLOCK_SIZE_RPMB_BLOCKS,
BLOCK_SIZE_RPMB_BLOCKS, true);
SS_DBG_IO("%s: block %" PRIu64 ", base %d, rpmb_block %d, ret %d\n",
__func__, block, dev_rpmb->base, rpmb_block, ret);
block_cache_complete_write(dev, block, !!ret);
}
static void block_device_tipc_rpmb_wait_for_io(struct block_device* dev) {
assert(0); /* TODO: use async read/write */
}
static struct block_device_tipc* dev_ns_to_state(struct block_device* dev) {
assert(dev);
return containerof(dev, struct block_device_tipc, dev_ns);
}
static void block_device_tipc_ns_start_read(struct block_device* dev,
data_block_t block) {
int ret;
uint8_t tmp[BLOCK_SIZE_MAIN]; /* TODO: pass data in? */
struct block_device_tipc* state = dev_ns_to_state(dev);
ret = ns_read_pos(state->ipc_handle, state->ns_handle,
block * BLOCK_SIZE_MAIN, tmp, BLOCK_SIZE_MAIN);
SS_DBG_IO("%s: block %" PRIu64 ", ret %d\n", __func__, block, ret);
block_cache_complete_read(dev, block, tmp, BLOCK_SIZE_MAIN,
ret != BLOCK_SIZE_MAIN);
}
static void block_device_tipc_ns_start_write(struct block_device* dev,
data_block_t block,
const void* data,
size_t data_size) {
int ret;
struct block_device_tipc* state = dev_ns_to_state(dev);
assert(data_size == BLOCK_SIZE_MAIN);
ret = ns_write_pos(state->ipc_handle, state->ns_handle,
block * BLOCK_SIZE_MAIN, data, data_size);
SS_DBG_IO("%s: block %" PRIu64 ", ret %d\n", __func__, block, ret);
block_cache_complete_write(dev, block, ret != BLOCK_SIZE_MAIN);
}
static void block_device_tipc_ns_wait_for_io(struct block_device* dev) {
assert(0); /* TODO: use async read/write */
}
static void block_device_tipc_init_dev_rpmb(struct block_device_rpmb* dev_rpmb,
struct block_device_tipc* state,
uint16_t base,
uint32_t block_count) {
dev_rpmb->dev.start_read = block_device_tipc_rpmb_start_read;
dev_rpmb->dev.start_write = block_device_tipc_rpmb_start_write;
dev_rpmb->dev.wait_for_io = block_device_tipc_rpmb_wait_for_io;
dev_rpmb->dev.block_count = block_count;
dev_rpmb->dev.block_size = BLOCK_SIZE_RPMB;
dev_rpmb->dev.block_num_size = 2;
dev_rpmb->dev.mac_size = 2;
dev_rpmb->dev.tamper_detecting = true;
list_initialize(&dev_rpmb->dev.io_ops);
dev_rpmb->state = state;
dev_rpmb->base = base;
}
int block_device_tipc_init(struct block_device_tipc* state,
handle_t ipc_handle,
const struct key* fs_key,
const struct rpmb_key* rpmb_key) {
int ret;
bool new_ns_fs;
uint8_t dummy;
uint32_t rpmb_block_count;
uint32_t rpmb_part1_block_count = 2;
uint16_t rpmb_part1_base = 1; /* TODO: change to 0 and overwrite old fs */
uint16_t rpmb_part2_base = rpmb_part1_base + rpmb_part1_block_count;
state->ipc_handle = ipc_handle;
/* init rpmb */
ret = rpmb_init(&state->rpmb_state, &state->ipc_handle, rpmb_key);
if (ret < 0) {
SS_ERR("%s: rpmb_init failed (%d)\n", __func__, ret);
goto err_rpmb_init;
}
if (BLOCK_COUNT_RPMB) {
rpmb_block_count = BLOCK_COUNT_RPMB;
ret = rpmb_check(state, rpmb_block_count * BLOCK_SIZE_RPMB_BLOCKS - 1);
if (ret) {
SS_ERR("%s: bad static rpmb size, %d\n", __func__,
rpmb_block_count);
goto err_bad_rpmb_size;
}
} else {
rpmb_block_count =
rpmb_search_size(state, 0); /* TODO: get hint from ns */
rpmb_block_count /= BLOCK_SIZE_RPMB_BLOCKS;
}
if (rpmb_block_count < rpmb_part2_base) {
ret = -1;
SS_ERR("%s: bad rpmb size, %d\n", __func__, rpmb_block_count);
goto err_bad_rpmb_size;
}
block_device_tipc_init_dev_rpmb(&state->dev_rpmb, state, rpmb_part2_base,
rpmb_block_count - rpmb_part2_base);
/* TODO: allow non-rpmb based tamper proof storage */
ret = fs_init(&state->tr_state_rpmb, fs_key, &state->dev_rpmb.dev,
&state->dev_rpmb.dev, false);
if (ret < 0) {
goto err_init_tr_state_rpmb;
}
state->fs_rpmb.tr_state = &state->tr_state_rpmb;
ret = client_create_port(&state->fs_rpmb.client_ctx,
STORAGE_CLIENT_TP_PORT);
if (ret < 0) {
goto err_fs_rpmb_create_port;
}
state->fs_rpmb_boot.tr_state = &state->tr_state_rpmb;
ret = client_create_port(&state->fs_rpmb_boot.client_ctx,
STORAGE_CLIENT_TDEA_PORT);
if (ret < 0) {
goto err_fs_rpmb_boot_create_port;
}
state->dev_ns.start_read = block_device_tipc_ns_start_read;
state->dev_ns.start_write = block_device_tipc_ns_start_write;
state->dev_ns.wait_for_io = block_device_tipc_ns_wait_for_io;
state->dev_ns.block_count = BLOCK_COUNT_MAIN;
state->dev_ns.block_size = BLOCK_SIZE_MAIN;
state->dev_ns.block_num_size = sizeof(data_block_t);
state->dev_ns.mac_size = sizeof(struct mac);
list_initialize(&state->dev_ns.io_ops);
ret = ns_open_file(state->ipc_handle, "0", &state->ns_handle, true);
if (ret < 0) {
/* RPMB fs only */
state->dev_ns.block_count = 0;
return 0;
}
/* Request empty file system if file is empty */
ret = ns_read_pos(state->ipc_handle, state->ns_handle, 0, &dummy,
sizeof(dummy));
new_ns_fs = ret < (int)sizeof(dummy);
state->fs_ns.tr_state = &state->tr_state_ns;
block_device_tipc_init_dev_rpmb(&state->dev_ns_rpmb, state, rpmb_part1_base,
rpmb_part1_block_count);
ret = fs_init(&state->tr_state_ns, fs_key, &state->dev_ns,
&state->dev_ns_rpmb.dev, new_ns_fs);
if (ret < 0) {
goto err_init_fs_ns_tr_state;
}
ret = client_create_port(&state->fs_ns.client_ctx, STORAGE_CLIENT_TD_PORT);
if (ret < 0) {
goto err_fs_ns_create_port;
}
return 0;
err_fs_ns_create_port:
/* undo fs_init */
err_init_fs_ns_tr_state:
ns_close_file(state->ipc_handle, state->ns_handle);
ipc_port_destroy(&state->fs_rpmb_boot.client_ctx);
err_fs_rpmb_boot_create_port:
ipc_port_destroy(&state->fs_rpmb.client_ctx);
err_fs_rpmb_create_port:
/* undo fs_init */
err_init_tr_state_rpmb:
rpmb_uninit(state->rpmb_state);
err_bad_rpmb_size:
err_rpmb_init:
return ret;
}
void block_device_tipc_uninit(struct block_device_tipc* state) {
if (state->dev_ns.block_count) {
ipc_port_destroy(&state->fs_ns.client_ctx);
/* undo fs_init */
ns_close_file(state->ipc_handle, state->ns_handle);
}
ipc_port_destroy(&state->fs_rpmb_boot.client_ctx);
ipc_port_destroy(&state->fs_rpmb.client_ctx);
/* undo fs_init */
rpmb_uninit(state->rpmb_state);
}