root/drivers/firmware/meson/meson_sm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. meson_sm_get_cmd
  2. __meson_sm_call
  3. meson_sm_map_shmem
  4. meson_sm_call
  5. meson_sm_call_read
  6. meson_sm_call_write
  7. serial_show
  8. meson_sm_probe

   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 
  25 struct meson_sm_cmd {
  26         unsigned int index;
  27         u32 smc_id;
  28 };
  29 #define CMD(d, s) { .index = (d), .smc_id = (s), }
  30 
  31 struct 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 
  38 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                 { /* sentinel */ },
  48         },
  49 };
  50 
  51 struct meson_sm_firmware {
  52         const struct meson_sm_chip *chip;
  53         void __iomem *sm_shmem_in_base;
  54         void __iomem *sm_shmem_out_base;
  55 };
  56 
  57 static struct meson_sm_firmware fw;
  58 
  59 static 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 
  70 static 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 
  79 static 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  * @cmd_index:  Index of the SMC32 function ID
  94  * @ret:        Returned value
  95  * @arg0:       SMC32 Argument 0
  96  * @arg1:       SMC32 Argument 1
  97  * @arg2:       SMC32 Argument 2
  98  * @arg3:       SMC32 Argument 3
  99  * @arg4:       SMC32 Argument 4
 100  *
 101  * Return:      0 on success, a negative value on error
 102  */
 103 int meson_sm_call(unsigned int cmd_index, u32 *ret, u32 arg0,
 104                   u32 arg1, u32 arg2, u32 arg3, u32 arg4)
 105 {
 106         u32 cmd, lret;
 107 
 108         if (!fw.chip)
 109                 return -ENOENT;
 110 
 111         cmd = meson_sm_get_cmd(fw.chip, cmd_index);
 112         if (!cmd)
 113                 return -EINVAL;
 114 
 115         lret = __meson_sm_call(cmd, arg0, arg1, arg2, arg3, arg4);
 116 
 117         if (ret)
 118                 *ret = lret;
 119 
 120         return 0;
 121 }
 122 EXPORT_SYMBOL(meson_sm_call);
 123 
 124 /**
 125  * meson_sm_call_read - retrieve data from secure-monitor
 126  *
 127  * @buffer:     Buffer to store the retrieved data
 128  * @bsize:      Size of the buffer
 129  * @cmd_index:  Index of the SMC32 function ID
 130  * @arg0:       SMC32 Argument 0
 131  * @arg1:       SMC32 Argument 1
 132  * @arg2:       SMC32 Argument 2
 133  * @arg3:       SMC32 Argument 3
 134  * @arg4:       SMC32 Argument 4
 135  *
 136  * Return:      size of read data on success, a negative value on error
 137  *              When 0 is returned there is no guarantee about the amount of
 138  *              data read and bsize bytes are copied in buffer.
 139  */
 140 int meson_sm_call_read(void *buffer, unsigned int bsize, unsigned int cmd_index,
 141                        u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4)
 142 {
 143         u32 size;
 144         int ret;
 145 
 146         if (!fw.chip)
 147                 return -ENOENT;
 148 
 149         if (!fw.chip->cmd_shmem_out_base)
 150                 return -EINVAL;
 151 
 152         if (bsize > fw.chip->shmem_size)
 153                 return -EINVAL;
 154 
 155         if (meson_sm_call(cmd_index, &size, arg0, arg1, arg2, arg3, arg4) < 0)
 156                 return -EINVAL;
 157 
 158         if (size > bsize)
 159                 return -EINVAL;
 160 
 161         ret = size;
 162 
 163         if (!size)
 164                 size = bsize;
 165 
 166         if (buffer)
 167                 memcpy(buffer, fw.sm_shmem_out_base, size);
 168 
 169         return ret;
 170 }
 171 EXPORT_SYMBOL(meson_sm_call_read);
 172 
 173 /**
 174  * meson_sm_call_write - send data to secure-monitor
 175  *
 176  * @buffer:     Buffer containing data to send
 177  * @size:       Size of the data to send
 178  * @cmd_index:  Index of the SMC32 function ID
 179  * @arg0:       SMC32 Argument 0
 180  * @arg1:       SMC32 Argument 1
 181  * @arg2:       SMC32 Argument 2
 182  * @arg3:       SMC32 Argument 3
 183  * @arg4:       SMC32 Argument 4
 184  *
 185  * Return:      size of sent data on success, a negative value on error
 186  */
 187 int meson_sm_call_write(void *buffer, unsigned int size, unsigned int cmd_index,
 188                         u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4)
 189 {
 190         u32 written;
 191 
 192         if (!fw.chip)
 193                 return -ENOENT;
 194 
 195         if (size > fw.chip->shmem_size)
 196                 return -EINVAL;
 197 
 198         if (!fw.chip->cmd_shmem_in_base)
 199                 return -EINVAL;
 200 
 201         memcpy(fw.sm_shmem_in_base, buffer, size);
 202 
 203         if (meson_sm_call(cmd_index, &written, arg0, arg1, arg2, arg3, arg4) < 0)
 204                 return -EINVAL;
 205 
 206         if (!written)
 207                 return -EINVAL;
 208 
 209         return written;
 210 }
 211 EXPORT_SYMBOL(meson_sm_call_write);
 212 
 213 #define SM_CHIP_ID_LENGTH       119
 214 #define SM_CHIP_ID_OFFSET       4
 215 #define SM_CHIP_ID_SIZE         12
 216 
 217 static ssize_t serial_show(struct device *dev, struct device_attribute *attr,
 218                          char *buf)
 219 {
 220         uint8_t *id_buf;
 221         int ret;
 222 
 223         id_buf = kmalloc(SM_CHIP_ID_LENGTH, GFP_KERNEL);
 224         if (!id_buf)
 225                 return -ENOMEM;
 226 
 227         ret = meson_sm_call_read(id_buf, SM_CHIP_ID_LENGTH, SM_GET_CHIP_ID,
 228                                  0, 0, 0, 0, 0);
 229         if (ret < 0) {
 230                 kfree(id_buf);
 231                 return ret;
 232         }
 233 
 234         ret = sprintf(buf, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
 235                         id_buf[SM_CHIP_ID_OFFSET + 0],
 236                         id_buf[SM_CHIP_ID_OFFSET + 1],
 237                         id_buf[SM_CHIP_ID_OFFSET + 2],
 238                         id_buf[SM_CHIP_ID_OFFSET + 3],
 239                         id_buf[SM_CHIP_ID_OFFSET + 4],
 240                         id_buf[SM_CHIP_ID_OFFSET + 5],
 241                         id_buf[SM_CHIP_ID_OFFSET + 6],
 242                         id_buf[SM_CHIP_ID_OFFSET + 7],
 243                         id_buf[SM_CHIP_ID_OFFSET + 8],
 244                         id_buf[SM_CHIP_ID_OFFSET + 9],
 245                         id_buf[SM_CHIP_ID_OFFSET + 10],
 246                         id_buf[SM_CHIP_ID_OFFSET + 11]);
 247 
 248         kfree(id_buf);
 249 
 250         return ret;
 251 }
 252 
 253 static DEVICE_ATTR_RO(serial);
 254 
 255 static struct attribute *meson_sm_sysfs_attributes[] = {
 256         &dev_attr_serial.attr,
 257         NULL,
 258 };
 259 
 260 static const struct attribute_group meson_sm_sysfs_attr_group = {
 261         .attrs = meson_sm_sysfs_attributes,
 262 };
 263 
 264 static const struct of_device_id meson_sm_ids[] = {
 265         { .compatible = "amlogic,meson-gxbb-sm", .data = &gxbb_chip },
 266         { /* sentinel */ },
 267 };
 268 
 269 static int __init meson_sm_probe(struct platform_device *pdev)
 270 {
 271         const struct meson_sm_chip *chip;
 272 
 273         chip = of_match_device(meson_sm_ids, &pdev->dev)->data;
 274 
 275         if (chip->cmd_shmem_in_base) {
 276                 fw.sm_shmem_in_base = meson_sm_map_shmem(chip->cmd_shmem_in_base,
 277                                                          chip->shmem_size);
 278                 if (WARN_ON(!fw.sm_shmem_in_base))
 279                         goto out;
 280         }
 281 
 282         if (chip->cmd_shmem_out_base) {
 283                 fw.sm_shmem_out_base = meson_sm_map_shmem(chip->cmd_shmem_out_base,
 284                                                           chip->shmem_size);
 285                 if (WARN_ON(!fw.sm_shmem_out_base))
 286                         goto out_in_base;
 287         }
 288 
 289         fw.chip = chip;
 290         pr_info("secure-monitor enabled\n");
 291 
 292         if (sysfs_create_group(&pdev->dev.kobj, &meson_sm_sysfs_attr_group))
 293                 goto out_in_base;
 294 
 295         return 0;
 296 
 297 out_in_base:
 298         iounmap(fw.sm_shmem_in_base);
 299 out:
 300         return -EINVAL;
 301 }
 302 
 303 static struct platform_driver meson_sm_driver = {
 304         .driver = {
 305                 .name = "meson-sm",
 306                 .of_match_table = of_match_ptr(meson_sm_ids),
 307         },
 308 };
 309 module_platform_driver_probe(meson_sm_driver, meson_sm_probe);

/* [<][>][^][v][top][bottom][index][help] */