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