kernel_samsung_a53x/drivers/soc/samsung/exynos-seh.c
2024-06-15 16:02:09 -03:00

157 lines
4.2 KiB
C
Executable file

/*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* EXYNOS - Smart Exception Handler
* Author: Jang Hyunsung <hs79.jang@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <asm/cacheflush.h>
#include <asm/memory.h>
#include <soc/samsung/exynos-smc.h>
#include <soc/samsung/exynos-el3_mon.h>
#include <soc/samsung/exynos-seh.h>
static char *smc_debug_mem;
static struct page *page_ptr;
static unsigned long cm_debug_func_addr = 0;
void exynos_seh_set_cm_debug_function(unsigned long addr)
{
cm_debug_func_addr = addr;
}
EXPORT_SYMBOL(exynos_seh_set_cm_debug_function);
static void exynos_smart_exception_handler(unsigned int id,
unsigned long elr, unsigned long esr,
unsigned long sctlr, unsigned long ttbr,
unsigned long far, unsigned long x6,
unsigned int offset)
{
int i;
unsigned long *ptr;
unsigned long tmp;
pr_err("========================================="
"=========================================\n");
if (id)
pr_err("%s: There has been an unexpected exception from "
"a LDFW which has smc id 0x%x\n\n", __func__, id);
else
pr_err("%s: There has been an unexpected exception from "
"the EL3 monitor.\n\n", __func__);
if (id) {
pr_err("elr_el1 : 0x%016lx, \tesr_el1 : 0x%016lx\n",
elr, esr);
pr_err("sctlr_el1 : 0x%016lx, \tttbr_el1 : 0x%016lx\n",
sctlr, ttbr);
pr_err("far_el1 : 0x%016lx, \tlr (EL1) : 0x%016lx\n\n",
far, x6);
} else {
pr_err("elr_el3 : 0x%016lx, \tesr_el3 : 0x%016lx\n",
elr, esr);
pr_err("sctlr_el3 : 0x%016lx, \tttbr_el3 : 0x%016lx\n",
sctlr, ttbr);
pr_err("far_el3 : 0x%016lx, \tscr_el3 : 0x%016lx\n\n",
far, x6);
}
if ((offset > 0x0 && offset < (PAGE_SIZE * 2))
&& !(offset % 0x8) && (smc_debug_mem)) {
/* Flush smc_debug_mem for cache coherency */
flush_dcache_page(page_ptr);
tmp = (unsigned long)smc_debug_mem;
tmp += (unsigned long)offset;
ptr = (unsigned long *)tmp;
for (i = 0; i < 15; i++) {
pr_err("x%02d : 0x%016lx, \tx%02d : 0x%016lx\n",
i * 2, ptr[i * 2],
i * 2 + 1, ptr[i * 2 + 1]);
}
pr_err("x%02d : 0x%016lx\n", i * 2, ptr[i * 2]);
}
pr_err("\n[WARNING] IT'S GOING TO CAUSE KERNEL PANIC FOR DEBUGGING.\n\n");
pr_err("========================================="
"=========================================\n");
#if IS_ENABLED(CONFIG_EXYNOS_CRYPTOMANAGER)
/* If exception is occured in CM LDFW, call debug function */
if ((id & 0x1000) & (cm_debug_func_addr != 0)) {
((void (*) (void))(cm_debug_func_addr))();
}
#endif
/* make kernel panic */
BUG();
/* SHOULD NOT be here */
while(1);
}
static int __init exynos_set_seh_address(void)
{
unsigned long addr = (unsigned long)exynos_smart_exception_handler;
unsigned long ret;
char *phys;
unsigned int size = PAGE_SIZE * 2;
pr_info("%s: send smc call with SMC_CMD_SET_SEH_ADDRESS.\n", __func__);
/* Set debug mem */
page_ptr = alloc_pages(GFP_KERNEL, 1);
if (!page_ptr) {
pr_err("%s: alloc_page for smc_debug failed.\n", __func__);
return 0;
}
smc_debug_mem = page_address(page_ptr);
if (!smc_debug_mem) {
pr_err("%s: kmalloc for smc_debug failed.\n", __func__);
return 0;
}
/* to map & flush memory */
memset(smc_debug_mem, 0x00, size);
flush_dcache_page(page_ptr);
phys = (char *)virt_to_phys(smc_debug_mem);
pr_err("%s: alloc kmem for smc_dbg virt: 0x%lx phys: 0x%lx size: %d.\n",
__func__, smc_debug_mem, phys, size);
ret = exynos_smc(SMC_CMD_SET_DEBUG_MEM, (u64)phys, (u64)size, 0);
/* correct return value is input size */
if (ret != size) {
pr_err("%s: Can not set the address to el3 monitor. "
"ret = 0x%lx. free the kmem\n", __func__, ret);
__free_pages(page_ptr, 1);
return 0;
}
ret = exynos_smc(SMC_CMD_SET_SEH_ADDRESS, addr, 0, 0);
/* return value not zero means failure */
if (ret)
pr_err("%s: did not set the seh address to el3 monitor. "
"ret = 0x%lx.\n", __func__, ret);
else
pr_err("%s: set the seh address to el3 monitor well.\n",
__func__);
return 0;
}
module_init(exynos_set_seh_address);
MODULE_LICENSE("GPL");