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