cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ne_pci_dev.c (15811B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
      4 */
      5
      6/**
      7 * DOC: Nitro Enclaves (NE) PCI device driver.
      8 */
      9
     10#include <linux/delay.h>
     11#include <linux/device.h>
     12#include <linux/list.h>
     13#include <linux/module.h>
     14#include <linux/mutex.h>
     15#include <linux/nitro_enclaves.h>
     16#include <linux/pci.h>
     17#include <linux/types.h>
     18#include <linux/wait.h>
     19
     20#include "ne_misc_dev.h"
     21#include "ne_pci_dev.h"
     22
     23/**
     24 * NE_DEFAULT_TIMEOUT_MSECS - Default timeout to wait for a reply from
     25 *			      the NE PCI device.
     26 */
     27#define NE_DEFAULT_TIMEOUT_MSECS	(120000) /* 120 sec */
     28
     29static const struct pci_device_id ne_pci_ids[] = {
     30	{ PCI_DEVICE(PCI_VENDOR_ID_AMAZON, PCI_DEVICE_ID_NE) },
     31	{ 0, }
     32};
     33
     34MODULE_DEVICE_TABLE(pci, ne_pci_ids);
     35
     36/**
     37 * ne_submit_request() - Submit command request to the PCI device based on the
     38 *			 command type.
     39 * @pdev:		PCI device to send the command to.
     40 * @cmd_type:		Command type of the request sent to the PCI device.
     41 * @cmd_request:	Command request payload.
     42 * @cmd_request_size:	Size of the command request payload.
     43 *
     44 * Context: Process context. This function is called with the ne_pci_dev mutex held.
     45 */
     46static void ne_submit_request(struct pci_dev *pdev, enum ne_pci_dev_cmd_type cmd_type,
     47			      void *cmd_request, size_t cmd_request_size)
     48{
     49	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
     50
     51	memcpy_toio(ne_pci_dev->iomem_base + NE_SEND_DATA, cmd_request, cmd_request_size);
     52
     53	iowrite32(cmd_type, ne_pci_dev->iomem_base + NE_COMMAND);
     54}
     55
     56/**
     57 * ne_retrieve_reply() - Retrieve reply from the PCI device.
     58 * @pdev:		PCI device to receive the reply from.
     59 * @cmd_reply:		Command reply payload.
     60 * @cmd_reply_size:	Size of the command reply payload.
     61 *
     62 * Context: Process context. This function is called with the ne_pci_dev mutex held.
     63 */
     64static void ne_retrieve_reply(struct pci_dev *pdev, struct ne_pci_dev_cmd_reply *cmd_reply,
     65			      size_t cmd_reply_size)
     66{
     67	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
     68
     69	memcpy_fromio(cmd_reply, ne_pci_dev->iomem_base + NE_RECV_DATA, cmd_reply_size);
     70}
     71
     72/**
     73 * ne_wait_for_reply() - Wait for a reply of a PCI device command.
     74 * @pdev:	PCI device for which a reply is waited.
     75 *
     76 * Context: Process context. This function is called with the ne_pci_dev mutex held.
     77 * Return:
     78 * * 0 on success.
     79 * * Negative return value on failure.
     80 */
     81static int ne_wait_for_reply(struct pci_dev *pdev)
     82{
     83	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
     84	int rc = -EINVAL;
     85
     86	/*
     87	 * TODO: Update to _interruptible and handle interrupted wait event
     88	 * e.g. -ERESTARTSYS, incoming signals + update timeout, if needed.
     89	 */
     90	rc = wait_event_timeout(ne_pci_dev->cmd_reply_wait_q,
     91				atomic_read(&ne_pci_dev->cmd_reply_avail) != 0,
     92				msecs_to_jiffies(NE_DEFAULT_TIMEOUT_MSECS));
     93	if (!rc)
     94		return -ETIMEDOUT;
     95
     96	return 0;
     97}
     98
     99int ne_do_request(struct pci_dev *pdev, enum ne_pci_dev_cmd_type cmd_type,
    100		  void *cmd_request, size_t cmd_request_size,
    101		  struct ne_pci_dev_cmd_reply *cmd_reply, size_t cmd_reply_size)
    102{
    103	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
    104	int rc = -EINVAL;
    105
    106	if (cmd_type <= INVALID_CMD || cmd_type >= MAX_CMD) {
    107		dev_err_ratelimited(&pdev->dev, "Invalid cmd type=%u\n", cmd_type);
    108
    109		return -EINVAL;
    110	}
    111
    112	if (!cmd_request) {
    113		dev_err_ratelimited(&pdev->dev, "Null cmd request for cmd type=%u\n",
    114				    cmd_type);
    115
    116		return -EINVAL;
    117	}
    118
    119	if (cmd_request_size > NE_SEND_DATA_SIZE) {
    120		dev_err_ratelimited(&pdev->dev, "Invalid req size=%zu for cmd type=%u\n",
    121				    cmd_request_size, cmd_type);
    122
    123		return -EINVAL;
    124	}
    125
    126	if (!cmd_reply) {
    127		dev_err_ratelimited(&pdev->dev, "Null cmd reply for cmd type=%u\n",
    128				    cmd_type);
    129
    130		return -EINVAL;
    131	}
    132
    133	if (cmd_reply_size > NE_RECV_DATA_SIZE) {
    134		dev_err_ratelimited(&pdev->dev, "Invalid reply size=%zu for cmd type=%u\n",
    135				    cmd_reply_size, cmd_type);
    136
    137		return -EINVAL;
    138	}
    139
    140	/*
    141	 * Use this mutex so that the PCI device handles one command request at
    142	 * a time.
    143	 */
    144	mutex_lock(&ne_pci_dev->pci_dev_mutex);
    145
    146	atomic_set(&ne_pci_dev->cmd_reply_avail, 0);
    147
    148	ne_submit_request(pdev, cmd_type, cmd_request, cmd_request_size);
    149
    150	rc = ne_wait_for_reply(pdev);
    151	if (rc < 0) {
    152		dev_err_ratelimited(&pdev->dev, "Error in wait for reply for cmd type=%u [rc=%d]\n",
    153				    cmd_type, rc);
    154
    155		goto unlock_mutex;
    156	}
    157
    158	ne_retrieve_reply(pdev, cmd_reply, cmd_reply_size);
    159
    160	atomic_set(&ne_pci_dev->cmd_reply_avail, 0);
    161
    162	if (cmd_reply->rc < 0) {
    163		rc = cmd_reply->rc;
    164
    165		dev_err_ratelimited(&pdev->dev, "Error in cmd process logic, cmd type=%u [rc=%d]\n",
    166				    cmd_type, rc);
    167
    168		goto unlock_mutex;
    169	}
    170
    171	rc = 0;
    172
    173unlock_mutex:
    174	mutex_unlock(&ne_pci_dev->pci_dev_mutex);
    175
    176	return rc;
    177}
    178
    179/**
    180 * ne_reply_handler() - Interrupt handler for retrieving a reply matching a
    181 *			request sent to the PCI device for enclave lifetime
    182 *			management.
    183 * @irq:	Received interrupt for a reply sent by the PCI device.
    184 * @args:	PCI device private data structure.
    185 *
    186 * Context: Interrupt context.
    187 * Return:
    188 * * IRQ_HANDLED on handled interrupt.
    189 */
    190static irqreturn_t ne_reply_handler(int irq, void *args)
    191{
    192	struct ne_pci_dev *ne_pci_dev = (struct ne_pci_dev *)args;
    193
    194	atomic_set(&ne_pci_dev->cmd_reply_avail, 1);
    195
    196	/* TODO: Update to _interruptible. */
    197	wake_up(&ne_pci_dev->cmd_reply_wait_q);
    198
    199	return IRQ_HANDLED;
    200}
    201
    202/**
    203 * ne_event_work_handler() - Work queue handler for notifying enclaves on a
    204 *			     state change received by the event interrupt
    205 *			     handler.
    206 * @work:	Item containing the NE PCI device for which an out-of-band event
    207 *		was issued.
    208 *
    209 * An out-of-band event is being issued by the Nitro Hypervisor when at least
    210 * one enclave is changing state without client interaction.
    211 *
    212 * Context: Work queue context.
    213 */
    214static void ne_event_work_handler(struct work_struct *work)
    215{
    216	struct ne_pci_dev_cmd_reply cmd_reply = {};
    217	struct ne_enclave *ne_enclave = NULL;
    218	struct ne_pci_dev *ne_pci_dev =
    219		container_of(work, struct ne_pci_dev, notify_work);
    220	struct pci_dev *pdev = ne_pci_dev->pdev;
    221	int rc = -EINVAL;
    222	struct slot_info_req slot_info_req = {};
    223
    224	mutex_lock(&ne_pci_dev->enclaves_list_mutex);
    225
    226	/*
    227	 * Iterate over all enclaves registered for the Nitro Enclaves
    228	 * PCI device and determine for which enclave(s) the out-of-band event
    229	 * is corresponding to.
    230	 */
    231	list_for_each_entry(ne_enclave, &ne_pci_dev->enclaves_list, enclave_list_entry) {
    232		mutex_lock(&ne_enclave->enclave_info_mutex);
    233
    234		/*
    235		 * Enclaves that were never started cannot receive out-of-band
    236		 * events.
    237		 */
    238		if (ne_enclave->state != NE_STATE_RUNNING)
    239			goto unlock;
    240
    241		slot_info_req.slot_uid = ne_enclave->slot_uid;
    242
    243		rc = ne_do_request(pdev, SLOT_INFO,
    244				   &slot_info_req, sizeof(slot_info_req),
    245				   &cmd_reply, sizeof(cmd_reply));
    246		if (rc < 0)
    247			dev_err(&pdev->dev, "Error in slot info [rc=%d]\n", rc);
    248
    249		/* Notify enclave process that the enclave state changed. */
    250		if (ne_enclave->state != cmd_reply.state) {
    251			ne_enclave->state = cmd_reply.state;
    252
    253			ne_enclave->has_event = true;
    254
    255			wake_up_interruptible(&ne_enclave->eventq);
    256		}
    257
    258unlock:
    259		 mutex_unlock(&ne_enclave->enclave_info_mutex);
    260	}
    261
    262	mutex_unlock(&ne_pci_dev->enclaves_list_mutex);
    263}
    264
    265/**
    266 * ne_event_handler() - Interrupt handler for PCI device out-of-band events.
    267 *			This interrupt does not supply any data in the MMIO
    268 *			region. It notifies a change in the state of any of
    269 *			the launched enclaves.
    270 * @irq:	Received interrupt for an out-of-band event.
    271 * @args:	PCI device private data structure.
    272 *
    273 * Context: Interrupt context.
    274 * Return:
    275 * * IRQ_HANDLED on handled interrupt.
    276 */
    277static irqreturn_t ne_event_handler(int irq, void *args)
    278{
    279	struct ne_pci_dev *ne_pci_dev = (struct ne_pci_dev *)args;
    280
    281	queue_work(ne_pci_dev->event_wq, &ne_pci_dev->notify_work);
    282
    283	return IRQ_HANDLED;
    284}
    285
    286/**
    287 * ne_setup_msix() - Setup MSI-X vectors for the PCI device.
    288 * @pdev:	PCI device to setup the MSI-X for.
    289 *
    290 * Context: Process context.
    291 * Return:
    292 * * 0 on success.
    293 * * Negative return value on failure.
    294 */
    295static int ne_setup_msix(struct pci_dev *pdev)
    296{
    297	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
    298	int nr_vecs = 0;
    299	int rc = -EINVAL;
    300
    301	nr_vecs = pci_msix_vec_count(pdev);
    302	if (nr_vecs < 0) {
    303		rc = nr_vecs;
    304
    305		dev_err(&pdev->dev, "Error in getting vec count [rc=%d]\n", rc);
    306
    307		return rc;
    308	}
    309
    310	rc = pci_alloc_irq_vectors(pdev, nr_vecs, nr_vecs, PCI_IRQ_MSIX);
    311	if (rc < 0) {
    312		dev_err(&pdev->dev, "Error in alloc MSI-X vecs [rc=%d]\n", rc);
    313
    314		return rc;
    315	}
    316
    317	/*
    318	 * This IRQ gets triggered every time the PCI device responds to a
    319	 * command request. The reply is then retrieved, reading from the MMIO
    320	 * space of the PCI device.
    321	 */
    322	rc = request_irq(pci_irq_vector(pdev, NE_VEC_REPLY), ne_reply_handler,
    323			 0, "enclave_cmd", ne_pci_dev);
    324	if (rc < 0) {
    325		dev_err(&pdev->dev, "Error in request irq reply [rc=%d]\n", rc);
    326
    327		goto free_irq_vectors;
    328	}
    329
    330	ne_pci_dev->event_wq = create_singlethread_workqueue("ne_pci_dev_wq");
    331	if (!ne_pci_dev->event_wq) {
    332		rc = -ENOMEM;
    333
    334		dev_err(&pdev->dev, "Cannot get wq for dev events [rc=%d]\n", rc);
    335
    336		goto free_reply_irq_vec;
    337	}
    338
    339	INIT_WORK(&ne_pci_dev->notify_work, ne_event_work_handler);
    340
    341	/*
    342	 * This IRQ gets triggered every time any enclave's state changes. Its
    343	 * handler then scans for the changes and propagates them to the user
    344	 * space.
    345	 */
    346	rc = request_irq(pci_irq_vector(pdev, NE_VEC_EVENT), ne_event_handler,
    347			 0, "enclave_evt", ne_pci_dev);
    348	if (rc < 0) {
    349		dev_err(&pdev->dev, "Error in request irq event [rc=%d]\n", rc);
    350
    351		goto destroy_wq;
    352	}
    353
    354	return 0;
    355
    356destroy_wq:
    357	destroy_workqueue(ne_pci_dev->event_wq);
    358free_reply_irq_vec:
    359	free_irq(pci_irq_vector(pdev, NE_VEC_REPLY), ne_pci_dev);
    360free_irq_vectors:
    361	pci_free_irq_vectors(pdev);
    362
    363	return rc;
    364}
    365
    366/**
    367 * ne_teardown_msix() - Teardown MSI-X vectors for the PCI device.
    368 * @pdev:	PCI device to teardown the MSI-X for.
    369 *
    370 * Context: Process context.
    371 */
    372static void ne_teardown_msix(struct pci_dev *pdev)
    373{
    374	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
    375
    376	free_irq(pci_irq_vector(pdev, NE_VEC_EVENT), ne_pci_dev);
    377
    378	flush_work(&ne_pci_dev->notify_work);
    379	destroy_workqueue(ne_pci_dev->event_wq);
    380
    381	free_irq(pci_irq_vector(pdev, NE_VEC_REPLY), ne_pci_dev);
    382
    383	pci_free_irq_vectors(pdev);
    384}
    385
    386/**
    387 * ne_pci_dev_enable() - Select the PCI device version and enable it.
    388 * @pdev:	PCI device to select version for and then enable.
    389 *
    390 * Context: Process context.
    391 * Return:
    392 * * 0 on success.
    393 * * Negative return value on failure.
    394 */
    395static int ne_pci_dev_enable(struct pci_dev *pdev)
    396{
    397	u8 dev_enable_reply = 0;
    398	u16 dev_version_reply = 0;
    399	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
    400
    401	iowrite16(NE_VERSION_MAX, ne_pci_dev->iomem_base + NE_VERSION);
    402
    403	dev_version_reply = ioread16(ne_pci_dev->iomem_base + NE_VERSION);
    404	if (dev_version_reply != NE_VERSION_MAX) {
    405		dev_err(&pdev->dev, "Error in pci dev version cmd\n");
    406
    407		return -EIO;
    408	}
    409
    410	iowrite8(NE_ENABLE_ON, ne_pci_dev->iomem_base + NE_ENABLE);
    411
    412	dev_enable_reply = ioread8(ne_pci_dev->iomem_base + NE_ENABLE);
    413	if (dev_enable_reply != NE_ENABLE_ON) {
    414		dev_err(&pdev->dev, "Error in pci dev enable cmd\n");
    415
    416		return -EIO;
    417	}
    418
    419	return 0;
    420}
    421
    422/**
    423 * ne_pci_dev_disable() - Disable the PCI device.
    424 * @pdev:	PCI device to disable.
    425 *
    426 * Context: Process context.
    427 */
    428static void ne_pci_dev_disable(struct pci_dev *pdev)
    429{
    430	u8 dev_disable_reply = 0;
    431	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
    432	const unsigned int sleep_time = 10; /* 10 ms */
    433	unsigned int sleep_time_count = 0;
    434
    435	iowrite8(NE_ENABLE_OFF, ne_pci_dev->iomem_base + NE_ENABLE);
    436
    437	/*
    438	 * Check for NE_ENABLE_OFF in a loop, to handle cases when the device
    439	 * state is not immediately set to disabled and going through a
    440	 * transitory state of disabling.
    441	 */
    442	while (sleep_time_count < NE_DEFAULT_TIMEOUT_MSECS) {
    443		dev_disable_reply = ioread8(ne_pci_dev->iomem_base + NE_ENABLE);
    444		if (dev_disable_reply == NE_ENABLE_OFF)
    445			return;
    446
    447		msleep_interruptible(sleep_time);
    448		sleep_time_count += sleep_time;
    449	}
    450
    451	dev_disable_reply = ioread8(ne_pci_dev->iomem_base + NE_ENABLE);
    452	if (dev_disable_reply != NE_ENABLE_OFF)
    453		dev_err(&pdev->dev, "Error in pci dev disable cmd\n");
    454}
    455
    456/**
    457 * ne_pci_probe() - Probe function for the NE PCI device.
    458 * @pdev:	PCI device to match with the NE PCI driver.
    459 * @id :	PCI device id table associated with the NE PCI driver.
    460 *
    461 * Context: Process context.
    462 * Return:
    463 * * 0 on success.
    464 * * Negative return value on failure.
    465 */
    466static int ne_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
    467{
    468	struct ne_pci_dev *ne_pci_dev = NULL;
    469	int rc = -EINVAL;
    470
    471	ne_pci_dev = kzalloc(sizeof(*ne_pci_dev), GFP_KERNEL);
    472	if (!ne_pci_dev)
    473		return -ENOMEM;
    474
    475	rc = pci_enable_device(pdev);
    476	if (rc < 0) {
    477		dev_err(&pdev->dev, "Error in pci dev enable [rc=%d]\n", rc);
    478
    479		goto free_ne_pci_dev;
    480	}
    481
    482	pci_set_master(pdev);
    483
    484	rc = pci_request_regions_exclusive(pdev, "nitro_enclaves");
    485	if (rc < 0) {
    486		dev_err(&pdev->dev, "Error in pci request regions [rc=%d]\n", rc);
    487
    488		goto disable_pci_dev;
    489	}
    490
    491	ne_pci_dev->iomem_base = pci_iomap(pdev, PCI_BAR_NE, 0);
    492	if (!ne_pci_dev->iomem_base) {
    493		rc = -ENOMEM;
    494
    495		dev_err(&pdev->dev, "Error in pci iomap [rc=%d]\n", rc);
    496
    497		goto release_pci_regions;
    498	}
    499
    500	pci_set_drvdata(pdev, ne_pci_dev);
    501
    502	rc = ne_setup_msix(pdev);
    503	if (rc < 0) {
    504		dev_err(&pdev->dev, "Error in pci dev msix setup [rc=%d]\n", rc);
    505
    506		goto iounmap_pci_bar;
    507	}
    508
    509	ne_pci_dev_disable(pdev);
    510
    511	rc = ne_pci_dev_enable(pdev);
    512	if (rc < 0) {
    513		dev_err(&pdev->dev, "Error in ne_pci_dev enable [rc=%d]\n", rc);
    514
    515		goto teardown_msix;
    516	}
    517
    518	atomic_set(&ne_pci_dev->cmd_reply_avail, 0);
    519	init_waitqueue_head(&ne_pci_dev->cmd_reply_wait_q);
    520	INIT_LIST_HEAD(&ne_pci_dev->enclaves_list);
    521	mutex_init(&ne_pci_dev->enclaves_list_mutex);
    522	mutex_init(&ne_pci_dev->pci_dev_mutex);
    523	ne_pci_dev->pdev = pdev;
    524
    525	ne_devs.ne_pci_dev = ne_pci_dev;
    526
    527	rc = misc_register(ne_devs.ne_misc_dev);
    528	if (rc < 0) {
    529		dev_err(&pdev->dev, "Error in misc dev register [rc=%d]\n", rc);
    530
    531		goto disable_ne_pci_dev;
    532	}
    533
    534	return 0;
    535
    536disable_ne_pci_dev:
    537	ne_devs.ne_pci_dev = NULL;
    538	ne_pci_dev_disable(pdev);
    539teardown_msix:
    540	ne_teardown_msix(pdev);
    541iounmap_pci_bar:
    542	pci_set_drvdata(pdev, NULL);
    543	pci_iounmap(pdev, ne_pci_dev->iomem_base);
    544release_pci_regions:
    545	pci_release_regions(pdev);
    546disable_pci_dev:
    547	pci_disable_device(pdev);
    548free_ne_pci_dev:
    549	kfree(ne_pci_dev);
    550
    551	return rc;
    552}
    553
    554/**
    555 * ne_pci_remove() - Remove function for the NE PCI device.
    556 * @pdev:	PCI device associated with the NE PCI driver.
    557 *
    558 * Context: Process context.
    559 */
    560static void ne_pci_remove(struct pci_dev *pdev)
    561{
    562	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
    563
    564	misc_deregister(ne_devs.ne_misc_dev);
    565
    566	ne_devs.ne_pci_dev = NULL;
    567
    568	ne_pci_dev_disable(pdev);
    569
    570	ne_teardown_msix(pdev);
    571
    572	pci_set_drvdata(pdev, NULL);
    573
    574	pci_iounmap(pdev, ne_pci_dev->iomem_base);
    575
    576	pci_release_regions(pdev);
    577
    578	pci_disable_device(pdev);
    579
    580	kfree(ne_pci_dev);
    581}
    582
    583/**
    584 * ne_pci_shutdown() - Shutdown function for the NE PCI device.
    585 * @pdev:	PCI device associated with the NE PCI driver.
    586 *
    587 * Context: Process context.
    588 */
    589static void ne_pci_shutdown(struct pci_dev *pdev)
    590{
    591	struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
    592
    593	if (!ne_pci_dev)
    594		return;
    595
    596	misc_deregister(ne_devs.ne_misc_dev);
    597
    598	ne_devs.ne_pci_dev = NULL;
    599
    600	ne_pci_dev_disable(pdev);
    601
    602	ne_teardown_msix(pdev);
    603
    604	pci_set_drvdata(pdev, NULL);
    605
    606	pci_iounmap(pdev, ne_pci_dev->iomem_base);
    607
    608	pci_release_regions(pdev);
    609
    610	pci_disable_device(pdev);
    611
    612	kfree(ne_pci_dev);
    613}
    614
    615/*
    616 * TODO: Add suspend / resume functions for power management w/ CONFIG_PM, if
    617 * needed.
    618 */
    619/* NE PCI device driver. */
    620struct pci_driver ne_pci_driver = {
    621	.name		= "nitro_enclaves",
    622	.id_table	= ne_pci_ids,
    623	.probe		= ne_pci_probe,
    624	.remove		= ne_pci_remove,
    625	.shutdown	= ne_pci_shutdown,
    626};