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