18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci/**
78c2ecf20Sopenharmony_ci * DOC: Nitro Enclaves (NE) PCI device driver.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/delay.h>
118c2ecf20Sopenharmony_ci#include <linux/device.h>
128c2ecf20Sopenharmony_ci#include <linux/list.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/mutex.h>
158c2ecf20Sopenharmony_ci#include <linux/nitro_enclaves.h>
168c2ecf20Sopenharmony_ci#include <linux/pci.h>
178c2ecf20Sopenharmony_ci#include <linux/types.h>
188c2ecf20Sopenharmony_ci#include <linux/wait.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "ne_misc_dev.h"
218c2ecf20Sopenharmony_ci#include "ne_pci_dev.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/**
248c2ecf20Sopenharmony_ci * NE_DEFAULT_TIMEOUT_MSECS - Default timeout to wait for a reply from
258c2ecf20Sopenharmony_ci *			      the NE PCI device.
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci#define NE_DEFAULT_TIMEOUT_MSECS	(120000) /* 120 sec */
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic const struct pci_device_id ne_pci_ids[] = {
308c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_AMAZON, PCI_DEVICE_ID_NE) },
318c2ecf20Sopenharmony_ci	{ 0, }
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ne_pci_ids);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/**
378c2ecf20Sopenharmony_ci * ne_submit_request() - Submit command request to the PCI device based on the
388c2ecf20Sopenharmony_ci *			 command type.
398c2ecf20Sopenharmony_ci * @pdev:		PCI device to send the command to.
408c2ecf20Sopenharmony_ci * @cmd_type:		Command type of the request sent to the PCI device.
418c2ecf20Sopenharmony_ci * @cmd_request:	Command request payload.
428c2ecf20Sopenharmony_ci * @cmd_request_size:	Size of the command request payload.
438c2ecf20Sopenharmony_ci *
448c2ecf20Sopenharmony_ci * Context: Process context. This function is called with the ne_pci_dev mutex held.
458c2ecf20Sopenharmony_ci */
468c2ecf20Sopenharmony_cistatic void ne_submit_request(struct pci_dev *pdev, enum ne_pci_dev_cmd_type cmd_type,
478c2ecf20Sopenharmony_ci			      void *cmd_request, size_t cmd_request_size)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	memcpy_toio(ne_pci_dev->iomem_base + NE_SEND_DATA, cmd_request, cmd_request_size);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	iowrite32(cmd_type, ne_pci_dev->iomem_base + NE_COMMAND);
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/**
578c2ecf20Sopenharmony_ci * ne_retrieve_reply() - Retrieve reply from the PCI device.
588c2ecf20Sopenharmony_ci * @pdev:		PCI device to receive the reply from.
598c2ecf20Sopenharmony_ci * @cmd_reply:		Command reply payload.
608c2ecf20Sopenharmony_ci * @cmd_reply_size:	Size of the command reply payload.
618c2ecf20Sopenharmony_ci *
628c2ecf20Sopenharmony_ci * Context: Process context. This function is called with the ne_pci_dev mutex held.
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_cistatic void ne_retrieve_reply(struct pci_dev *pdev, struct ne_pci_dev_cmd_reply *cmd_reply,
658c2ecf20Sopenharmony_ci			      size_t cmd_reply_size)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	memcpy_fromio(cmd_reply, ne_pci_dev->iomem_base + NE_RECV_DATA, cmd_reply_size);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/**
738c2ecf20Sopenharmony_ci * ne_wait_for_reply() - Wait for a reply of a PCI device command.
748c2ecf20Sopenharmony_ci * @pdev:	PCI device for which a reply is waited.
758c2ecf20Sopenharmony_ci *
768c2ecf20Sopenharmony_ci * Context: Process context. This function is called with the ne_pci_dev mutex held.
778c2ecf20Sopenharmony_ci * Return:
788c2ecf20Sopenharmony_ci * * 0 on success.
798c2ecf20Sopenharmony_ci * * Negative return value on failure.
808c2ecf20Sopenharmony_ci */
818c2ecf20Sopenharmony_cistatic int ne_wait_for_reply(struct pci_dev *pdev)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
848c2ecf20Sopenharmony_ci	int rc = -EINVAL;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	/*
878c2ecf20Sopenharmony_ci	 * TODO: Update to _interruptible and handle interrupted wait event
888c2ecf20Sopenharmony_ci	 * e.g. -ERESTARTSYS, incoming signals + update timeout, if needed.
898c2ecf20Sopenharmony_ci	 */
908c2ecf20Sopenharmony_ci	rc = wait_event_timeout(ne_pci_dev->cmd_reply_wait_q,
918c2ecf20Sopenharmony_ci				atomic_read(&ne_pci_dev->cmd_reply_avail) != 0,
928c2ecf20Sopenharmony_ci				msecs_to_jiffies(NE_DEFAULT_TIMEOUT_MSECS));
938c2ecf20Sopenharmony_ci	if (!rc)
948c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return 0;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ciint ne_do_request(struct pci_dev *pdev, enum ne_pci_dev_cmd_type cmd_type,
1008c2ecf20Sopenharmony_ci		  void *cmd_request, size_t cmd_request_size,
1018c2ecf20Sopenharmony_ci		  struct ne_pci_dev_cmd_reply *cmd_reply, size_t cmd_reply_size)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
1048c2ecf20Sopenharmony_ci	int rc = -EINVAL;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (cmd_type <= INVALID_CMD || cmd_type >= MAX_CMD) {
1078c2ecf20Sopenharmony_ci		dev_err_ratelimited(&pdev->dev, "Invalid cmd type=%u\n", cmd_type);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci		return -EINVAL;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (!cmd_request) {
1138c2ecf20Sopenharmony_ci		dev_err_ratelimited(&pdev->dev, "Null cmd request for cmd type=%u\n",
1148c2ecf20Sopenharmony_ci				    cmd_type);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci		return -EINVAL;
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	if (cmd_request_size > NE_SEND_DATA_SIZE) {
1208c2ecf20Sopenharmony_ci		dev_err_ratelimited(&pdev->dev, "Invalid req size=%zu for cmd type=%u\n",
1218c2ecf20Sopenharmony_ci				    cmd_request_size, cmd_type);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci		return -EINVAL;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (!cmd_reply) {
1278c2ecf20Sopenharmony_ci		dev_err_ratelimited(&pdev->dev, "Null cmd reply for cmd type=%u\n",
1288c2ecf20Sopenharmony_ci				    cmd_type);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci		return -EINVAL;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (cmd_reply_size > NE_RECV_DATA_SIZE) {
1348c2ecf20Sopenharmony_ci		dev_err_ratelimited(&pdev->dev, "Invalid reply size=%zu for cmd type=%u\n",
1358c2ecf20Sopenharmony_ci				    cmd_reply_size, cmd_type);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci		return -EINVAL;
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/*
1418c2ecf20Sopenharmony_ci	 * Use this mutex so that the PCI device handles one command request at
1428c2ecf20Sopenharmony_ci	 * a time.
1438c2ecf20Sopenharmony_ci	 */
1448c2ecf20Sopenharmony_ci	mutex_lock(&ne_pci_dev->pci_dev_mutex);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	atomic_set(&ne_pci_dev->cmd_reply_avail, 0);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	ne_submit_request(pdev, cmd_type, cmd_request, cmd_request_size);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	rc = ne_wait_for_reply(pdev);
1518c2ecf20Sopenharmony_ci	if (rc < 0) {
1528c2ecf20Sopenharmony_ci		dev_err_ratelimited(&pdev->dev, "Error in wait for reply for cmd type=%u [rc=%d]\n",
1538c2ecf20Sopenharmony_ci				    cmd_type, rc);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci		goto unlock_mutex;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	ne_retrieve_reply(pdev, cmd_reply, cmd_reply_size);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	atomic_set(&ne_pci_dev->cmd_reply_avail, 0);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (cmd_reply->rc < 0) {
1638c2ecf20Sopenharmony_ci		rc = cmd_reply->rc;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		dev_err_ratelimited(&pdev->dev, "Error in cmd process logic, cmd type=%u [rc=%d]\n",
1668c2ecf20Sopenharmony_ci				    cmd_type, rc);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci		goto unlock_mutex;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	rc = 0;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ciunlock_mutex:
1748c2ecf20Sopenharmony_ci	mutex_unlock(&ne_pci_dev->pci_dev_mutex);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	return rc;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci/**
1808c2ecf20Sopenharmony_ci * ne_reply_handler() - Interrupt handler for retrieving a reply matching a
1818c2ecf20Sopenharmony_ci *			request sent to the PCI device for enclave lifetime
1828c2ecf20Sopenharmony_ci *			management.
1838c2ecf20Sopenharmony_ci * @irq:	Received interrupt for a reply sent by the PCI device.
1848c2ecf20Sopenharmony_ci * @args:	PCI device private data structure.
1858c2ecf20Sopenharmony_ci *
1868c2ecf20Sopenharmony_ci * Context: Interrupt context.
1878c2ecf20Sopenharmony_ci * Return:
1888c2ecf20Sopenharmony_ci * * IRQ_HANDLED on handled interrupt.
1898c2ecf20Sopenharmony_ci */
1908c2ecf20Sopenharmony_cistatic irqreturn_t ne_reply_handler(int irq, void *args)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	struct ne_pci_dev *ne_pci_dev = (struct ne_pci_dev *)args;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	atomic_set(&ne_pci_dev->cmd_reply_avail, 1);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/* TODO: Update to _interruptible. */
1978c2ecf20Sopenharmony_ci	wake_up(&ne_pci_dev->cmd_reply_wait_q);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci/**
2038c2ecf20Sopenharmony_ci * ne_event_work_handler() - Work queue handler for notifying enclaves on a
2048c2ecf20Sopenharmony_ci *			     state change received by the event interrupt
2058c2ecf20Sopenharmony_ci *			     handler.
2068c2ecf20Sopenharmony_ci * @work:	Item containing the NE PCI device for which an out-of-band event
2078c2ecf20Sopenharmony_ci *		was issued.
2088c2ecf20Sopenharmony_ci *
2098c2ecf20Sopenharmony_ci * An out-of-band event is being issued by the Nitro Hypervisor when at least
2108c2ecf20Sopenharmony_ci * one enclave is changing state without client interaction.
2118c2ecf20Sopenharmony_ci *
2128c2ecf20Sopenharmony_ci * Context: Work queue context.
2138c2ecf20Sopenharmony_ci */
2148c2ecf20Sopenharmony_cistatic void ne_event_work_handler(struct work_struct *work)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	struct ne_pci_dev_cmd_reply cmd_reply = {};
2178c2ecf20Sopenharmony_ci	struct ne_enclave *ne_enclave = NULL;
2188c2ecf20Sopenharmony_ci	struct ne_pci_dev *ne_pci_dev =
2198c2ecf20Sopenharmony_ci		container_of(work, struct ne_pci_dev, notify_work);
2208c2ecf20Sopenharmony_ci	struct pci_dev *pdev = ne_pci_dev->pdev;
2218c2ecf20Sopenharmony_ci	int rc = -EINVAL;
2228c2ecf20Sopenharmony_ci	struct slot_info_req slot_info_req = {};
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	mutex_lock(&ne_pci_dev->enclaves_list_mutex);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/*
2278c2ecf20Sopenharmony_ci	 * Iterate over all enclaves registered for the Nitro Enclaves
2288c2ecf20Sopenharmony_ci	 * PCI device and determine for which enclave(s) the out-of-band event
2298c2ecf20Sopenharmony_ci	 * is corresponding to.
2308c2ecf20Sopenharmony_ci	 */
2318c2ecf20Sopenharmony_ci	list_for_each_entry(ne_enclave, &ne_pci_dev->enclaves_list, enclave_list_entry) {
2328c2ecf20Sopenharmony_ci		mutex_lock(&ne_enclave->enclave_info_mutex);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci		/*
2358c2ecf20Sopenharmony_ci		 * Enclaves that were never started cannot receive out-of-band
2368c2ecf20Sopenharmony_ci		 * events.
2378c2ecf20Sopenharmony_ci		 */
2388c2ecf20Sopenharmony_ci		if (ne_enclave->state != NE_STATE_RUNNING)
2398c2ecf20Sopenharmony_ci			goto unlock;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci		slot_info_req.slot_uid = ne_enclave->slot_uid;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		rc = ne_do_request(pdev, SLOT_INFO,
2448c2ecf20Sopenharmony_ci				   &slot_info_req, sizeof(slot_info_req),
2458c2ecf20Sopenharmony_ci				   &cmd_reply, sizeof(cmd_reply));
2468c2ecf20Sopenharmony_ci		if (rc < 0)
2478c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "Error in slot info [rc=%d]\n", rc);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci		/* Notify enclave process that the enclave state changed. */
2508c2ecf20Sopenharmony_ci		if (ne_enclave->state != cmd_reply.state) {
2518c2ecf20Sopenharmony_ci			ne_enclave->state = cmd_reply.state;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci			ne_enclave->has_event = true;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci			wake_up_interruptible(&ne_enclave->eventq);
2568c2ecf20Sopenharmony_ci		}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ciunlock:
2598c2ecf20Sopenharmony_ci		 mutex_unlock(&ne_enclave->enclave_info_mutex);
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	mutex_unlock(&ne_pci_dev->enclaves_list_mutex);
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci/**
2668c2ecf20Sopenharmony_ci * ne_event_handler() - Interrupt handler for PCI device out-of-band events.
2678c2ecf20Sopenharmony_ci *			This interrupt does not supply any data in the MMIO
2688c2ecf20Sopenharmony_ci *			region. It notifies a change in the state of any of
2698c2ecf20Sopenharmony_ci *			the launched enclaves.
2708c2ecf20Sopenharmony_ci * @irq:	Received interrupt for an out-of-band event.
2718c2ecf20Sopenharmony_ci * @args:	PCI device private data structure.
2728c2ecf20Sopenharmony_ci *
2738c2ecf20Sopenharmony_ci * Context: Interrupt context.
2748c2ecf20Sopenharmony_ci * Return:
2758c2ecf20Sopenharmony_ci * * IRQ_HANDLED on handled interrupt.
2768c2ecf20Sopenharmony_ci */
2778c2ecf20Sopenharmony_cistatic irqreturn_t ne_event_handler(int irq, void *args)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	struct ne_pci_dev *ne_pci_dev = (struct ne_pci_dev *)args;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	queue_work(ne_pci_dev->event_wq, &ne_pci_dev->notify_work);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci/**
2878c2ecf20Sopenharmony_ci * ne_setup_msix() - Setup MSI-X vectors for the PCI device.
2888c2ecf20Sopenharmony_ci * @pdev:	PCI device to setup the MSI-X for.
2898c2ecf20Sopenharmony_ci *
2908c2ecf20Sopenharmony_ci * Context: Process context.
2918c2ecf20Sopenharmony_ci * Return:
2928c2ecf20Sopenharmony_ci * * 0 on success.
2938c2ecf20Sopenharmony_ci * * Negative return value on failure.
2948c2ecf20Sopenharmony_ci */
2958c2ecf20Sopenharmony_cistatic int ne_setup_msix(struct pci_dev *pdev)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
2988c2ecf20Sopenharmony_ci	int nr_vecs = 0;
2998c2ecf20Sopenharmony_ci	int rc = -EINVAL;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	nr_vecs = pci_msix_vec_count(pdev);
3028c2ecf20Sopenharmony_ci	if (nr_vecs < 0) {
3038c2ecf20Sopenharmony_ci		rc = nr_vecs;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Error in getting vec count [rc=%d]\n", rc);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci		return rc;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	rc = pci_alloc_irq_vectors(pdev, nr_vecs, nr_vecs, PCI_IRQ_MSIX);
3118c2ecf20Sopenharmony_ci	if (rc < 0) {
3128c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Error in alloc MSI-X vecs [rc=%d]\n", rc);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci		return rc;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	/*
3188c2ecf20Sopenharmony_ci	 * This IRQ gets triggered every time the PCI device responds to a
3198c2ecf20Sopenharmony_ci	 * command request. The reply is then retrieved, reading from the MMIO
3208c2ecf20Sopenharmony_ci	 * space of the PCI device.
3218c2ecf20Sopenharmony_ci	 */
3228c2ecf20Sopenharmony_ci	rc = request_irq(pci_irq_vector(pdev, NE_VEC_REPLY), ne_reply_handler,
3238c2ecf20Sopenharmony_ci			 0, "enclave_cmd", ne_pci_dev);
3248c2ecf20Sopenharmony_ci	if (rc < 0) {
3258c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Error in request irq reply [rc=%d]\n", rc);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci		goto free_irq_vectors;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	ne_pci_dev->event_wq = create_singlethread_workqueue("ne_pci_dev_wq");
3318c2ecf20Sopenharmony_ci	if (!ne_pci_dev->event_wq) {
3328c2ecf20Sopenharmony_ci		rc = -ENOMEM;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Cannot get wq for dev events [rc=%d]\n", rc);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci		goto free_reply_irq_vec;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	INIT_WORK(&ne_pci_dev->notify_work, ne_event_work_handler);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	/*
3428c2ecf20Sopenharmony_ci	 * This IRQ gets triggered every time any enclave's state changes. Its
3438c2ecf20Sopenharmony_ci	 * handler then scans for the changes and propagates them to the user
3448c2ecf20Sopenharmony_ci	 * space.
3458c2ecf20Sopenharmony_ci	 */
3468c2ecf20Sopenharmony_ci	rc = request_irq(pci_irq_vector(pdev, NE_VEC_EVENT), ne_event_handler,
3478c2ecf20Sopenharmony_ci			 0, "enclave_evt", ne_pci_dev);
3488c2ecf20Sopenharmony_ci	if (rc < 0) {
3498c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Error in request irq event [rc=%d]\n", rc);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci		goto destroy_wq;
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	return 0;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cidestroy_wq:
3578c2ecf20Sopenharmony_ci	destroy_workqueue(ne_pci_dev->event_wq);
3588c2ecf20Sopenharmony_cifree_reply_irq_vec:
3598c2ecf20Sopenharmony_ci	free_irq(pci_irq_vector(pdev, NE_VEC_REPLY), ne_pci_dev);
3608c2ecf20Sopenharmony_cifree_irq_vectors:
3618c2ecf20Sopenharmony_ci	pci_free_irq_vectors(pdev);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	return rc;
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci/**
3678c2ecf20Sopenharmony_ci * ne_teardown_msix() - Teardown MSI-X vectors for the PCI device.
3688c2ecf20Sopenharmony_ci * @pdev:	PCI device to teardown the MSI-X for.
3698c2ecf20Sopenharmony_ci *
3708c2ecf20Sopenharmony_ci * Context: Process context.
3718c2ecf20Sopenharmony_ci */
3728c2ecf20Sopenharmony_cistatic void ne_teardown_msix(struct pci_dev *pdev)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	free_irq(pci_irq_vector(pdev, NE_VEC_EVENT), ne_pci_dev);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	flush_work(&ne_pci_dev->notify_work);
3798c2ecf20Sopenharmony_ci	flush_workqueue(ne_pci_dev->event_wq);
3808c2ecf20Sopenharmony_ci	destroy_workqueue(ne_pci_dev->event_wq);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	free_irq(pci_irq_vector(pdev, NE_VEC_REPLY), ne_pci_dev);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	pci_free_irq_vectors(pdev);
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci/**
3888c2ecf20Sopenharmony_ci * ne_pci_dev_enable() - Select the PCI device version and enable it.
3898c2ecf20Sopenharmony_ci * @pdev:	PCI device to select version for and then enable.
3908c2ecf20Sopenharmony_ci *
3918c2ecf20Sopenharmony_ci * Context: Process context.
3928c2ecf20Sopenharmony_ci * Return:
3938c2ecf20Sopenharmony_ci * * 0 on success.
3948c2ecf20Sopenharmony_ci * * Negative return value on failure.
3958c2ecf20Sopenharmony_ci */
3968c2ecf20Sopenharmony_cistatic int ne_pci_dev_enable(struct pci_dev *pdev)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	u8 dev_enable_reply = 0;
3998c2ecf20Sopenharmony_ci	u16 dev_version_reply = 0;
4008c2ecf20Sopenharmony_ci	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	iowrite16(NE_VERSION_MAX, ne_pci_dev->iomem_base + NE_VERSION);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	dev_version_reply = ioread16(ne_pci_dev->iomem_base + NE_VERSION);
4058c2ecf20Sopenharmony_ci	if (dev_version_reply != NE_VERSION_MAX) {
4068c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Error in pci dev version cmd\n");
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci		return -EIO;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	iowrite8(NE_ENABLE_ON, ne_pci_dev->iomem_base + NE_ENABLE);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	dev_enable_reply = ioread8(ne_pci_dev->iomem_base + NE_ENABLE);
4148c2ecf20Sopenharmony_ci	if (dev_enable_reply != NE_ENABLE_ON) {
4158c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Error in pci dev enable cmd\n");
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci		return -EIO;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	return 0;
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci/**
4248c2ecf20Sopenharmony_ci * ne_pci_dev_disable() - Disable the PCI device.
4258c2ecf20Sopenharmony_ci * @pdev:	PCI device to disable.
4268c2ecf20Sopenharmony_ci *
4278c2ecf20Sopenharmony_ci * Context: Process context.
4288c2ecf20Sopenharmony_ci */
4298c2ecf20Sopenharmony_cistatic void ne_pci_dev_disable(struct pci_dev *pdev)
4308c2ecf20Sopenharmony_ci{
4318c2ecf20Sopenharmony_ci	u8 dev_disable_reply = 0;
4328c2ecf20Sopenharmony_ci	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
4338c2ecf20Sopenharmony_ci	const unsigned int sleep_time = 10; /* 10 ms */
4348c2ecf20Sopenharmony_ci	unsigned int sleep_time_count = 0;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	iowrite8(NE_ENABLE_OFF, ne_pci_dev->iomem_base + NE_ENABLE);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	/*
4398c2ecf20Sopenharmony_ci	 * Check for NE_ENABLE_OFF in a loop, to handle cases when the device
4408c2ecf20Sopenharmony_ci	 * state is not immediately set to disabled and going through a
4418c2ecf20Sopenharmony_ci	 * transitory state of disabling.
4428c2ecf20Sopenharmony_ci	 */
4438c2ecf20Sopenharmony_ci	while (sleep_time_count < NE_DEFAULT_TIMEOUT_MSECS) {
4448c2ecf20Sopenharmony_ci		dev_disable_reply = ioread8(ne_pci_dev->iomem_base + NE_ENABLE);
4458c2ecf20Sopenharmony_ci		if (dev_disable_reply == NE_ENABLE_OFF)
4468c2ecf20Sopenharmony_ci			return;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci		msleep_interruptible(sleep_time);
4498c2ecf20Sopenharmony_ci		sleep_time_count += sleep_time;
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	dev_disable_reply = ioread8(ne_pci_dev->iomem_base + NE_ENABLE);
4538c2ecf20Sopenharmony_ci	if (dev_disable_reply != NE_ENABLE_OFF)
4548c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Error in pci dev disable cmd\n");
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci/**
4588c2ecf20Sopenharmony_ci * ne_pci_probe() - Probe function for the NE PCI device.
4598c2ecf20Sopenharmony_ci * @pdev:	PCI device to match with the NE PCI driver.
4608c2ecf20Sopenharmony_ci * @id :	PCI device id table associated with the NE PCI driver.
4618c2ecf20Sopenharmony_ci *
4628c2ecf20Sopenharmony_ci * Context: Process context.
4638c2ecf20Sopenharmony_ci * Return:
4648c2ecf20Sopenharmony_ci * * 0 on success.
4658c2ecf20Sopenharmony_ci * * Negative return value on failure.
4668c2ecf20Sopenharmony_ci */
4678c2ecf20Sopenharmony_cistatic int ne_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	struct ne_pci_dev *ne_pci_dev = NULL;
4708c2ecf20Sopenharmony_ci	int rc = -EINVAL;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	ne_pci_dev = kzalloc(sizeof(*ne_pci_dev), GFP_KERNEL);
4738c2ecf20Sopenharmony_ci	if (!ne_pci_dev)
4748c2ecf20Sopenharmony_ci		return -ENOMEM;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	rc = pci_enable_device(pdev);
4778c2ecf20Sopenharmony_ci	if (rc < 0) {
4788c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Error in pci dev enable [rc=%d]\n", rc);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		goto free_ne_pci_dev;
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	rc = pci_request_regions_exclusive(pdev, "nitro_enclaves");
4848c2ecf20Sopenharmony_ci	if (rc < 0) {
4858c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Error in pci request regions [rc=%d]\n", rc);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci		goto disable_pci_dev;
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	ne_pci_dev->iomem_base = pci_iomap(pdev, PCI_BAR_NE, 0);
4918c2ecf20Sopenharmony_ci	if (!ne_pci_dev->iomem_base) {
4928c2ecf20Sopenharmony_ci		rc = -ENOMEM;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Error in pci iomap [rc=%d]\n", rc);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci		goto release_pci_regions;
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, ne_pci_dev);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	rc = ne_setup_msix(pdev);
5028c2ecf20Sopenharmony_ci	if (rc < 0) {
5038c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Error in pci dev msix setup [rc=%d]\n", rc);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci		goto iounmap_pci_bar;
5068c2ecf20Sopenharmony_ci	}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	ne_pci_dev_disable(pdev);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	rc = ne_pci_dev_enable(pdev);
5118c2ecf20Sopenharmony_ci	if (rc < 0) {
5128c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Error in ne_pci_dev enable [rc=%d]\n", rc);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci		goto teardown_msix;
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	atomic_set(&ne_pci_dev->cmd_reply_avail, 0);
5188c2ecf20Sopenharmony_ci	init_waitqueue_head(&ne_pci_dev->cmd_reply_wait_q);
5198c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ne_pci_dev->enclaves_list);
5208c2ecf20Sopenharmony_ci	mutex_init(&ne_pci_dev->enclaves_list_mutex);
5218c2ecf20Sopenharmony_ci	mutex_init(&ne_pci_dev->pci_dev_mutex);
5228c2ecf20Sopenharmony_ci	ne_pci_dev->pdev = pdev;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	ne_devs.ne_pci_dev = ne_pci_dev;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	rc = misc_register(ne_devs.ne_misc_dev);
5278c2ecf20Sopenharmony_ci	if (rc < 0) {
5288c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Error in misc dev register [rc=%d]\n", rc);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci		goto disable_ne_pci_dev;
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	return 0;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_cidisable_ne_pci_dev:
5368c2ecf20Sopenharmony_ci	ne_devs.ne_pci_dev = NULL;
5378c2ecf20Sopenharmony_ci	ne_pci_dev_disable(pdev);
5388c2ecf20Sopenharmony_citeardown_msix:
5398c2ecf20Sopenharmony_ci	ne_teardown_msix(pdev);
5408c2ecf20Sopenharmony_ciiounmap_pci_bar:
5418c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
5428c2ecf20Sopenharmony_ci	pci_iounmap(pdev, ne_pci_dev->iomem_base);
5438c2ecf20Sopenharmony_cirelease_pci_regions:
5448c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
5458c2ecf20Sopenharmony_cidisable_pci_dev:
5468c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
5478c2ecf20Sopenharmony_cifree_ne_pci_dev:
5488c2ecf20Sopenharmony_ci	kfree(ne_pci_dev);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	return rc;
5518c2ecf20Sopenharmony_ci}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci/**
5548c2ecf20Sopenharmony_ci * ne_pci_remove() - Remove function for the NE PCI device.
5558c2ecf20Sopenharmony_ci * @pdev:	PCI device associated with the NE PCI driver.
5568c2ecf20Sopenharmony_ci *
5578c2ecf20Sopenharmony_ci * Context: Process context.
5588c2ecf20Sopenharmony_ci */
5598c2ecf20Sopenharmony_cistatic void ne_pci_remove(struct pci_dev *pdev)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	misc_deregister(ne_devs.ne_misc_dev);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	ne_devs.ne_pci_dev = NULL;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	ne_pci_dev_disable(pdev);
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	ne_teardown_msix(pdev);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	pci_iounmap(pdev, ne_pci_dev->iomem_base);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	kfree(ne_pci_dev);
5808c2ecf20Sopenharmony_ci}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci/**
5838c2ecf20Sopenharmony_ci * ne_pci_shutdown() - Shutdown function for the NE PCI device.
5848c2ecf20Sopenharmony_ci * @pdev:	PCI device associated with the NE PCI driver.
5858c2ecf20Sopenharmony_ci *
5868c2ecf20Sopenharmony_ci * Context: Process context.
5878c2ecf20Sopenharmony_ci */
5888c2ecf20Sopenharmony_cistatic void ne_pci_shutdown(struct pci_dev *pdev)
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	if (!ne_pci_dev)
5938c2ecf20Sopenharmony_ci		return;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	misc_deregister(ne_devs.ne_misc_dev);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	ne_devs.ne_pci_dev = NULL;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	ne_pci_dev_disable(pdev);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	ne_teardown_msix(pdev);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	pci_iounmap(pdev, ne_pci_dev->iomem_base);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	kfree(ne_pci_dev);
6128c2ecf20Sopenharmony_ci}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci/*
6158c2ecf20Sopenharmony_ci * TODO: Add suspend / resume functions for power management w/ CONFIG_PM, if
6168c2ecf20Sopenharmony_ci * needed.
6178c2ecf20Sopenharmony_ci */
6188c2ecf20Sopenharmony_ci/* NE PCI device driver. */
6198c2ecf20Sopenharmony_cistruct pci_driver ne_pci_driver = {
6208c2ecf20Sopenharmony_ci	.name		= "nitro_enclaves",
6218c2ecf20Sopenharmony_ci	.id_table	= ne_pci_ids,
6228c2ecf20Sopenharmony_ci	.probe		= ne_pci_probe,
6238c2ecf20Sopenharmony_ci	.remove		= ne_pci_remove,
6248c2ecf20Sopenharmony_ci	.shutdown	= ne_pci_shutdown,
6258c2ecf20Sopenharmony_ci};
626