18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (C) 2012-2019 ARM Limited (or its affiliates). */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/kernel.h> 58c2ecf20Sopenharmony_ci#include <linux/fips.h> 68c2ecf20Sopenharmony_ci#include <linux/notifier.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "cc_driver.h" 98c2ecf20Sopenharmony_ci#include "cc_fips.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistatic void fips_dsr(unsigned long devarg); 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistruct cc_fips_handle { 148c2ecf20Sopenharmony_ci struct tasklet_struct tasklet; 158c2ecf20Sopenharmony_ci struct notifier_block nb; 168c2ecf20Sopenharmony_ci struct cc_drvdata *drvdata; 178c2ecf20Sopenharmony_ci}; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* The function called once at driver entry point to check 208c2ecf20Sopenharmony_ci * whether TEE FIPS error occurred. 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_cistatic bool cc_get_tee_fips_status(struct cc_drvdata *drvdata) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci u32 reg; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci reg = cc_ioread(drvdata, CC_REG(GPR_HOST)); 278c2ecf20Sopenharmony_ci /* Did the TEE report status? */ 288c2ecf20Sopenharmony_ci if (reg & CC_FIPS_SYNC_TEE_STATUS) 298c2ecf20Sopenharmony_ci /* Yes. Is it OK? */ 308c2ecf20Sopenharmony_ci return (reg & CC_FIPS_SYNC_MODULE_OK); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci /* No. It's either not in use or will be reported later */ 338c2ecf20Sopenharmony_ci return true; 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * This function should push the FIPS REE library status towards the TEE library 388c2ecf20Sopenharmony_ci * by writing the error state to HOST_GPR0 register. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_civoid cc_set_ree_fips_status(struct cc_drvdata *drvdata, bool status) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci int val = CC_FIPS_SYNC_REE_STATUS; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (drvdata->hw_rev < CC_HW_REV_712) 458c2ecf20Sopenharmony_ci return; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci val |= (status ? CC_FIPS_SYNC_MODULE_OK : CC_FIPS_SYNC_MODULE_ERROR); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_REG(HOST_GPR0), val); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* Push REE side FIPS test failure to TEE side */ 538c2ecf20Sopenharmony_cistatic int cc_ree_fips_failure(struct notifier_block *nb, unsigned long unused1, 548c2ecf20Sopenharmony_ci void *unused2) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct cc_fips_handle *fips_h = 578c2ecf20Sopenharmony_ci container_of(nb, struct cc_fips_handle, nb); 588c2ecf20Sopenharmony_ci struct cc_drvdata *drvdata = fips_h->drvdata; 598c2ecf20Sopenharmony_ci struct device *dev = drvdata_to_dev(drvdata); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci cc_set_ree_fips_status(drvdata, false); 628c2ecf20Sopenharmony_ci dev_info(dev, "Notifying TEE of FIPS test failure...\n"); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return NOTIFY_OK; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_civoid cc_fips_fini(struct cc_drvdata *drvdata) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct cc_fips_handle *fips_h = drvdata->fips_handle; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (drvdata->hw_rev < CC_HW_REV_712 || !fips_h) 728c2ecf20Sopenharmony_ci return; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci atomic_notifier_chain_unregister(&fips_fail_notif_chain, &fips_h->nb); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* Kill tasklet */ 778c2ecf20Sopenharmony_ci tasklet_kill(&fips_h->tasklet); 788c2ecf20Sopenharmony_ci drvdata->fips_handle = NULL; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_civoid fips_handler(struct cc_drvdata *drvdata) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct cc_fips_handle *fips_handle_ptr = drvdata->fips_handle; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (drvdata->hw_rev < CC_HW_REV_712) 868c2ecf20Sopenharmony_ci return; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci tasklet_schedule(&fips_handle_ptr->tasklet); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic inline void tee_fips_error(struct device *dev) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci if (fips_enabled) 948c2ecf20Sopenharmony_ci panic("ccree: TEE reported cryptographic error in fips mode!\n"); 958c2ecf20Sopenharmony_ci else 968c2ecf20Sopenharmony_ci dev_err(dev, "TEE reported error!\n"); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* 1008c2ecf20Sopenharmony_ci * This function check if cryptocell tee fips error occurred 1018c2ecf20Sopenharmony_ci * and in such case triggers system error 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_civoid cc_tee_handle_fips_error(struct cc_drvdata *p_drvdata) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct device *dev = drvdata_to_dev(p_drvdata); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (!cc_get_tee_fips_status(p_drvdata)) 1088c2ecf20Sopenharmony_ci tee_fips_error(dev); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* Deferred service handler, run as interrupt-fired tasklet */ 1128c2ecf20Sopenharmony_cistatic void fips_dsr(unsigned long devarg) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct cc_drvdata *drvdata = (struct cc_drvdata *)devarg; 1158c2ecf20Sopenharmony_ci u32 irq, val; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci irq = (drvdata->irq & (CC_GPR0_IRQ_MASK)); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (irq) { 1208c2ecf20Sopenharmony_ci cc_tee_handle_fips_error(drvdata); 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* after verifying that there is nothing to do, 1248c2ecf20Sopenharmony_ci * unmask AXI completion interrupt. 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_ci val = (CC_REG(HOST_IMR) & ~irq); 1278c2ecf20Sopenharmony_ci cc_iowrite(drvdata, CC_REG(HOST_IMR), val); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* The function called once at driver entry point .*/ 1318c2ecf20Sopenharmony_ciint cc_fips_init(struct cc_drvdata *p_drvdata) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct cc_fips_handle *fips_h; 1348c2ecf20Sopenharmony_ci struct device *dev = drvdata_to_dev(p_drvdata); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (p_drvdata->hw_rev < CC_HW_REV_712) 1378c2ecf20Sopenharmony_ci return 0; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci fips_h = devm_kzalloc(dev, sizeof(*fips_h), GFP_KERNEL); 1408c2ecf20Sopenharmony_ci if (!fips_h) 1418c2ecf20Sopenharmony_ci return -ENOMEM; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci p_drvdata->fips_handle = fips_h; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci dev_dbg(dev, "Initializing fips tasklet\n"); 1468c2ecf20Sopenharmony_ci tasklet_init(&fips_h->tasklet, fips_dsr, (unsigned long)p_drvdata); 1478c2ecf20Sopenharmony_ci fips_h->drvdata = p_drvdata; 1488c2ecf20Sopenharmony_ci fips_h->nb.notifier_call = cc_ree_fips_failure; 1498c2ecf20Sopenharmony_ci atomic_notifier_chain_register(&fips_fail_notif_chain, &fips_h->nb); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci cc_tee_handle_fips_error(p_drvdata); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci} 155