linux/drivers/firmware/meson/meson_sm.c
<<
>>
Prefs
   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
  31struct meson_sm_cmd {
  32        unsigned int index;
  33        u32 smc_id;
  34};
  35#define CMD(d, s) { .index = (d), .smc_id = (s), }
  36
  37struct 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
  44struct 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
  57struct 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
  63static struct meson_sm_firmware fw;
  64
  65static 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
  76static 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
  85static 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 */
 109int 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}
 128EXPORT_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 */
 146int 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}
 177EXPORT_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 */
 193int 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}
 217EXPORT_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
 223static 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
 259static DEVICE_ATTR_RO(serial);
 260
 261static struct attribute *meson_sm_sysfs_attributes[] = {
 262        &dev_attr_serial.attr,
 263        NULL,
 264};
 265
 266static const struct attribute_group meson_sm_sysfs_attr_group = {
 267        .attrs = meson_sm_sysfs_attributes,
 268};
 269
 270static const struct of_device_id meson_sm_ids[] = {
 271        { .compatible = "amlogic,meson-gxbb-sm", .data = &gxbb_chip },
 272        { /* sentinel */ },
 273};
 274
 275static 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
 303out_in_base:
 304        iounmap(fw.sm_shmem_in_base);
 305out:
 306        return -EINVAL;
 307}
 308
 309static struct platform_driver meson_sm_driver = {
 310        .driver = {
 311                .name = "meson-sm",
 312                .of_match_table = of_match_ptr(meson_sm_ids),
 313        },
 314};
 315module_platform_driver_probe(meson_sm_driver, meson_sm_probe);
 316