OvmfPkg/Virtio: take RingBaseShift in SetQueueAddress()
[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
53a4c604
BS
492 IN VRING *Ring,\r
493 IN UINT64 RingBaseShift\r
9399f68a
LE
494 )\r
495{\r
496 VIRTIO_1_0_DEV *Dev;\r
497 EFI_STATUS Status;\r
498 UINT64 Address;\r
499 UINT16 Enable;\r
500\r
53a4c604
BS
501 ASSERT (RingBaseShift == 0);\r
502\r
9399f68a
LE
503 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
504\r
505 Address = (UINTN)Ring->Desc;\r
506 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
507 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueDesc),\r
508 sizeof Address, &Address);\r
509 if (EFI_ERROR (Status)) {\r
510 return Status;\r
511 }\r
512\r
513 Address = (UINTN)Ring->Avail.Flags;\r
514 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
515 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueAvail),\r
516 sizeof Address, &Address);\r
517 if (EFI_ERROR (Status)) {\r
518 return Status;\r
519 }\r
520\r
521 Address = (UINTN)Ring->Used.Flags;\r
522 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
523 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueUsed),\r
524 sizeof Address, &Address);\r
525 if (EFI_ERROR (Status)) {\r
526 return Status;\r
527 }\r
528\r
529 Enable = 1;\r
530 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
531 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueEnable),\r
532 sizeof Enable, &Enable);\r
533 return Status;\r
534}\r
535\r
536\r
537STATIC\r
538EFI_STATUS\r
539EFIAPI\r
540Virtio10SetQueueSel (\r
541 IN VIRTIO_DEVICE_PROTOCOL *This,\r
542 IN UINT16 Index\r
543 )\r
544{\r
545 VIRTIO_1_0_DEV *Dev;\r
546 EFI_STATUS Status;\r
547\r
548 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\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 return Status;\r
554}\r
555\r
556\r
557STATIC\r
558EFI_STATUS\r
559EFIAPI\r
560Virtio10SetQueueNotify (\r
561 IN VIRTIO_DEVICE_PROTOCOL *This,\r
562 IN UINT16 Index\r
563 )\r
564{\r
565 VIRTIO_1_0_DEV *Dev;\r
566 EFI_STATUS Status;\r
567 UINT16 SavedQueueSelect;\r
568 UINT16 NotifyOffset;\r
569\r
570 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
571\r
572 //\r
573 // Read NotifyOffset first. NotifyOffset is queue specific, so we have\r
574 // to stash & restore the current queue selector around it.\r
575 //\r
576 // So, start with saving the current queue selector.\r
577 //\r
578 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE,\r
579 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSelect),\r
580 sizeof SavedQueueSelect, &SavedQueueSelect);\r
581 if (EFI_ERROR (Status)) {\r
582 return Status;\r
583 }\r
584\r
585 //\r
586 // Select the requested queue.\r
587 //\r
588 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
589 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSelect),\r
590 sizeof Index, &Index);\r
591 if (EFI_ERROR (Status)) {\r
592 return Status;\r
593 }\r
594\r
595 //\r
596 // Read the QueueNotifyOff field.\r
597 //\r
598 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE,\r
599 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueNotifyOff),\r
600 sizeof NotifyOffset, &NotifyOffset);\r
601 if (EFI_ERROR (Status)) {\r
602 return Status;\r
603 }\r
604\r
605 //\r
606 // Re-select the original queue.\r
607 //\r
608 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
609 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSelect),\r
610 sizeof SavedQueueSelect, &SavedQueueSelect);\r
611 if (EFI_ERROR (Status)) {\r
612 return Status;\r
613 }\r
614\r
615 //\r
616 // We can now kick the queue.\r
617 //\r
618 Status = Virtio10Transfer (Dev->PciIo, &Dev->NotifyConfig, TRUE,\r
619 NotifyOffset * Dev->NotifyOffsetMultiplier,\r
620 sizeof Index, &Index);\r
621 return Status;\r
622}\r
623\r
624\r
625STATIC\r
626EFI_STATUS\r
627EFIAPI\r
628Virtio10SetQueueAlign (\r
629 IN VIRTIO_DEVICE_PROTOCOL *This,\r
630 IN UINT32 Alignment\r
631 )\r
632{\r
633 return (Alignment == EFI_PAGE_SIZE) ? EFI_SUCCESS : EFI_UNSUPPORTED;\r
634}\r
635\r
636\r
637STATIC\r
638EFI_STATUS\r
639EFIAPI\r
640Virtio10SetPageSize (\r
641 IN VIRTIO_DEVICE_PROTOCOL *This,\r
642 IN UINT32 PageSize\r
643 )\r
644{\r
645 return (PageSize == EFI_PAGE_SIZE) ? EFI_SUCCESS : EFI_UNSUPPORTED;\r
646}\r
647\r
648\r
649STATIC\r
650EFI_STATUS\r
651EFIAPI\r
652Virtio10GetQueueNumMax (\r
653 IN VIRTIO_DEVICE_PROTOCOL *This,\r
654 OUT UINT16 *QueueNumMax\r
655 )\r
656{\r
657 VIRTIO_1_0_DEV *Dev;\r
658 EFI_STATUS Status;\r
659\r
660 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
661\r
662 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE,\r
663 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, QueueSize),\r
664 sizeof *QueueNumMax, QueueNumMax);\r
665 return Status;\r
666}\r
667\r
668\r
669STATIC\r
670EFI_STATUS\r
671EFIAPI\r
672Virtio10SetQueueNum (\r
673 IN VIRTIO_DEVICE_PROTOCOL *This,\r
674 IN UINT16 QueueSize\r
675 )\r
676{\r
677 EFI_STATUS Status;\r
678 UINT16 CurrentSize;\r
679\r
680 //\r
681 // This member function is required for VirtIo MMIO, and a no-op in\r
682 // VirtIo PCI 0.9.5. In VirtIo 1.0, drivers can theoretically use this\r
683 // member to reduce memory consumption, but none of our drivers do. So\r
684 // just check that they set the size that is already in effect.\r
685 //\r
686 Status = Virtio10GetQueueNumMax (This, &CurrentSize);\r
687 if (EFI_ERROR (Status)) {\r
688 return Status;\r
689 }\r
690 return (CurrentSize == QueueSize) ? EFI_SUCCESS : EFI_UNSUPPORTED;\r
691}\r
692\r
693\r
694STATIC\r
695EFI_STATUS\r
696EFIAPI\r
697Virtio10GetDeviceStatus (\r
698 IN VIRTIO_DEVICE_PROTOCOL *This,\r
699 OUT UINT8 *DeviceStatus\r
700 )\r
701{\r
702 VIRTIO_1_0_DEV *Dev;\r
703 EFI_STATUS Status;\r
704\r
705 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
706\r
707 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, FALSE,\r
708 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DeviceStatus),\r
709 sizeof *DeviceStatus, DeviceStatus);\r
710 return Status;\r
711}\r
712\r
713\r
714STATIC\r
715EFI_STATUS\r
716EFIAPI\r
717Virtio10SetDeviceStatus (\r
718 IN VIRTIO_DEVICE_PROTOCOL *This,\r
719 IN UINT8 DeviceStatus\r
720 )\r
721{\r
722 VIRTIO_1_0_DEV *Dev;\r
723 EFI_STATUS Status;\r
724\r
725 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
726\r
727 Status = Virtio10Transfer (Dev->PciIo, &Dev->CommonConfig, TRUE,\r
728 OFFSET_OF (VIRTIO_PCI_COMMON_CFG, DeviceStatus),\r
729 sizeof DeviceStatus, &DeviceStatus);\r
730 return Status;\r
731}\r
732\r
733\r
734STATIC\r
735EFI_STATUS\r
736EFIAPI\r
737Virtio10WriteDevice (\r
738 IN VIRTIO_DEVICE_PROTOCOL *This,\r
739 IN UINTN FieldOffset,\r
740 IN UINTN FieldSize,\r
741 IN UINT64 Value\r
742 )\r
743{\r
744 VIRTIO_1_0_DEV *Dev;\r
745 EFI_STATUS Status;\r
746\r
747 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
748\r
749 Status = Virtio10Transfer (Dev->PciIo, &Dev->SpecificConfig, TRUE,\r
750 FieldOffset, FieldSize, &Value);\r
751 return Status;\r
752}\r
753\r
754\r
755STATIC\r
756EFI_STATUS\r
757EFIAPI\r
758Virtio10ReadDevice (\r
759 IN VIRTIO_DEVICE_PROTOCOL *This,\r
760 IN UINTN FieldOffset,\r
761 IN UINTN FieldSize,\r
762 IN UINTN BufferSize,\r
763 OUT VOID *Buffer\r
764 )\r
765{\r
766 VIRTIO_1_0_DEV *Dev;\r
767 EFI_STATUS Status;\r
768\r
769 if (FieldSize != BufferSize) {\r
770 return EFI_INVALID_PARAMETER;\r
771 }\r
772\r
773 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
774\r
775 Status = Virtio10Transfer (Dev->PciIo, &Dev->SpecificConfig, FALSE,\r
776 FieldOffset, FieldSize, Buffer);\r
777 return Status;\r
778}\r
779\r
de1c57c5
BS
780STATIC\r
781EFI_STATUS\r
782EFIAPI\r
783Virtio10AllocateSharedPages (\r
784 IN VIRTIO_DEVICE_PROTOCOL *This,\r
785 IN UINTN Pages,\r
786 IN OUT VOID **HostAddress\r
787 )\r
788{\r
789 VIRTIO_1_0_DEV *Dev;\r
790 EFI_STATUS Status;\r
791\r
792 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
793\r
794 Status = Dev->PciIo->AllocateBuffer (\r
795 Dev->PciIo,\r
796 AllocateAnyPages,\r
797 EfiBootServicesData,\r
798 Pages,\r
799 HostAddress,\r
800 EFI_PCI_ATTRIBUTE_MEMORY_CACHED\r
801 );\r
802 return Status;\r
803}\r
804\r
805STATIC\r
806VOID\r
807EFIAPI\r
808Virtio10FreeSharedPages (\r
809 IN VIRTIO_DEVICE_PROTOCOL *This,\r
810 IN UINTN Pages,\r
811 IN VOID *HostAddress\r
812 )\r
813{\r
814 VIRTIO_1_0_DEV *Dev;\r
815\r
816 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
817\r
818 Dev->PciIo->FreeBuffer (\r
819 Dev->PciIo,\r
820 Pages,\r
821 HostAddress\r
822 );\r
823}\r
824\r
825STATIC\r
826EFI_STATUS\r
827EFIAPI\r
828Virtio10MapSharedBuffer (\r
829 IN VIRTIO_DEVICE_PROTOCOL *This,\r
830 IN VIRTIO_MAP_OPERATION Operation,\r
831 IN VOID *HostAddress,\r
832 IN OUT UINTN *NumberOfBytes,\r
833 OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
834 OUT VOID **Mapping\r
835 )\r
836{\r
837 EFI_STATUS Status;\r
838 VIRTIO_1_0_DEV *Dev;\r
839 EFI_PCI_IO_PROTOCOL_OPERATION PciIoOperation;\r
840\r
841 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
842\r
843 //\r
844 // Map VIRTIO_MAP_OPERATION to EFI_PCI_IO_PROTOCOL_OPERATION\r
845 //\r
846 switch (Operation) {\r
847 case VirtioOperationBusMasterRead:\r
848 PciIoOperation = EfiPciIoOperationBusMasterRead;\r
849 break;\r
850 case VirtioOperationBusMasterWrite:\r
851 PciIoOperation = EfiPciIoOperationBusMasterWrite;\r
852 break;\r
853 case VirtioOperationBusMasterCommonBuffer:\r
854 PciIoOperation = EfiPciIoOperationBusMasterCommonBuffer;\r
855 break;\r
856 default:\r
857 return EFI_INVALID_PARAMETER;\r
858 }\r
859\r
860 Status = Dev->PciIo->Map (\r
861 Dev->PciIo,\r
862 PciIoOperation,\r
863 HostAddress,\r
864 NumberOfBytes,\r
865 DeviceAddress,\r
866 Mapping\r
867 );\r
868 return Status;\r
869}\r
870\r
871STATIC\r
872EFI_STATUS\r
873EFIAPI\r
874Virtio10UnmapSharedBuffer (\r
875 IN VIRTIO_DEVICE_PROTOCOL *This,\r
876 IN VOID *Mapping\r
877 )\r
878{\r
879 EFI_STATUS Status;\r
880 VIRTIO_1_0_DEV *Dev;\r
881\r
882 Dev = VIRTIO_1_0_FROM_VIRTIO_DEVICE (This);\r
883\r
884 Status = Dev->PciIo->Unmap (\r
885 Dev->PciIo,\r
886 Mapping\r
887 );\r
888\r
889 return Status;\r
890}\r
9399f68a
LE
891\r
892STATIC CONST VIRTIO_DEVICE_PROTOCOL mVirtIoTemplate = {\r
893 VIRTIO_SPEC_REVISION (1, 0, 0),\r
894 0, // SubSystemDeviceId, filled in dynamically\r
895 Virtio10GetDeviceFeatures,\r
896 Virtio10SetGuestFeatures,\r
897 Virtio10SetQueueAddress,\r
898 Virtio10SetQueueSel,\r
899 Virtio10SetQueueNotify,\r
900 Virtio10SetQueueAlign,\r
901 Virtio10SetPageSize,\r
902 Virtio10GetQueueNumMax,\r
903 Virtio10SetQueueNum,\r
904 Virtio10GetDeviceStatus,\r
905 Virtio10SetDeviceStatus,\r
906 Virtio10WriteDevice,\r
de1c57c5
BS
907 Virtio10ReadDevice,\r
908 Virtio10AllocateSharedPages,\r
909 Virtio10FreeSharedPages,\r
910 Virtio10MapSharedBuffer,\r
911 Virtio10UnmapSharedBuffer\r
9399f68a
LE
912};\r
913\r
914\r
915//\r
916// EFI_DRIVER_BINDING_PROTOCOL member functions\r
917//\r
918\r
919STATIC\r
920EFI_STATUS\r
921EFIAPI\r
922Virtio10BindingSupported (\r
923 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
924 IN EFI_HANDLE DeviceHandle,\r
925 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
926 )\r
927{\r
928 EFI_STATUS Status;\r
929 EFI_PCI_IO_PROTOCOL *PciIo;\r
930 PCI_TYPE00 Pci;\r
931\r
932 Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
933 (VOID **)&PciIo, This->DriverBindingHandle,\r
934 DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);\r
935 if (EFI_ERROR (Status)) {\r
936 return Status;\r
937 }\r
938\r
939 Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0,\r
940 sizeof Pci / sizeof (UINT32), &Pci);\r
941 if (EFI_ERROR (Status)) {\r
942 goto CloseProtocol;\r
943 }\r
944\r
76569ca8 945 Status = EFI_UNSUPPORTED;\r
9399f68a
LE
946 //\r
947 // Recognize non-transitional modern devices. Also, we'll have to parse the\r
948 // PCI capability list, so make sure the CapabilityPtr field will be valid.\r
949 //\r
950 if (Pci.Hdr.VendorId == VIRTIO_VENDOR_ID &&\r
951 Pci.Hdr.DeviceId >= 0x1040 &&\r
952 Pci.Hdr.DeviceId <= 0x107F &&\r
953 Pci.Hdr.RevisionID >= 0x01 &&\r
954 Pci.Device.SubsystemID >= 0x40 &&\r
955 (Pci.Hdr.Status & EFI_PCI_STATUS_CAPABILITY) != 0) {\r
76569ca8
LE
956 //\r
957 // The virtio-vga device is special. It can be driven both as a VGA device\r
958 // with a linear framebuffer, and through its underlying, modern,\r
959 // virtio-gpu-pci device, which has no linear framebuffer itself. For\r
960 // compatibility with guest OSes that insist on inheriting a linear\r
961 // framebuffer from the firmware, we should leave virtio-vga to\r
962 // QemuVideoDxe, and support only virtio-gpu-pci here.\r
963 //\r
964 // Both virtio-vga and virtio-gpu-pci have DeviceId 0x1050, but only the\r
965 // former has device class PCI_CLASS_DISPLAY_VGA.\r
966 //\r
967 if (Pci.Hdr.DeviceId != 0x1050 || !IS_PCI_VGA (&Pci)) {\r
968 Status = EFI_SUCCESS;\r
969 }\r
9399f68a
LE
970 }\r
971\r
972CloseProtocol:\r
973 gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
974 This->DriverBindingHandle, DeviceHandle);\r
975\r
976 return Status;\r
977}\r
978\r
979\r
980STATIC\r
981EFI_STATUS\r
982EFIAPI\r
983Virtio10BindingStart (\r
984 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
985 IN EFI_HANDLE DeviceHandle,\r
986 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
987 )\r
988{\r
989 VIRTIO_1_0_DEV *Device;\r
990 EFI_STATUS Status;\r
991 PCI_TYPE00 Pci;\r
992 UINT64 SetAttributes;\r
993\r
994 Device = AllocateZeroPool (sizeof *Device);\r
995 if (Device == NULL) {\r
996 return EFI_OUT_OF_RESOURCES;\r
997 }\r
998\r
999 Device->Signature = VIRTIO_1_0_SIGNATURE;\r
1000 CopyMem (&Device->VirtIo, &mVirtIoTemplate, sizeof mVirtIoTemplate);\r
1001\r
1002 Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
1003 (VOID **)&Device->PciIo, This->DriverBindingHandle,\r
1004 DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);\r
1005 if (EFI_ERROR (Status)) {\r
1006 goto FreeDevice;\r
1007 }\r
1008\r
1009 Status = Device->PciIo->Pci.Read (Device->PciIo, EfiPciIoWidthUint32, 0,\r
1010 sizeof Pci / sizeof (UINT32), &Pci);\r
1011 if (EFI_ERROR (Status)) {\r
1012 goto ClosePciIo;\r
1013 }\r
1014\r
1015 Device->VirtIo.SubSystemDeviceId = Pci.Hdr.DeviceId - 0x1040;\r
1016\r
1017 Status = ParseCapabilities (Device, Pci.Device.CapabilityPtr);\r
1018 if (EFI_ERROR (Status)) {\r
1019 goto ClosePciIo;\r
1020 }\r
1021\r
1022 Status = Device->PciIo->Attributes (Device->PciIo,\r
1023 EfiPciIoAttributeOperationGet, 0,\r
1024 &Device->OriginalPciAttributes);\r
1025 if (EFI_ERROR (Status)) {\r
1026 goto ClosePciIo;\r
1027 }\r
1028\r
de1c57c5
BS
1029 SetAttributes = (EFI_PCI_IO_ATTRIBUTE_BUS_MASTER |\r
1030 EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE);\r
9399f68a
LE
1031 UpdateAttributes (&Device->CommonConfig, &SetAttributes);\r
1032 UpdateAttributes (&Device->NotifyConfig, &SetAttributes);\r
1033 UpdateAttributes (&Device->SpecificConfig, &SetAttributes);\r
1034 Status = Device->PciIo->Attributes (Device->PciIo,\r
1035 EfiPciIoAttributeOperationEnable, SetAttributes,\r
1036 NULL);\r
1037 if (EFI_ERROR (Status)) {\r
1038 goto ClosePciIo;\r
1039 }\r
1040\r
1041 Status = gBS->InstallProtocolInterface (&DeviceHandle,\r
1042 &gVirtioDeviceProtocolGuid, EFI_NATIVE_INTERFACE,\r
1043 &Device->VirtIo);\r
1044 if (EFI_ERROR (Status)) {\r
1045 goto RestorePciAttributes;\r
1046 }\r
1047\r
1048 return EFI_SUCCESS;\r
1049\r
1050RestorePciAttributes:\r
1051 Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationSet,\r
1052 Device->OriginalPciAttributes, NULL);\r
1053\r
1054ClosePciIo:\r
1055 gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
1056 This->DriverBindingHandle, DeviceHandle);\r
1057\r
1058FreeDevice:\r
1059 FreePool (Device);\r
1060\r
1061 return Status;\r
1062}\r
1063\r
1064\r
1065STATIC\r
1066EFI_STATUS\r
1067EFIAPI\r
1068Virtio10BindingStop (\r
1069 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
1070 IN EFI_HANDLE DeviceHandle,\r
1071 IN UINTN NumberOfChildren,\r
1072 IN EFI_HANDLE *ChildHandleBuffer\r
1073 )\r
1074{\r
1075 EFI_STATUS Status;\r
1076 VIRTIO_DEVICE_PROTOCOL *VirtIo;\r
1077 VIRTIO_1_0_DEV *Device;\r
1078\r
1079 Status = gBS->OpenProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,\r
1080 (VOID **)&VirtIo, This->DriverBindingHandle,\r
1081 DeviceHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
1082 if (EFI_ERROR (Status)) {\r
1083 return Status;\r
1084 }\r
1085\r
1086 Device = VIRTIO_1_0_FROM_VIRTIO_DEVICE (VirtIo);\r
1087\r
1088 Status = gBS->UninstallProtocolInterface (DeviceHandle,\r
1089 &gVirtioDeviceProtocolGuid, &Device->VirtIo);\r
1090 if (EFI_ERROR (Status)) {\r
1091 return Status;\r
1092 }\r
1093\r
1094 Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationSet,\r
1095 Device->OriginalPciAttributes, NULL);\r
1096 gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
1097 This->DriverBindingHandle, DeviceHandle);\r
1098 FreePool (Device);\r
1099\r
1100 return EFI_SUCCESS;\r
1101}\r
1102\r
1103\r
1104STATIC EFI_DRIVER_BINDING_PROTOCOL mDriverBinding = {\r
1105 &Virtio10BindingSupported,\r
1106 &Virtio10BindingStart,\r
1107 &Virtio10BindingStop,\r
1108 0x10, // Version\r
1109 NULL, // ImageHandle, to be overwritten\r
1110 NULL // DriverBindingHandle, to be overwritten\r
1111};\r
1112\r
1113\r
1114//\r
1115// EFI_COMPONENT_NAME_PROTOCOL and EFI_COMPONENT_NAME2_PROTOCOL\r
1116// implementations\r
1117//\r
1118\r
1119STATIC\r
1120EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {\r
1121 { "eng;en", L"Virtio 1.0 PCI Driver" },\r
1122 { NULL, NULL }\r
1123};\r
1124\r
1125STATIC\r
1126EFI_COMPONENT_NAME_PROTOCOL mComponentName;\r
1127\r
1128STATIC\r
1129EFI_STATUS\r
1130EFIAPI\r
1131Virtio10GetDriverName (\r
1132 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
1133 IN CHAR8 *Language,\r
1134 OUT CHAR16 **DriverName\r
1135 )\r
1136{\r
1137 return LookupUnicodeString2 (\r
1138 Language,\r
1139 This->SupportedLanguages,\r
1140 mDriverNameTable,\r
1141 DriverName,\r
1142 (BOOLEAN)(This == &mComponentName) // Iso639Language\r
1143 );\r
1144}\r
1145\r
1146STATIC\r
1147EFI_STATUS\r
1148EFIAPI\r
1149Virtio10GetDeviceName (\r
1150 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
1151 IN EFI_HANDLE DeviceHandle,\r
1152 IN EFI_HANDLE ChildHandle,\r
1153 IN CHAR8 *Language,\r
1154 OUT CHAR16 **ControllerName\r
1155 )\r
1156{\r
1157 return EFI_UNSUPPORTED;\r
1158}\r
1159\r
1160STATIC\r
1161EFI_COMPONENT_NAME_PROTOCOL mComponentName = {\r
1162 &Virtio10GetDriverName,\r
1163 &Virtio10GetDeviceName,\r
1164 "eng"\r
1165};\r
1166\r
1167STATIC\r
1168EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {\r
1169 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &Virtio10GetDriverName,\r
1170 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &Virtio10GetDeviceName,\r
1171 "en"\r
1172};\r
1173\r
1174\r
1175//\r
1176// Entry point of this driver\r
1177//\r
1178\r
1179EFI_STATUS\r
1180EFIAPI\r
1181Virtio10EntryPoint (\r
1182 IN EFI_HANDLE ImageHandle,\r
1183 IN EFI_SYSTEM_TABLE *SystemTable\r
1184 )\r
1185{\r
1186 return EfiLibInstallDriverBindingComponentName2 (\r
1187 ImageHandle,\r
1188 SystemTable,\r
1189 &mDriverBinding,\r
1190 ImageHandle,\r
1191 &mComponentName,\r
1192 &mComponentName2\r
1193 );\r
1194}\r