blob: 7f85a3f0da7b2e2f00e6576a91124db219966da1 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2023 ASR Micro Limited
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#ifdef CONFIG_TEE
#include <linux/tee_drv.h>
#endif
#include <uapi/linux/geu_ioctl.h>
#include "asr-fuse-optee.h"
#include "asr-geu-optee.h"
static struct teec_uuid pta_fuse_uuid = ASR_FUSE_ACCESS_UUID;
static int asr_fuse_read_sim_lock(int *sim_lock)
{
return asrgeu_optee_acquire_ta_buff(&pta_fuse_uuid, CMD_FUSE_READ_SIM_LOCK, sim_lock, 4, 0);
}
static int asr_fuse_burn_sim_lock(void)
{
int sim_lock = 0;
asrgeu_optee_acquire_ta_buff(&pta_fuse_uuid, CMD_FUSE_BURN_SIM_LOCK, &sim_lock, 4, 0);
return sim_lock;
}
static int asr_fuse_get_trusted_boot_mode(int *tb_mode)
{
return asrgeu_optee_acquire_ta_buff(&pta_fuse_uuid, CMD_FUSE_READ_TRUSTED_MODE, tb_mode, 4, 0);
}
static int asr_fuse_read_oem_key_hash(uint32_t *key_hash)
{
return asrgeu_optee_acquire_ta_buff(&pta_fuse_uuid, CMD_FUSE_READ_OEM_KEY_HASH, key_hash, 32, 0);
}
static int asr_fuse_read_life_cycle(uint32_t *life_cycle)
{
return asrgeu_optee_acquire_ta_buff(&pta_fuse_uuid, CMD_FUSE_READ_LIFE_CYCLE, life_cycle, 2, 0);
}
static int asr_fuse_read_oem_uid(uint32_t *uid)
{
return asrgeu_optee_acquire_ta_buff(&pta_fuse_uuid, CMD_FUSE_READ_OEM_UUID, uid, 8, 0);
}
static DEFINE_MUTEX(sim_lock_mutex);
static DEFINE_MUTEX(tb_mode_mutex);
static ssize_t sim_lock_read(struct device *dev,
struct device_attribute *attr, char *buf)
{
int len = 0;
unsigned int sim_lock;
mutex_lock(&sim_lock_mutex);
asr_fuse_read_sim_lock(&sim_lock);
len = sprintf(buf, "%x\n", sim_lock);
mutex_unlock(&sim_lock_mutex);
return (ssize_t)len;
}
static ssize_t sim_lock_write(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int ret, enable, sim_lock;
ret = sscanf(buf, "%d", &enable);
if(ret != 1 || enable != 1) {
return size;
}
mutex_lock(&sim_lock_mutex);
sim_lock = asr_fuse_burn_sim_lock();
if (!sim_lock)
dev_info(dev, "burn sim lock fails\n");
else
dev_info(dev, "burn sim lock done\n");
mutex_unlock(&sim_lock_mutex);
return size;
}
static ssize_t tb_mode_read(struct device *dev,
struct device_attribute *attr, char *buf)
{
int len = 0, i;
int tb_mode;
unsigned int fused_key_hash[8];
mutex_lock(&tb_mode_mutex);
asr_fuse_get_trusted_boot_mode(&tb_mode);
asr_fuse_read_oem_key_hash(fused_key_hash);
len = sprintf(buf, "Trusted boot mode: %d\n", tb_mode);
len += sprintf(buf+len, "OEM Key Hash(High --> Low):\n");
for (i = 7; i >= 0; i--)
len += sprintf(buf+len, "%08x ", fused_key_hash[i]);
len += sprintf(buf+len, "\n");
mutex_unlock(&tb_mode_mutex);
return (ssize_t)len;
}
static DEVICE_ATTR(simlock, S_IRUGO | S_IWUSR, sim_lock_read, sim_lock_write);
static DEVICE_ATTR(tb_mode, S_IRUGO, tb_mode_read, NULL);
static struct attribute *fuse_dev_attrs[] = {
&dev_attr_simlock.attr,
&dev_attr_tb_mode.attr,
NULL
};
ATTRIBUTE_GROUPS(fuse_dev);
static int asr_fuse_iotcl_read_key_hash(void __user *key_hash, unsigned int len)
{
unsigned int oem_key_hash[8];
if (!access_ok(key_hash, len))
return -EFAULT;
asr_fuse_read_oem_key_hash(oem_key_hash);
if (copy_to_user(key_hash, oem_key_hash, len)) {
return -EFAULT;
}
return 0;
}
static int asr_fuse_iotcl_read_life_cycle(void __user *life_cyle, unsigned int len)
{
unsigned int asr_life_cyle[2];
if (!access_ok(life_cyle, 2 * sizeof(unsigned int)))
return -EFAULT;
asr_fuse_read_life_cycle(asr_life_cyle);
if (copy_to_user(life_cyle, asr_life_cyle, sizeof(unsigned int) * 2)) {
return -EFAULT;
}
return 0;
}
static int asr_fuse_iotcl_read_oem_uid(void __user *oem_uid_hi, void __user *oem_uid_lo)
{
unsigned int oem_uid[2];
if (!access_ok(oem_uid_hi, sizeof(unsigned int)) ||
!access_ok(oem_uid_lo, sizeof(unsigned int)))
return -EFAULT;
asr_fuse_read_oem_uid(oem_uid);
if (copy_to_user(oem_uid_hi, &oem_uid[0], sizeof(unsigned int)))
return -EFAULT;
if (copy_to_user(oem_uid_lo, &oem_uid[1], sizeof(unsigned int)))
return -EFAULT;
return 0;
}
static int asr_fuse_iotcl_burn_simlock(void __user *sim_lock)
{
int simlock = 0;
simlock = asr_fuse_burn_sim_lock();
if (simlock == 0) {
pr_err("asr fuse: burn sim lock fails\n");
return -EFAULT;
} else {
if(copy_to_user(sim_lock, (void *)(&simlock), 4)) {
pr_err("asr fuse: get sim lock error\n");
return -EFAULT;
}
}
return 0;
}
static int asr_fuse_iotcl_get_simlock(void __user *sim_lock)
{
int simlock;
asr_fuse_read_sim_lock(&simlock);
if(copy_to_user(sim_lock, (void *)(&simlock), 4)) {
pr_err("asr fuse: get sim lock error\n");
return -EFAULT;
}
return 0;
}
static int asr_fuse_iotcl_get_trust_mode(void __user *tb_mode)
{
int trusted_mode;
asr_fuse_get_trusted_boot_mode(&trusted_mode);
if(copy_to_user(tb_mode, (void *)(&trusted_mode), 4)) {
pr_err("asr fuse: get trusted boot error\n");
return -EFAULT;
}
return 0;
}
static long asr_fuse_ioctl(struct file *file, u_int cmd, u_long arg)
{
int ret = 0;
struct geu_arg geu_arg;
if (copy_from_user(&geu_arg, (void __user *)arg, sizeof(geu_arg)))
return -EFAULT;
switch (cmd) {
case GEU_READ_OEMHASHKEY:
ret = asr_fuse_iotcl_read_key_hash((void __user *)geu_arg.arg0,
geu_arg.arg1);
break;
case GEU_READ_LIFECYCLE:
ret = asr_fuse_iotcl_read_life_cycle((void __user *)geu_arg.arg0,
geu_arg.arg1);
break;
case GEU_READ_OEM_UUID:
ret = asr_fuse_iotcl_read_oem_uid((void __user *)geu_arg.arg0,
(void __user *)geu_arg.arg1);
break;
case GEU_BURN_SIM_LOCK:
ret = asr_fuse_iotcl_burn_simlock((void __user *)arg);
break;
case GEU_GET_SIM_LOCK:
ret = asr_fuse_iotcl_get_simlock((void __user *)arg);
break;
case GEU_GET_TRUST_MODE:
ret = asr_fuse_iotcl_get_trust_mode((void __user *)arg);
break;
default:
pr_err("asr fuse: iotcl invald command %x\n", cmd);
ret = -EINVAL;
}
return ret;
}
static int asr_fuse_open(struct inode *inode, struct file *file)
{
return 0;
}
static int asr_fuse_close(struct inode *inode, struct file *file)
{
return 0;
}
static const struct file_operations asr_fuse_fops = {
.owner = THIS_MODULE,
.open = asr_fuse_open,
.release = asr_fuse_close,
.unlocked_ioctl = asr_fuse_ioctl,
};
int asr_geu_fuse_register(struct asr_geu_dev *geu_dd)
{
int ret = 0;
struct asr_geu_fuse *fuse;
struct miscdevice *misc;
struct device *dev = geu_dd->dev;
fuse = devm_kzalloc(dev, sizeof(struct asr_geu_fuse), GFP_KERNEL);
if (!fuse)
return -ENOMEM;
geu_dd->asr_fuse = fuse;
misc = &fuse->fuse_misc;
/* register the device */
misc->name = "geu"; /* to be compatiable with old apps */
misc->minor = MISC_DYNAMIC_MINOR;
misc->fops = &asr_fuse_fops;
misc->this_device = NULL;
misc->groups = fuse_dev_groups;
ret = misc_register(misc);
if (ret < 0) {
dev_err(dev,
"unable to register device node /dev/geu\n");
devm_kfree(dev, fuse);
return ret;
}
return 0;
}
int asr_geu_fuse_unregister(struct asr_geu_dev *geu_dd)
{
struct device *dev = geu_dd->dev;
struct asr_geu_fuse *fuse = geu_dd->asr_fuse;
misc_deregister(&fuse->fuse_misc);
devm_kfree(dev, fuse);
return 0;
}
MODULE_DESCRIPTION("ASR Fuse Read/Write driver with optee-os.");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Yu Zhang");