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