]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/Library/VirtioLib/VirtioLib.c
OvmfPkg: extract VirtioLib from 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/BaseMemoryLib.h>
19 #include <Library/DebugLib.h>
20 #include <Library/MemoryAllocationLib.h>
21
22 #include <Library/VirtioLib.h>
23
24
25 /**
26
27 Write a word into Region 0 of the device specified by PciIo.
28
29 Region 0 must be an iomem region. This is an internal function for the
30 driver-specific VIRTIO_CFG_WRITE() macros.
31
32 @param[in] PciIo Target PCI device.
33
34 @param[in] FieldOffset Destination offset.
35
36 @param[in] FieldSize Destination field size, must be in { 1, 2, 4, 8 }.
37
38 @param[in] Value Little endian value to write, converted to UINT64.
39 The least significant FieldSize bytes will be used.
40
41
42 @return Status code returned by PciIo->Io.Write().
43
44 **/
45 EFIAPI
46 EFI_STATUS
47 VirtioWrite (
48 IN EFI_PCI_IO_PROTOCOL *PciIo,
49 IN UINTN FieldOffset,
50 IN UINTN FieldSize,
51 IN UINT64 Value
52 )
53 {
54 UINTN Count;
55 EFI_PCI_IO_PROTOCOL_WIDTH Width;
56
57 Count = 1;
58 switch (FieldSize) {
59 case 1:
60 Width = EfiPciIoWidthUint8;
61 break;
62
63 case 2:
64 Width = EfiPciIoWidthUint16;
65 break;
66
67 case 8:
68 Count = 2;
69 // fall through
70
71 case 4:
72 Width = EfiPciIoWidthUint32;
73 break;
74
75 default:
76 ASSERT (FALSE);
77 }
78
79 return PciIo->Io.Write (
80 PciIo,
81 Width,
82 PCI_BAR_IDX0,
83 FieldOffset,
84 Count,
85 &Value
86 );
87 }
88
89
90 /**
91
92 Read a word from Region 0 of the device specified by PciIo.
93
94 Region 0 must be an iomem region. This is an internal function for the
95 driver-specific VIRTIO_CFG_READ() macros.
96
97 @param[in] PciIo Source PCI device.
98
99 @param[in] FieldOffset Source offset.
100
101 @param[in] FieldSize Source field size, must be in { 1, 2, 4, 8 }.
102
103 @param[in] BufferSize Number of bytes available in the target buffer. Must
104 equal FieldSize.
105
106 @param[out] Buffer Target buffer.
107
108
109 @return Status code returned by PciIo->Io.Read().
110
111 **/
112 EFIAPI
113 EFI_STATUS
114 VirtioRead (
115 IN EFI_PCI_IO_PROTOCOL *PciIo,
116 IN UINTN FieldOffset,
117 IN UINTN FieldSize,
118 IN UINTN BufferSize,
119 OUT VOID *Buffer
120 )
121 {
122 UINTN Count;
123 EFI_PCI_IO_PROTOCOL_WIDTH Width;
124
125 ASSERT (FieldSize == BufferSize);
126
127 Count = 1;
128 switch (FieldSize) {
129 case 1:
130 Width = EfiPciIoWidthUint8;
131 break;
132
133 case 2:
134 Width = EfiPciIoWidthUint16;
135 break;
136
137 case 8:
138 Count = 2;
139 // fall through
140
141 case 4:
142 Width = EfiPciIoWidthUint32;
143 break;
144
145 default:
146 ASSERT (FALSE);
147 }
148
149 return PciIo->Io.Read (
150 PciIo,
151 Width,
152 PCI_BAR_IDX0,
153 FieldOffset,
154 Count,
155 Buffer
156 );
157 }
158
159
160 /**
161
162 Configure a virtio ring.
163
164 This function sets up internal storage (the guest-host communication area)
165 and lays out several "navigation" (ie. no-ownership) pointers to parts of
166 that storage.
167
168 Relevant sections from the virtio-0.9.5 spec:
169 - 1.1 Virtqueues,
170 - 2.3 Virtqueue Configuration.
171
172 @param[in] The number of descriptors to allocate for the
173 virtio ring, as requested by the host.
174
175 @param[out] Ring The virtio ring to set up.
176
177 @retval EFI_OUT_OF_RESOURCES AllocatePages() failed to allocate contiguous
178 pages for the requested QueueSize. Fields of
179 Ring have indeterminate value.
180
181 @retval EFI_SUCCESS Allocation and setup successful. Ring->Base
182 (and nothing else) is responsible for
183 deallocation.
184
185 **/
186 EFI_STATUS
187 EFIAPI
188 VirtioRingInit (
189 IN UINT16 QueueSize,
190 OUT VRING *Ring
191 )
192 {
193 UINTN RingSize;
194 volatile UINT8 *RingPagesPtr;
195
196 RingSize = ALIGN_VALUE (
197 sizeof *Ring->Desc * QueueSize +
198 sizeof *Ring->Avail.Flags +
199 sizeof *Ring->Avail.Idx +
200 sizeof *Ring->Avail.Ring * QueueSize +
201 sizeof *Ring->Avail.UsedEvent,
202 EFI_PAGE_SIZE);
203
204 RingSize += ALIGN_VALUE (
205 sizeof *Ring->Used.Flags +
206 sizeof *Ring->Used.Idx +
207 sizeof *Ring->Used.UsedElem * QueueSize +
208 sizeof *Ring->Used.AvailEvent,
209 EFI_PAGE_SIZE);
210
211 Ring->NumPages = EFI_SIZE_TO_PAGES (RingSize);
212 Ring->Base = AllocatePages (Ring->NumPages);
213 if (Ring->Base == NULL) {
214 return EFI_OUT_OF_RESOURCES;
215 }
216 SetMem (Ring->Base, RingSize, 0x00);
217 RingPagesPtr = Ring->Base;
218
219 Ring->Desc = (volatile VOID *) RingPagesPtr;
220 RingPagesPtr += sizeof *Ring->Desc * QueueSize;
221
222 Ring->Avail.Flags = (volatile VOID *) RingPagesPtr;
223 RingPagesPtr += sizeof *Ring->Avail.Flags;
224
225 Ring->Avail.Idx = (volatile VOID *) RingPagesPtr;
226 RingPagesPtr += sizeof *Ring->Avail.Idx;
227
228 Ring->Avail.Ring = (volatile VOID *) RingPagesPtr;
229 RingPagesPtr += sizeof *Ring->Avail.Ring * QueueSize;
230
231 Ring->Avail.UsedEvent = (volatile VOID *) RingPagesPtr;
232 RingPagesPtr += sizeof *Ring->Avail.UsedEvent;
233
234 RingPagesPtr = (volatile UINT8 *) Ring->Base +
235 ALIGN_VALUE (RingPagesPtr - (volatile UINT8 *) Ring->Base,
236 EFI_PAGE_SIZE);
237
238 Ring->Used.Flags = (volatile VOID *) RingPagesPtr;
239 RingPagesPtr += sizeof *Ring->Used.Flags;
240
241 Ring->Used.Idx = (volatile VOID *) RingPagesPtr;
242 RingPagesPtr += sizeof *Ring->Used.Idx;
243
244 Ring->Used.UsedElem = (volatile VOID *) RingPagesPtr;
245 RingPagesPtr += sizeof *Ring->Used.UsedElem * QueueSize;
246
247 Ring->Used.AvailEvent = (volatile VOID *) RingPagesPtr;
248 RingPagesPtr += sizeof *Ring->Used.AvailEvent;
249
250 Ring->QueueSize = QueueSize;
251 return EFI_SUCCESS;
252 }
253
254
255 /**
256
257 Tear down the internal resources of a configured virtio ring.
258
259 The caller is responsible to stop the host from using this ring before
260 invoking this function: the VSTAT_DRIVER_OK bit must be clear in
261 VhdrDeviceStatus.
262
263 @param[out] Ring The virtio ring to clean up.
264
265 **/
266 VOID
267 EFIAPI
268 VirtioRingUninit (
269 IN OUT VRING *Ring
270 )
271 {
272 FreePages (Ring->Base, Ring->NumPages);
273 SetMem (Ring, sizeof *Ring, 0x00);
274 }
275
276
277 /**
278
279 Append a contiguous buffer for transmission / reception via the virtio ring.
280
281 This function implements the following sections from virtio-0.9.5:
282 - 2.4.1.1 Placing Buffers into the Descriptor Table
283 - 2.4.1.2 Updating the Available Ring
284
285 Free space is taken as granted, since the individual drivers support only
286 synchronous requests and host side status is processed in lock-step with
287 request submission. It is the calling driver's responsibility to verify the
288 ring size in advance.
289
290 @param[in out] Ring The virtio ring to append the buffer to, as a
291 descriptor.
292
293 @param [in] BufferPhysAddr (Guest pseudo-physical) start address of the
294 transmit / receive buffer.
295
296 @param [in] BufferSize Number of bytes to transmit or receive.
297
298 @param [in] Flags A bitmask of VRING_DESC_F_* flags. The caller
299 computes this mask dependent on further buffers
300 to append and transfer direction.
301 VRING_DESC_F_INDIRECT is unsupported. The
302 VRING_DESC.Next field is always set, but the
303 host only interprets it dependent on
304 VRING_DESC_F_NEXT.
305
306 @param [in] HeadIdx The index identifying the head buffer (first
307 buffer appended) belonging to this same
308 request.
309
310 @param [in out] NextAvailIdx On input, the index identifying the next
311 descriptor available to carry the buffer. On
312 output, incremented by one, modulo 2^16.
313
314 **/
315 VOID
316 EFIAPI
317 AppendDesc (
318 IN OUT VRING *Ring,
319 IN UINTN BufferPhysAddr,
320 IN UINT32 BufferSize,
321 IN UINT16 Flags,
322 IN UINT16 HeadIdx,
323 IN OUT UINT16 *NextAvailIdx
324 )
325 {
326 volatile VRING_DESC *Desc;
327
328 Desc = &Ring->Desc[*NextAvailIdx % Ring->QueueSize];
329 Desc->Addr = BufferPhysAddr;
330 Desc->Len = BufferSize;
331 Desc->Flags = Flags;
332 Ring->Avail.Ring[(*NextAvailIdx)++ % Ring->QueueSize] =
333 HeadIdx % Ring->QueueSize;
334 Desc->Next = *NextAvailIdx % Ring->QueueSize;
335 }