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