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