1 /*
2 * Based on drivers/char/sunxi-sysinfo/sunxi-sysinfo.c
3 *
4 * Copyright (C) 2015 Allwinnertech Ltd.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <linux/module.h>
20 #include <linux/printk.h>
21 #include <linux/kernel.h>
22 #include <linux/device.h>
23 #include <linux/vmalloc.h>
24 #include <sunxi-sid.h>
25 #include <linux/uaccess.h>
26 #include <linux/fs.h>
27 #include <linux/miscdevice.h>
28 #include <linux/compat.h>
29
30 #include "sunxi-sysinfo-user.h"
31
32 static s8 key_name[SUNXI_KEY_NAME_LEN];
33
soc_info_open(struct inode * inode,struct file * file)34 static int soc_info_open(struct inode *inode, struct file *file)
35 {
36 return 0;
37 }
38
soc_info_release(struct inode * inode,struct file * file)39 static int soc_info_release(struct inode *inode, struct file *file)
40 {
41 return 0;
42 }
43
soc_info_ioctl(struct file * file,unsigned int ioctl_num,unsigned long ioctl_param)44 static long soc_info_ioctl(struct file *file, unsigned int ioctl_num,
45 unsigned long ioctl_param)
46 {
47 int ret = 0;
48 char id[17] = "";
49
50 memset(id, 0, sizeof(id));
51
52 pr_debug("IOCTRL cmd: %#x, param: %#lx\n", ioctl_num, ioctl_param);
53 switch (ioctl_num) {
54 case CHECK_SOC_SECURE_ATTR:
55 ret = sunxi_soc_is_secure();
56 if (ret)
57 pr_debug("soc is secure. return value: %d\n", ret);
58 else
59 pr_debug("soc is normal. return value: %d\n", ret);
60 break;
61 case CHECK_SOC_VERSION:
62 ret = sunxi_get_soc_ver();
63 pr_debug("soc version:%x\n", ret);
64 break;
65 case CHECK_SOC_BONDING:
66 sunxi_get_soc_chipid_str(id);
67 ret = copy_to_user((void __user *)ioctl_param, id, 8);
68 pr_debug("soc id:%s\n", id);
69 break;
70 case CHECK_SOC_CHIPID:
71 sunxi_get_soc_chipid_str(id);
72 ret = copy_to_user((void __user *)ioctl_param, id, 16);
73 pr_debug("soc chipid:%s\n", id);
74 break;
75 case CHECK_SOC_FT_ZONE:
76 sunxi_get_soc_ft_zone_str(id);
77 ret = copy_to_user((void __user *)ioctl_param, id, 8);
78 pr_debug("ft zone:%s\n", id);
79 break;
80 case CHECK_SOC_ROTPK_STATUS:
81 sunxi_get_soc_rotpk_status_str(id);
82 ret = copy_to_user((void __user *)ioctl_param, id, 8);
83 pr_debug("rotpk status:%s\n", id);
84 break;
85 default:
86 pr_err("Unsupported cmd:%d\n", ioctl_num);
87 ret = -EINVAL;
88 break;
89 }
90 return ret;
91 }
92
93 #ifdef CONFIG_COMPAT
soc_info_compat_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)94 static long soc_info_compat_ioctl(struct file *filp, unsigned int cmd,
95 unsigned long arg)
96 {
97 unsigned long translated_arg = (unsigned long)compat_ptr(arg);
98
99 return soc_info_ioctl(filp, cmd, translated_arg);
100 }
101 #endif
102
103 static const struct file_operations soc_info_ops = {
104 .owner = THIS_MODULE,
105 .open = soc_info_open,
106 .release = soc_info_release,
107 #ifdef CONFIG_COMPAT
108 .compat_ioctl = soc_info_compat_ioctl,
109 #endif
110 .unlocked_ioctl = soc_info_ioctl,
111 };
112
113 struct miscdevice soc_info_device = {
114 .minor = MISC_DYNAMIC_MINOR,
115 .name = "sunxi_soc_info",
116 .fops = &soc_info_ops,
117 };
118
sys_info_show(struct class * class,struct class_attribute * attr,char * buf)119 static ssize_t sys_info_show(struct class *class,
120 struct class_attribute *attr, char *buf)
121 {
122 int i;
123 int databuf[4] = {0};
124 char tmpbuf[129] = {0};
125 size_t size = 0;
126
127 /* platform */
128 sunxi_get_platform(tmpbuf, 129);
129 size += sprintf(buf + size, "sunxi_platform : %s\n", tmpbuf);
130
131 /* secure */
132 size += sprintf(buf + size, "sunxi_secure : ");
133 if (sunxi_soc_is_secure()) {
134 size += sprintf(buf + size, "%s\n", "secure");
135 /* rotpk status */
136 memset(tmpbuf, 0x0, sizeof(tmpbuf));
137 sunxi_get_soc_rotpk_status_str(tmpbuf);
138 size += sprintf(buf + size, "sunxi_rotpk : %s\n", tmpbuf);
139 } else
140 size += sprintf(buf + size, "%s\n", "normal");
141
142 #ifdef CONFIG_SUNXI_QA_TEST
143 /* chipid */
144 sunxi_get_soc_chipid((u8 *)databuf);
145
146 for (i = 0; i < 4; i++)
147 sprintf(tmpbuf + i*8, "%08x", databuf[i]);
148 tmpbuf[128] = 0;
149 size += sprintf(buf + size, "sunxi_chipid : %s\n", tmpbuf);
150 #endif
151 /* serial */
152 sunxi_get_serial((u8 *)databuf);
153 for (i = 0; i < 4; i++)
154 sprintf(tmpbuf + i*8, "%08x", databuf[i]);
155 tmpbuf[128] = 0;
156 size += sprintf(buf + size, "sunxi_serial : %s\n", tmpbuf);
157
158 /* chiptype */
159 sunxi_get_soc_chipid_str(tmpbuf);
160 size += sprintf(buf + size, "sunxi_chiptype : %s\n", tmpbuf);
161
162 /* socbatch number */
163 size += sprintf(buf + size, "sunxi_batchno : %#x\n",
164 sunxi_get_soc_ver());
165
166 return size;
167 }
168
key_info_show(struct class * class,struct class_attribute * attr,char * buf)169 static ssize_t key_info_show(struct class *class,
170 struct class_attribute *attr, char *buf)
171 {
172 s32 i;
173 u32 *key_data = NULL;
174 size_t size = 0;
175
176 key_data = vmalloc(256);
177 if (key_data == NULL)
178 return -ENOMEM;
179
180 memset(key_data, 0, 256*4);
181 sunxi_efuse_readn(key_name, key_data, 256);
182 for (i = 0; i < 256; i++) {
183 if ((i > 0) && (key_data[i] == 0))
184 break;
185 if ((i > 0) && (i % 8 == 0))
186 size += sprintf(buf + size, "\n");
187
188 size += sprintf(buf + size, "%08x ", key_data[i]);
189 }
190 size += sprintf(buf + size, "\n");
191
192 vfree(key_data);
193 return size;
194 }
195
key_info_store(struct class * class,struct class_attribute * attr,const char * buf,size_t count)196 static ssize_t key_info_store(struct class *class, struct class_attribute *attr,
197 const char *buf, size_t count)
198 {
199 if (count >= SUNXI_KEY_NAME_LEN)
200 return -EINVAL;
201
202 memset(key_name, 0, SUNXI_KEY_NAME_LEN);
203 strncpy(key_name, buf, count);
204 return count;
205 }
206
207 static struct class_attribute info_class_attrs[] = {
208 __ATTR(sys_info, 0644, sys_info_show, NULL),
209 __ATTR(key_info, 0644, key_info_show, key_info_store),
210 };
211
212 static struct class info_class = {
213 .name = "sunxi_info",
214 .owner = THIS_MODULE,
215 };
216
sunxi_sys_info_init(void)217 static int __init sunxi_sys_info_init(void)
218 {
219 s32 ret = 0, i;
220
221 ret = class_register(&info_class);
222 if (ret != 0)
223 return ret;
224 /* need some class specific sysfs attributes */
225 for (i = 0; i < ARRAY_SIZE(info_class_attrs); i++) {
226 ret = class_create_file(&info_class, &info_class_attrs[i]);
227 if (ret)
228 goto out_class_create_file_failed;
229 }
230
231 ret = misc_register(&soc_info_device);
232 if (ret != 0) {
233 pr_err("%s: misc_register() failed!(%d)\n", __func__, ret);
234 class_unregister(&info_class);
235 return ret;
236 }
237 return ret;
238 out_class_create_file_failed:
239 class_unregister(&info_class);
240 return ret;
241 }
242
sunxi_sys_info_exit(void)243 static void __exit sunxi_sys_info_exit(void)
244 {
245 misc_deregister(&soc_info_device);
246 class_unregister(&info_class);
247 }
248
249 module_init(sunxi_sys_info_init);
250 module_exit(sunxi_sys_info_exit);
251 MODULE_LICENSE("GPL v2");
252 MODULE_AUTHOR("xiafeng<xiafeng@allwinnertech.com>");
253 MODULE_DESCRIPTION("sunxi sys info.");
254