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