162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci/** 762306a36Sopenharmony_ci * DOC: Nitro Enclaves (NE) PCI device driver. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/device.h> 1262306a36Sopenharmony_ci#include <linux/list.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/mutex.h> 1562306a36Sopenharmony_ci#include <linux/nitro_enclaves.h> 1662306a36Sopenharmony_ci#include <linux/pci.h> 1762306a36Sopenharmony_ci#include <linux/types.h> 1862306a36Sopenharmony_ci#include <linux/wait.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "ne_misc_dev.h" 2162306a36Sopenharmony_ci#include "ne_pci_dev.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/** 2462306a36Sopenharmony_ci * NE_DEFAULT_TIMEOUT_MSECS - Default timeout to wait for a reply from 2562306a36Sopenharmony_ci * the NE PCI device. 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci#define NE_DEFAULT_TIMEOUT_MSECS (120000) /* 120 sec */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic const struct pci_device_id ne_pci_ids[] = { 3062306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMAZON, PCI_DEVICE_ID_NE) }, 3162306a36Sopenharmony_ci { 0, } 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ne_pci_ids); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/** 3762306a36Sopenharmony_ci * ne_submit_request() - Submit command request to the PCI device based on the 3862306a36Sopenharmony_ci * command type. 3962306a36Sopenharmony_ci * @pdev: PCI device to send the command to. 4062306a36Sopenharmony_ci * @cmd_type: Command type of the request sent to the PCI device. 4162306a36Sopenharmony_ci * @cmd_request: Command request payload. 4262306a36Sopenharmony_ci * @cmd_request_size: Size of the command request payload. 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_pci_dev mutex held. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_cistatic void ne_submit_request(struct pci_dev *pdev, enum ne_pci_dev_cmd_type cmd_type, 4762306a36Sopenharmony_ci void *cmd_request, size_t cmd_request_size) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci memcpy_toio(ne_pci_dev->iomem_base + NE_SEND_DATA, cmd_request, cmd_request_size); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci iowrite32(cmd_type, ne_pci_dev->iomem_base + NE_COMMAND); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/** 5762306a36Sopenharmony_ci * ne_retrieve_reply() - Retrieve reply from the PCI device. 5862306a36Sopenharmony_ci * @pdev: PCI device to receive the reply from. 5962306a36Sopenharmony_ci * @cmd_reply: Command reply payload. 6062306a36Sopenharmony_ci * @cmd_reply_size: Size of the command reply payload. 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_pci_dev mutex held. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_cistatic void ne_retrieve_reply(struct pci_dev *pdev, struct ne_pci_dev_cmd_reply *cmd_reply, 6562306a36Sopenharmony_ci size_t cmd_reply_size) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci memcpy_fromio(cmd_reply, ne_pci_dev->iomem_base + NE_RECV_DATA, cmd_reply_size); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/** 7362306a36Sopenharmony_ci * ne_wait_for_reply() - Wait for a reply of a PCI device command. 7462306a36Sopenharmony_ci * @pdev: PCI device for which a reply is waited. 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * Context: Process context. This function is called with the ne_pci_dev mutex held. 7762306a36Sopenharmony_ci * Return: 7862306a36Sopenharmony_ci * * 0 on success. 7962306a36Sopenharmony_ci * * Negative return value on failure. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_cistatic int ne_wait_for_reply(struct pci_dev *pdev) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev); 8462306a36Sopenharmony_ci int rc = -EINVAL; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* 8762306a36Sopenharmony_ci * TODO: Update to _interruptible and handle interrupted wait event 8862306a36Sopenharmony_ci * e.g. -ERESTARTSYS, incoming signals + update timeout, if needed. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci rc = wait_event_timeout(ne_pci_dev->cmd_reply_wait_q, 9162306a36Sopenharmony_ci atomic_read(&ne_pci_dev->cmd_reply_avail) != 0, 9262306a36Sopenharmony_ci msecs_to_jiffies(NE_DEFAULT_TIMEOUT_MSECS)); 9362306a36Sopenharmony_ci if (!rc) 9462306a36Sopenharmony_ci return -ETIMEDOUT; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return 0; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ciint ne_do_request(struct pci_dev *pdev, enum ne_pci_dev_cmd_type cmd_type, 10062306a36Sopenharmony_ci void *cmd_request, size_t cmd_request_size, 10162306a36Sopenharmony_ci struct ne_pci_dev_cmd_reply *cmd_reply, size_t cmd_reply_size) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev); 10462306a36Sopenharmony_ci int rc = -EINVAL; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (cmd_type <= INVALID_CMD || cmd_type >= MAX_CMD) { 10762306a36Sopenharmony_ci dev_err_ratelimited(&pdev->dev, "Invalid cmd type=%u\n", cmd_type); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return -EINVAL; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (!cmd_request) { 11362306a36Sopenharmony_ci dev_err_ratelimited(&pdev->dev, "Null cmd request for cmd type=%u\n", 11462306a36Sopenharmony_ci cmd_type); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return -EINVAL; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (cmd_request_size > NE_SEND_DATA_SIZE) { 12062306a36Sopenharmony_ci dev_err_ratelimited(&pdev->dev, "Invalid req size=%zu for cmd type=%u\n", 12162306a36Sopenharmony_ci cmd_request_size, cmd_type); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return -EINVAL; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (!cmd_reply) { 12762306a36Sopenharmony_ci dev_err_ratelimited(&pdev->dev, "Null cmd reply for cmd type=%u\n", 12862306a36Sopenharmony_ci cmd_type); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return -EINVAL; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (cmd_reply_size > NE_RECV_DATA_SIZE) { 13462306a36Sopenharmony_ci dev_err_ratelimited(&pdev->dev, "Invalid reply size=%zu for cmd type=%u\n", 13562306a36Sopenharmony_ci cmd_reply_size, cmd_type); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return -EINVAL; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* 14162306a36Sopenharmony_ci * Use this mutex so that the PCI device handles one command request at 14262306a36Sopenharmony_ci * a time. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci mutex_lock(&ne_pci_dev->pci_dev_mutex); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci atomic_set(&ne_pci_dev->cmd_reply_avail, 0); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci ne_submit_request(pdev, cmd_type, cmd_request, cmd_request_size); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci rc = ne_wait_for_reply(pdev); 15162306a36Sopenharmony_ci if (rc < 0) { 15262306a36Sopenharmony_ci dev_err_ratelimited(&pdev->dev, "Error in wait for reply for cmd type=%u [rc=%d]\n", 15362306a36Sopenharmony_ci cmd_type, rc); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci goto unlock_mutex; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci ne_retrieve_reply(pdev, cmd_reply, cmd_reply_size); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci atomic_set(&ne_pci_dev->cmd_reply_avail, 0); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (cmd_reply->rc < 0) { 16362306a36Sopenharmony_ci rc = cmd_reply->rc; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci dev_err_ratelimited(&pdev->dev, "Error in cmd process logic, cmd type=%u [rc=%d]\n", 16662306a36Sopenharmony_ci cmd_type, rc); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci goto unlock_mutex; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci rc = 0; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ciunlock_mutex: 17462306a36Sopenharmony_ci mutex_unlock(&ne_pci_dev->pci_dev_mutex); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return rc; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/** 18062306a36Sopenharmony_ci * ne_reply_handler() - Interrupt handler for retrieving a reply matching a 18162306a36Sopenharmony_ci * request sent to the PCI device for enclave lifetime 18262306a36Sopenharmony_ci * management. 18362306a36Sopenharmony_ci * @irq: Received interrupt for a reply sent by the PCI device. 18462306a36Sopenharmony_ci * @args: PCI device private data structure. 18562306a36Sopenharmony_ci * 18662306a36Sopenharmony_ci * Context: Interrupt context. 18762306a36Sopenharmony_ci * Return: 18862306a36Sopenharmony_ci * * IRQ_HANDLED on handled interrupt. 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_cistatic irqreturn_t ne_reply_handler(int irq, void *args) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct ne_pci_dev *ne_pci_dev = (struct ne_pci_dev *)args; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci atomic_set(&ne_pci_dev->cmd_reply_avail, 1); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* TODO: Update to _interruptible. */ 19762306a36Sopenharmony_ci wake_up(&ne_pci_dev->cmd_reply_wait_q); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return IRQ_HANDLED; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/** 20362306a36Sopenharmony_ci * ne_event_work_handler() - Work queue handler for notifying enclaves on a 20462306a36Sopenharmony_ci * state change received by the event interrupt 20562306a36Sopenharmony_ci * handler. 20662306a36Sopenharmony_ci * @work: Item containing the NE PCI device for which an out-of-band event 20762306a36Sopenharmony_ci * was issued. 20862306a36Sopenharmony_ci * 20962306a36Sopenharmony_ci * An out-of-band event is being issued by the Nitro Hypervisor when at least 21062306a36Sopenharmony_ci * one enclave is changing state without client interaction. 21162306a36Sopenharmony_ci * 21262306a36Sopenharmony_ci * Context: Work queue context. 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_cistatic void ne_event_work_handler(struct work_struct *work) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci struct ne_pci_dev_cmd_reply cmd_reply = {}; 21762306a36Sopenharmony_ci struct ne_enclave *ne_enclave = NULL; 21862306a36Sopenharmony_ci struct ne_pci_dev *ne_pci_dev = 21962306a36Sopenharmony_ci container_of(work, struct ne_pci_dev, notify_work); 22062306a36Sopenharmony_ci struct pci_dev *pdev = ne_pci_dev->pdev; 22162306a36Sopenharmony_ci int rc = -EINVAL; 22262306a36Sopenharmony_ci struct slot_info_req slot_info_req = {}; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci mutex_lock(&ne_pci_dev->enclaves_list_mutex); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* 22762306a36Sopenharmony_ci * Iterate over all enclaves registered for the Nitro Enclaves 22862306a36Sopenharmony_ci * PCI device and determine for which enclave(s) the out-of-band event 22962306a36Sopenharmony_ci * is corresponding to. 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci list_for_each_entry(ne_enclave, &ne_pci_dev->enclaves_list, enclave_list_entry) { 23262306a36Sopenharmony_ci mutex_lock(&ne_enclave->enclave_info_mutex); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* 23562306a36Sopenharmony_ci * Enclaves that were never started cannot receive out-of-band 23662306a36Sopenharmony_ci * events. 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ci if (ne_enclave->state != NE_STATE_RUNNING) 23962306a36Sopenharmony_ci goto unlock; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci slot_info_req.slot_uid = ne_enclave->slot_uid; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci rc = ne_do_request(pdev, SLOT_INFO, 24462306a36Sopenharmony_ci &slot_info_req, sizeof(slot_info_req), 24562306a36Sopenharmony_ci &cmd_reply, sizeof(cmd_reply)); 24662306a36Sopenharmony_ci if (rc < 0) 24762306a36Sopenharmony_ci dev_err(&pdev->dev, "Error in slot info [rc=%d]\n", rc); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* Notify enclave process that the enclave state changed. */ 25062306a36Sopenharmony_ci if (ne_enclave->state != cmd_reply.state) { 25162306a36Sopenharmony_ci ne_enclave->state = cmd_reply.state; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci ne_enclave->has_event = true; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci wake_up_interruptible(&ne_enclave->eventq); 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ciunlock: 25962306a36Sopenharmony_ci mutex_unlock(&ne_enclave->enclave_info_mutex); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci mutex_unlock(&ne_pci_dev->enclaves_list_mutex); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/** 26662306a36Sopenharmony_ci * ne_event_handler() - Interrupt handler for PCI device out-of-band events. 26762306a36Sopenharmony_ci * This interrupt does not supply any data in the MMIO 26862306a36Sopenharmony_ci * region. It notifies a change in the state of any of 26962306a36Sopenharmony_ci * the launched enclaves. 27062306a36Sopenharmony_ci * @irq: Received interrupt for an out-of-band event. 27162306a36Sopenharmony_ci * @args: PCI device private data structure. 27262306a36Sopenharmony_ci * 27362306a36Sopenharmony_ci * Context: Interrupt context. 27462306a36Sopenharmony_ci * Return: 27562306a36Sopenharmony_ci * * IRQ_HANDLED on handled interrupt. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_cistatic irqreturn_t ne_event_handler(int irq, void *args) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct ne_pci_dev *ne_pci_dev = (struct ne_pci_dev *)args; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci queue_work(ne_pci_dev->event_wq, &ne_pci_dev->notify_work); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci return IRQ_HANDLED; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci/** 28762306a36Sopenharmony_ci * ne_setup_msix() - Setup MSI-X vectors for the PCI device. 28862306a36Sopenharmony_ci * @pdev: PCI device to setup the MSI-X for. 28962306a36Sopenharmony_ci * 29062306a36Sopenharmony_ci * Context: Process context. 29162306a36Sopenharmony_ci * Return: 29262306a36Sopenharmony_ci * * 0 on success. 29362306a36Sopenharmony_ci * * Negative return value on failure. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_cistatic int ne_setup_msix(struct pci_dev *pdev) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev); 29862306a36Sopenharmony_ci int nr_vecs = 0; 29962306a36Sopenharmony_ci int rc = -EINVAL; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci nr_vecs = pci_msix_vec_count(pdev); 30262306a36Sopenharmony_ci if (nr_vecs < 0) { 30362306a36Sopenharmony_ci rc = nr_vecs; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci dev_err(&pdev->dev, "Error in getting vec count [rc=%d]\n", rc); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return rc; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci rc = pci_alloc_irq_vectors(pdev, nr_vecs, nr_vecs, PCI_IRQ_MSIX); 31162306a36Sopenharmony_ci if (rc < 0) { 31262306a36Sopenharmony_ci dev_err(&pdev->dev, "Error in alloc MSI-X vecs [rc=%d]\n", rc); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return rc; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* 31862306a36Sopenharmony_ci * This IRQ gets triggered every time the PCI device responds to a 31962306a36Sopenharmony_ci * command request. The reply is then retrieved, reading from the MMIO 32062306a36Sopenharmony_ci * space of the PCI device. 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_ci rc = request_irq(pci_irq_vector(pdev, NE_VEC_REPLY), ne_reply_handler, 32362306a36Sopenharmony_ci 0, "enclave_cmd", ne_pci_dev); 32462306a36Sopenharmony_ci if (rc < 0) { 32562306a36Sopenharmony_ci dev_err(&pdev->dev, "Error in request irq reply [rc=%d]\n", rc); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci goto free_irq_vectors; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci ne_pci_dev->event_wq = create_singlethread_workqueue("ne_pci_dev_wq"); 33162306a36Sopenharmony_ci if (!ne_pci_dev->event_wq) { 33262306a36Sopenharmony_ci rc = -ENOMEM; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot get wq for dev events [rc=%d]\n", rc); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci goto free_reply_irq_vec; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci INIT_WORK(&ne_pci_dev->notify_work, ne_event_work_handler); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* 34262306a36Sopenharmony_ci * This IRQ gets triggered every time any enclave's state changes. Its 34362306a36Sopenharmony_ci * handler then scans for the changes and propagates them to the user 34462306a36Sopenharmony_ci * space. 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ci rc = request_irq(pci_irq_vector(pdev, NE_VEC_EVENT), ne_event_handler, 34762306a36Sopenharmony_ci 0, "enclave_evt", ne_pci_dev); 34862306a36Sopenharmony_ci if (rc < 0) { 34962306a36Sopenharmony_ci dev_err(&pdev->dev, "Error in request irq event [rc=%d]\n", rc); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci goto destroy_wq; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci return 0; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cidestroy_wq: 35762306a36Sopenharmony_ci destroy_workqueue(ne_pci_dev->event_wq); 35862306a36Sopenharmony_cifree_reply_irq_vec: 35962306a36Sopenharmony_ci free_irq(pci_irq_vector(pdev, NE_VEC_REPLY), ne_pci_dev); 36062306a36Sopenharmony_cifree_irq_vectors: 36162306a36Sopenharmony_ci pci_free_irq_vectors(pdev); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return rc; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci/** 36762306a36Sopenharmony_ci * ne_teardown_msix() - Teardown MSI-X vectors for the PCI device. 36862306a36Sopenharmony_ci * @pdev: PCI device to teardown the MSI-X for. 36962306a36Sopenharmony_ci * 37062306a36Sopenharmony_ci * Context: Process context. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_cistatic void ne_teardown_msix(struct pci_dev *pdev) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci free_irq(pci_irq_vector(pdev, NE_VEC_EVENT), ne_pci_dev); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci flush_work(&ne_pci_dev->notify_work); 37962306a36Sopenharmony_ci destroy_workqueue(ne_pci_dev->event_wq); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci free_irq(pci_irq_vector(pdev, NE_VEC_REPLY), ne_pci_dev); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci pci_free_irq_vectors(pdev); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/** 38762306a36Sopenharmony_ci * ne_pci_dev_enable() - Select the PCI device version and enable it. 38862306a36Sopenharmony_ci * @pdev: PCI device to select version for and then enable. 38962306a36Sopenharmony_ci * 39062306a36Sopenharmony_ci * Context: Process context. 39162306a36Sopenharmony_ci * Return: 39262306a36Sopenharmony_ci * * 0 on success. 39362306a36Sopenharmony_ci * * Negative return value on failure. 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_cistatic int ne_pci_dev_enable(struct pci_dev *pdev) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci u8 dev_enable_reply = 0; 39862306a36Sopenharmony_ci u16 dev_version_reply = 0; 39962306a36Sopenharmony_ci struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci iowrite16(NE_VERSION_MAX, ne_pci_dev->iomem_base + NE_VERSION); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci dev_version_reply = ioread16(ne_pci_dev->iomem_base + NE_VERSION); 40462306a36Sopenharmony_ci if (dev_version_reply != NE_VERSION_MAX) { 40562306a36Sopenharmony_ci dev_err(&pdev->dev, "Error in pci dev version cmd\n"); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return -EIO; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci iowrite8(NE_ENABLE_ON, ne_pci_dev->iomem_base + NE_ENABLE); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci dev_enable_reply = ioread8(ne_pci_dev->iomem_base + NE_ENABLE); 41362306a36Sopenharmony_ci if (dev_enable_reply != NE_ENABLE_ON) { 41462306a36Sopenharmony_ci dev_err(&pdev->dev, "Error in pci dev enable cmd\n"); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci return -EIO; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci/** 42362306a36Sopenharmony_ci * ne_pci_dev_disable() - Disable the PCI device. 42462306a36Sopenharmony_ci * @pdev: PCI device to disable. 42562306a36Sopenharmony_ci * 42662306a36Sopenharmony_ci * Context: Process context. 42762306a36Sopenharmony_ci */ 42862306a36Sopenharmony_cistatic void ne_pci_dev_disable(struct pci_dev *pdev) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci u8 dev_disable_reply = 0; 43162306a36Sopenharmony_ci struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev); 43262306a36Sopenharmony_ci const unsigned int sleep_time = 10; /* 10 ms */ 43362306a36Sopenharmony_ci unsigned int sleep_time_count = 0; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci iowrite8(NE_ENABLE_OFF, ne_pci_dev->iomem_base + NE_ENABLE); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* 43862306a36Sopenharmony_ci * Check for NE_ENABLE_OFF in a loop, to handle cases when the device 43962306a36Sopenharmony_ci * state is not immediately set to disabled and going through a 44062306a36Sopenharmony_ci * transitory state of disabling. 44162306a36Sopenharmony_ci */ 44262306a36Sopenharmony_ci while (sleep_time_count < NE_DEFAULT_TIMEOUT_MSECS) { 44362306a36Sopenharmony_ci dev_disable_reply = ioread8(ne_pci_dev->iomem_base + NE_ENABLE); 44462306a36Sopenharmony_ci if (dev_disable_reply == NE_ENABLE_OFF) 44562306a36Sopenharmony_ci return; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci msleep_interruptible(sleep_time); 44862306a36Sopenharmony_ci sleep_time_count += sleep_time; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci dev_disable_reply = ioread8(ne_pci_dev->iomem_base + NE_ENABLE); 45262306a36Sopenharmony_ci if (dev_disable_reply != NE_ENABLE_OFF) 45362306a36Sopenharmony_ci dev_err(&pdev->dev, "Error in pci dev disable cmd\n"); 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci/** 45762306a36Sopenharmony_ci * ne_pci_probe() - Probe function for the NE PCI device. 45862306a36Sopenharmony_ci * @pdev: PCI device to match with the NE PCI driver. 45962306a36Sopenharmony_ci * @id : PCI device id table associated with the NE PCI driver. 46062306a36Sopenharmony_ci * 46162306a36Sopenharmony_ci * Context: Process context. 46262306a36Sopenharmony_ci * Return: 46362306a36Sopenharmony_ci * * 0 on success. 46462306a36Sopenharmony_ci * * Negative return value on failure. 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_cistatic int ne_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct ne_pci_dev *ne_pci_dev = NULL; 46962306a36Sopenharmony_ci int rc = -EINVAL; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci ne_pci_dev = kzalloc(sizeof(*ne_pci_dev), GFP_KERNEL); 47262306a36Sopenharmony_ci if (!ne_pci_dev) 47362306a36Sopenharmony_ci return -ENOMEM; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci rc = pci_enable_device(pdev); 47662306a36Sopenharmony_ci if (rc < 0) { 47762306a36Sopenharmony_ci dev_err(&pdev->dev, "Error in pci dev enable [rc=%d]\n", rc); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci goto free_ne_pci_dev; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci pci_set_master(pdev); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci rc = pci_request_regions_exclusive(pdev, "nitro_enclaves"); 48562306a36Sopenharmony_ci if (rc < 0) { 48662306a36Sopenharmony_ci dev_err(&pdev->dev, "Error in pci request regions [rc=%d]\n", rc); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci goto disable_pci_dev; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci ne_pci_dev->iomem_base = pci_iomap(pdev, PCI_BAR_NE, 0); 49262306a36Sopenharmony_ci if (!ne_pci_dev->iomem_base) { 49362306a36Sopenharmony_ci rc = -ENOMEM; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci dev_err(&pdev->dev, "Error in pci iomap [rc=%d]\n", rc); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci goto release_pci_regions; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci pci_set_drvdata(pdev, ne_pci_dev); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci rc = ne_setup_msix(pdev); 50362306a36Sopenharmony_ci if (rc < 0) { 50462306a36Sopenharmony_ci dev_err(&pdev->dev, "Error in pci dev msix setup [rc=%d]\n", rc); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci goto iounmap_pci_bar; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci ne_pci_dev_disable(pdev); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci rc = ne_pci_dev_enable(pdev); 51262306a36Sopenharmony_ci if (rc < 0) { 51362306a36Sopenharmony_ci dev_err(&pdev->dev, "Error in ne_pci_dev enable [rc=%d]\n", rc); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci goto teardown_msix; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci atomic_set(&ne_pci_dev->cmd_reply_avail, 0); 51962306a36Sopenharmony_ci init_waitqueue_head(&ne_pci_dev->cmd_reply_wait_q); 52062306a36Sopenharmony_ci INIT_LIST_HEAD(&ne_pci_dev->enclaves_list); 52162306a36Sopenharmony_ci mutex_init(&ne_pci_dev->enclaves_list_mutex); 52262306a36Sopenharmony_ci mutex_init(&ne_pci_dev->pci_dev_mutex); 52362306a36Sopenharmony_ci ne_pci_dev->pdev = pdev; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci ne_devs.ne_pci_dev = ne_pci_dev; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci rc = misc_register(ne_devs.ne_misc_dev); 52862306a36Sopenharmony_ci if (rc < 0) { 52962306a36Sopenharmony_ci dev_err(&pdev->dev, "Error in misc dev register [rc=%d]\n", rc); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci goto disable_ne_pci_dev; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci return 0; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cidisable_ne_pci_dev: 53762306a36Sopenharmony_ci ne_devs.ne_pci_dev = NULL; 53862306a36Sopenharmony_ci ne_pci_dev_disable(pdev); 53962306a36Sopenharmony_citeardown_msix: 54062306a36Sopenharmony_ci ne_teardown_msix(pdev); 54162306a36Sopenharmony_ciiounmap_pci_bar: 54262306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 54362306a36Sopenharmony_ci pci_iounmap(pdev, ne_pci_dev->iomem_base); 54462306a36Sopenharmony_cirelease_pci_regions: 54562306a36Sopenharmony_ci pci_release_regions(pdev); 54662306a36Sopenharmony_cidisable_pci_dev: 54762306a36Sopenharmony_ci pci_disable_device(pdev); 54862306a36Sopenharmony_cifree_ne_pci_dev: 54962306a36Sopenharmony_ci kfree(ne_pci_dev); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci return rc; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci/** 55562306a36Sopenharmony_ci * ne_pci_remove() - Remove function for the NE PCI device. 55662306a36Sopenharmony_ci * @pdev: PCI device associated with the NE PCI driver. 55762306a36Sopenharmony_ci * 55862306a36Sopenharmony_ci * Context: Process context. 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_cistatic void ne_pci_remove(struct pci_dev *pdev) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci misc_deregister(ne_devs.ne_misc_dev); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci ne_devs.ne_pci_dev = NULL; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci ne_pci_dev_disable(pdev); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci ne_teardown_msix(pdev); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci pci_iounmap(pdev, ne_pci_dev->iomem_base); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci pci_release_regions(pdev); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci pci_disable_device(pdev); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci kfree(ne_pci_dev); 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci/** 58462306a36Sopenharmony_ci * ne_pci_shutdown() - Shutdown function for the NE PCI device. 58562306a36Sopenharmony_ci * @pdev: PCI device associated with the NE PCI driver. 58662306a36Sopenharmony_ci * 58762306a36Sopenharmony_ci * Context: Process context. 58862306a36Sopenharmony_ci */ 58962306a36Sopenharmony_cistatic void ne_pci_shutdown(struct pci_dev *pdev) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (!ne_pci_dev) 59462306a36Sopenharmony_ci return; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci misc_deregister(ne_devs.ne_misc_dev); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci ne_devs.ne_pci_dev = NULL; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci ne_pci_dev_disable(pdev); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci ne_teardown_msix(pdev); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci pci_iounmap(pdev, ne_pci_dev->iomem_base); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci pci_release_regions(pdev); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci pci_disable_device(pdev); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci kfree(ne_pci_dev); 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci/* 61662306a36Sopenharmony_ci * TODO: Add suspend / resume functions for power management w/ CONFIG_PM, if 61762306a36Sopenharmony_ci * needed. 61862306a36Sopenharmony_ci */ 61962306a36Sopenharmony_ci/* NE PCI device driver. */ 62062306a36Sopenharmony_cistruct pci_driver ne_pci_driver = { 62162306a36Sopenharmony_ci .name = "nitro_enclaves", 62262306a36Sopenharmony_ci .id_table = ne_pci_ids, 62362306a36Sopenharmony_ci .probe = ne_pci_probe, 62462306a36Sopenharmony_ci .remove = ne_pci_remove, 62562306a36Sopenharmony_ci .shutdown = ne_pci_shutdown, 62662306a36Sopenharmony_ci}; 627