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