linux/drivers/firmware/meson/meson_sm.c
<<
>>
Prefs
   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
  38struct 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
  51struct 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
  57static struct meson_sm_firmware fw;
  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 * @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 */
 103int 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}
 122EXPORT_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 */
 140int 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}
 171EXPORT_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 */
 187int 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}
 211EXPORT_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
 217static 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
 253static DEVICE_ATTR_RO(serial);
 254
 255static struct attribute *meson_sm_sysfs_attributes[] = {
 256        &dev_attr_serial.attr,
 257        NULL,
 258};
 259
 260static const struct attribute_group meson_sm_sysfs_attr_group = {
 261        .attrs = meson_sm_sysfs_attributes,
 262};
 263
 264static const struct of_device_id meson_sm_ids[] = {
 265        { .compatible = "amlogic,meson-gxbb-sm", .data = &gxbb_chip },
 266        { /* sentinel */ },
 267};
 268
 269static 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
 297out_in_base:
 298        iounmap(fw.sm_shmem_in_base);
 299out:
 300        return -EINVAL;
 301}
 302
 303static struct platform_driver meson_sm_driver = {
 304        .driver = {
 305                .name = "meson-sm",
 306                .of_match_table = of_match_ptr(meson_sm_ids),
 307        },
 308};
 309module_platform_driver_probe(meson_sm_driver, meson_sm_probe);
 310