blob: 958b14a19adc8eef73d75f1a947ce3a8fdebbe1e [file] [log] [blame]
/*
* drivers/amlogic/unifykey/storagekey.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
/* extern from bl31 */
/*
* when RET_OK
* query: retval=1: key exsit,=0: key not exsit;
* tell: retvak = key size
* status: retval=1: secure, retval=0: non-secure
*/
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/scatterlist.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/kallsyms.h>
#include <linux/amlogic/efuse.h>
#include <linux/amlogic/unifykey/security_key.h>
#include <linux/amlogic/unifykey/key_manage.h>
#include <linux/of.h>
#include "unifykey.h"
#include "amlkey_if.h"
#include "normal_key.h"
/* key buffer status */
/* bit0, dirty flag*/
#define KEYBUFFER_CLEAN (0 << 0)
#define KEYBUFFER_DIRTY (1 << 0)
#define SECUESTORAGE_HEAD_SIZE (256)
#define SECUESTORAGE_WHOLE_SIZE (0x40000)
#define OTHER_METHOD_CALL
#undef pr_fmt
#define pr_fmt(fmt) "unifykey: " fmt
struct storagekey_info_t {
uint8_t *buffer;
uint32_t size;
uint32_t status;
};
static struct storagekey_info_t storagekey_info = {
.buffer = NULL,
/* default size */
.size = SECUESTORAGE_WHOLE_SIZE,
.status = KEYBUFFER_CLEAN,
};
store_key_ops store_key_read;
store_key_ops store_key_write;
#ifndef OTHER_METHOD_CALL
int store_operation_init(void)
{
int ret = 0;
if (kallsyms_lookup_name("nand_key_read")) {
pr_info(" %s() nand storeage ops!\n", __func__);
store_key_read = nand_key_read;
store_key_write = nand_key_write;
} else if (kallsyms_lookup_name("emmc_key_read")) {
pr_info(" %s() emmc storeage ops!\n", __func__);
store_key_read = emmc_key_read;
store_key_write = emmc_key_write;
} else {
ret = -1;
pr_err(" %s() fail!\n", __func__);
}
return ret;
}
#endif
void storage_ops_read(store_key_ops read)
{
#ifdef OTHER_METHOD_CALL
store_key_read = read;
#endif
}
EXPORT_SYMBOL(storage_ops_read);
void storage_ops_write(store_key_ops write)
{
#ifdef OTHER_METHOD_CALL
store_key_write = write;
#endif
}
EXPORT_SYMBOL(storage_ops_write);
/**
*1.init
* return ok 0, fail 1
*/
static int32_t _amlkey_init_gen(uint8_t *seed, uint32_t len, int encrypt_type)
{
int32_t ret = 0;
uint32_t actual_size = 0;
#ifndef OTHER_METHOD_CALL
ret = store_operation_init();
if (ret < 0) {
ret = -1;
pr_err(" %s store_operation_init fail!\n", __func__);
goto _out;
}
#endif
/* do nothing for now*/
pr_info("%s() enter!\n", __func__);
if (storagekey_info.buffer != NULL) {
pr_err("%s() %d: already init!\n", __func__, __LINE__);
goto _out;
}
/* get buffer from bl31 */
storagekey_info.buffer =
secure_storage_getbuffer(&storagekey_info.size);
if (storagekey_info.buffer == NULL) {
pr_err("%s() %d: can't get buffer from bl31!\n",
__func__, __LINE__);
ret = -1;
goto _out;
}
/* full fill key infos from storage. */
if (store_key_read) {
ret = store_key_read(storagekey_info.buffer,
storagekey_info.size, &actual_size);
if (ret != 0) {
pr_err("fail to read key data\n");
memset(storagekey_info.buffer, 0,
SECUESTORAGE_HEAD_SIZE);
ret = -1;
goto _out;
}
if (actual_size >= (1U << 20)) {
pr_err("really need more than 1M mem? please check\n");
memset(storagekey_info.buffer, 0,
SECUESTORAGE_HEAD_SIZE);
ret = -1;
goto _out;
}
} else {
pr_err("store_key_read is NULL\n");
pr_err("check whether emmc_key/nand_key driver is enabled\n");
ret = -1;
goto _out;
}
storagekey_info.size = actual_size;
pr_info("%s() storagekey_info.buffer=%p, storagekey_info.size = %0x!\n",
__func__,
storagekey_info.buffer,
storagekey_info.size);
_out:
return ret;
}
int32_t amlkey_init_m8b(uint8_t *seed, uint32_t len, int encrypt_type)
{
int32_t ret = 0;
uint32_t actual_size = 0;
#ifndef OTHER_METHOD_CALL
ret = store_operation_init();
if (ret < 0) {
ret = -1;
pr_err(" %s store_operation_init fail!\n", __func__);
goto _out;
}
#endif
/* do nothing for now*/
pr_info("%s() enter!\n", __func__);
if (storagekey_info.buffer != NULL) {
pr_err("%s() %d: already init!\n", __func__, __LINE__);
goto _out;
}
if ((void *)kallsyms_lookup_name("nand_key_read")
== (void *)store_key_read)
secure_storage_type(0);
else
secure_storage_type(1);
/* get buffer from bl31 */
storagekey_info.buffer =
secure_storage_getbuffer(&storagekey_info.size);
if (storagekey_info.buffer == NULL) {
pr_err("%s() %d: can't get buffer from bl31!\n",
__func__, __LINE__);
ret = -1;
goto _out;
}
if (encrypt_type == -1)
encrypt_type = 0;
secure_storage_set_enctype(encrypt_type);
/* full fill key infos from storage. */
if (store_key_read) {
ret = store_key_read(storagekey_info.buffer,
storagekey_info.size, &actual_size);
if (ret != 0) {
pr_err("fail to read key data\n");
memset(storagekey_info.buffer, 0,
SECUESTORAGE_HEAD_SIZE);
ret = -1;
goto _out;
}
if (actual_size >= (1U << 20)) {
pr_err("really need more than 1M mem? please check\n");
memset(storagekey_info.buffer, 0,
SECUESTORAGE_HEAD_SIZE);
ret = -1;
goto _out;
}
} else {
pr_err("store_key_read is NULL\n");
pr_err("check whether emmc_key/nand_key driver is enabled\n");
ret = -1;
goto _out;
}
storagekey_info.size = actual_size;
pr_info("%s() storagekey_info.buffer=%p, storagekey_info.size = %0x!\n",
__func__,
storagekey_info.buffer,
storagekey_info.size);
storagekey_info.size = actual_size;
secure_storage_notifier_ex(actual_size);
_out:
return ret;
}
/**
*2. query if the key already programmed
* return: exsit 1, non 0
*/
static u32 _amlkey_isexsit(const uint8_t *name)
{
int32_t ret = 0;
uint32_t retval;
if (name == NULL) {
pr_err("%s() %d, invalid key ", __func__, __LINE__);
return 0;
}
ret = secure_storage_query((uint8_t *)name, &retval);
if (ret) {
pr_err("%s() %d: ret %d\n", __func__, __LINE__, ret);
retval = 0;
}
return (int32_t)retval;
}
/**
* 3. query if the prgrammed key is secure
* return secure 1, non 0;
*/
static u32 _amlkey_get_attr(const uint8_t *name)
{
int32_t ret = 0;
uint32_t retval;
if (name == NULL) {
pr_err("%s() %d, invalid key ", __func__, __LINE__);
return 0;
}
ret = secure_storage_status((uint8_t *)name, &retval);
if (ret) {
pr_err("%s() %d: ret %d\n", __func__, __LINE__, ret);
retval = 0;
}
return (int32_t)retval;
}
/**
* 3.1 query if the prgrammed key is secure
* return secure 1, non 0;
*/
int32_t amlkey_issecure(const uint8_t *name)
{
return amlkey_get_attr(name) & KEY_UNIFY_ATTR_SECURE_MASK;
}
/**
* 3.2 query if the prgrammed key is encrypt
* return encrypt 1, non 0;
*/
int32_t amlkey_isencrypt(const uint8_t *name)
{
return amlkey_get_attr(name) & KEY_UNIFY_ATTR_ENCRYPT_MASK;
}
/**
* 4. actual bytes of key value
* return actual size.
*/
static unsigned int _amlkey_size(const uint8_t *name)
{
ssize_t size = 0;
int32_t ret = 0;
uint32_t retval;
if (name == NULL) {
pr_err("%s() %d, invalid key ", __func__, __LINE__);
return 0;
}
ret = secure_storage_tell((uint8_t *)name, &retval);
if (ret) {
pr_err("%s() %d: ret %d\n", __func__, __LINE__, ret);
retval = 0;
}
size = retval;
return size;
}
/**
*5. read non-secure key in bytes, return bytes readback actully.
* return actual size read back.
*/
static unsigned int _amlkey_read(const uint8_t *name,
uint8_t *buffer,
uint32_t len)
{
int32_t ret = 0;
unsigned int retval = 0;
uint32_t actul_len;
if (name == NULL) {
pr_err("%s() %d, invalid key ", __func__, __LINE__);
return 0;
}
ret = secure_storage_read((uint8_t *)name, buffer, len, &actul_len);
if (ret) {
pr_err("%s() %d: return %d\n", __func__, __LINE__, ret);
retval = 0;
goto _out;
}
retval = actul_len;
_out:
return retval;
}
/**
* 6.write key with attr in bytes , return bytes readback actully
* attr: bit0, secure/non-secure
* bit8, encrypt/non-encrypt
* return actual size write down.
*/
static ssize_t _amlkey_write(const uint8_t *name,
uint8_t *buffer,
uint32_t len,
uint32_t attr)
{
int32_t ret = 0;
ssize_t retval = 0;
uint32_t actual_length;
uint8_t *buf = NULL;
if (name == NULL) {
pr_err("%s() %d, invalid key ", __func__, __LINE__);
return retval;
}
pr_info("%s %d\n", __func__, __LINE__);
ret = secure_storage_write((uint8_t *)name,
buffer, len,
attr);
if (ret) {
pr_err("%s() %d: return %d\n", __func__, __LINE__, ret);
retval = 0;
goto _out;
} else {
retval = (ssize_t)len;
/* write down! */
if (storagekey_info.buffer != NULL) {
buf = kmalloc(storagekey_info.size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
memcpy(buf, storagekey_info.buffer,
storagekey_info.size);
if (store_key_write)
ret = store_key_write(buf,
storagekey_info.size,
&actual_length);
if (ret) {
pr_err("%s() %d, store_key_write fail\n",
__func__, __LINE__);
retval = 0;
}
kfree(buf);
}
}
_out:
return retval;
}
/**
* 7. get the hash value of programmed secure key | 32bytes length, sha256
* return success 0, fail -1
*/
static int32_t _amlkey_hash_4_secure(const uint8_t *name, uint8_t *hash)
{
int32_t ret = 0;
ret = secure_storage_verify((uint8_t *)name, hash);
return ret;
}
/**
* 7. del key by name
* return success 0, fail -1
*/
int32_t _amlkey_del(const uint8_t *name)
{
int32_t ret = 0;
uint32_t actual_length;
if ((ret == 0) && (storagekey_info.buffer != NULL)) {
/* flush back */
if (store_key_write)
ret = store_key_write(storagekey_info.buffer,
storagekey_info.size, &actual_length);
if (ret) {
pr_err("%s() %d, store_key_write fail\n",
__func__,
__LINE__);
}
} else {
pr_err("%s() %d, remove key fail\n",
__func__,
__LINE__);
}
return ret;
}
#define DEF_NORMAL_BLOCK_SIZE (256 * 1024)
static DEFINE_MUTEX(normalkey_lock);
static u32 normal_blksz = DEF_NORMAL_BLOCK_SIZE;
static u32 normal_flashsize = DEF_NORMAL_BLOCK_SIZE;
static u8 *normal_block;
static s32 _amlkey_init_normal(u8 *seed, u32 len, int encrypt_type)
{
int ret;
if (!normal_block)
return -1;
#ifndef OTHER_METHOD_CALL
ret = store_operation_init();
if (ret < 0) {
pr_err(" %s store_operation_init fail!\n", __func__);
return ret;
}
#endif
if (!store_key_read) {
pr_err("no storage found\n");
return -1;
}
if (normalkey_init())
return -1;
mutex_lock(&normalkey_lock);
ret = store_key_read(normal_block,
normal_blksz,
&normal_flashsize);
if (ret) {
pr_err("read storage fail\n");
goto finish;
}
ret = normalkey_readfromblock(normal_block, normal_flashsize);
if (ret) {
pr_err("init block key fail\n");
goto finish;
}
ret = 0;
finish:
if (ret)
normalkey_deinit();
mutex_unlock(&normalkey_lock);
return ret;
}
static u32 _amlkey_exist_normal(const u8 *name)
{
struct storage_object *obj;
mutex_lock(&normalkey_lock);
obj = normalkey_get(name);
mutex_unlock(&normalkey_lock);
return !!obj;
}
static u32 _amlkey_get_attr_normal(const u8 *name)
{
u32 attr = 0;
struct storage_object *obj;
mutex_lock(&normalkey_lock);
obj = normalkey_get(name);
if (obj)
attr = obj->attribute;
mutex_unlock(&normalkey_lock);
return attr;
}
static unsigned int _amlkey_size_normal(const u8 *name)
{
unsigned int size = 0;
struct storage_object *obj;
mutex_lock(&normalkey_lock);
obj = normalkey_get(name);
if (obj)
size = obj->datasize;
mutex_unlock(&normalkey_lock);
return size;
}
static unsigned int _amlkey_read_normal(const u8 *name, u8 *buffer, u32 len)
{
unsigned int size = 0;
struct storage_object *obj;
mutex_lock(&normalkey_lock);
obj = normalkey_get(name);
if (obj && len >= obj->datasize) {
size = obj->datasize;
memcpy(buffer, obj->dataptr, size);
}
mutex_unlock(&normalkey_lock);
return size;
}
static ssize_t _amlkey_write_normal(const u8 *name, u8 *buffer,
u32 len, u32 attr)
{
int ret;
u32 wrtsz = 0;
if (attr & OBJ_ATTR_SECURE) {
pr_err("can't write secure key\n");
return 0;
}
if (!store_key_write) {
pr_err("no storage found\n");
return 0;
}
mutex_lock(&normalkey_lock);
ret = normalkey_add(name, buffer, len, attr);
if (ret) {
pr_err("write key fail\n");
ret = 0;
goto unlock;
}
ret = normalkey_writetoblock(normal_block, normal_flashsize);
if (ret) {
pr_err("write block fail\n");
ret = 0;
goto unlock;
}
ret = store_key_write(normal_block,
normal_flashsize,
&wrtsz);
if (ret) {
pr_err("write storage fail\n");
ret = 0;
goto unlock;
}
ret = len;
unlock:
mutex_unlock(&normalkey_lock);
return ret;
}
static s32 _amlkey_hash_normal(const u8 *name, u8 *hash)
{
int ret = -1;
struct storage_object *obj;
mutex_lock(&normalkey_lock);
obj = normalkey_get(name);
if (obj) {
ret = 0;
memcpy(hash, obj->hashptr, 32);
}
mutex_unlock(&normalkey_lock);
return ret;
}
int normal_key_init(struct platform_device *pdev)
{
u32 blksz;
int ret;
ret = device_property_read_u32(&pdev->dev, "blocksize", &blksz);
if (!ret && blksz && PAGE_ALIGNED(blksz)) {
normal_blksz = blksz;
pr_info("block size from config: %x\n", blksz);
}
normal_block = kmalloc(normal_blksz, GFP_KERNEL);
if (!normal_block)
return -1;
return 0;
}
enum amlkey_if_type {
IFTYPE_SECURE_STORAGE,
IFTYPE_NORMAL_STORAGE,
IFTYPE_MAX
};
struct amlkey_if amlkey_ifs[] = {
[IFTYPE_SECURE_STORAGE] = {
.init = _amlkey_init_gen,
.exsit = _amlkey_isexsit,
.get_attr = _amlkey_get_attr,
.size = _amlkey_size,
.read = _amlkey_read,
.write = _amlkey_write,
.hash = _amlkey_hash_4_secure,
},
[IFTYPE_NORMAL_STORAGE] = {
.init = _amlkey_init_normal,
.exsit = _amlkey_exist_normal,
.get_attr = _amlkey_get_attr_normal,
.size = _amlkey_size_normal,
.read = _amlkey_read_normal,
.write = _amlkey_write_normal,
.hash = _amlkey_hash_normal,
}
};
struct amlkey_if *amlkey_if = &amlkey_ifs[IFTYPE_SECURE_STORAGE];
int amlkey_if_init(struct platform_device *pdev, int secure)
{
int ret = 0;
if (secure) {
amlkey_if = &amlkey_ifs[IFTYPE_SECURE_STORAGE];
return ret;
}
pr_info("normal key used!\n");
ret = normal_key_init(pdev);
amlkey_if = &amlkey_ifs[IFTYPE_NORMAL_STORAGE];
return ret;
}