18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2013 Broadcom Corporation 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 58c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as 68c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 98c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 108c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 118c2ecf20Sopenharmony_ci * GNU General Public License for more details. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <stdarg.h> 158c2ecf20Sopenharmony_ci#include <linux/smp.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/ioport.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 208c2ecf20Sopenharmony_ci#include <linux/of_address.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "bcm_kona_smc.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic u32 bcm_smc_buffer_phys; /* physical address */ 258c2ecf20Sopenharmony_cistatic void __iomem *bcm_smc_buffer; /* virtual address */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct bcm_kona_smc_data { 288c2ecf20Sopenharmony_ci unsigned service_id; 298c2ecf20Sopenharmony_ci unsigned arg0; 308c2ecf20Sopenharmony_ci unsigned arg1; 318c2ecf20Sopenharmony_ci unsigned arg2; 328c2ecf20Sopenharmony_ci unsigned arg3; 338c2ecf20Sopenharmony_ci unsigned result; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic const struct of_device_id bcm_kona_smc_ids[] __initconst = { 378c2ecf20Sopenharmony_ci {.compatible = "brcm,kona-smc"}, 388c2ecf20Sopenharmony_ci {.compatible = "bcm,kona-smc"}, /* deprecated name */ 398c2ecf20Sopenharmony_ci {}, 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* Map in the args buffer area */ 438c2ecf20Sopenharmony_ciint __init bcm_kona_smc_init(void) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct device_node *node; 468c2ecf20Sopenharmony_ci const __be32 *prop_val; 478c2ecf20Sopenharmony_ci u64 prop_size = 0; 488c2ecf20Sopenharmony_ci unsigned long buffer_size; 498c2ecf20Sopenharmony_ci u32 buffer_phys; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* Read buffer addr and size from the device tree node */ 528c2ecf20Sopenharmony_ci node = of_find_matching_node(NULL, bcm_kona_smc_ids); 538c2ecf20Sopenharmony_ci if (!node) 548c2ecf20Sopenharmony_ci return -ENODEV; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci prop_val = of_get_address(node, 0, &prop_size, NULL); 578c2ecf20Sopenharmony_ci of_node_put(node); 588c2ecf20Sopenharmony_ci if (!prop_val) 598c2ecf20Sopenharmony_ci return -EINVAL; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* We assume space for four 32-bit arguments */ 628c2ecf20Sopenharmony_ci if (prop_size < 4 * sizeof(u32) || prop_size > (u64)ULONG_MAX) 638c2ecf20Sopenharmony_ci return -EINVAL; 648c2ecf20Sopenharmony_ci buffer_size = (unsigned long)prop_size; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci buffer_phys = be32_to_cpup(prop_val); 678c2ecf20Sopenharmony_ci if (!buffer_phys) 688c2ecf20Sopenharmony_ci return -EINVAL; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci bcm_smc_buffer = ioremap(buffer_phys, buffer_size); 718c2ecf20Sopenharmony_ci if (!bcm_smc_buffer) 728c2ecf20Sopenharmony_ci return -ENOMEM; 738c2ecf20Sopenharmony_ci bcm_smc_buffer_phys = buffer_phys; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci pr_info("Kona Secure API initialized\n"); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* 818c2ecf20Sopenharmony_ci * int bcm_kona_do_smc(u32 service_id, u32 buffer_addr) 828c2ecf20Sopenharmony_ci * 838c2ecf20Sopenharmony_ci * Only core 0 can run the secure monitor code. If an "smc" request 848c2ecf20Sopenharmony_ci * is initiated on a different core it must be redirected to core 0 858c2ecf20Sopenharmony_ci * for execution. We rely on the caller to handle this. 868c2ecf20Sopenharmony_ci * 878c2ecf20Sopenharmony_ci * Each "smc" request supplies a service id and the address of a 888c2ecf20Sopenharmony_ci * buffer containing parameters related to the service to be 898c2ecf20Sopenharmony_ci * performed. A flags value defines the behavior of the level 2 908c2ecf20Sopenharmony_ci * cache and interrupt handling while the secure monitor executes. 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * Parameters to the "smc" request are passed in r4-r6 as follows: 938c2ecf20Sopenharmony_ci * r4 service id 948c2ecf20Sopenharmony_ci * r5 flags (SEC_ROM_*) 958c2ecf20Sopenharmony_ci * r6 physical address of buffer with other parameters 968c2ecf20Sopenharmony_ci * 978c2ecf20Sopenharmony_ci * Execution of an "smc" request produces two distinct results. 988c2ecf20Sopenharmony_ci * 998c2ecf20Sopenharmony_ci * First, the secure monitor call itself (regardless of the specific 1008c2ecf20Sopenharmony_ci * service request) can succeed, or can produce an error. When an 1018c2ecf20Sopenharmony_ci * "smc" request completes this value is found in r12; it should 1028c2ecf20Sopenharmony_ci * always be SEC_EXIT_NORMAL. 1038c2ecf20Sopenharmony_ci * 1048c2ecf20Sopenharmony_ci * In addition, the particular service performed produces a result. 1058c2ecf20Sopenharmony_ci * The values that should be expected depend on the service. We 1068c2ecf20Sopenharmony_ci * therefore return this value to the caller, so it can handle the 1078c2ecf20Sopenharmony_ci * request result appropriately. This result value is found in r0 1088c2ecf20Sopenharmony_ci * when the "smc" request completes. 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_cistatic int bcm_kona_do_smc(u32 service_id, u32 buffer_phys) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci register u32 ip asm("ip"); /* Also called r12 */ 1138c2ecf20Sopenharmony_ci register u32 r0 asm("r0"); 1148c2ecf20Sopenharmony_ci register u32 r4 asm("r4"); 1158c2ecf20Sopenharmony_ci register u32 r5 asm("r5"); 1168c2ecf20Sopenharmony_ci register u32 r6 asm("r6"); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci r4 = service_id; 1198c2ecf20Sopenharmony_ci r5 = 0x3; /* Keep IRQ and FIQ off in SM */ 1208c2ecf20Sopenharmony_ci r6 = buffer_phys; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci asm volatile ( 1238c2ecf20Sopenharmony_ci /* Make sure we got the registers we want */ 1248c2ecf20Sopenharmony_ci __asmeq("%0", "ip") 1258c2ecf20Sopenharmony_ci __asmeq("%1", "r0") 1268c2ecf20Sopenharmony_ci __asmeq("%2", "r4") 1278c2ecf20Sopenharmony_ci __asmeq("%3", "r5") 1288c2ecf20Sopenharmony_ci __asmeq("%4", "r6") 1298c2ecf20Sopenharmony_ci ".arch_extension sec\n" 1308c2ecf20Sopenharmony_ci " smc #0\n" 1318c2ecf20Sopenharmony_ci : "=r" (ip), "=r" (r0) 1328c2ecf20Sopenharmony_ci : "r" (r4), "r" (r5), "r" (r6) 1338c2ecf20Sopenharmony_ci : "r1", "r2", "r3", "r7", "lr"); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci BUG_ON(ip != SEC_EXIT_NORMAL); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return r0; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* __bcm_kona_smc() should only run on CPU 0, with pre-emption disabled */ 1418c2ecf20Sopenharmony_cistatic void __bcm_kona_smc(void *info) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct bcm_kona_smc_data *data = info; 1448c2ecf20Sopenharmony_ci u32 __iomem *args = bcm_smc_buffer; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci BUG_ON(smp_processor_id() != 0); 1478c2ecf20Sopenharmony_ci BUG_ON(!args); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* Copy the four 32 bit argument values into the bounce area */ 1508c2ecf20Sopenharmony_ci writel_relaxed(data->arg0, args++); 1518c2ecf20Sopenharmony_ci writel_relaxed(data->arg1, args++); 1528c2ecf20Sopenharmony_ci writel_relaxed(data->arg2, args++); 1538c2ecf20Sopenharmony_ci writel(data->arg3, args); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* Flush caches for input data passed to Secure Monitor */ 1568c2ecf20Sopenharmony_ci flush_cache_all(); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* Trap into Secure Monitor and record the request result */ 1598c2ecf20Sopenharmony_ci data->result = bcm_kona_do_smc(data->service_id, bcm_smc_buffer_phys); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ciunsigned bcm_kona_smc(unsigned service_id, unsigned arg0, unsigned arg1, 1638c2ecf20Sopenharmony_ci unsigned arg2, unsigned arg3) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct bcm_kona_smc_data data; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci data.service_id = service_id; 1688c2ecf20Sopenharmony_ci data.arg0 = arg0; 1698c2ecf20Sopenharmony_ci data.arg1 = arg1; 1708c2ecf20Sopenharmony_ci data.arg2 = arg2; 1718c2ecf20Sopenharmony_ci data.arg3 = arg3; 1728c2ecf20Sopenharmony_ci data.result = 0; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* 1758c2ecf20Sopenharmony_ci * Due to a limitation of the secure monitor, we must use the SMP 1768c2ecf20Sopenharmony_ci * infrastructure to forward all secure monitor calls to Core 0. 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_ci smp_call_function_single(0, __bcm_kona_smc, &data, 1); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci return data.result; 1818c2ecf20Sopenharmony_ci} 182