cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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");