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 (6084B)


      1// SPDX-License-Identifier: GPL-2.0
      2//
      3// Copyright (C) 2012 Samsung Electronics.
      4// Kyungmin Park <kyungmin.park@samsung.com>
      5// Tomasz Figa <t.figa@samsung.com>
      6
      7#include <linux/kernel.h>
      8#include <linux/io.h>
      9#include <linux/init.h>
     10#include <linux/of.h>
     11#include <linux/of_address.h>
     12
     13#include <asm/cacheflush.h>
     14#include <asm/cputype.h>
     15#include <asm/firmware.h>
     16#include <asm/hardware/cache-l2x0.h>
     17#include <asm/suspend.h>
     18
     19#include "common.h"
     20#include "smc.h"
     21
     22#define EXYNOS_BOOT_ADDR	0x8
     23#define EXYNOS_BOOT_FLAG	0xc
     24
     25static void exynos_save_cp15(void)
     26{
     27	/* Save Power control and Diagnostic registers */
     28	asm ("mrc p15, 0, %0, c15, c0, 0\n"
     29	     "mrc p15, 0, %1, c15, c0, 1\n"
     30	     : "=r" (cp15_save_power), "=r" (cp15_save_diag)
     31	     : : "cc");
     32}
     33
     34static int exynos_do_idle(unsigned long mode)
     35{
     36	switch (mode) {
     37	case FW_DO_IDLE_AFTR:
     38		if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
     39			exynos_save_cp15();
     40		writel_relaxed(__pa_symbol(exynos_cpu_resume_ns),
     41			       sysram_ns_base_addr + 0x24);
     42		writel_relaxed(EXYNOS_AFTR_MAGIC, sysram_ns_base_addr + 0x20);
     43		if (soc_is_exynos3250()) {
     44			flush_cache_all();
     45			exynos_smc(SMC_CMD_SAVE, OP_TYPE_CORE,
     46				   SMC_POWERSTATE_IDLE, 0);
     47			exynos_smc(SMC_CMD_SHUTDOWN, OP_TYPE_CLUSTER,
     48				   SMC_POWERSTATE_IDLE, 0);
     49		} else
     50			exynos_smc(SMC_CMD_CPU0AFTR, 0, 0, 0);
     51		break;
     52	case FW_DO_IDLE_SLEEP:
     53		exynos_smc(SMC_CMD_SLEEP, 0, 0, 0);
     54	}
     55	return 0;
     56}
     57
     58static int exynos_cpu_boot(int cpu)
     59{
     60	/*
     61	 * Exynos3250 doesn't need to send smc command for secondary CPU boot
     62	 * because Exynos3250 removes WFE in secure mode.
     63	 *
     64	 * On Exynos5 devices the call is ignored by trustzone firmware.
     65	 */
     66	if (!soc_is_exynos4210() && !soc_is_exynos4412())
     67		return 0;
     68
     69	/*
     70	 * The second parameter of SMC_CMD_CPU1BOOT command means CPU id.
     71	 */
     72	exynos_smc(SMC_CMD_CPU1BOOT, cpu, 0, 0);
     73	return 0;
     74}
     75
     76static int exynos_set_cpu_boot_addr(int cpu, unsigned long boot_addr)
     77{
     78	void __iomem *boot_reg;
     79
     80	if (!sysram_ns_base_addr)
     81		return -ENODEV;
     82
     83	boot_reg = sysram_ns_base_addr + 0x1c;
     84
     85	/*
     86	 * Almost all Exynos-series of SoCs that run in secure mode don't need
     87	 * additional offset for every CPU, with Exynos4412 being the only
     88	 * exception.
     89	 */
     90	if (soc_is_exynos4412())
     91		boot_reg += 4 * cpu;
     92
     93	writel_relaxed(boot_addr, boot_reg);
     94	return 0;
     95}
     96
     97static int exynos_get_cpu_boot_addr(int cpu, unsigned long *boot_addr)
     98{
     99	void __iomem *boot_reg;
    100
    101	if (!sysram_ns_base_addr)
    102		return -ENODEV;
    103
    104	boot_reg = sysram_ns_base_addr + 0x1c;
    105
    106	if (soc_is_exynos4412())
    107		boot_reg += 4 * cpu;
    108
    109	*boot_addr = readl_relaxed(boot_reg);
    110	return 0;
    111}
    112
    113static int exynos_cpu_suspend(unsigned long arg)
    114{
    115	flush_cache_all();
    116	outer_flush_all();
    117
    118	exynos_smc(SMC_CMD_SLEEP, 0, 0, 0);
    119
    120	pr_info("Failed to suspend the system\n");
    121	writel(0, sysram_ns_base_addr + EXYNOS_BOOT_FLAG);
    122	return 1;
    123}
    124
    125static int exynos_suspend(void)
    126{
    127	if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
    128		exynos_save_cp15();
    129
    130	writel(EXYNOS_SLEEP_MAGIC, sysram_ns_base_addr + EXYNOS_BOOT_FLAG);
    131	writel(__pa_symbol(exynos_cpu_resume_ns),
    132		sysram_ns_base_addr + EXYNOS_BOOT_ADDR);
    133
    134	return cpu_suspend(0, exynos_cpu_suspend);
    135}
    136
    137static int exynos_resume(void)
    138{
    139	writel(0, sysram_ns_base_addr + EXYNOS_BOOT_FLAG);
    140
    141	return 0;
    142}
    143
    144static const struct firmware_ops exynos_firmware_ops = {
    145	.do_idle		= IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_do_idle : NULL,
    146	.set_cpu_boot_addr	= exynos_set_cpu_boot_addr,
    147	.get_cpu_boot_addr	= exynos_get_cpu_boot_addr,
    148	.cpu_boot		= exynos_cpu_boot,
    149	.suspend		= IS_ENABLED(CONFIG_PM_SLEEP) ? exynos_suspend : NULL,
    150	.resume			= IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_resume : NULL,
    151};
    152
    153static void exynos_l2_write_sec(unsigned long val, unsigned reg)
    154{
    155	static int l2cache_enabled;
    156
    157	switch (reg) {
    158	case L2X0_CTRL:
    159		if (val & L2X0_CTRL_EN) {
    160			/*
    161			 * Before the cache can be enabled, due to firmware
    162			 * design, SMC_CMD_L2X0INVALL must be called.
    163			 */
    164			if (!l2cache_enabled) {
    165				exynos_smc(SMC_CMD_L2X0INVALL, 0, 0, 0);
    166				l2cache_enabled = 1;
    167			}
    168		} else {
    169			l2cache_enabled = 0;
    170		}
    171		exynos_smc(SMC_CMD_L2X0CTRL, val, 0, 0);
    172		break;
    173
    174	case L2X0_DEBUG_CTRL:
    175		exynos_smc(SMC_CMD_L2X0DEBUG, val, 0, 0);
    176		break;
    177
    178	default:
    179		WARN_ONCE(1, "%s: ignoring write to reg 0x%x\n", __func__, reg);
    180	}
    181}
    182
    183static void exynos_l2_configure(const struct l2x0_regs *regs)
    184{
    185	exynos_smc(SMC_CMD_L2X0SETUP1, regs->tag_latency, regs->data_latency,
    186		   regs->prefetch_ctrl);
    187	exynos_smc(SMC_CMD_L2X0SETUP2, regs->pwr_ctrl, regs->aux_ctrl, 0);
    188}
    189
    190bool __init exynos_secure_firmware_available(void)
    191{
    192	struct device_node *nd;
    193	const __be32 *addr;
    194
    195	nd = of_find_compatible_node(NULL, NULL,
    196					"samsung,secure-firmware");
    197	if (!nd)
    198		return false;
    199
    200	addr = of_get_address(nd, 0, NULL, NULL);
    201	of_node_put(nd);
    202	if (!addr) {
    203		pr_err("%s: No address specified.\n", __func__);
    204		return false;
    205	}
    206
    207	return true;
    208}
    209
    210void __init exynos_firmware_init(void)
    211{
    212	if (!exynos_secure_firmware_available())
    213		return;
    214
    215	pr_info("Running under secure firmware.\n");
    216
    217	register_firmware_ops(&exynos_firmware_ops);
    218
    219	/*
    220	 * Exynos 4 SoCs (based on Cortex A9 and equipped with L2C-310),
    221	 * running under secure firmware, require certain registers of L2
    222	 * cache controller to be written in secure mode. Here .write_sec
    223	 * callback is provided to perform necessary SMC calls.
    224	 */
    225	if (IS_ENABLED(CONFIG_CACHE_L2X0) &&
    226	    read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
    227		outer_cache.write_sec = exynos_l2_write_sec;
    228		outer_cache.configure = exynos_l2_configure;
    229	}
    230}
    231
    232#define REG_CPU_STATE_ADDR	(sysram_ns_base_addr + 0x28)
    233#define BOOT_MODE_MASK		0x1f
    234
    235void exynos_set_boot_flag(unsigned int cpu, unsigned int mode)
    236{
    237	unsigned int tmp;
    238
    239	tmp = readl_relaxed(REG_CPU_STATE_ADDR + cpu * 4);
    240
    241	if (mode & BOOT_MODE_MASK)
    242		tmp &= ~BOOT_MODE_MASK;
    243
    244	tmp |= mode;
    245	writel_relaxed(tmp, REG_CPU_STATE_ADDR + cpu * 4);
    246}
    247
    248void exynos_clear_boot_flag(unsigned int cpu, unsigned int mode)
    249{
    250	unsigned int tmp;
    251
    252	tmp = readl_relaxed(REG_CPU_STATE_ADDR + cpu * 4);
    253	tmp &= ~mode;
    254	writel_relaxed(tmp, REG_CPU_STATE_ADDR + cpu * 4);
    255}