Skip to content

Commit ad2b698

Browse files
andraprsgregkh
authored andcommitted
nitro_enclaves: Handle PCI device command requests
The Nitro Enclaves PCI device exposes a MMIO space that this driver uses to submit command requests and to receive command replies e.g. for enclave creation / termination or setting enclave resources. Add logic for handling PCI device command requests based on the given command type. Register an MSI-X interrupt vector for command reply notifications to handle this type of communication events. Changelog v9 -> v10 * Update commit message to include the changelog before the SoB tag(s). v8 -> v9 * No changes. v7 -> v8 * Update function signature for submit request and retrive reply functions as they only returned 0, no error code. * Include command type value in the error logs of ne_do_request(). v6 -> v7 * No changes. v5 -> v6 * Update documentation to kernel-doc format. v4 -> v5 * Remove sanity checks for situations that shouldn't happen, only if buggy system or broken logic at all. v3 -> v4 * Use dev_err instead of custom NE log pattern. * Return IRQ_NONE when interrupts are not handled. v2 -> v3 * Remove the WARN_ON calls. * Update static calls sanity checks. * Remove "ratelimited" from the logs that are not in the ioctl call paths. v1 -> v2 * Add log pattern for NE. * Remove the BUG_ON calls. * Update goto labels to match their purpose. * Add fix for kbuild report: https://lore.kernel.org/lkml/202004231644.xTmN4Z1z%25lkp@intel.com/ Reviewed-by: Alexander Graf <graf@amazon.com> Signed-off-by: Alexandru-Catalin Vasile <lexnv@amazon.com> Signed-off-by: Andra Paraschiv <andraprs@amazon.com> Link: https://lore.kernel.org/r/20200921121732.44291-6-andraprs@amazon.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 89308c1 commit ad2b698

File tree

1 file changed

+189
-0
lines changed

1 file changed

+189
-0
lines changed

drivers/virt/nitro_enclaves/ne_pci_dev.c

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,172 @@ static const struct pci_device_id ne_pci_ids[] = {
3333

3434
MODULE_DEVICE_TABLE(pci, ne_pci_ids);
3535

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+
*/
46+
static 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+
*/
64+
static 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+
*/
81+
static 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+
99+
int 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+
173+
unlock_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+
*/
190+
static 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+
36202
/**
37203
* ne_setup_msix() - Setup MSI-X vectors for the PCI device.
38204
* @pdev: PCI device to setup the MSI-X for.
@@ -44,6 +210,7 @@ MODULE_DEVICE_TABLE(pci, ne_pci_ids);
44210
*/
45211
static int ne_setup_msix(struct pci_dev *pdev)
46212
{
213+
struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
47214
int nr_vecs = 0;
48215
int rc = -EINVAL;
49216

@@ -63,7 +230,25 @@ static int ne_setup_msix(struct pci_dev *pdev)
63230
return rc;
64231
}
65232

233+
/*
234+
* This IRQ gets triggered every time the PCI device responds to a
235+
* command request. The reply is then retrieved, reading from the MMIO
236+
* space of the PCI device.
237+
*/
238+
rc = request_irq(pci_irq_vector(pdev, NE_VEC_REPLY), ne_reply_handler,
239+
0, "enclave_cmd", ne_pci_dev);
240+
if (rc < 0) {
241+
dev_err(&pdev->dev, "Error in request irq reply [rc=%d]\n", rc);
242+
243+
goto free_irq_vectors;
244+
}
245+
66246
return 0;
247+
248+
free_irq_vectors:
249+
pci_free_irq_vectors(pdev);
250+
251+
return rc;
67252
}
68253

69254
/**
@@ -74,6 +259,10 @@ static int ne_setup_msix(struct pci_dev *pdev)
74259
*/
75260
static void ne_teardown_msix(struct pci_dev *pdev)
76261
{
262+
struct ne_pci_dev *ne_pci_dev = pci_get_drvdata(pdev);
263+
264+
free_irq(pci_irq_vector(pdev, NE_VEC_REPLY), ne_pci_dev);
265+
77266
pci_free_irq_vectors(pdev);
78267
}
79268

0 commit comments

Comments
 (0)