blob: f9cfdceaee7efcaa8c2247def6a6c922587afbc3 [file] [log] [blame]
/*
* drivers/amlogic/secure_monitor/flash_secure.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.
*
*/
#include <linux/cdev.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <linux/crc32.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/kthread.h>
#include <asm/irqflags.h>
#include <linux/errno.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/libfdt_env.h>
#include <linux/of_reserved_mem.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/dma-contiguous.h>
#include <linux/amlogic/meson-secure.h>
#include <asm/compiler.h>
#include "flash_secure.h"
#define SECURE_MONITOR_MODULE_NAME "secure_monitor"
#define SECURE_MONITOR_DRIVER_NAME "secure_monitor"
#define SECURE_MONITOR_DEVICE_NAME "secure_monitor"
#define SECURE_MONITOR_CLASS_NAME "secure_monitor"
static uint32_t sm_share_mem_addr;
static uint32_t sm_share_mem_size;
/*
* SHARE MEM:
* Communicate Head: 1KB
* communicate Head lock mutex location: the last 4B of HEAD; inited by secureOS
* Data: 128KB
*/
#define SHARE_MEM_HEAD_OFFSET 0x0
#define SHARE_MEM_DATA_OFFSET 0x400
#define SHARE_MEM_PHY_SIZE 0x20400
#define FLASH_BUF_SIZE 0x20000
#define SHARE_MEM_CMD_FREE 0x00000000
#define SHARE_MEM_CMD_WRITE 0x00000001
struct __NS_SHARE_MEM_HEAD {
unsigned int cmdlock;
unsigned int cmd;
unsigned int state;
/* can store input data position in NS_SHARE_MEM_CONTENT */
unsigned int input;
unsigned int inlen;
unsigned int output;
unsigned int outlen;
};
struct secure_monitor_arg {
unsigned char *pfbuf;
unsigned char *psbuf;
};
static struct secure_monitor_arg secure_monitor_buf;
static struct task_struct *secure_task;
static int secure_writer_monitor(void *arg);
static int secure_monitor_start(void)
{
int ret = 0;
sm_share_mem_addr = meson_secure_mem_flash_start();
sm_share_mem_size = meson_secure_mem_flash_size();
pr_info("%s:%d\n", __func__, __LINE__);
pr_info("sm share mem start: 0x%x, size: 0x%x\r\n",
sm_share_mem_addr, sm_share_mem_size);
secure_monitor_buf.pfbuf = kmalloc(FLASH_BUF_SIZE, GFP_KERNEL);
if (!secure_monitor_buf.pfbuf) {
pr_err("nandbuf create fail!\n");
ret = -ENOMEM;
goto flash_monitor_probe_exit;
}
secure_monitor_buf.psbuf = ioremap_cached(
sm_share_mem_addr,
sm_share_mem_size);
if (!secure_monitor_buf.psbuf) {
pr_err("ioremap share memory fail\n");
ret = -ENOMEM;
goto flash_monitor_probe_exit1;
}
secure_task = kthread_run(secure_writer_monitor,
(void *)(&secure_monitor_buf), "secure_flash");
if (IS_ERR_OR_NULL(secure_task)) {
pr_err("create secure task failed\n");
ret = -ENODEV;
goto flash_monitor_probe_exit2;
}
goto flash_monitor_probe_exit;
flash_monitor_probe_exit2:
if (secure_monitor_buf.psbuf)
iounmap(secure_monitor_buf.psbuf);
secure_monitor_buf.psbuf = NULL;
flash_monitor_probe_exit1:
kfree(secure_monitor_buf.pfbuf);
secure_monitor_buf.pfbuf = NULL;
flash_monitor_probe_exit:
return ret;
}
void write_to_flash(unsigned char *psrc, unsigned int size)
{
#ifdef CONFIG_AMLOGIC_M8B_NAND
int error = 0;
pr_info("%s %d save secure here\n", __func__, __LINE__);
error = secure_storage_nand_write(psrc, size);
if (error)
pr_err("save secure failed\n");
pr_info("///////////////////////////////////////save secure success//////////////////////////////////\n");
return;
#endif
#ifdef CONFIG_SPI_NOR_SECURE_STORAGE
unsigned char *secure_ptr = psrc;
int error = 0;
pr_info("%s %d save secure here\n", __func__, __LINE__);
error = secure_storage_spi_write(secure_ptr, size);
if (error)
pr_err("save secure failed\n");
pr_info("///////////////////////////////////////save secure success//////////////////////////////////\n");
return;
#endif
#ifdef CONFIG_EMMC_SECURE_STORAGE
unsigned char *secure_ptr = psrc;
int error = 0;
pr_info("%s %d save secure here\n", __func__, __LINE__);
error = mmc_secure_storage_ops(secure_ptr, size, 1);
if (error)
pr_err("save secure failed\n");
pr_info("///////////////////////////////////////save secure success//////////////////////////////////\n");
return;
#endif
}
static int secure_writer_monitor(void *arg)
{
struct secure_monitor_arg *parg = (struct secure_monitor_arg *)arg;
unsigned char *pfbuf = parg->pfbuf;
unsigned long flags;
struct __NS_SHARE_MEM_HEAD *pshead = (struct __NS_SHARE_MEM_HEAD *)
(parg->psbuf+SHARE_MEM_HEAD_OFFSET);
unsigned char *psdata = parg->psbuf + SHARE_MEM_DATA_OFFSET;
bool w_busy = false;
while (1) {
if (w_busy) {
write_to_flash(pfbuf, FLASH_BUF_SIZE);
w_busy = false;
}
flags = arch_local_irq_save();
if (lock_mutex_try(&(pshead->cmdlock))) {
if (pshead->cmd == SHARE_MEM_CMD_WRITE) {
memcpy(pfbuf, psdata, FLASH_BUF_SIZE);
pshead->cmd = SHARE_MEM_CMD_FREE;
w_busy = true;
pr_info("************kernel detect write flag*****\n");
}
unlock_mutex(&(pshead->cmdlock));
}
arch_local_irq_restore(flags);
if (!w_busy) {
if (kthread_should_stop())
break;
msleep(200);
}
}
return 0;
}
static int __init secure_monitor_init(void)
{
int ret = -1;
pr_info("%s:%d\n", __func__, __LINE__);
if (meson_secure_enabled()) {
ret = secure_monitor_start();
if (ret != 0) {
pr_err("fail to register sm drv err:%d\n", ret);
return -ENODEV;
}
return ret;
} else
return 0;
}
static void __exit secure_monitor_exit(void)
{
pr_info("**************flash_secure_remove start!\n");
if (secure_task) {
kthread_stop(secure_task);
secure_task = NULL;
}
if (secure_monitor_buf.psbuf)
iounmap(secure_monitor_buf.psbuf);
secure_monitor_buf.psbuf = NULL;
kfree(secure_monitor_buf.pfbuf);
secure_monitor_buf.pfbuf = NULL;
pr_info("**************flash_secure_remove end!\n");
}
module_init(secure_monitor_init);
module_exit(secure_monitor_exit);
MODULE_DESCRIPTION("AMLOGIC secure monitor driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yan Wang <yan.wang@amlogic.com>");