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

firmware.c (7812B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2017 Linaro Ltd.
      4 */
      5
      6#include <linux/device.h>
      7#include <linux/firmware.h>
      8#include <linux/kernel.h>
      9#include <linux/iommu.h>
     10#include <linux/io.h>
     11#include <linux/of.h>
     12#include <linux/of_address.h>
     13#include <linux/platform_device.h>
     14#include <linux/of_device.h>
     15#include <linux/qcom_scm.h>
     16#include <linux/sizes.h>
     17#include <linux/soc/qcom/mdt_loader.h>
     18
     19#include "core.h"
     20#include "firmware.h"
     21#include "hfi_venus_io.h"
     22
     23#define VENUS_PAS_ID			9
     24#define VENUS_FW_MEM_SIZE		(6 * SZ_1M)
     25#define VENUS_FW_START_ADDR		0x0
     26
     27static void venus_reset_cpu(struct venus_core *core)
     28{
     29	u32 fw_size = core->fw.mapped_mem_size;
     30	void __iomem *wrapper_base;
     31
     32	if (IS_V6(core))
     33		wrapper_base = core->wrapper_tz_base;
     34	else
     35		wrapper_base = core->wrapper_base;
     36
     37	writel(0, wrapper_base + WRAPPER_FW_START_ADDR);
     38	writel(fw_size, wrapper_base + WRAPPER_FW_END_ADDR);
     39	writel(0, wrapper_base + WRAPPER_CPA_START_ADDR);
     40	writel(fw_size, wrapper_base + WRAPPER_CPA_END_ADDR);
     41	writel(fw_size, wrapper_base + WRAPPER_NONPIX_START_ADDR);
     42	writel(fw_size, wrapper_base + WRAPPER_NONPIX_END_ADDR);
     43
     44	if (IS_V6(core)) {
     45		/* Bring XTSS out of reset */
     46		writel(0, wrapper_base + WRAPPER_TZ_XTSS_SW_RESET);
     47	} else {
     48		writel(0x0, wrapper_base + WRAPPER_CPU_CGC_DIS);
     49		writel(0x0, wrapper_base + WRAPPER_CPU_CLOCK_CONFIG);
     50
     51		/* Bring ARM9 out of reset */
     52		writel(0, wrapper_base + WRAPPER_A9SS_SW_RESET);
     53	}
     54}
     55
     56int venus_set_hw_state(struct venus_core *core, bool resume)
     57{
     58	int ret;
     59
     60	if (core->use_tz) {
     61		ret = qcom_scm_set_remote_state(resume, 0);
     62		if (resume && ret == -EINVAL)
     63			ret = 0;
     64		return ret;
     65	}
     66
     67	if (resume) {
     68		venus_reset_cpu(core);
     69	} else {
     70		if (IS_V6(core))
     71			writel(1, core->wrapper_tz_base + WRAPPER_TZ_XTSS_SW_RESET);
     72		else
     73			writel(1, core->wrapper_base + WRAPPER_A9SS_SW_RESET);
     74	}
     75
     76	return 0;
     77}
     78
     79static int venus_load_fw(struct venus_core *core, const char *fwname,
     80			 phys_addr_t *mem_phys, size_t *mem_size)
     81{
     82	const struct firmware *mdt;
     83	struct device_node *node;
     84	struct device *dev;
     85	struct resource r;
     86	ssize_t fw_size;
     87	void *mem_va;
     88	int ret;
     89
     90	*mem_phys = 0;
     91	*mem_size = 0;
     92
     93	dev = core->dev;
     94	node = of_parse_phandle(dev->of_node, "memory-region", 0);
     95	if (!node) {
     96		dev_err(dev, "no memory-region specified\n");
     97		return -EINVAL;
     98	}
     99
    100	ret = of_address_to_resource(node, 0, &r);
    101	if (ret)
    102		goto err_put_node;
    103
    104	ret = request_firmware(&mdt, fwname, dev);
    105	if (ret < 0)
    106		goto err_put_node;
    107
    108	fw_size = qcom_mdt_get_size(mdt);
    109	if (fw_size < 0) {
    110		ret = fw_size;
    111		goto err_release_fw;
    112	}
    113
    114	*mem_phys = r.start;
    115	*mem_size = resource_size(&r);
    116
    117	if (*mem_size < fw_size || fw_size > VENUS_FW_MEM_SIZE) {
    118		ret = -EINVAL;
    119		goto err_release_fw;
    120	}
    121
    122	mem_va = memremap(r.start, *mem_size, MEMREMAP_WC);
    123	if (!mem_va) {
    124		dev_err(dev, "unable to map memory region: %pR\n", &r);
    125		ret = -ENOMEM;
    126		goto err_release_fw;
    127	}
    128
    129	if (core->use_tz)
    130		ret = qcom_mdt_load(dev, mdt, fwname, VENUS_PAS_ID,
    131				    mem_va, *mem_phys, *mem_size, NULL);
    132	else
    133		ret = qcom_mdt_load_no_init(dev, mdt, fwname, VENUS_PAS_ID,
    134					    mem_va, *mem_phys, *mem_size, NULL);
    135
    136	memunmap(mem_va);
    137err_release_fw:
    138	release_firmware(mdt);
    139err_put_node:
    140	of_node_put(node);
    141	return ret;
    142}
    143
    144static int venus_boot_no_tz(struct venus_core *core, phys_addr_t mem_phys,
    145			    size_t mem_size)
    146{
    147	struct iommu_domain *iommu;
    148	struct device *dev;
    149	int ret;
    150
    151	dev = core->fw.dev;
    152	if (!dev)
    153		return -EPROBE_DEFER;
    154
    155	iommu = core->fw.iommu_domain;
    156	core->fw.mapped_mem_size = mem_size;
    157
    158	ret = iommu_map(iommu, VENUS_FW_START_ADDR, mem_phys, mem_size,
    159			IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV);
    160	if (ret) {
    161		dev_err(dev, "could not map video firmware region\n");
    162		return ret;
    163	}
    164
    165	venus_reset_cpu(core);
    166
    167	return 0;
    168}
    169
    170static int venus_shutdown_no_tz(struct venus_core *core)
    171{
    172	const size_t mapped = core->fw.mapped_mem_size;
    173	struct iommu_domain *iommu;
    174	size_t unmapped;
    175	u32 reg;
    176	struct device *dev = core->fw.dev;
    177	void __iomem *wrapper_base = core->wrapper_base;
    178	void __iomem *wrapper_tz_base = core->wrapper_tz_base;
    179
    180	if (IS_V6(core)) {
    181		/* Assert the reset to XTSS */
    182		reg = readl_relaxed(wrapper_tz_base + WRAPPER_TZ_XTSS_SW_RESET);
    183		reg |= WRAPPER_XTSS_SW_RESET_BIT;
    184		writel_relaxed(reg, wrapper_tz_base + WRAPPER_TZ_XTSS_SW_RESET);
    185	} else {
    186		/* Assert the reset to ARM9 */
    187		reg = readl_relaxed(wrapper_base + WRAPPER_A9SS_SW_RESET);
    188		reg |= WRAPPER_A9SS_SW_RESET_BIT;
    189		writel_relaxed(reg, wrapper_base + WRAPPER_A9SS_SW_RESET);
    190	}
    191	/* Make sure reset is asserted before the mapping is removed */
    192	mb();
    193
    194	iommu = core->fw.iommu_domain;
    195
    196	if (core->fw.mapped_mem_size && iommu) {
    197		unmapped = iommu_unmap(iommu, VENUS_FW_START_ADDR, mapped);
    198
    199		if (unmapped != mapped)
    200			dev_err(dev, "failed to unmap firmware\n");
    201		else
    202			core->fw.mapped_mem_size = 0;
    203	}
    204
    205	return 0;
    206}
    207
    208int venus_boot(struct venus_core *core)
    209{
    210	struct device *dev = core->dev;
    211	const struct venus_resources *res = core->res;
    212	const char *fwpath = NULL;
    213	phys_addr_t mem_phys;
    214	size_t mem_size;
    215	int ret;
    216
    217	if (!IS_ENABLED(CONFIG_QCOM_MDT_LOADER) ||
    218	    (core->use_tz && !qcom_scm_is_available()))
    219		return -EPROBE_DEFER;
    220
    221	ret = of_property_read_string_index(dev->of_node, "firmware-name", 0,
    222					    &fwpath);
    223	if (ret)
    224		fwpath = core->res->fwname;
    225
    226	ret = venus_load_fw(core, fwpath, &mem_phys, &mem_size);
    227	if (ret) {
    228		dev_err(dev, "fail to load video firmware\n");
    229		return -EINVAL;
    230	}
    231
    232	core->fw.mem_size = mem_size;
    233	core->fw.mem_phys = mem_phys;
    234
    235	if (core->use_tz)
    236		ret = qcom_scm_pas_auth_and_reset(VENUS_PAS_ID);
    237	else
    238		ret = venus_boot_no_tz(core, mem_phys, mem_size);
    239
    240	if (ret)
    241		return ret;
    242
    243	if (core->use_tz && res->cp_size) {
    244		ret = qcom_scm_mem_protect_video_var(res->cp_start,
    245						     res->cp_size,
    246						     res->cp_nonpixel_start,
    247						     res->cp_nonpixel_size);
    248		if (ret) {
    249			qcom_scm_pas_shutdown(VENUS_PAS_ID);
    250			dev_err(dev, "set virtual address ranges fail (%d)\n",
    251				ret);
    252			return ret;
    253		}
    254	}
    255
    256	return 0;
    257}
    258
    259int venus_shutdown(struct venus_core *core)
    260{
    261	int ret;
    262
    263	if (core->use_tz)
    264		ret = qcom_scm_pas_shutdown(VENUS_PAS_ID);
    265	else
    266		ret = venus_shutdown_no_tz(core);
    267
    268	return ret;
    269}
    270
    271int venus_firmware_init(struct venus_core *core)
    272{
    273	struct platform_device_info info;
    274	struct iommu_domain *iommu_dom;
    275	struct platform_device *pdev;
    276	struct device_node *np;
    277	int ret;
    278
    279	np = of_get_child_by_name(core->dev->of_node, "video-firmware");
    280	if (!np) {
    281		core->use_tz = true;
    282		return 0;
    283	}
    284
    285	memset(&info, 0, sizeof(info));
    286	info.fwnode = &np->fwnode;
    287	info.parent = core->dev;
    288	info.name = np->name;
    289	info.dma_mask = DMA_BIT_MASK(32);
    290
    291	pdev = platform_device_register_full(&info);
    292	if (IS_ERR(pdev)) {
    293		of_node_put(np);
    294		return PTR_ERR(pdev);
    295	}
    296
    297	pdev->dev.of_node = np;
    298
    299	ret = of_dma_configure(&pdev->dev, np, true);
    300	if (ret) {
    301		dev_err(core->dev, "dma configure fail\n");
    302		goto err_unregister;
    303	}
    304
    305	core->fw.dev = &pdev->dev;
    306
    307	iommu_dom = iommu_domain_alloc(&platform_bus_type);
    308	if (!iommu_dom) {
    309		dev_err(core->fw.dev, "Failed to allocate iommu domain\n");
    310		ret = -ENOMEM;
    311		goto err_unregister;
    312	}
    313
    314	ret = iommu_attach_device(iommu_dom, core->fw.dev);
    315	if (ret) {
    316		dev_err(core->fw.dev, "could not attach device\n");
    317		goto err_iommu_free;
    318	}
    319
    320	core->fw.iommu_domain = iommu_dom;
    321
    322	of_node_put(np);
    323
    324	return 0;
    325
    326err_iommu_free:
    327	iommu_domain_free(iommu_dom);
    328err_unregister:
    329	platform_device_unregister(pdev);
    330	of_node_put(np);
    331	return ret;
    332}
    333
    334void venus_firmware_deinit(struct venus_core *core)
    335{
    336	struct iommu_domain *iommu;
    337
    338	if (!core->fw.dev)
    339		return;
    340
    341	iommu = core->fw.iommu_domain;
    342
    343	iommu_detach_device(iommu, core->fw.dev);
    344
    345	if (core->fw.iommu_domain) {
    346		iommu_domain_free(iommu);
    347		core->fw.iommu_domain = NULL;
    348	}
    349
    350	platform_device_unregister(to_platform_device(core->fw.dev));
    351}