]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/VirtioLib/VirtioLib.c
MdeModulePkg/DiskIoDxe: fix source/destination pointer of overrun transfer
[mirror_edk2.git] / OvmfPkg / Library / VirtioLib / VirtioLib.c
CommitLineData
263559b8 1/** @file\r
2\r
3 Utility functions used by virtio device drivers.\r
4\r
5 Copyright (C) 2012, Red Hat, Inc.\r
6\r
7 This program and the accompanying materials are licensed and made available\r
8 under the terms and conditions of the BSD License which accompanies this\r
9 distribution. The full text of the license may be found at\r
10 http://opensource.org/licenses/bsd-license.php\r
11\r
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
13 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
14\r
15**/\r
16\r
17#include <IndustryStandard/Pci22.h>\r
e371e7e5 18#include <Library/BaseLib.h>\r
263559b8 19#include <Library/BaseMemoryLib.h>\r
20#include <Library/DebugLib.h>\r
21#include <Library/MemoryAllocationLib.h>\r
e371e7e5 22#include <Library/UefiBootServicesTableLib.h>\r
263559b8 23\r
24#include <Library/VirtioLib.h>\r
25\r
26\r
27/**\r
28\r
29 Write a word into Region 0 of the device specified by PciIo.\r
30\r
31 Region 0 must be an iomem region. This is an internal function for the\r
32 driver-specific VIRTIO_CFG_WRITE() macros.\r
33\r
34 @param[in] PciIo Target PCI device.\r
35\r
36 @param[in] FieldOffset Destination offset.\r
37\r
38 @param[in] FieldSize Destination field size, must be in { 1, 2, 4, 8 }.\r
39\r
40 @param[in] Value Little endian value to write, converted to UINT64.\r
41 The least significant FieldSize bytes will be used.\r
42\r
43\r
44 @return Status code returned by PciIo->Io.Write().\r
45\r
46**/\r
263559b8 47EFI_STATUS\r
9de0355b 48EFIAPI\r
263559b8 49VirtioWrite (\r
50 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
51 IN UINTN FieldOffset,\r
52 IN UINTN FieldSize,\r
53 IN UINT64 Value\r
54 )\r
55{\r
56 UINTN Count;\r
57 EFI_PCI_IO_PROTOCOL_WIDTH Width;\r
58\r
59 Count = 1;\r
60 switch (FieldSize) {\r
61 case 1:\r
62 Width = EfiPciIoWidthUint8;\r
63 break;\r
64\r
65 case 2:\r
66 Width = EfiPciIoWidthUint16;\r
67 break;\r
68\r
69 case 8:\r
70 Count = 2;\r
71 // fall through\r
72\r
73 case 4:\r
74 Width = EfiPciIoWidthUint32;\r
75 break;\r
76\r
77 default:\r
78 ASSERT (FALSE);\r
9de0355b 79 return EFI_INVALID_PARAMETER;\r
263559b8 80 }\r
81\r
82 return PciIo->Io.Write (\r
83 PciIo,\r
84 Width,\r
85 PCI_BAR_IDX0,\r
86 FieldOffset,\r
87 Count,\r
88 &Value\r
89 );\r
90}\r
91\r
92\r
93/**\r
94\r
95 Read a word from Region 0 of the device specified by PciIo.\r
96\r
97 Region 0 must be an iomem region. This is an internal function for the\r
98 driver-specific VIRTIO_CFG_READ() macros.\r
99\r
100 @param[in] PciIo Source PCI device.\r
101\r
102 @param[in] FieldOffset Source offset.\r
103\r
104 @param[in] FieldSize Source field size, must be in { 1, 2, 4, 8 }.\r
105\r
106 @param[in] BufferSize Number of bytes available in the target buffer. Must\r
107 equal FieldSize.\r
108\r
109 @param[out] Buffer Target buffer.\r
110\r
111\r
112 @return Status code returned by PciIo->Io.Read().\r
113\r
114**/\r
263559b8 115EFI_STATUS\r
9de0355b 116EFIAPI\r
263559b8 117VirtioRead (\r
118 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
119 IN UINTN FieldOffset,\r
120 IN UINTN FieldSize,\r
121 IN UINTN BufferSize,\r
122 OUT VOID *Buffer\r
123 )\r
124{\r
125 UINTN Count;\r
126 EFI_PCI_IO_PROTOCOL_WIDTH Width;\r
127\r
128 ASSERT (FieldSize == BufferSize);\r
129\r
130 Count = 1;\r
131 switch (FieldSize) {\r
132 case 1:\r
133 Width = EfiPciIoWidthUint8;\r
134 break;\r
135\r
136 case 2:\r
137 Width = EfiPciIoWidthUint16;\r
138 break;\r
139\r
140 case 8:\r
141 Count = 2;\r
142 // fall through\r
143\r
144 case 4:\r
145 Width = EfiPciIoWidthUint32;\r
146 break;\r
147\r
148 default:\r
149 ASSERT (FALSE);\r
9de0355b 150 return EFI_INVALID_PARAMETER;\r
263559b8 151 }\r
152\r
153 return PciIo->Io.Read (\r
154 PciIo,\r
155 Width,\r
156 PCI_BAR_IDX0,\r
157 FieldOffset,\r
158 Count,\r
159 Buffer\r
160 );\r
161}\r
162\r
163\r
164/**\r
165\r
166 Configure a virtio ring.\r
167\r
168 This function sets up internal storage (the guest-host communication area)\r
169 and lays out several "navigation" (ie. no-ownership) pointers to parts of\r
170 that storage.\r
171\r
172 Relevant sections from the virtio-0.9.5 spec:\r
173 - 1.1 Virtqueues,\r
174 - 2.3 Virtqueue Configuration.\r
175\r
176 @param[in] The number of descriptors to allocate for the\r
177 virtio ring, as requested by the host.\r
178\r
179 @param[out] Ring The virtio ring to set up.\r
180\r
181 @retval EFI_OUT_OF_RESOURCES AllocatePages() failed to allocate contiguous\r
182 pages for the requested QueueSize. Fields of\r
183 Ring have indeterminate value.\r
184\r
185 @retval EFI_SUCCESS Allocation and setup successful. Ring->Base\r
186 (and nothing else) is responsible for\r
187 deallocation.\r
188\r
189**/\r
190EFI_STATUS\r
191EFIAPI\r
192VirtioRingInit (\r
193 IN UINT16 QueueSize,\r
194 OUT VRING *Ring\r
195 )\r
196{\r
197 UINTN RingSize;\r
198 volatile UINT8 *RingPagesPtr;\r
199\r
200 RingSize = ALIGN_VALUE (\r
201 sizeof *Ring->Desc * QueueSize +\r
202 sizeof *Ring->Avail.Flags +\r
203 sizeof *Ring->Avail.Idx +\r
204 sizeof *Ring->Avail.Ring * QueueSize +\r
205 sizeof *Ring->Avail.UsedEvent,\r
206 EFI_PAGE_SIZE);\r
207\r
208 RingSize += ALIGN_VALUE (\r
209 sizeof *Ring->Used.Flags +\r
210 sizeof *Ring->Used.Idx +\r
211 sizeof *Ring->Used.UsedElem * QueueSize +\r
212 sizeof *Ring->Used.AvailEvent,\r
213 EFI_PAGE_SIZE);\r
214\r
215 Ring->NumPages = EFI_SIZE_TO_PAGES (RingSize);\r
216 Ring->Base = AllocatePages (Ring->NumPages);\r
217 if (Ring->Base == NULL) {\r
218 return EFI_OUT_OF_RESOURCES;\r
219 }\r
220 SetMem (Ring->Base, RingSize, 0x00);\r
221 RingPagesPtr = Ring->Base;\r
222\r
223 Ring->Desc = (volatile VOID *) RingPagesPtr;\r
224 RingPagesPtr += sizeof *Ring->Desc * QueueSize;\r
225\r
226 Ring->Avail.Flags = (volatile VOID *) RingPagesPtr;\r
227 RingPagesPtr += sizeof *Ring->Avail.Flags;\r
228\r
229 Ring->Avail.Idx = (volatile VOID *) RingPagesPtr;\r
230 RingPagesPtr += sizeof *Ring->Avail.Idx;\r
231\r
232 Ring->Avail.Ring = (volatile VOID *) RingPagesPtr;\r
233 RingPagesPtr += sizeof *Ring->Avail.Ring * QueueSize;\r
234\r
235 Ring->Avail.UsedEvent = (volatile VOID *) RingPagesPtr;\r
236 RingPagesPtr += sizeof *Ring->Avail.UsedEvent;\r
237\r
238 RingPagesPtr = (volatile UINT8 *) Ring->Base +\r
239 ALIGN_VALUE (RingPagesPtr - (volatile UINT8 *) Ring->Base,\r
240 EFI_PAGE_SIZE);\r
241\r
242 Ring->Used.Flags = (volatile VOID *) RingPagesPtr;\r
243 RingPagesPtr += sizeof *Ring->Used.Flags;\r
244\r
245 Ring->Used.Idx = (volatile VOID *) RingPagesPtr;\r
246 RingPagesPtr += sizeof *Ring->Used.Idx;\r
247\r
248 Ring->Used.UsedElem = (volatile VOID *) RingPagesPtr;\r
249 RingPagesPtr += sizeof *Ring->Used.UsedElem * QueueSize;\r
250\r
251 Ring->Used.AvailEvent = (volatile VOID *) RingPagesPtr;\r
252 RingPagesPtr += sizeof *Ring->Used.AvailEvent;\r
253\r
254 Ring->QueueSize = QueueSize;\r
255 return EFI_SUCCESS;\r
256}\r
257\r
258\r
259/**\r
260\r
261 Tear down the internal resources of a configured virtio ring.\r
262\r
263 The caller is responsible to stop the host from using this ring before\r
264 invoking this function: the VSTAT_DRIVER_OK bit must be clear in\r
265 VhdrDeviceStatus.\r
266\r
267 @param[out] Ring The virtio ring to clean up.\r
268\r
269**/\r
270VOID\r
271EFIAPI\r
272VirtioRingUninit (\r
273 IN OUT VRING *Ring\r
274 )\r
275{\r
276 FreePages (Ring->Base, Ring->NumPages);\r
277 SetMem (Ring, sizeof *Ring, 0x00);\r
278}\r
279\r
280\r
e371e7e5 281/**\r
282\r
283 Turn off interrupt notifications from the host, and prepare for appending\r
284 multiple descriptors to the virtio ring.\r
285\r
286 The calling driver must be in VSTAT_DRIVER_OK state.\r
287\r
f2965f4e 288 @param[in,out] Ring The virtio ring we intend to append descriptors to.\r
e371e7e5 289\r
290 @param[out] Indices The DESC_INDICES structure to initialize.\r
291\r
292**/\r
293VOID\r
294EFIAPI\r
295VirtioPrepare (\r
296 IN OUT VRING *Ring,\r
297 OUT DESC_INDICES *Indices\r
298 )\r
299{\r
300 //\r
301 // Prepare for virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device.\r
302 // We're going to poll the answer, the host should not send an interrupt.\r
303 //\r
304 *Ring->Avail.Flags = (UINT16) VRING_AVAIL_F_NO_INTERRUPT;\r
305\r
306 //\r
307 // Prepare for virtio-0.9.5, 2.4.1 Supplying Buffers to the Device.\r
308 //\r
635a3ca2 309 // Since we support only one in-flight descriptor chain, we can always build\r
310 // that chain starting at entry #0 of the descriptor table.\r
311 //\r
312 Indices->HeadDescIdx = 0;\r
313 Indices->NextDescIdx = Indices->HeadDescIdx;\r
e371e7e5 314}\r
315\r
316\r
263559b8 317/**\r
318\r
319 Append a contiguous buffer for transmission / reception via the virtio ring.\r
320\r
635a3ca2 321 This function implements the following section from virtio-0.9.5:\r
263559b8 322 - 2.4.1.1 Placing Buffers into the Descriptor Table\r
263559b8 323\r
324 Free space is taken as granted, since the individual drivers support only\r
325 synchronous requests and host side status is processed in lock-step with\r
326 request submission. It is the calling driver's responsibility to verify the\r
327 ring size in advance.\r
328\r
e371e7e5 329 The caller is responsible for initializing *Indices with VirtioPrepare()\r
330 first.\r
331\r
11a5fdf4 332 @param[in,out] Ring The virtio ring to append the buffer to, as a\r
333 descriptor.\r
334\r
335 @param[in] BufferPhysAddr (Guest pseudo-physical) start address of the\r
336 transmit / receive buffer.\r
337\r
338 @param[in] BufferSize Number of bytes to transmit or receive.\r
339\r
340 @param[in] Flags A bitmask of VRING_DESC_F_* flags. The caller\r
341 computes this mask dependent on further buffers to\r
342 append and transfer direction.\r
343 VRING_DESC_F_INDIRECT is unsupported. The\r
344 VRING_DESC.Next field is always set, but the host\r
345 only interprets it dependent on VRING_DESC_F_NEXT.\r
346\r
347 @param[in,out] Indices Indices->HeadDescIdx is not accessed.\r
348 On input, Indices->NextDescIdx identifies the next\r
349 descriptor to carry the buffer. On output,\r
350 Indices->NextDescIdx is incremented by one, modulo\r
351 2^16.\r
263559b8 352\r
353**/\r
354VOID\r
355EFIAPI\r
7fcacd6c 356VirtioAppendDesc (\r
e371e7e5 357 IN OUT VRING *Ring,\r
358 IN UINTN BufferPhysAddr,\r
359 IN UINT32 BufferSize,\r
360 IN UINT16 Flags,\r
361 IN OUT DESC_INDICES *Indices\r
263559b8 362 )\r
363{\r
364 volatile VRING_DESC *Desc;\r
365\r
635a3ca2 366 Desc = &Ring->Desc[Indices->NextDescIdx++ % Ring->QueueSize];\r
263559b8 367 Desc->Addr = BufferPhysAddr;\r
368 Desc->Len = BufferSize;\r
369 Desc->Flags = Flags;\r
635a3ca2 370 Desc->Next = Indices->NextDescIdx % Ring->QueueSize;\r
e371e7e5 371}\r
372\r
373\r
374/**\r
375\r
635a3ca2 376 Notify the host about the descriptor chain just built, and wait until the\r
377 host processes it.\r
e371e7e5 378\r
379 @param[in] PciIo The target virtio PCI device to notify.\r
380\r
381 @param[in] VirtQueueId Identifies the queue for the target device.\r
382\r
a7615fa8 383 @param[in,out] Ring The virtio ring with descriptors to submit.\r
e371e7e5 384\r
a7615fa8 385 @param[in] Indices Indices->NextDescIdx is not accessed.\r
386 Indices->HeadDescIdx identifies the head descriptor\r
387 of the descriptor chain.\r
e371e7e5 388\r
389\r
390 @return Error code from VirtioWrite() if it fails.\r
391\r
392 @retval EFI_SUCCESS Otherwise, the host processed all descriptors.\r
393\r
394**/\r
395EFI_STATUS\r
396EFIAPI\r
397VirtioFlush (\r
398 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
399 IN UINT16 VirtQueueId,\r
400 IN OUT VRING *Ring,\r
401 IN DESC_INDICES *Indices\r
402 )\r
403{\r
635a3ca2 404 UINT16 NextAvailIdx;\r
e371e7e5 405 EFI_STATUS Status;\r
406 UINTN PollPeriodUsecs;\r
407\r
635a3ca2 408 //\r
409 // virtio-0.9.5, 2.4.1.2 Updating the Available Ring\r
410 //\r
411 // It is not exactly clear from the wording of the virtio-0.9.5\r
412 // specification, but each entry in the Available Ring references only the\r
413 // head descriptor of any given descriptor chain.\r
414 //\r
415 NextAvailIdx = *Ring->Avail.Idx;\r
416 Ring->Avail.Ring[NextAvailIdx++ % Ring->QueueSize] =\r
417 Indices->HeadDescIdx % Ring->QueueSize;\r
418\r
e371e7e5 419 //\r
420 // virtio-0.9.5, 2.4.1.3 Updating the Index Field\r
421 //\r
422 MemoryFence();\r
635a3ca2 423 *Ring->Avail.Idx = NextAvailIdx;\r
e371e7e5 424\r
425 //\r
426 // virtio-0.9.5, 2.4.1.4 Notifying the Device -- gratuitous notifications are\r
427 // OK.\r
428 //\r
429 MemoryFence();\r
430 Status = VirtioWrite (\r
431 PciIo,\r
432 OFFSET_OF (VIRTIO_HDR, VhdrQueueNotify),\r
433 sizeof (UINT16),\r
434 VirtQueueId\r
435 );\r
436 if (EFI_ERROR (Status)) {\r
437 return Status;\r
438 }\r
439\r
440 //\r
441 // virtio-0.9.5, 2.4.2 Receiving Used Buffers From the Device\r
442 // Wait until the host processes and acknowledges our descriptor chain. The\r
443 // condition we use for polling is greatly simplified and relies on the\r
444 // synchronous, lock-step progress.\r
445 //\r
446 // Keep slowing down until we reach a poll period of slightly above 1 ms.\r
447 //\r
448 PollPeriodUsecs = 1;\r
449 MemoryFence();\r
635a3ca2 450 while (*Ring->Used.Idx != NextAvailIdx) {\r
e371e7e5 451 gBS->Stall (PollPeriodUsecs); // calls AcpiTimerLib::MicroSecondDelay\r
452\r
453 if (PollPeriodUsecs < 1024) {\r
454 PollPeriodUsecs *= 2;\r
455 }\r
456 MemoryFence();\r
457 }\r
458\r
dc9447bd 459 MemoryFence();\r
e371e7e5 460 return EFI_SUCCESS;\r
263559b8 461}\r