oleavr-rgl-a500-mini-linux-.../drivers/misc/sst_storage/sst_storage.c
Ole André Vadla Ravnås 169c65d57e Initial commit
2022-05-07 01:01:45 +02:00

543 lines
11 KiB
C

/* secure no-voliate memory(nand/emmc) driver for sunxi platform
*
* Copyright (C) 2014 Allwinner Ltd.
*
* Author:
* Ryan Chen <yanjianbo@allwinnertech.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/crc32.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/kdev_t.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/miscdevice.h>
#include <linux/kthread.h>
#include <linux/workqueue.h>
#include "sst_storage.h"
#ifdef OEM_STORE_IN_FS
#define EMMC_SEC_STORE "/data/oem_secure_store"
#endif
#define SEC_BLK_SIZE (4096)
#define MAX_SECURE_STORAGE_MAX_ITEM (32)
static unsigned int secure_storage_inited;
/*
* EMMC parameters
*/
#define SDMMC_SECTOR_SIZE (512)
#define SDMMC_SECURE_STORAGE_START_ADD (6*1024*1024/512)
#define SDMMC_ITEM_SIZE (4*1024/512)
static char *sd_oem_path = "/dev/block/mmcblk0";
/*
* Nand parameters
*/
static char *nand_oem_path = "/dev/block/by-name/bootloader";
static struct secblc_op_t secblk_op;
static fcry_pt nfcr;
/*
* secure storage map
*
* section 0:
* name1:length1
* name2:length2
* ...
* section 1:
* data1 ( name1 data )
* section 2 :
* data2 ( name2 data )
* ...
*/
#define FLASH_TYPE_NAND 0
#define FLASH_TYPE_SD1 1
#define FLASH_TYPE_SD2 2
#define FLASH_TYPE_UNKNOW -1
static int flash_boot_type = FLASH_TYPE_UNKNOW;
#define SST_STORAGE_READ _IO('V', 1)
#define SST_STORAGE_WRITE _IO('V', 2)
extern char *saved_command_line;
void sunxi_dump(void *addr, unsigned int size)
{
int j;
char *buf = (char *)addr;
for (j = 0; j < size; j++) {
printk("%02x ", buf[j] & 0xff);
if (j%15 == 0 && j) {
printk("\n");
}
}
printk("\n");
return ;
}
int _sst_user_ioctl(char *filename, int ioctl, void *param)
{
struct file *fd;
int ret = -1;
mm_segment_t old_fs = get_fs();
if (!filename) {
pr_err("- filename NULL\n");
return -1;
}
old_fs = get_fs();
set_fs(KERNEL_DS);
fd = filp_open(filename, O_WRONLY|O_CREAT, 0666);
if (IS_ERR(fd)) {
pr_err(" -file open fail\n");
return -1;
}
do {
if ((fd->f_op == NULL) || (fd->f_op->unlocked_ioctl == NULL)) {
pr_info(" -file can't to write!!\n");
break;
}
ret = fd->f_op->unlocked_ioctl(
fd,
ioctl,
(unsigned long)(param));
} while (false);
vfs_fsync(fd, 0);
filp_close(fd, NULL);
set_fs(old_fs);
return ret;
}
int _sst_user_read(char *filename, char *buf, ssize_t len, int offset)
{
struct file *fd;
int retLen = -1;
mm_segment_t old_fs;
if (!filename || !buf) {
pr_err("- filename/buf NULL\n");
return -1;
}
old_fs = get_fs();
set_fs(KERNEL_DS);
fd = filp_open(filename, O_RDONLY, 0);
if (IS_ERR(fd)) {
pr_err(" -file open fail\n");
return -1;
}
do {
if ((fd->f_op == NULL) || (fd->f_op->read == NULL)) {
pr_err(" -file can't to open!!\n");
break;
}
if (fd->f_pos != offset) {
if (fd->f_op->llseek) {
if (fd->f_op->llseek(fd, offset, 0) != offset) {
pr_err(" -failed to seek!!\n");
break;
}
} else {
fd->f_pos = offset;
}
}
retLen = fd->f_op->read(fd,
buf,
len,
&fd->f_pos);
} while (false);
filp_close(fd, NULL);
set_fs(old_fs);
return retLen;
}
int _sst_user_write(char *filename, char *buf, ssize_t len, int offset)
{
struct file *fd;
int retLen = -1;
mm_segment_t old_fs = get_fs();
pr_info("Write to %s\n", filename);
if (!filename || !buf) {
pr_err("- filename/buf NULL\n");
return -1;
}
old_fs = get_fs();
set_fs(KERNEL_DS);
fd = filp_open(filename, O_WRONLY|O_CREAT, 0666);
if (IS_ERR(fd)) {
pr_err(" -file open fail %s \n", filename);
return -1;
}
do {
if ((fd->f_op == NULL) || (fd->f_op->write == NULL)) {
pr_err(" -file can't to write!!\n");
break;
}
if (fd->f_pos != offset) {
if (fd->f_op->llseek) {
if (fd->f_op->llseek(fd, offset, 0) != offset) {
pr_err(" -failed to seek!!\n");
break;
}
} else {
fd->f_pos = offset;
}
}
retLen = fd->f_op->write(fd,
buf,
len,
&fd->f_pos);
} while (false);
vfs_fsync(fd, 0);
filp_close(fd, NULL);
set_fs(old_fs);
pr_info("Write %x to %s done\n", retLen, filename);
return retLen;
}
static int get_para_from_cmdline(const char *cmdline, const char *name, char *value)
{
char *value_p = value;
if (!cmdline || !name || !value) {
return -1;
}
for (; *cmdline != 0;) {
if (*cmdline++ == ' ') {
if (0 == strncmp(cmdline, name, strlen(name))) {
cmdline += strlen(name);
if (*cmdline++ != '=') {
continue;
}
while (*cmdline != 0 && *cmdline != ' ') {
*value_p++ = *cmdline++;
}
return value_p - value;
}
}
}
return 0;
}
static int get_flash_type(void)
{
char ctype[16];
memset(ctype, 0, 16);
if (get_para_from_cmdline(saved_command_line , "boot_type",
ctype) <= 0) {
pr_err("Get boot type cmd line fail\n");
return -1;
}
flash_boot_type = simple_strtol(ctype, NULL, 10);
pr_info("Boot type %d\n", flash_boot_type);
return 0;
}
/*nand secure storage read/write*/
static int _nand_read(int id, char *buf, ssize_t len)
{
if (!buf) {
pr_err("-buf NULL\n");
return -1;
}
if (id > MAX_SECURE_STORAGE_MAX_ITEM) {
pr_err("out of range id %x\n", id);
return -1;
}
secblk_op.item = id;
secblk_op.buf = buf;
secblk_op.len = len;
return _sst_user_ioctl(nand_oem_path, SECBLK_READ, &secblk_op);
}
static int _nand_write(int id, char *buf, ssize_t len)
{
if (!buf) {
pr_err("- buf NULL\n");
return -1;
}
if (id > MAX_SECURE_STORAGE_MAX_ITEM) {
pr_err("out of range id %x\n", id);
return -1;
}
secblk_op.item = id;
secblk_op.buf = buf;
secblk_op.len = len;
return _sst_user_ioctl(nand_oem_path, SECBLK_WRITE, &secblk_op);
}
/*emmc secure storage read/write*/
static int _sd_read(char *buf, int len, int offset)
{
int ret ;
ret = _sst_user_read(sd_oem_path, buf, len, offset);
if (ret != len) {
pr_err("_sst_user_read: read request len 0x%x, actual read 0x%x\n",
len, ret);
return -1;
}
return 0 ;
}
static int _sd_write(char *buf, int len, int offset)
{
int ret;
ret = _sst_user_write(sd_oem_path, buf, len, offset);
if (ret != len) {
pr_err("_sst_user_write: write request len 0x%x, actual write 0x%x\n",
len, ret);
return -1;
}
return 0 ;
}
static int nv_write(int id, char *buf, ssize_t len, ssize_t offset)
{
int ret ;
switch (flash_boot_type) {
case FLASH_TYPE_NAND:
ret = _nand_write(id, buf, len);
break;
case FLASH_TYPE_SD1:
case FLASH_TYPE_SD2:
ret = _sd_write(buf, len, offset);
break;
default:
pr_err("Unknown no-volatile device\n");
ret = -1 ;
break;
}
return ret ;
}
static int nv_read(int id, char *buf, ssize_t len, ssize_t offset)
{
int ret ;
switch (flash_boot_type) {
case FLASH_TYPE_NAND:
ret = _nand_read(id, buf, len);
break;
case FLASH_TYPE_SD1:
case FLASH_TYPE_SD2:
ret = _sd_read(buf, len, offset);
break;
default:
pr_err("Unknown no-volatile device\n");
ret = -1 ;
break;
}
return ret ;
}
static int sst_storage_open(struct inode *inode, struct file *file)
{
int ret = 0;
return ret;
}
static int sst_storage_release(struct inode *inode, struct file *file)
{
return 0;
}
static long sst_storage_ioctl(struct file *file, unsigned int ioctl_num,
unsigned long ioctl_param)
{
int err = 0;
mutex_lock(&nfcr->mutex);
if (copy_from_user(&nfcr->key_store, (void __user *)ioctl_param, sizeof(nfcr->key_store))) {
err = -EFAULT;
goto _out;
}
nfcr->cmd = ioctl_num;
#if 0
pr_info("size of struct sst_storage_data %d\n", sizeof(struct sst_storage_data));
pr_info("sst_storage_ioctl: ioctl_num=%d\n", ioctl_num);
pr_info("nfcr = %p\n", nfcr);
pr_info("id = %d\n", nfcr->key_store.id);
pr_info("len = %d\n", nfcr->key_store.len);
pr_info("offset = %d\n", nfcr->key_store.offset);
#endif
switch (ioctl_num) {
case SST_STORAGE_READ:
schedule_work(&nfcr->work);
wait_for_completion(&nfcr->work_end);
/*sunxi_dump(nfcr->temp_data, 50);*/
err = nfcr->ret;
if (!err) {
if (copy_to_user((void __user *)(unsigned long)nfcr->key_store.buf, nfcr->temp_data, nfcr->key_store.len)) {
err = -EFAULT;
pr_err("copy_to_user: err:%d\n", err);
goto _out;
}
}
break;
case SST_STORAGE_WRITE:
if (copy_from_user(nfcr->temp_data, (void __user *)(unsigned long)nfcr->key_store.buf, nfcr->key_store.len)) {
err = -EFAULT;
pr_err("copy_from_user: err:%d\n", err);
goto _out;
}
schedule_work(&nfcr->work);
wait_for_completion(&nfcr->work_end);
err = nfcr->ret;
break;
default:
pr_err("sst_storage_ioctl: un supported cmd:%d\n", ioctl_num);
break;
}
_out:
memset(nfcr->temp_data, 0, SEC_BLK_SIZE);
memset(&nfcr->key_store, 0, sizeof(nfcr->key_store));
nfcr->cmd = -1;
mutex_unlock(&nfcr->mutex);
return err;
}
static const struct file_operations sst_storage_ops = {
.owner = THIS_MODULE,
.open = sst_storage_open,
.release = sst_storage_release,
.unlocked_ioctl = sst_storage_ioctl,
.compat_ioctl = sst_storage_ioctl,
};
struct miscdevice sst_storage_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "sst_storage",
.fops = &sst_storage_ops,
};
static void sst_storage_work(struct work_struct *data)
{
fcry_pt fcpt = container_of(data, struct fcrypt, work);
switch (fcpt->cmd) {
case SST_STORAGE_READ:
fcpt->ret = nv_read(fcpt->key_store.id, fcpt->temp_data, fcpt->key_store.len, fcpt->key_store.offset);
break;
case SST_STORAGE_WRITE:
fcpt->ret = nv_write(fcpt->key_store.id, fcpt->temp_data, fcpt->key_store.len, fcpt->key_store.offset);
break;
default:
fcpt->ret = -1;
pr_err("sst_storage_work: un supported cmd:%d\n", fcpt->cmd);
break;
}
if ((fcpt->cmd == SST_STORAGE_READ) || (fcpt->cmd == SST_STORAGE_WRITE))
complete(&fcpt->work_end);
}
static void __exit sunxi_secure_storage_exit(void)
{
int ret;
pr_debug("sunxi secure storage driver exit\n");
ret = misc_deregister(&sst_storage_device);
if (ret) {
pr_err("%s: cannot deregister miscdev.(return value-%d)\n", __func__, ret);
}
kfree(nfcr->temp_data);
kfree(nfcr);
}
static int __init sunxi_secure_storage_init(void)
{
int ret;
if (!secure_storage_inited) {
get_flash_type();
}
secure_storage_inited = 1;
ret = misc_register(&sst_storage_device);
if (ret) {
pr_err("%s: cannot deregister miscdev.(return value-%d)\n", __func__, ret);
return ret;
}
nfcr = kmalloc(sizeof(*nfcr), GFP_KERNEL);
if (!nfcr) {
pr_err(" - Malloc failed\n");
return -1;
}
memset(nfcr, 0, sizeof(*nfcr));
nfcr->temp_data = kmalloc(SEC_BLK_SIZE, GFP_KERNEL);
if (!nfcr->temp_data) {
pr_err("sst_storage_ioctl: error to kmalloc\n");
return -1;
}
memset(nfcr->temp_data, 0, SEC_BLK_SIZE);
INIT_WORK(&nfcr->work, sst_storage_work);
init_completion(&nfcr->work_end);
mutex_init(&nfcr->mutex);
return 0;
}
module_init(sunxi_secure_storage_init);
module_exit(sunxi_secure_storage_exit);
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("yanjianbo");
MODULE_DESCRIPTION ("secure storage");