18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Implementation of the Xen vTPM device frontend
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author:  Daniel De Graaf <dgdegra@tycho.nsa.gov>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/errno.h>
88c2ecf20Sopenharmony_ci#include <linux/err.h>
98c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
108c2ecf20Sopenharmony_ci#include <linux/freezer.h>
118c2ecf20Sopenharmony_ci#include <xen/xen.h>
128c2ecf20Sopenharmony_ci#include <xen/events.h>
138c2ecf20Sopenharmony_ci#include <xen/interface/io/tpmif.h>
148c2ecf20Sopenharmony_ci#include <xen/grant_table.h>
158c2ecf20Sopenharmony_ci#include <xen/xenbus.h>
168c2ecf20Sopenharmony_ci#include <xen/page.h>
178c2ecf20Sopenharmony_ci#include "tpm.h"
188c2ecf20Sopenharmony_ci#include <xen/platform_pci.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct tpm_private {
218c2ecf20Sopenharmony_ci	struct tpm_chip *chip;
228c2ecf20Sopenharmony_ci	struct xenbus_device *dev;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	struct vtpm_shared_page *shr;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	unsigned int evtchn;
278c2ecf20Sopenharmony_ci	int ring_ref;
288c2ecf20Sopenharmony_ci	domid_t backend_id;
298c2ecf20Sopenharmony_ci	int irq;
308c2ecf20Sopenharmony_ci	wait_queue_head_t read_queue;
318c2ecf20Sopenharmony_ci};
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cienum status_bits {
348c2ecf20Sopenharmony_ci	VTPM_STATUS_RUNNING  = 0x1,
358c2ecf20Sopenharmony_ci	VTPM_STATUS_IDLE     = 0x2,
368c2ecf20Sopenharmony_ci	VTPM_STATUS_RESULT   = 0x4,
378c2ecf20Sopenharmony_ci	VTPM_STATUS_CANCELED = 0x8,
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask,
418c2ecf20Sopenharmony_ci					bool check_cancel, bool *canceled)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	u8 status = chip->ops->status(chip);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	*canceled = false;
468c2ecf20Sopenharmony_ci	if ((status & mask) == mask)
478c2ecf20Sopenharmony_ci		return true;
488c2ecf20Sopenharmony_ci	if (check_cancel && chip->ops->req_canceled(chip, status)) {
498c2ecf20Sopenharmony_ci		*canceled = true;
508c2ecf20Sopenharmony_ci		return true;
518c2ecf20Sopenharmony_ci	}
528c2ecf20Sopenharmony_ci	return false;
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask,
568c2ecf20Sopenharmony_ci		unsigned long timeout, wait_queue_head_t *queue,
578c2ecf20Sopenharmony_ci		bool check_cancel)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	unsigned long stop;
608c2ecf20Sopenharmony_ci	long rc;
618c2ecf20Sopenharmony_ci	u8 status;
628c2ecf20Sopenharmony_ci	bool canceled = false;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	/* check current status */
658c2ecf20Sopenharmony_ci	status = chip->ops->status(chip);
668c2ecf20Sopenharmony_ci	if ((status & mask) == mask)
678c2ecf20Sopenharmony_ci		return 0;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	stop = jiffies + timeout;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (chip->flags & TPM_CHIP_FLAG_IRQ) {
728c2ecf20Sopenharmony_ciagain:
738c2ecf20Sopenharmony_ci		timeout = stop - jiffies;
748c2ecf20Sopenharmony_ci		if ((long)timeout <= 0)
758c2ecf20Sopenharmony_ci			return -ETIME;
768c2ecf20Sopenharmony_ci		rc = wait_event_interruptible_timeout(*queue,
778c2ecf20Sopenharmony_ci			wait_for_tpm_stat_cond(chip, mask, check_cancel,
788c2ecf20Sopenharmony_ci					       &canceled),
798c2ecf20Sopenharmony_ci			timeout);
808c2ecf20Sopenharmony_ci		if (rc > 0) {
818c2ecf20Sopenharmony_ci			if (canceled)
828c2ecf20Sopenharmony_ci				return -ECANCELED;
838c2ecf20Sopenharmony_ci			return 0;
848c2ecf20Sopenharmony_ci		}
858c2ecf20Sopenharmony_ci		if (rc == -ERESTARTSYS && freezing(current)) {
868c2ecf20Sopenharmony_ci			clear_thread_flag(TIF_SIGPENDING);
878c2ecf20Sopenharmony_ci			goto again;
888c2ecf20Sopenharmony_ci		}
898c2ecf20Sopenharmony_ci	} else {
908c2ecf20Sopenharmony_ci		do {
918c2ecf20Sopenharmony_ci			tpm_msleep(TPM_TIMEOUT);
928c2ecf20Sopenharmony_ci			status = chip->ops->status(chip);
938c2ecf20Sopenharmony_ci			if ((status & mask) == mask)
948c2ecf20Sopenharmony_ci				return 0;
958c2ecf20Sopenharmony_ci		} while (time_before(jiffies, stop));
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci	return -ETIME;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic u8 vtpm_status(struct tpm_chip *chip)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct tpm_private *priv = dev_get_drvdata(&chip->dev);
1038c2ecf20Sopenharmony_ci	switch (priv->shr->state) {
1048c2ecf20Sopenharmony_ci	case VTPM_STATE_IDLE:
1058c2ecf20Sopenharmony_ci		return VTPM_STATUS_IDLE | VTPM_STATUS_CANCELED;
1068c2ecf20Sopenharmony_ci	case VTPM_STATE_FINISH:
1078c2ecf20Sopenharmony_ci		return VTPM_STATUS_IDLE | VTPM_STATUS_RESULT;
1088c2ecf20Sopenharmony_ci	case VTPM_STATE_SUBMIT:
1098c2ecf20Sopenharmony_ci	case VTPM_STATE_CANCEL: /* cancel requested, not yet canceled */
1108c2ecf20Sopenharmony_ci		return VTPM_STATUS_RUNNING;
1118c2ecf20Sopenharmony_ci	default:
1128c2ecf20Sopenharmony_ci		return 0;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic bool vtpm_req_canceled(struct tpm_chip *chip, u8 status)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	return status & VTPM_STATUS_CANCELED;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic void vtpm_cancel(struct tpm_chip *chip)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct tpm_private *priv = dev_get_drvdata(&chip->dev);
1248c2ecf20Sopenharmony_ci	priv->shr->state = VTPM_STATE_CANCEL;
1258c2ecf20Sopenharmony_ci	wmb();
1268c2ecf20Sopenharmony_ci	notify_remote_via_evtchn(priv->evtchn);
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic unsigned int shr_data_offset(struct vtpm_shared_page *shr)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	return sizeof(*shr) + sizeof(u32) * shr->nr_extra_pages;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct tpm_private *priv = dev_get_drvdata(&chip->dev);
1378c2ecf20Sopenharmony_ci	struct vtpm_shared_page *shr = priv->shr;
1388c2ecf20Sopenharmony_ci	unsigned int offset = shr_data_offset(shr);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	u32 ordinal;
1418c2ecf20Sopenharmony_ci	unsigned long duration;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (offset > PAGE_SIZE)
1448c2ecf20Sopenharmony_ci		return -EINVAL;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	if (offset + count > PAGE_SIZE)
1478c2ecf20Sopenharmony_ci		return -EINVAL;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/* Wait for completion of any existing command or cancellation */
1508c2ecf20Sopenharmony_ci	if (wait_for_tpm_stat(chip, VTPM_STATUS_IDLE, chip->timeout_c,
1518c2ecf20Sopenharmony_ci			&priv->read_queue, true) < 0) {
1528c2ecf20Sopenharmony_ci		vtpm_cancel(chip);
1538c2ecf20Sopenharmony_ci		return -ETIME;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	memcpy(offset + (u8 *)shr, buf, count);
1578c2ecf20Sopenharmony_ci	shr->length = count;
1588c2ecf20Sopenharmony_ci	barrier();
1598c2ecf20Sopenharmony_ci	shr->state = VTPM_STATE_SUBMIT;
1608c2ecf20Sopenharmony_ci	wmb();
1618c2ecf20Sopenharmony_ci	notify_remote_via_evtchn(priv->evtchn);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	ordinal = be32_to_cpu(((struct tpm_header *)buf)->ordinal);
1648c2ecf20Sopenharmony_ci	duration = tpm_calc_ordinal_duration(chip, ordinal);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	if (wait_for_tpm_stat(chip, VTPM_STATUS_IDLE, duration,
1678c2ecf20Sopenharmony_ci			&priv->read_queue, true) < 0) {
1688c2ecf20Sopenharmony_ci		/* got a signal or timeout, try to cancel */
1698c2ecf20Sopenharmony_ci		vtpm_cancel(chip);
1708c2ecf20Sopenharmony_ci		return -ETIME;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return 0;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic int vtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct tpm_private *priv = dev_get_drvdata(&chip->dev);
1798c2ecf20Sopenharmony_ci	struct vtpm_shared_page *shr = priv->shr;
1808c2ecf20Sopenharmony_ci	unsigned int offset = shr_data_offset(shr);
1818c2ecf20Sopenharmony_ci	size_t length = shr->length;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (shr->state == VTPM_STATE_IDLE)
1848c2ecf20Sopenharmony_ci		return -ECANCELED;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* In theory the wait at the end of _send makes this one unnecessary */
1878c2ecf20Sopenharmony_ci	if (wait_for_tpm_stat(chip, VTPM_STATUS_RESULT, chip->timeout_c,
1888c2ecf20Sopenharmony_ci			&priv->read_queue, true) < 0) {
1898c2ecf20Sopenharmony_ci		vtpm_cancel(chip);
1908c2ecf20Sopenharmony_ci		return -ETIME;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	if (offset > PAGE_SIZE)
1948c2ecf20Sopenharmony_ci		return -EIO;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (offset + length > PAGE_SIZE)
1978c2ecf20Sopenharmony_ci		length = PAGE_SIZE - offset;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	if (length > count)
2008c2ecf20Sopenharmony_ci		length = count;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	memcpy(buf, offset + (u8 *)shr, length);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	return length;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic const struct tpm_class_ops tpm_vtpm = {
2088c2ecf20Sopenharmony_ci	.status = vtpm_status,
2098c2ecf20Sopenharmony_ci	.recv = vtpm_recv,
2108c2ecf20Sopenharmony_ci	.send = vtpm_send,
2118c2ecf20Sopenharmony_ci	.cancel = vtpm_cancel,
2128c2ecf20Sopenharmony_ci	.req_complete_mask = VTPM_STATUS_IDLE | VTPM_STATUS_RESULT,
2138c2ecf20Sopenharmony_ci	.req_complete_val  = VTPM_STATUS_IDLE | VTPM_STATUS_RESULT,
2148c2ecf20Sopenharmony_ci	.req_canceled      = vtpm_req_canceled,
2158c2ecf20Sopenharmony_ci};
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic irqreturn_t tpmif_interrupt(int dummy, void *dev_id)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct tpm_private *priv = dev_id;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	switch (priv->shr->state) {
2228c2ecf20Sopenharmony_ci	case VTPM_STATE_IDLE:
2238c2ecf20Sopenharmony_ci	case VTPM_STATE_FINISH:
2248c2ecf20Sopenharmony_ci		wake_up_interruptible(&priv->read_queue);
2258c2ecf20Sopenharmony_ci		break;
2268c2ecf20Sopenharmony_ci	case VTPM_STATE_SUBMIT:
2278c2ecf20Sopenharmony_ci	case VTPM_STATE_CANCEL:
2288c2ecf20Sopenharmony_ci	default:
2298c2ecf20Sopenharmony_ci		break;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic int setup_chip(struct device *dev, struct tpm_private *priv)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	struct tpm_chip *chip;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	chip = tpmm_chip_alloc(dev, &tpm_vtpm);
2398c2ecf20Sopenharmony_ci	if (IS_ERR(chip))
2408c2ecf20Sopenharmony_ci		return PTR_ERR(chip);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	init_waitqueue_head(&priv->read_queue);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	priv->chip = chip;
2458c2ecf20Sopenharmony_ci	dev_set_drvdata(&chip->dev, priv);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	return 0;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci/* caller must clean up in case of errors */
2518c2ecf20Sopenharmony_cistatic int setup_ring(struct xenbus_device *dev, struct tpm_private *priv)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct xenbus_transaction xbt;
2548c2ecf20Sopenharmony_ci	const char *message = NULL;
2558c2ecf20Sopenharmony_ci	int rv;
2568c2ecf20Sopenharmony_ci	grant_ref_t gref;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	priv->shr = (void *)__get_free_page(GFP_KERNEL|__GFP_ZERO);
2598c2ecf20Sopenharmony_ci	if (!priv->shr) {
2608c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, -ENOMEM, "allocating shared ring");
2618c2ecf20Sopenharmony_ci		return -ENOMEM;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	rv = xenbus_grant_ring(dev, priv->shr, 1, &gref);
2658c2ecf20Sopenharmony_ci	if (rv < 0)
2668c2ecf20Sopenharmony_ci		return rv;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	priv->ring_ref = gref;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	rv = xenbus_alloc_evtchn(dev, &priv->evtchn);
2718c2ecf20Sopenharmony_ci	if (rv)
2728c2ecf20Sopenharmony_ci		return rv;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	rv = bind_evtchn_to_irqhandler(priv->evtchn, tpmif_interrupt, 0,
2758c2ecf20Sopenharmony_ci				       "tpmif", priv);
2768c2ecf20Sopenharmony_ci	if (rv <= 0) {
2778c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, rv, "allocating TPM irq");
2788c2ecf20Sopenharmony_ci		return rv;
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci	priv->irq = rv;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci again:
2838c2ecf20Sopenharmony_ci	rv = xenbus_transaction_start(&xbt);
2848c2ecf20Sopenharmony_ci	if (rv) {
2858c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, rv, "starting transaction");
2868c2ecf20Sopenharmony_ci		return rv;
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	rv = xenbus_printf(xbt, dev->nodename,
2908c2ecf20Sopenharmony_ci			"ring-ref", "%u", priv->ring_ref);
2918c2ecf20Sopenharmony_ci	if (rv) {
2928c2ecf20Sopenharmony_ci		message = "writing ring-ref";
2938c2ecf20Sopenharmony_ci		goto abort_transaction;
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	rv = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
2978c2ecf20Sopenharmony_ci			priv->evtchn);
2988c2ecf20Sopenharmony_ci	if (rv) {
2998c2ecf20Sopenharmony_ci		message = "writing event-channel";
3008c2ecf20Sopenharmony_ci		goto abort_transaction;
3018c2ecf20Sopenharmony_ci	}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	rv = xenbus_printf(xbt, dev->nodename, "feature-protocol-v2", "1");
3048c2ecf20Sopenharmony_ci	if (rv) {
3058c2ecf20Sopenharmony_ci		message = "writing feature-protocol-v2";
3068c2ecf20Sopenharmony_ci		goto abort_transaction;
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	rv = xenbus_transaction_end(xbt, 0);
3108c2ecf20Sopenharmony_ci	if (rv == -EAGAIN)
3118c2ecf20Sopenharmony_ci		goto again;
3128c2ecf20Sopenharmony_ci	if (rv) {
3138c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, rv, "completing transaction");
3148c2ecf20Sopenharmony_ci		return rv;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	xenbus_switch_state(dev, XenbusStateInitialised);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	return 0;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci abort_transaction:
3228c2ecf20Sopenharmony_ci	xenbus_transaction_end(xbt, 1);
3238c2ecf20Sopenharmony_ci	if (message)
3248c2ecf20Sopenharmony_ci		xenbus_dev_error(dev, rv, "%s", message);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	return rv;
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic void ring_free(struct tpm_private *priv)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	if (!priv)
3328c2ecf20Sopenharmony_ci		return;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	if (priv->ring_ref)
3358c2ecf20Sopenharmony_ci		gnttab_end_foreign_access(priv->ring_ref, 0,
3368c2ecf20Sopenharmony_ci				(unsigned long)priv->shr);
3378c2ecf20Sopenharmony_ci	else
3388c2ecf20Sopenharmony_ci		free_page((unsigned long)priv->shr);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	if (priv->irq)
3418c2ecf20Sopenharmony_ci		unbind_from_irqhandler(priv->irq, priv);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	kfree(priv);
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic int tpmfront_probe(struct xenbus_device *dev,
3478c2ecf20Sopenharmony_ci		const struct xenbus_device_id *id)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct tpm_private *priv;
3508c2ecf20Sopenharmony_ci	int rv;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
3538c2ecf20Sopenharmony_ci	if (!priv) {
3548c2ecf20Sopenharmony_ci		xenbus_dev_fatal(dev, -ENOMEM, "allocating priv structure");
3558c2ecf20Sopenharmony_ci		return -ENOMEM;
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	rv = setup_chip(&dev->dev, priv);
3598c2ecf20Sopenharmony_ci	if (rv) {
3608c2ecf20Sopenharmony_ci		kfree(priv);
3618c2ecf20Sopenharmony_ci		return rv;
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	rv = setup_ring(dev, priv);
3658c2ecf20Sopenharmony_ci	if (rv) {
3668c2ecf20Sopenharmony_ci		ring_free(priv);
3678c2ecf20Sopenharmony_ci		return rv;
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	tpm_get_timeouts(priv->chip);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	return tpm_chip_register(priv->chip);
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic int tpmfront_remove(struct xenbus_device *dev)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	struct tpm_chip *chip = dev_get_drvdata(&dev->dev);
3788c2ecf20Sopenharmony_ci	struct tpm_private *priv = dev_get_drvdata(&chip->dev);
3798c2ecf20Sopenharmony_ci	tpm_chip_unregister(chip);
3808c2ecf20Sopenharmony_ci	ring_free(priv);
3818c2ecf20Sopenharmony_ci	dev_set_drvdata(&chip->dev, NULL);
3828c2ecf20Sopenharmony_ci	return 0;
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic int tpmfront_resume(struct xenbus_device *dev)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	/* A suspend/resume/migrate will interrupt a vTPM anyway */
3888c2ecf20Sopenharmony_ci	tpmfront_remove(dev);
3898c2ecf20Sopenharmony_ci	return tpmfront_probe(dev, NULL);
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic void backend_changed(struct xenbus_device *dev,
3938c2ecf20Sopenharmony_ci		enum xenbus_state backend_state)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	switch (backend_state) {
3968c2ecf20Sopenharmony_ci	case XenbusStateInitialised:
3978c2ecf20Sopenharmony_ci	case XenbusStateConnected:
3988c2ecf20Sopenharmony_ci		if (dev->state == XenbusStateConnected)
3998c2ecf20Sopenharmony_ci			break;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci		if (!xenbus_read_unsigned(dev->otherend, "feature-protocol-v2",
4028c2ecf20Sopenharmony_ci					  0)) {
4038c2ecf20Sopenharmony_ci			xenbus_dev_fatal(dev, -EINVAL,
4048c2ecf20Sopenharmony_ci					"vTPM protocol 2 required");
4058c2ecf20Sopenharmony_ci			return;
4068c2ecf20Sopenharmony_ci		}
4078c2ecf20Sopenharmony_ci		xenbus_switch_state(dev, XenbusStateConnected);
4088c2ecf20Sopenharmony_ci		break;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	case XenbusStateClosing:
4118c2ecf20Sopenharmony_ci	case XenbusStateClosed:
4128c2ecf20Sopenharmony_ci		device_unregister(&dev->dev);
4138c2ecf20Sopenharmony_ci		xenbus_frontend_closed(dev);
4148c2ecf20Sopenharmony_ci		break;
4158c2ecf20Sopenharmony_ci	default:
4168c2ecf20Sopenharmony_ci		break;
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic const struct xenbus_device_id tpmfront_ids[] = {
4218c2ecf20Sopenharmony_ci	{ "vtpm" },
4228c2ecf20Sopenharmony_ci	{ "" }
4238c2ecf20Sopenharmony_ci};
4248c2ecf20Sopenharmony_ciMODULE_ALIAS("xen:vtpm");
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic struct xenbus_driver tpmfront_driver = {
4278c2ecf20Sopenharmony_ci	.ids = tpmfront_ids,
4288c2ecf20Sopenharmony_ci	.probe = tpmfront_probe,
4298c2ecf20Sopenharmony_ci	.remove = tpmfront_remove,
4308c2ecf20Sopenharmony_ci	.resume = tpmfront_resume,
4318c2ecf20Sopenharmony_ci	.otherend_changed = backend_changed,
4328c2ecf20Sopenharmony_ci};
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic int __init xen_tpmfront_init(void)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	if (!xen_domain())
4378c2ecf20Sopenharmony_ci		return -ENODEV;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	if (!xen_has_pv_devices())
4408c2ecf20Sopenharmony_ci		return -ENODEV;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	return xenbus_register_frontend(&tpmfront_driver);
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_cimodule_init(xen_tpmfront_init);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistatic void __exit xen_tpmfront_exit(void)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	xenbus_unregister_driver(&tpmfront_driver);
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_cimodule_exit(xen_tpmfront_exit);
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ciMODULE_AUTHOR("Daniel De Graaf <dgdegra@tycho.nsa.gov>");
4538c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Xen vTPM Driver");
4548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
455