1 /* 2 * Amlogic Secure Monitor driver 3 * 4 * Copyright (C) 2016 Endless Mobile, Inc. 5 * Author: Carlo Caione <carlo@endlessm.com> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * version 2 as published by the Free Software Foundation. 10 * 11 * You should have received a copy of the GNU General Public License 12 * along with this program. If not, see <http://www.gnu.org/licenses/>. 13 */ 14 15 #define pr_fmt(fmt) "meson-sm: " fmt 16 17 #include <linux/arm-smccc.h> 18 #include <linux/bug.h> 19 #include <linux/io.h> 20 #include <linux/module.h> 21 #include <linux/of.h> 22 #include <linux/of_device.h> 23 #include <linux/platform_device.h> 24 #include <linux/printk.h> 25 #include <linux/types.h> 26 #include <linux/sizes.h> 27 #include <linux/slab.h> 28 29 #include <linux/firmware/meson/meson_sm.h> 30 31 struct meson_sm_cmd { 32 unsigned int index; 33 u32 smc_id; 34 }; 35 #define CMD(d, s) { .index = (d), .smc_id = (s), } 36 37 struct meson_sm_chip { 38 unsigned int shmem_size; 39 u32 cmd_shmem_in_base; 40 u32 cmd_shmem_out_base; 41 struct meson_sm_cmd cmd[]; 42 }; 43 44 struct meson_sm_chip gxbb_chip = { 45 .shmem_size = SZ_4K, 46 .cmd_shmem_in_base = 0x82000020, 47 .cmd_shmem_out_base = 0x82000021, 48 .cmd = { 49 CMD(SM_EFUSE_READ, 0x82000030), 50 CMD(SM_EFUSE_WRITE, 0x82000031), 51 CMD(SM_EFUSE_USER_MAX, 0x82000033), 52 CMD(SM_GET_CHIP_ID, 0x82000044), 53 { /* sentinel */ }, 54 }, 55 }; 56 57 struct meson_sm_firmware { 58 const struct meson_sm_chip *chip; 59 void __iomem *sm_shmem_in_base; 60 void __iomem *sm_shmem_out_base; 61 }; 62 63 static struct meson_sm_firmware fw; 64 65 static u32 meson_sm_get_cmd(const struct meson_sm_chip *chip, 66 unsigned int cmd_index) 67 { 68 const struct meson_sm_cmd *cmd = chip->cmd; 69 70 while (cmd->smc_id && cmd->index != cmd_index) 71 cmd++; 72 73 return cmd->smc_id; 74 } 75 76 static u32 __meson_sm_call(u32 cmd, u32 arg0, u32 arg1, u32 arg2, 77 u32 arg3, u32 arg4) 78 { 79 struct arm_smccc_res res; 80 81 arm_smccc_smc(cmd, arg0, arg1, arg2, arg3, arg4, 0, 0, &res); 82 return res.a0; 83 } 84 85 static void __iomem *meson_sm_map_shmem(u32 cmd_shmem, unsigned int size) 86 { 87 u32 sm_phy_base; 88 89 sm_phy_base = __meson_sm_call(cmd_shmem, 0, 0, 0, 0, 0); 90 if (!sm_phy_base) 91 return 0; 92 93 return ioremap_cache(sm_phy_base, size); 94 } 95 96 /** 97 * meson_sm_call - generic SMC32 call to the secure-monitor 98 * 99 * @cmd_index: Index of the SMC32 function ID 100 * @ret: Returned value 101 * @arg0: SMC32 Argument 0 102 * @arg1: SMC32 Argument 1 103 * @arg2: SMC32 Argument 2 104 * @arg3: SMC32 Argument 3 105 * @arg4: SMC32 Argument 4 106 * 107 * Return: 0 on success, a negative value on error 108 */ 109 int meson_sm_call(unsigned int cmd_index, u32 *ret, u32 arg0, 110 u32 arg1, u32 arg2, u32 arg3, u32 arg4) 111 { 112 u32 cmd, lret; 113 114 if (!fw.chip) 115 return -ENOENT; 116 117 cmd = meson_sm_get_cmd(fw.chip, cmd_index); 118 if (!cmd) 119 return -EINVAL; 120 121 lret = __meson_sm_call(cmd, arg0, arg1, arg2, arg3, arg4); 122 123 if (ret) 124 *ret = lret; 125 126 return 0; 127 } 128 EXPORT_SYMBOL(meson_sm_call); 129 130 /** 131 * meson_sm_call_read - retrieve data from secure-monitor 132 * 133 * @buffer: Buffer to store the retrieved data 134 * @bsize: Size of the buffer 135 * @cmd_index: Index of the SMC32 function ID 136 * @arg0: SMC32 Argument 0 137 * @arg1: SMC32 Argument 1 138 * @arg2: SMC32 Argument 2 139 * @arg3: SMC32 Argument 3 140 * @arg4: SMC32 Argument 4 141 * 142 * Return: size of read data on success, a negative value on error 143 * When 0 is returned there is no guarantee about the amount of 144 * data read and bsize bytes are copied in buffer. 145 */ 146 int meson_sm_call_read(void *buffer, unsigned int bsize, unsigned int cmd_index, 147 u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) 148 { 149 u32 size; 150 int ret; 151 152 if (!fw.chip) 153 return -ENOENT; 154 155 if (!fw.chip->cmd_shmem_out_base) 156 return -EINVAL; 157 158 if (bsize > fw.chip->shmem_size) 159 return -EINVAL; 160 161 if (meson_sm_call(cmd_index, &size, arg0, arg1, arg2, arg3, arg4) < 0) 162 return -EINVAL; 163 164 if (size > bsize) 165 return -EINVAL; 166 167 ret = size; 168 169 if (!size) 170 size = bsize; 171 172 if (buffer) 173 memcpy(buffer, fw.sm_shmem_out_base, size); 174 175 return ret; 176 } 177 EXPORT_SYMBOL(meson_sm_call_read); 178 179 /** 180 * meson_sm_call_write - send data to secure-monitor 181 * 182 * @buffer: Buffer containing data to send 183 * @size: Size of the data to send 184 * @cmd_index: Index of the SMC32 function ID 185 * @arg0: SMC32 Argument 0 186 * @arg1: SMC32 Argument 1 187 * @arg2: SMC32 Argument 2 188 * @arg3: SMC32 Argument 3 189 * @arg4: SMC32 Argument 4 190 * 191 * Return: size of sent data on success, a negative value on error 192 */ 193 int meson_sm_call_write(void *buffer, unsigned int size, unsigned int cmd_index, 194 u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) 195 { 196 u32 written; 197 198 if (!fw.chip) 199 return -ENOENT; 200 201 if (size > fw.chip->shmem_size) 202 return -EINVAL; 203 204 if (!fw.chip->cmd_shmem_in_base) 205 return -EINVAL; 206 207 memcpy(fw.sm_shmem_in_base, buffer, size); 208 209 if (meson_sm_call(cmd_index, &written, arg0, arg1, arg2, arg3, arg4) < 0) 210 return -EINVAL; 211 212 if (!written) 213 return -EINVAL; 214 215 return written; 216 } 217 EXPORT_SYMBOL(meson_sm_call_write); 218 219 #define SM_CHIP_ID_LENGTH 119 220 #define SM_CHIP_ID_OFFSET 4 221 #define SM_CHIP_ID_SIZE 12 222 223 static ssize_t serial_show(struct device *dev, struct device_attribute *attr, 224 char *buf) 225 { 226 uint8_t *id_buf; 227 int ret; 228 229 id_buf = kmalloc(SM_CHIP_ID_LENGTH, GFP_KERNEL); 230 if (!id_buf) 231 return -ENOMEM; 232 233 ret = meson_sm_call_read(id_buf, SM_CHIP_ID_LENGTH, SM_GET_CHIP_ID, 234 0, 0, 0, 0, 0); 235 if (ret < 0) { 236 kfree(id_buf); 237 return ret; 238 } 239 240 ret = sprintf(buf, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", 241 id_buf[SM_CHIP_ID_OFFSET + 0], 242 id_buf[SM_CHIP_ID_OFFSET + 1], 243 id_buf[SM_CHIP_ID_OFFSET + 2], 244 id_buf[SM_CHIP_ID_OFFSET + 3], 245 id_buf[SM_CHIP_ID_OFFSET + 4], 246 id_buf[SM_CHIP_ID_OFFSET + 5], 247 id_buf[SM_CHIP_ID_OFFSET + 6], 248 id_buf[SM_CHIP_ID_OFFSET + 7], 249 id_buf[SM_CHIP_ID_OFFSET + 8], 250 id_buf[SM_CHIP_ID_OFFSET + 9], 251 id_buf[SM_CHIP_ID_OFFSET + 10], 252 id_buf[SM_CHIP_ID_OFFSET + 11]); 253 254 kfree(id_buf); 255 256 return ret; 257 } 258 259 static DEVICE_ATTR_RO(serial); 260 261 static struct attribute *meson_sm_sysfs_attributes[] = { 262 &dev_attr_serial.attr, 263 NULL, 264 }; 265 266 static const struct attribute_group meson_sm_sysfs_attr_group = { 267 .attrs = meson_sm_sysfs_attributes, 268 }; 269 270 static const struct of_device_id meson_sm_ids[] = { 271 { .compatible = "amlogic,meson-gxbb-sm", .data = &gxbb_chip }, 272 { /* sentinel */ }, 273 }; 274 275 static int __init meson_sm_probe(struct platform_device *pdev) 276 { 277 const struct meson_sm_chip *chip; 278 279 chip = of_match_device(meson_sm_ids, &pdev->dev)->data; 280 281 if (chip->cmd_shmem_in_base) { 282 fw.sm_shmem_in_base = meson_sm_map_shmem(chip->cmd_shmem_in_base, 283 chip->shmem_size); 284 if (WARN_ON(!fw.sm_shmem_in_base)) 285 goto out; 286 } 287 288 if (chip->cmd_shmem_out_base) { 289 fw.sm_shmem_out_base = meson_sm_map_shmem(chip->cmd_shmem_out_base, 290 chip->shmem_size); 291 if (WARN_ON(!fw.sm_shmem_out_base)) 292 goto out_in_base; 293 } 294 295 fw.chip = chip; 296 pr_info("secure-monitor enabled\n"); 297 298 if (sysfs_create_group(&pdev->dev.kobj, &meson_sm_sysfs_attr_group)) 299 goto out_in_base; 300 301 return 0; 302 303 out_in_base: 304 iounmap(fw.sm_shmem_in_base); 305 out: 306 return -EINVAL; 307 } 308 309 static struct platform_driver meson_sm_driver = { 310 .driver = { 311 .name = "meson-sm", 312 .of_match_table = of_match_ptr(meson_sm_ids), 313 }, 314 }; 315 module_platform_driver_probe(meson_sm_driver, meson_sm_probe); 316