/* * Based on drivers/char/sunxi-sysinfo/sunxi-sysinfo.c * * Copyright (C) 2015 Allwinnertech Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include "sunxi-sysinfo-user.h" static s8 key_name[SUNXI_KEY_NAME_LEN]; static int soc_info_open(struct inode *inode, struct file *file) { return 0; } static int soc_info_release(struct inode *inode, struct file *file) { return 0; } static long soc_info_ioctl(struct file *file, unsigned int ioctl_num, unsigned long ioctl_param) { int ret = 0; unsigned int customer_id = 0; unsigned int mediaid; char id[9] = ""; char rotpk[32] = ""; char sn[24] = ""; pr_debug("IOCTRL cmd: %#x, param: %#lx\n", ioctl_num, ioctl_param); switch (ioctl_num) { case CHECK_SOC_SECURE_ATTR: case CHECK_SECURE_BOOT_ATTR: ret = sunxi_soc_is_secure(); if (ret) pr_debug("soc is secure. return value: %d\n", ret); else pr_debug("soc is normal. return value: %d\n", ret); break; case CHECK_SOC_VERSION: ret = sunxi_get_soc_ver(); pr_debug("soc version:%x\n", ret); break; case CHECK_SOC_BONDING: sunxi_get_soc_chipid_str(id); if (copy_to_user((void __user *)ioctl_param, id, 8)) ret = -EFAULT; pr_debug("soc id:%s\n", id); break; case CHECK_SOC_ROTPK: #ifdef NO_EXPORT_PK_SUPPORT if (copy_from_user(rotpk, (void __user *)ioctl_param, 32)) ret = -EFAULT; ret = sunxi_check_soc_rotpk((void *)rotpk, 32); #else ret = sunxi_get_soc_rotpk((void *)rotpk, 32); if (ret > 0 && copy_to_user((void __user *)ioctl_param, rotpk, 32)) ret = -EFAULT; #endif break; case CHECK_SOC_CUSTOMER_ID: customer_id = sunxi_get_soc_customerid(); if (copy_to_user((void __user *)ioctl_param, &customer_id, 4)) ret = -EFAULT; pr_debug("customer id is :%d\n", customer_id); break; case CHECK_SOC_MEDIA_ID: mediaid = sunxi_get_soc_mediaid(); if (copy_to_user((void __user *)ioctl_param, &mediaid, 4)) ret = -EFAULT; pr_debug("media id is :%d\n", mediaid); break; case SYS_EFUSE_GET_SN: ret = sunxi_get_soc_sn((void *)sn, 24); if (ret > 0 && copy_to_user((void __user *)ioctl_param, sn, 24)) ret = -EFAULT; break; default: pr_err("Unsupported cmd:%d\n", ioctl_num); ret = -EINVAL; break; } return ret; } #ifdef CONFIG_COMPAT static long soc_info_compat_ioctl(struct file *file, unsigned int ioctl, unsigned long param) { return soc_info_ioctl(file, ioctl, param); } #endif static const struct file_operations soc_info_ops = { .owner = THIS_MODULE, .open = soc_info_open, .release = soc_info_release, .unlocked_ioctl = soc_info_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = soc_info_compat_ioctl, #endif }; struct miscdevice soc_info_device = { .minor = MISC_DYNAMIC_MINOR, .name = "sunxi_soc_info", .fops = &soc_info_ops, }; static ssize_t sys_info_show(struct class *class, struct class_attribute *attr, char *buf) { int i, vid; int databuf[4] = {0}; char tmpbuf[129] = {0}; size_t size = 0; /* platform */ sunxi_get_platform(tmpbuf, 129); size += sprintf(buf + size, "sunxi_platform : %s\n", tmpbuf); /* secure */ size += sprintf(buf + size, "sunxi_secure : "); if (sunxi_soc_is_secure()) size += sprintf(buf + size, "%s\n", "secure"); else size += sprintf(buf + size, "%s\n", "normal"); /* chipid */ sunxi_get_soc_chipid((u8 *)databuf); for (i = 0; i < 4; i++) sprintf(tmpbuf + i*8, "%08x", databuf[i]); tmpbuf[128] = 0; size += sprintf(buf + size, "sunxi_chipid : %s\n", tmpbuf); /* serial */ sunxi_get_serial((u8 *)databuf); for (i = 0; i < 4; i++) sprintf(tmpbuf + i*8, "%08x", databuf[i]); tmpbuf[128] = 0; size += sprintf(buf + size, "sunxi_serial : %s\n", tmpbuf); /* chiptype */ sunxi_get_soc_chipid_str(tmpbuf); size += sprintf(buf + size, "sunxi_chiptype : %s\n", tmpbuf); /* socbatch number */ size += sprintf(buf + size, "sunxi_batchno : %#x\n", sunxi_get_soc_ver()&0x0ffff); vid = sunxi_get_soc_customerid(); size += sprintf(buf + size, "sunxi_customerid : %x\n", vid); return size; } static ssize_t key_info_show(struct class *class, struct class_attribute *attr, char *buf) { s32 i, len; u32 *key_data = NULL; size_t size = 0; key_data = vmalloc(256*4); if (key_data == NULL) return -ENOMEM; memset(key_data, 0, 256*4); len = sunxi_efuse_readn_ext(key_name, key_data, 256*4); if (len <= 0) { vfree(key_data); return -EACCES; } size += sprintf(buf + size, "%s", key_name); for (i = 0; i < (len >> 2); i++) { if ((i > 0) && (i % 8 == 0)) size += sprintf(buf + size, "\n"); size += sprintf(buf + size, "%08x ", key_data[i]); } size += sprintf(buf + size, "\n"); vfree(key_data); return size; } static ssize_t key_info_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { if (count >= SUNXI_KEY_NAME_LEN) return -EINVAL; memset(key_name, 0, SUNXI_KEY_NAME_LEN); strncpy(key_name, buf, count); return count; } static struct class_attribute info_class_attrs[] = { __ATTR(sys_info, 0644, sys_info_show, NULL), __ATTR(key_info, 0644, key_info_show, key_info_store), __ATTR_NULL, }; static struct class info_class = { .name = "sunxi_info", .owner = THIS_MODULE, .class_attrs = info_class_attrs, }; static int __init sunxi_sys_info_init(void) { s32 ret = 0; ret = class_register(&info_class); if (ret != 0) return ret; ret = misc_register(&soc_info_device); if (ret != 0) { pr_err("%s: misc_register() failed!(%d)\n", __func__, ret); class_unregister(&info_class); return ret; } return ret; } static void __exit sunxi_sys_info_exit(void) { if (misc_deregister(&soc_info_device)) pr_err("misc_deregister() failed!\n"); class_unregister(&info_class); } module_init(sunxi_sys_info_init); module_exit(sunxi_sys_info_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("xiafeng"); MODULE_DESCRIPTION("sunxi sys info.");