]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Virtio10Dxe/Virtio10.c
OvmfPkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / OvmfPkg / Virtio10Dxe / Virtio10.c
CommitLineData
9399f68a
LE
1/** @file\r
2 A non-transitional driver for VirtIo 1.0 PCI devices.\r
3\r
4 Copyright (C) 2016, Red Hat, Inc.\r
de1c57c5 5 Copyright (C) 2017, AMD Inc, All rights reserved.<BR>\r
9399f68a 6\r
b26f0cf9 7 SPDX-License-Identifier: BSD-2-Clause-Patent\r
9399f68a
LE
8**/\r
9\r
10#include <IndustryStandard/Pci.h>\r
11#include <IndustryStandard/Virtio.h>\r
12#include <Protocol/PciIo.h>\r
de1c57c5 13#include <Protocol/PciRootBridgeIo.h>\r
9399f68a
LE
14#include <Protocol/VirtioDevice.h>\r
15#include <Library/BaseMemoryLib.h>\r
16#include <Library/DebugLib.h>\r
17#include <Library/MemoryAllocationLib.h>\r
5685a243
LE
18#include <Library/PciCapLib.h>\r
19#include <Library/PciCapPciIoLib.h>\r
9399f68a
LE
20#include <Library/UefiBootServicesTableLib.h>\r
21#include <Library/UefiLib.h>\r
22\r
23#include "Virtio10.h"\r
24\r
25\r
26//\r
27// Utility functions\r
28//\r
29\r
30/**\r
31 Transfer data between the caller and a register in a virtio-1.0 register\r
32 block.\r
33\r
34 @param[in] PciIo The EFI_PCI_IO_PROTOCOL instance that represents\r
35 the device.\r
36\r
37 @param[in] Config The "fat pointer" structure that identifies the\r
38 register block to access.\r
39\r
40 @param[in] Write TRUE if the register should be written, FALSE if\r
41 the register should be read.\r
42\r
43 @param[in] FieldOffset The offset of the register within the register\r
44 block.\r
45\r
46 @param[in] FieldSize The size of the register within the register\r
47 block. Can be one of 1, 2, 4 and 8. Accesses to\r
48 8-byte registers are broken up into two 4-byte\r
49 accesses.\r
50\r
51 @param[in,out] Buffer When Write is TRUE, the register is written with\r
52 data from Buffer. When Write is FALSE, the caller\r
53 receives the register value into Buffer.\r
54\r
55 @retval EFI_SUCCESS Register access successful.\r
56\r
57 @retval EFI_INVALID_PARAMETER The register block pointed-to by Config\r
58 doesn't exist; or FieldOffset and FieldSize\r
59 would overflow the register block; or\r
60 FieldSize is invalid.\r
61\r
62 @return Error codes from\r
63 EFI_PCI_IO_PROTOCOL.(Io|Mem).(Read|Write)\r
64 member functions.\r
65**/\r
66STATIC\r
67EFI_STATUS\r
68Virtio10Transfer (\r
69 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
70 IN VIRTIO_1_0_CONFIG *Config,\r
71 IN BOOLEAN Write,\r
72 IN UINTN FieldOffset,\r
73 IN UINTN FieldSize,\r
74 IN OUT VOID *Buffer\r
75 )\r
76{\r
77 UINTN Count;\r
78 EFI_PCI_IO_PROTOCOL_WIDTH Width;\r
79 EFI_PCI_IO_PROTOCOL_ACCESS *BarType;\r
80 EFI_PCI_IO_PROTOCOL_IO_MEM Access;\r
81\r
82 if (!Config->Exists ||\r
83 FieldSize > Config->Length ||\r
84 FieldOffset > Config->Length - FieldSize) {\r
85 return EFI_INVALID_PARAMETER;\r
86 }\r
87\r
88 Count = 1;\r
89 switch (FieldSize) {\r
90 case 1:\r
91 Width = EfiPciIoWidthUint8;\r
92 break;\r
93\r
94 case 2:\r
95 Width = EfiPciIoWidthUint16;\r
96 break;\r
97\r
98 case 8:\r
99 Count = 2;\r
100 //\r
101 // fall through\r
102 //\r
103\r
104 case 4:\r
105 Width = EfiPciIoWidthUint32;\r
106 break;\r
107\r
108 default:\r
109 return EFI_INVALID_PARAMETER;\r
110 }\r
111\r
112 BarType = (Config->BarType == Virtio10BarTypeMem) ? &PciIo->Mem : &PciIo->Io;\r
113 Access = Write ? BarType->Write : BarType->Read;\r
114\r
115 return Access (PciIo, Width, Config->Bar, Config->Offset + FieldOffset,\r
116 Count, Buffer);\r
117}\r
118\r
119\r
120/**\r
121 Determine if a PCI BAR is IO or MMIO.\r
122\r
123 @param[in] PciIo The EFI_PCI_IO_PROTOCOL instance that represents the\r
124 device.\r
125\r
126 @param[in] BarIndex The number of the BAR whose type the caller is\r
127 interested in.\r
128\r
129 @param[out] BarType On output, a VIRTIO_1_0_BAR_TYPE value that gives the\r
130 type of the BAR.\r
131\r
132 @retval EFI_SUCCESS The BAR type has been recognized and stored in\r
133 BarType.\r
134\r
135 @retval EFI_UNSUPPORTED The BAR type couldn't be identified.\r
136\r
137 @return Error codes from\r
138 EFI_PCI_IO_PROTOCOL.GetBarAttributes().\r
139**/\r
140STATIC\r
141EFI_STATUS\r
142GetBarType (\r
143 IN EFI_PCI_IO_PROTOCOL *PciIo,\r
144 IN UINT8 BarIndex,\r
145 OUT VIRTIO_1_0_BAR_TYPE *BarType\r
146 )\r
147{\r
148 EFI_STATUS Status;\r
149 VOID *Resources;\r
150\r
151 Status = PciIo->GetBarAttributes (PciIo, BarIndex, NULL, &Resources);\r
152 if (EFI_ERROR (Status)) {\r
153 return Status;\r
154 }\r
155\r
156 Status = EFI_UNSUPPORTED;\r
157\r
158 if (*(UINT8 *)Resources == ACPI_QWORD_ADDRESS_SPACE_DESCRIPTOR) {\r
159 EFI_ACPI_QWORD_ADDRESS_SPACE_DESCRIPTOR *Descriptor;\r
160\r
161 Descriptor = Resources;\r
162 switch (Descriptor->ResType) {\r
163 case ACPI_ADDRESS_SPACE_TYPE_MEM:\r
164 *BarType = Virtio10BarTypeMem;\r
165 Status = EFI_SUCCESS;\r
166 break;\r
167\r
168 case ACPI_ADDRESS_SPACE_TYPE_IO:\r
169 *BarType = Virtio10BarTypeIo;\r
170 Status = EFI_SUCCESS;\r
171 break;\r
172\r
173 default:\r
174 break;\r
175 }\r
176 }\r
177\r
178 FreePool (Resources);\r
179 return Status;\r
180}\r
181\r
182\r
9399f68a
LE
183/*\r
184 Traverse the PCI capabilities list of a virtio-1.0 device, and capture the\r
185 locations of the interesting virtio-1.0 register blocks.\r
186\r
187 @param[in,out] Device The VIRTIO_1_0_DEV structure that identifies\r
188 the device. On input, the caller is responsible\r
189 that the Device->PciIo member be live, and that\r
190 the CommonConfig, NotifyConfig,\r
191 NotifyOffsetMultiplier and SpecificConfig\r
192 members be zeroed. On output, said members\r
193 will have been updated from the PCI\r
194 capabilities found.\r
195\r
9399f68a
LE
196 @retval EFI_SUCCESS Traversal successful.\r
197\r
5685a243
LE
198 @return Error codes from PciCapPciIoLib, PciCapLib, and the\r
199 GetBarType() helper function.\r
9399f68a
LE
200*/\r
201STATIC\r
202EFI_STATUS\r
203ParseCapabilities (\r
5685a243 204 IN OUT VIRTIO_1_0_DEV *Device\r
9399f68a
LE
205 )\r
206{\r
5685a243
LE
207 EFI_STATUS Status;\r
208 PCI_CAP_DEV *PciDevice;\r
209 PCI_CAP_LIST *CapList;\r
210 UINT16 VendorInstance;\r
211 PCI_CAP *VendorCap;\r
212\r
213 Status = PciCapPciIoDeviceInit (Device->PciIo, &PciDevice);\r
214 if (EFI_ERROR (Status)) {\r
215 return Status;\r
216 }\r
217 Status = PciCapListInit (PciDevice, &CapList);\r
218 if (EFI_ERROR (Status)) {\r
219 goto UninitPciDevice;\r
220 }\r
221\r
222 for (VendorInstance = 0;\r
223 !EFI_ERROR (PciCapListFindCap (CapList, PciCapNormal,\r
224 EFI_PCI_CAPABILITY_ID_VENDOR, VendorInstance,\r
225 &VendorCap));\r
226 VendorInstance++) {\r
9399f68a
LE
227 UINT8 CapLen;\r
228 VIRTIO_PCI_CAP VirtIoCap;\r
229 VIRTIO_1_0_CONFIG *ParsedConfig;\r
230\r
9399f68a
LE
231 //\r
232 // Big enough to accommodate a VIRTIO_PCI_CAP structure?\r
233 //\r
5685a243
LE
234 Status = PciCapRead (PciDevice, VendorCap,\r
235 OFFSET_OF (EFI_PCI_CAPABILITY_VENDOR_HDR, Length), &CapLen,\r
236 sizeof CapLen);\r
9399f68a 237 if (EFI_ERROR (Status)) {\r
5685a243 238 goto UninitCapList;\r
9399f68a 239 }\r
5685a243 240 if (CapLen < sizeof VirtIoCap) {\r
9399f68a
LE
241 //\r
242 // Too small, move to next.\r
243 //\r
244 continue;\r
245 }\r
246\r
247 //\r
248 // Read interesting part of capability.\r
249 //\r
5685a243 250 Status = PciCapRead (PciDevice, VendorCap, 0, &VirtIoCap, sizeof VirtIoCap);\r
9399f68a 251 if (EFI_ERROR (Status)) {\r
5685a243 252 goto UninitCapList;\r
9399f68a 253 }\r
5685a243 254\r
9399f68a
LE
255 switch (VirtIoCap.ConfigType) {\r
256 case VIRTIO_PCI_CAP_COMMON_CFG:\r
257 ParsedConfig = &Device->CommonConfig;\r
258 break;\r
259 case VIRTIO_PCI_CAP_NOTIFY_CFG:\r
260 ParsedConfig = &Device->NotifyConfig;\r
261 break;\r
262 case VIRTIO_PCI_CAP_DEVICE_CFG:\r
263 ParsedConfig = &Device->SpecificConfig;\r
264 break;\r
265 default:\r
266 //\r
267 // Capability is not interesting.\r
268 //\r
269 continue;\r
270 }\r
271\r
272 //\r
273 // Save the location of the register block into ParsedConfig.\r
274 //\r
275 Status = GetBarType (Device->PciIo, VirtIoCap.Bar, &ParsedConfig->BarType);\r
276 if (EFI_ERROR (Status)) {\r
5685a243 277 goto UninitCapList;\r
9399f68a
LE
278 }\r
279 ParsedConfig->Bar = VirtIoCap.Bar;\r
280 ParsedConfig->Offset = VirtIoCap.Offset;\r
281 ParsedConfig->Length = VirtIoCap.Length;\r
282\r
283 if (VirtIoCap.ConfigType == VIRTIO_PCI_CAP_NOTIFY_CFG) {\r
284 //\r
285 // This capability has an additional field called NotifyOffsetMultiplier;\r
286 // parse it too.\r
287 //\r
5685a243 288 if (CapLen < sizeof VirtIoCap + sizeof Device->NotifyOffsetMultiplier) {\r
9399f68a
LE
289 //\r
290 // Too small, move to next.\r
291 //\r
292 continue;\r
293 }\r
294\r
5685a243
LE
295 Status = PciCapRead (PciDevice, VendorCap, sizeof VirtIoCap,\r
296 &Device->NotifyOffsetMultiplier,\r
297 sizeof Device->NotifyOffsetMultiplier);\r
9399f68a 298 if (EFI_ERROR (Status)) {\r
5685a243 299 goto UninitCapList;\r
9399f68a
LE
300 }\r
301 }\r
302\r
303 //\r
304 // Capability parsed successfully.\r
305 //\r
306 ParsedConfig->Exists = TRUE;\r
307 }\r
308\r
5685a243
LE
309 ASSERT_EFI_ERROR (Status);\r
310\r
311UninitCapList:\r
312 PciCapListUninit (CapList);\r
313\r
314UninitPciDevice:\r
315 PciCapPciIoDeviceUninit (PciDevice);\r
316\r
317 return Status;\r
9399f68a
LE
318}\r
319\r
320\r
321/**\r
322 Accumulate the BAR type of a virtio-1.0 register block into a UINT64\r
323 attribute map, such that the latter is suitable for enabling IO / MMIO\r
324 decoding with EFI_PCI_IO_PROTOCOL.Attributes().\r
325\r
326 @param[in] Config The "fat pointer" structure that identifies the\r
327 register block. It is allowed for the register\r
328 block not to exist.\r
329\r
330 @param[in,out] Attributes On output, if the register block exists,\r
331 EFI_PCI_IO_ATTRIBUTE_MEMORY or\r
332 EFI_PCI_IO_ATTRIBUTE_IO is OR-ed into Attributes,\r
333 according to the register block's BAR type.\r
334**/\r
335STATIC\r
336VOID\r
337UpdateAttributes (\r
338 IN VIRTIO_1_0_CONFIG *Config,\r
339 IN OUT UINT64 *Attributes\r
340 )\r
341{\r
342 if (Config->Exists) {\r
343 *Attributes |= (Config->BarType == Virtio10BarTypeMem) ?\r
344 EFI_PCI_IO_ATTRIBUTE_MEMORY:\r
345 EFI_PCI_IO_ATTRIBUTE_IO;\r
346 }\r
347}\r
348\r
349\r
350//\r
351// VIRTIO_DEVICE_PROTOCOL member functions\r
352//\r
353\r
354STATIC\r
355EFI_STATUS\r
356EFIAPI\r
357Virtio10GetDeviceFeatures (\r
358 IN VIRTIO_DEVICE_PROTOCOL *This,\r
359 OUT UINT64 *DeviceFeatures\r
360 )\r
361{\r
362 VIRTIO_1_0_DEV *Dev;\r
363 UINT32 Selector;\r
364 UINT32 Features32[2];\r
365\r
366 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
367\r
368 for (Selector = 0; Selector < 2; ++Selector) {\r
369 EFI_STATUS Status;\r
370\r
371 //\r
372 // Select the low or high half of the features.\r
373 //\r
374 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
375 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DeviceFeatureSelect),\r
376 sizeof Selector, &Selector);\r
377 if (EFI_ERROR (Status)) {\r
378 return Status;\r
379 }\r
380\r
381 //\r
382 // Fetch that half.\r
383 //\r
384 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE,\r
385 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DeviceFeature),\r
386 sizeof Features32[Selector], &Features32[Selector]);\r
387 if (EFI_ERROR (Status)) {\r
388 return Status;\r
389 }\r
390 }\r
391\r
392 *DeviceFeatures = LShiftU64 (Features32[1], 32) | Features32[0];\r
393 return EFI_SUCCESS;\r
394}\r
395\r
396\r
397STATIC\r
398EFI_STATUS\r
399EFIAPI\r
400Virtio10SetGuestFeatures (\r
401 IN VIRTIO_DEVICE_PROTOCOL *This,\r
402 IN UINT64 Features\r
403 )\r
404{\r
405 VIRTIO_1_0_DEV *Dev;\r
406 UINT32 Selector;\r
407 UINT32 Features32[2];\r
408\r
409 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
410\r
411 Features32[0] = (UINT32)Features;\r
412 Features32[1] = (UINT32)RShiftU64 (Features, 32);\r
413\r
414 for (Selector = 0; Selector < 2; ++Selector) {\r
415 EFI_STATUS Status;\r
416\r
417 //\r
418 // Select the low or high half of the features.\r
419 //\r
420 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
421 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DriverFeatureSelect),\r
422 sizeof Selector, &Selector);\r
423 if (EFI_ERROR (Status)) {\r
424 return Status;\r
425 }\r
426\r
427 //\r
428 // Write that half.\r
429 //\r
430 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
431 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DriverFeature),\r
432 sizeof Features32[Selector], &Features32[Selector]);\r
433 if (EFI_ERROR (Status)) {\r
434 return Status;\r
435 }\r
436 }\r
437\r
438 return EFI_SUCCESS;\r
439}\r
440\r
441\r
442STATIC\r
443EFI_STATUS\r
444EFIAPI\r
445Virtio10SetQueueAddress (\r
446 IN VIRTIO_DEVICE_PROTOCOL *This,\r
53a4c604
BS
447 IN VRING *Ring,\r
448 IN UINT64 RingBaseShift\r
9399f68a
LE
449 )\r
450{\r
451 VIRTIO_1_0_DEV *Dev;\r
452 EFI_STATUS Status;\r
453 UINT64 Address;\r
454 UINT16 Enable;\r
455\r
456 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
457\r
458 Address = (UINTN)Ring->Desc;\r
60ee5629 459 Address += RingBaseShift;\r
9399f68a
LE
460 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
461 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueDesc),\r
462 sizeof Address, &Address);\r
463 if (EFI_ERROR (Status)) {\r
464 return Status;\r
465 }\r
466\r
467 Address = (UINTN)Ring->Avail.Flags;\r
60ee5629 468 Address += RingBaseShift;\r
9399f68a
LE
469 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
470 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueAvail),\r
471 sizeof Address, &Address);\r
472 if (EFI_ERROR (Status)) {\r
473 return Status;\r
474 }\r
475\r
476 Address = (UINTN)Ring->Used.Flags;\r
60ee5629 477 Address += RingBaseShift;\r
9399f68a
LE
478 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
479 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueUsed),\r
480 sizeof Address, &Address);\r
481 if (EFI_ERROR (Status)) {\r
482 return Status;\r
483 }\r
484\r
485 Enable = 1;\r
486 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
487 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueEnable),\r
488 sizeof Enable, &Enable);\r
489 return Status;\r
490}\r
491\r
492\r
493STATIC\r
494EFI_STATUS\r
495EFIAPI\r
496Virtio10SetQueueSel (\r
497 IN VIRTIO_DEVICE_PROTOCOL *This,\r
498 IN UINT16 Index\r
499 )\r
500{\r
501 VIRTIO_1_0_DEV *Dev;\r
502 EFI_STATUS Status;\r
503\r
504 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
505\r
506 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
507 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSelect),\r
508 sizeof Index, &Index);\r
509 return Status;\r
510}\r
511\r
512\r
513STATIC\r
514EFI_STATUS\r
515EFIAPI\r
516Virtio10SetQueueNotify (\r
517 IN VIRTIO_DEVICE_PROTOCOL *This,\r
518 IN UINT16 Index\r
519 )\r
520{\r
521 VIRTIO_1_0_DEV *Dev;\r
522 EFI_STATUS Status;\r
523 UINT16 SavedQueueSelect;\r
524 UINT16 NotifyOffset;\r
525\r
526 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
527\r
528 //\r
529 // Read NotifyOffset first. NotifyOffset is queue specific, so we have\r
530 // to stash & restore the current queue selector around it.\r
531 //\r
532 // So, start with saving the current queue selector.\r
533 //\r
534 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE,\r
535 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSelect),\r
536 sizeof SavedQueueSelect, &SavedQueueSelect);\r
537 if (EFI_ERROR (Status)) {\r
538 return Status;\r
539 }\r
540\r
541 //\r
542 // Select the requested queue.\r
543 //\r
544 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
545 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSelect),\r
546 sizeof Index, &Index);\r
547 if (EFI_ERROR (Status)) {\r
548 return Status;\r
549 }\r
550\r
551 //\r
552 // Read the QueueNotifyOff field.\r
553 //\r
554 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE,\r
555 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueNotifyOff),\r
556 sizeof NotifyOffset, &NotifyOffset);\r
557 if (EFI_ERROR (Status)) {\r
558 return Status;\r
559 }\r
560\r
561 //\r
562 // Re-select the original queue.\r
563 //\r
564 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
565 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSelect),\r
566 sizeof SavedQueueSelect, &SavedQueueSelect);\r
567 if (EFI_ERROR (Status)) {\r
568 return Status;\r
569 }\r
570\r
571 //\r
572 // We can now kick the queue.\r
573 //\r
574 Status = Virtio10Transfer (Dev->PciIo, &Dev->NotifyConfig, TRUE,\r
575 NotifyOffset * Dev->NotifyOffsetMultiplier,\r
576 sizeof Index, &Index);\r
577 return Status;\r
578}\r
579\r
580\r
581STATIC\r
582EFI_STATUS\r
583EFIAPI\r
584Virtio10SetQueueAlign (\r
585 IN VIRTIO_DEVICE_PROTOCOL *This,\r
586 IN UINT32 Alignment\r
587 )\r
588{\r
589 return (Alignment == EFI_PAGE_SIZE) ? EFI_SUCCESS : EFI_UNSUPPORTED;\r
590}\r
591\r
592\r
593STATIC\r
594EFI_STATUS\r
595EFIAPI\r
596Virtio10SetPageSize (\r
597 IN VIRTIO_DEVICE_PROTOCOL *This,\r
598 IN UINT32 PageSize\r
599 )\r
600{\r
601 return (PageSize == EFI_PAGE_SIZE) ? EFI_SUCCESS : EFI_UNSUPPORTED;\r
602}\r
603\r
604\r
605STATIC\r
606EFI_STATUS\r
607EFIAPI\r
608Virtio10GetQueueNumMax (\r
609 IN VIRTIO_DEVICE_PROTOCOL *This,\r
610 OUT UINT16 *QueueNumMax\r
611 )\r
612{\r
613 VIRTIO_1_0_DEV *Dev;\r
614 EFI_STATUS Status;\r
615\r
616 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
617\r
618 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE,\r
619 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSize),\r
620 sizeof *QueueNumMax, QueueNumMax);\r
621 return Status;\r
622}\r
623\r
624\r
625STATIC\r
626EFI_STATUS\r
627EFIAPI\r
628Virtio10SetQueueNum (\r
629 IN VIRTIO_DEVICE_PROTOCOL *This,\r
630 IN UINT16 QueueSize\r
631 )\r
632{\r
633 EFI_STATUS Status;\r
634 UINT16 CurrentSize;\r
635\r
636 //\r
637 // This member function is required for VirtIo MMIO, and a no-op in\r
638 // VirtIo PCI 0.9.5. In VirtIo 1.0, drivers can theoretically use this\r
639 // member to reduce memory consumption, but none of our drivers do. So\r
640 // just check that they set the size that is already in effect.\r
641 //\r
642 Status = Virtio10GetQueueNumMax (This, &CurrentSize);\r
643 if (EFI_ERROR (Status)) {\r
644 return Status;\r
645 }\r
646 return (CurrentSize == QueueSize) ? EFI_SUCCESS : EFI_UNSUPPORTED;\r
647}\r
648\r
649\r
650STATIC\r
651EFI_STATUS\r
652EFIAPI\r
653Virtio10GetDeviceStatus (\r
654 IN VIRTIO_DEVICE_PROTOCOL *This,\r
655 OUT UINT8 *DeviceStatus\r
656 )\r
657{\r
658 VIRTIO_1_0_DEV *Dev;\r
659 EFI_STATUS Status;\r
660\r
661 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
662\r
663 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE,\r
664 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DeviceStatus),\r
665 sizeof *DeviceStatus, DeviceStatus);\r
666 return Status;\r
667}\r
668\r
669\r
670STATIC\r
671EFI_STATUS\r
672EFIAPI\r
673Virtio10SetDeviceStatus (\r
674 IN VIRTIO_DEVICE_PROTOCOL *This,\r
675 IN UINT8 DeviceStatus\r
676 )\r
677{\r
678 VIRTIO_1_0_DEV *Dev;\r
679 EFI_STATUS Status;\r
680\r
681 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
682\r
683 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
684 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DeviceStatus),\r
685 sizeof DeviceStatus, &DeviceStatus);\r
686 return Status;\r
687}\r
688\r
689\r
690STATIC\r
691EFI_STATUS\r
692EFIAPI\r
693Virtio10WriteDevice (\r
694 IN VIRTIO_DEVICE_PROTOCOL *This,\r
695 IN UINTN FieldOffset,\r
696 IN UINTN FieldSize,\r
697 IN UINT64 Value\r
698 )\r
699{\r
700 VIRTIO_1_0_DEV *Dev;\r
701 EFI_STATUS Status;\r
702\r
703 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
704\r
705 Status = Virtio10Transfer (Dev->PciIo, &Dev->SpecificConfig, TRUE,\r
706 FieldOffset, FieldSize, &Value);\r
707 return Status;\r
708}\r
709\r
710\r
711STATIC\r
712EFI_STATUS\r
713EFIAPI\r
714Virtio10ReadDevice (\r
715 IN VIRTIO_DEVICE_PROTOCOL *This,\r
716 IN UINTN FieldOffset,\r
717 IN UINTN FieldSize,\r
718 IN UINTN BufferSize,\r
719 OUT VOID *Buffer\r
720 )\r
721{\r
722 VIRTIO_1_0_DEV *Dev;\r
723 EFI_STATUS Status;\r
724\r
725 if (FieldSize != BufferSize) {\r
726 return EFI_INVALID_PARAMETER;\r
727 }\r
728\r
729 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
730\r
731 Status = Virtio10Transfer (Dev->PciIo, &Dev->SpecificConfig, FALSE,\r
732 FieldOffset, FieldSize, Buffer);\r
733 return Status;\r
734}\r
735\r
de1c57c5
BS
736STATIC\r
737EFI_STATUS\r
738EFIAPI\r
739Virtio10AllocateSharedPages (\r
740 IN VIRTIO_DEVICE_PROTOCOL *This,\r
741 IN UINTN Pages,\r
742 IN OUT VOID **HostAddress\r
743 )\r
744{\r
745 VIRTIO_1_0_DEV *Dev;\r
746 EFI_STATUS Status;\r
747\r
748 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
749\r
750 Status = Dev->PciIo->AllocateBuffer (\r
751 Dev->PciIo,\r
752 AllocateAnyPages,\r
753 EfiBootServicesData,\r
754 Pages,\r
755 HostAddress,\r
756 EFI_PCI_ATTRIBUTE_MEMORY_CACHED\r
757 );\r
758 return Status;\r
759}\r
760\r
761STATIC\r
762VOID\r
763EFIAPI\r
764Virtio10FreeSharedPages (\r
765 IN VIRTIO_DEVICE_PROTOCOL *This,\r
766 IN UINTN Pages,\r
767 IN VOID *HostAddress\r
768 )\r
769{\r
770 VIRTIO_1_0_DEV *Dev;\r
771\r
772 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
773\r
774 Dev->PciIo->FreeBuffer (\r
775 Dev->PciIo,\r
776 Pages,\r
777 HostAddress\r
778 );\r
779}\r
780\r
781STATIC\r
782EFI_STATUS\r
783EFIAPI\r
784Virtio10MapSharedBuffer (\r
785 IN VIRTIO_DEVICE_PROTOCOL *This,\r
786 IN VIRTIO_MAP_OPERATION Operation,\r
787 IN VOID *HostAddress,\r
788 IN OUT UINTN *NumberOfBytes,\r
789 OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
790 OUT VOID **Mapping\r
791 )\r
792{\r
793 EFI_STATUS Status;\r
794 VIRTIO_1_0_DEV *Dev;\r
795 EFI_PCI_IO_PROTOCOL_OPERATION PciIoOperation;\r
796\r
797 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
798\r
799 //\r
800 // Map VIRTIO_MAP_OPERATION to EFI_PCI_IO_PROTOCOL_OPERATION\r
801 //\r
802 switch (Operation) {\r
803 case VirtioOperationBusMasterRead:\r
804 PciIoOperation = EfiPciIoOperationBusMasterRead;\r
805 break;\r
806 case VirtioOperationBusMasterWrite:\r
807 PciIoOperation = EfiPciIoOperationBusMasterWrite;\r
808 break;\r
809 case VirtioOperationBusMasterCommonBuffer:\r
810 PciIoOperation = EfiPciIoOperationBusMasterCommonBuffer;\r
811 break;\r
812 default:\r
813 return EFI_INVALID_PARAMETER;\r
814 }\r
815\r
816 Status = Dev->PciIo->Map (\r
817 Dev->PciIo,\r
818 PciIoOperation,\r
819 HostAddress,\r
820 NumberOfBytes,\r
821 DeviceAddress,\r
822 Mapping\r
823 );\r
824 return Status;\r
825}\r
826\r
827STATIC\r
828EFI_STATUS\r
829EFIAPI\r
830Virtio10UnmapSharedBuffer (\r
831 IN VIRTIO_DEVICE_PROTOCOL *This,\r
832 IN VOID *Mapping\r
833 )\r
834{\r
835 EFI_STATUS Status;\r
836 VIRTIO_1_0_DEV *Dev;\r
837\r
838 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
839\r
840 Status = Dev->PciIo->Unmap (\r
841 Dev->PciIo,\r
842 Mapping\r
843 );\r
844\r
845 return Status;\r
846}\r
9399f68a
LE
847\r
848STATIC CONST VIRTIO_DEVICE_PROTOCOL mVirtIoTemplate = {\r
849 VIRTIO_SPEC_REVISION (1, 0, 0),\r
850 0, // SubSystemDeviceId, filled in dynamically\r
851 Virtio10GetDeviceFeatures,\r
852 Virtio10SetGuestFeatures,\r
853 Virtio10SetQueueAddress,\r
854 Virtio10SetQueueSel,\r
855 Virtio10SetQueueNotify,\r
856 Virtio10SetQueueAlign,\r
857 Virtio10SetPageSize,\r
858 Virtio10GetQueueNumMax,\r
859 Virtio10SetQueueNum,\r
860 Virtio10GetDeviceStatus,\r
861 Virtio10SetDeviceStatus,\r
862 Virtio10WriteDevice,\r
de1c57c5
BS
863 Virtio10ReadDevice,\r
864 Virtio10AllocateSharedPages,\r
865 Virtio10FreeSharedPages,\r
866 Virtio10MapSharedBuffer,\r
867 Virtio10UnmapSharedBuffer\r
9399f68a
LE
868};\r
869\r
870\r
871//\r
872// EFI_DRIVER_BINDING_PROTOCOL member functions\r
873//\r
874\r
875STATIC\r
876EFI_STATUS\r
877EFIAPI\r
878Virtio10BindingSupported (\r
879 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
880 IN EFI_HANDLE DeviceHandle,\r
881 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
882 )\r
883{\r
884 EFI_STATUS Status;\r
885 EFI_PCI_IO_PROTOCOL *PciIo;\r
886 PCI_TYPE00 Pci;\r
887\r
888 Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
889 (VOID **)&PciIo, This->DriverBindingHandle,\r
890 DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);\r
891 if (EFI_ERROR (Status)) {\r
892 return Status;\r
893 }\r
894\r
895 Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0,\r
896 sizeof Pci / sizeof (UINT32), &Pci);\r
897 if (EFI_ERROR (Status)) {\r
898 goto CloseProtocol;\r
899 }\r
900\r
76569ca8 901 Status = EFI_UNSUPPORTED;\r
9399f68a
LE
902 //\r
903 // Recognize non-transitional modern devices. Also, we'll have to parse the\r
904 // PCI capability list, so make sure the CapabilityPtr field will be valid.\r
905 //\r
906 if (Pci.Hdr.VendorId == VIRTIO_VENDOR_ID &&\r
907 Pci.Hdr.DeviceId >= 0x1040 &&\r
908 Pci.Hdr.DeviceId <= 0x107F &&\r
909 Pci.Hdr.RevisionID >= 0x01 &&\r
910 Pci.Device.SubsystemID >= 0x40 &&\r
911 (Pci.Hdr.Status & EFI_PCI_STATUS_CAPABILITY) != 0) {\r
76569ca8
LE
912 //\r
913 // The virtio-vga device is special. It can be driven both as a VGA device\r
914 // with a linear framebuffer, and through its underlying, modern,\r
915 // virtio-gpu-pci device, which has no linear framebuffer itself. For\r
916 // compatibility with guest OSes that insist on inheriting a linear\r
917 // framebuffer from the firmware, we should leave virtio-vga to\r
918 // QemuVideoDxe, and support only virtio-gpu-pci here.\r
919 //\r
920 // Both virtio-vga and virtio-gpu-pci have DeviceId 0x1050, but only the\r
921 // former has device class PCI_CLASS_DISPLAY_VGA.\r
922 //\r
923 if (Pci.Hdr.DeviceId != 0x1050 || !IS_PCI_VGA (&Pci)) {\r
924 Status = EFI_SUCCESS;\r
925 }\r
9399f68a
LE
926 }\r
927\r
928CloseProtocol:\r
929 gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
930 This->DriverBindingHandle, DeviceHandle);\r
931\r
932 return Status;\r
933}\r
934\r
935\r
936STATIC\r
937EFI_STATUS\r
938EFIAPI\r
939Virtio10BindingStart (\r
940 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
941 IN EFI_HANDLE DeviceHandle,\r
942 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
943 )\r
944{\r
945 VIRTIO_1_0_DEV *Device;\r
946 EFI_STATUS Status;\r
947 PCI_TYPE00 Pci;\r
948 UINT64 SetAttributes;\r
949\r
950 Device = AllocateZeroPool (sizeof *Device);\r
951 if (Device == NULL) {\r
952 return EFI_OUT_OF_RESOURCES;\r
953 }\r
954\r
955 Device->Signature = VIRTIO_1_0_SIGNATURE;\r
956 CopyMem (&Device->VirtIo, &mVirtIoTemplate, sizeof mVirtIoTemplate);\r
957\r
958 Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
959 (VOID **)&Device->PciIo, This->DriverBindingHandle,\r
960 DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);\r
961 if (EFI_ERROR (Status)) {\r
962 goto FreeDevice;\r
963 }\r
964\r
965 Status = Device->PciIo->Pci.Read (Device->PciIo, EfiPciIoWidthUint32, 0,\r
966 sizeof Pci / sizeof (UINT32), &Pci);\r
967 if (EFI_ERROR (Status)) {\r
968 goto ClosePciIo;\r
969 }\r
970\r
971 Device->VirtIo.SubSystemDeviceId = Pci.Hdr.DeviceId - 0x1040;\r
972\r
5685a243 973 Status = ParseCapabilities (Device);\r
9399f68a
LE
974 if (EFI_ERROR (Status)) {\r
975 goto ClosePciIo;\r
976 }\r
977\r
978 Status = Device->PciIo->Attributes (Device->PciIo,\r
979 EfiPciIoAttributeOperationGet, 0,\r
980 &Device->OriginalPciAttributes);\r
981 if (EFI_ERROR (Status)) {\r
982 goto ClosePciIo;\r
983 }\r
984\r
de1c57c5
BS
985 SetAttributes = (EFI_PCI_IO_ATTRIBUTE_BUS_MASTER |\r
986 EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE);\r
9399f68a
LE
987 UpdateAttributes (&Device->CommonConfig, &SetAttributes);\r
988 UpdateAttributes (&Device->NotifyConfig, &SetAttributes);\r
989 UpdateAttributes (&Device->SpecificConfig, &SetAttributes);\r
990 Status = Device->PciIo->Attributes (Device->PciIo,\r
991 EfiPciIoAttributeOperationEnable, SetAttributes,\r
992 NULL);\r
993 if (EFI_ERROR (Status)) {\r
994 goto ClosePciIo;\r
995 }\r
996\r
997 Status = gBS->InstallProtocolInterface (&DeviceHandle,\r
998 &gVirtioDeviceProtocolGuid, EFI_NATIVE_INTERFACE,\r
999 &Device->VirtIo);\r
1000 if (EFI_ERROR (Status)) {\r
1001 goto RestorePciAttributes;\r
1002 }\r
1003\r
1004 return EFI_SUCCESS;\r
1005\r
1006RestorePciAttributes:\r
1007 Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationSet,\r
1008 Device->OriginalPciAttributes, NULL);\r
1009\r
1010ClosePciIo:\r
1011 gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
1012 This->DriverBindingHandle, DeviceHandle);\r
1013\r
1014FreeDevice:\r
1015 FreePool (Device);\r
1016\r
1017 return Status;\r
1018}\r
1019\r
1020\r
1021STATIC\r
1022EFI_STATUS\r
1023EFIAPI\r
1024Virtio10BindingStop (\r
1025 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
1026 IN EFI_HANDLE DeviceHandle,\r
1027 IN UINTN NumberOfChildren,\r
1028 IN EFI_HANDLE *ChildHandleBuffer\r
1029 )\r
1030{\r
1031 EFI_STATUS Status;\r
1032 VIRTIO_DEVICE_PROTOCOL *VirtIo;\r
1033 VIRTIO_1_0_DEV *Device;\r
1034\r
1035 Status = gBS->OpenProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,\r
1036 (VOID **)&VirtIo, This->DriverBindingHandle,\r
1037 DeviceHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
1038 if (EFI_ERROR (Status)) {\r
1039 return Status;\r
1040 }\r
1041\r
1042 Device = VIRTIO_1_0_FROM_VIRTIO_DEVICE (VirtIo);\r
1043\r
1044 Status = gBS->UninstallProtocolInterface (DeviceHandle,\r
1045 &gVirtioDeviceProtocolGuid, &Device->VirtIo);\r
1046 if (EFI_ERROR (Status)) {\r
1047 return Status;\r
1048 }\r
1049\r
1050 Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationSet,\r
1051 Device->OriginalPciAttributes, NULL);\r
1052 gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
1053 This->DriverBindingHandle, DeviceHandle);\r
1054 FreePool (Device);\r
1055\r
1056 return EFI_SUCCESS;\r
1057}\r
1058\r
1059\r
1060STATIC EFI_DRIVER_BINDING_PROTOCOL mDriverBinding = {\r
1061 &Virtio10BindingSupported,\r
1062 &Virtio10BindingStart,\r
1063 &Virtio10BindingStop,\r
1064 0x10, // Version\r
1065 NULL, // ImageHandle, to be overwritten\r
1066 NULL // DriverBindingHandle, to be overwritten\r
1067};\r
1068\r
1069\r
1070//\r
1071// EFI_COMPONENT_NAME_PROTOCOL and EFI_COMPONENT_NAME2_PROTOCOL\r
1072// implementations\r
1073//\r
1074\r
1075STATIC\r
1076EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {\r
1077 { "eng;en", L"Virtio 1.0 PCI Driver" },\r
1078 { NULL, NULL }\r
1079};\r
1080\r
1081STATIC\r
1082EFI_COMPONENT_NAME_PROTOCOL mComponentName;\r
1083\r
1084STATIC\r
1085EFI_STATUS\r
1086EFIAPI\r
1087Virtio10GetDriverName (\r
1088 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
1089 IN CHAR8 *Language,\r
1090 OUT CHAR16 **DriverName\r
1091 )\r
1092{\r
1093 return LookupUnicodeString2 (\r
1094 Language,\r
1095 This->SupportedLanguages,\r
1096 mDriverNameTable,\r
1097 DriverName,\r
1098 (BOOLEAN)(This == &mComponentName) // Iso639Language\r
1099 );\r
1100}\r
1101\r
1102STATIC\r
1103EFI_STATUS\r
1104EFIAPI\r
1105Virtio10GetDeviceName (\r
1106 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
1107 IN EFI_HANDLE DeviceHandle,\r
1108 IN EFI_HANDLE ChildHandle,\r
1109 IN CHAR8 *Language,\r
1110 OUT CHAR16 **ControllerName\r
1111 )\r
1112{\r
1113 return EFI_UNSUPPORTED;\r
1114}\r
1115\r
1116STATIC\r
1117EFI_COMPONENT_NAME_PROTOCOL mComponentName = {\r
1118 &Virtio10GetDriverName,\r
1119 &Virtio10GetDeviceName,\r
1120 "eng"\r
1121};\r
1122\r
1123STATIC\r
1124EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {\r
1125 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &Virtio10GetDriverName,\r
1126 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &Virtio10GetDeviceName,\r
1127 "en"\r
1128};\r
1129\r
1130\r
1131//\r
1132// Entry point of this driver\r
1133//\r
1134\r
1135EFI_STATUS\r
1136EFIAPI\r
1137Virtio10EntryPoint (\r
1138 IN EFI_HANDLE ImageHandle,\r
1139 IN EFI_SYSTEM_TABLE *SystemTable\r
1140 )\r
1141{\r
1142 return EfiLibInstallDriverBindingComponentName2 (\r
1143 ImageHandle,\r
1144 SystemTable,\r
1145 &mDriverBinding,\r
1146 ImageHandle,\r
1147 &mComponentName,\r
1148 &mComponentName2\r
1149 );\r
1150}\r