162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (C) 2012-2019 ARM Limited (or its affiliates). */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/kernel.h> 562306a36Sopenharmony_ci#include <linux/fips.h> 662306a36Sopenharmony_ci#include <linux/notifier.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "cc_driver.h" 962306a36Sopenharmony_ci#include "cc_fips.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cistatic void fips_dsr(unsigned long devarg); 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistruct cc_fips_handle { 1462306a36Sopenharmony_ci struct tasklet_struct tasklet; 1562306a36Sopenharmony_ci struct notifier_block nb; 1662306a36Sopenharmony_ci struct cc_drvdata *drvdata; 1762306a36Sopenharmony_ci}; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* The function called once at driver entry point to check 2062306a36Sopenharmony_ci * whether TEE FIPS error occurred. 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_cistatic bool cc_get_tee_fips_status(struct cc_drvdata *drvdata) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci u32 reg; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci reg = cc_ioread(drvdata, CC_REG(GPR_HOST)); 2762306a36Sopenharmony_ci /* Did the TEE report status? */ 2862306a36Sopenharmony_ci if (reg & CC_FIPS_SYNC_TEE_STATUS) 2962306a36Sopenharmony_ci /* Yes. Is it OK? */ 3062306a36Sopenharmony_ci return (reg & CC_FIPS_SYNC_MODULE_OK); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci /* No. It's either not in use or will be reported later */ 3362306a36Sopenharmony_ci return true; 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * This function should push the FIPS REE library status towards the TEE library 3862306a36Sopenharmony_ci * by writing the error state to HOST_GPR0 register. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_civoid cc_set_ree_fips_status(struct cc_drvdata *drvdata, bool status) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci int val = CC_FIPS_SYNC_REE_STATUS; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (drvdata->hw_rev < CC_HW_REV_712) 4562306a36Sopenharmony_ci return; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci val |= (status ? CC_FIPS_SYNC_MODULE_OK : CC_FIPS_SYNC_MODULE_ERROR); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci cc_iowrite(drvdata, CC_REG(HOST_GPR0), val); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* Push REE side FIPS test failure to TEE side */ 5362306a36Sopenharmony_cistatic int cc_ree_fips_failure(struct notifier_block *nb, unsigned long unused1, 5462306a36Sopenharmony_ci void *unused2) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct cc_fips_handle *fips_h = 5762306a36Sopenharmony_ci container_of(nb, struct cc_fips_handle, nb); 5862306a36Sopenharmony_ci struct cc_drvdata *drvdata = fips_h->drvdata; 5962306a36Sopenharmony_ci struct device *dev = drvdata_to_dev(drvdata); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci cc_set_ree_fips_status(drvdata, false); 6262306a36Sopenharmony_ci dev_info(dev, "Notifying TEE of FIPS test failure...\n"); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci return NOTIFY_OK; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_civoid cc_fips_fini(struct cc_drvdata *drvdata) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct cc_fips_handle *fips_h = drvdata->fips_handle; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (drvdata->hw_rev < CC_HW_REV_712 || !fips_h) 7262306a36Sopenharmony_ci return; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci atomic_notifier_chain_unregister(&fips_fail_notif_chain, &fips_h->nb); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* Kill tasklet */ 7762306a36Sopenharmony_ci tasklet_kill(&fips_h->tasklet); 7862306a36Sopenharmony_ci drvdata->fips_handle = NULL; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_civoid fips_handler(struct cc_drvdata *drvdata) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct cc_fips_handle *fips_handle_ptr = drvdata->fips_handle; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (drvdata->hw_rev < CC_HW_REV_712) 8662306a36Sopenharmony_ci return; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci tasklet_schedule(&fips_handle_ptr->tasklet); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic inline void tee_fips_error(struct device *dev) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci if (fips_enabled) 9462306a36Sopenharmony_ci panic("ccree: TEE reported cryptographic error in fips mode!\n"); 9562306a36Sopenharmony_ci else 9662306a36Sopenharmony_ci dev_err(dev, "TEE reported error!\n"); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* 10062306a36Sopenharmony_ci * This function check if cryptocell tee fips error occurred 10162306a36Sopenharmony_ci * and in such case triggers system error 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_civoid cc_tee_handle_fips_error(struct cc_drvdata *p_drvdata) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct device *dev = drvdata_to_dev(p_drvdata); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (!cc_get_tee_fips_status(p_drvdata)) 10862306a36Sopenharmony_ci tee_fips_error(dev); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* Deferred service handler, run as interrupt-fired tasklet */ 11262306a36Sopenharmony_cistatic void fips_dsr(unsigned long devarg) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct cc_drvdata *drvdata = (struct cc_drvdata *)devarg; 11562306a36Sopenharmony_ci u32 irq, val; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci irq = (drvdata->irq & (CC_GPR0_IRQ_MASK)); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (irq) { 12062306a36Sopenharmony_ci cc_tee_handle_fips_error(drvdata); 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* after verifying that there is nothing to do, 12462306a36Sopenharmony_ci * unmask AXI completion interrupt. 12562306a36Sopenharmony_ci */ 12662306a36Sopenharmony_ci val = (CC_REG(HOST_IMR) & ~irq); 12762306a36Sopenharmony_ci cc_iowrite(drvdata, CC_REG(HOST_IMR), val); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* The function called once at driver entry point .*/ 13162306a36Sopenharmony_ciint cc_fips_init(struct cc_drvdata *p_drvdata) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct cc_fips_handle *fips_h; 13462306a36Sopenharmony_ci struct device *dev = drvdata_to_dev(p_drvdata); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (p_drvdata->hw_rev < CC_HW_REV_712) 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci fips_h = devm_kzalloc(dev, sizeof(*fips_h), GFP_KERNEL); 14062306a36Sopenharmony_ci if (!fips_h) 14162306a36Sopenharmony_ci return -ENOMEM; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci p_drvdata->fips_handle = fips_h; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci dev_dbg(dev, "Initializing fips tasklet\n"); 14662306a36Sopenharmony_ci tasklet_init(&fips_h->tasklet, fips_dsr, (unsigned long)p_drvdata); 14762306a36Sopenharmony_ci fips_h->drvdata = p_drvdata; 14862306a36Sopenharmony_ci fips_h->nb.notifier_call = cc_ree_fips_failure; 14962306a36Sopenharmony_ci atomic_notifier_chain_register(&fips_fail_notif_chain, &fips_h->nb); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci cc_tee_handle_fips_error(p_drvdata); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci} 155