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