]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Virtio10Dxe/Virtio10.c
OvmfPkg: Virtio10Dxe: non-transitional driver for virtio-1.0 PCI devices
[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
825 //\r
826 // Recognize non-transitional modern devices. Also, we'll have to parse the\r
827 // PCI capability list, so make sure the CapabilityPtr field will be valid.\r
828 //\r
829 if (Pci.Hdr.VendorId == VIRTIO_VENDOR_ID &&\r
830 Pci.Hdr.DeviceId >= 0x1040 &&\r
831 Pci.Hdr.DeviceId <= 0x107F &&\r
832 Pci.Hdr.RevisionID >= 0x01 &&\r
833 Pci.Device.SubsystemID >= 0x40 &&\r
834 (Pci.Hdr.Status & EFI_PCI_STATUS_CAPABILITY) != 0) {\r
835 Status = EFI_SUCCESS;\r
836 } else {\r
837 Status = EFI_UNSUPPORTED;\r
838 }\r
839\r
840CloseProtocol:\r
841 gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
842 This->DriverBindingHandle, DeviceHandle);\r
843\r
844 return Status;\r
845}\r
846\r
847\r
848STATIC\r
849EFI_STATUS\r
850EFIAPI\r
851Virtio10BindingStart (\r
852 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
853 IN EFI_HANDLE DeviceHandle,\r
854 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
855 )\r
856{\r
857 VIRTIO_1_0_DEV *Device;\r
858 EFI_STATUS Status;\r
859 PCI_TYPE00 Pci;\r
860 UINT64 SetAttributes;\r
861\r
862 Device = AllocateZeroPool (sizeof *Device);\r
863 if (Device == NULL) {\r
864 return EFI_OUT_OF_RESOURCES;\r
865 }\r
866\r
867 Device->Signature = VIRTIO_1_0_SIGNATURE;\r
868 CopyMem (&Device->VirtIo, &mVirtIoTemplate, sizeof mVirtIoTemplate);\r
869\r
870 Status = gBS->OpenProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
871 (VOID **)&Device->PciIo, This->DriverBindingHandle,\r
872 DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);\r
873 if (EFI_ERROR (Status)) {\r
874 goto FreeDevice;\r
875 }\r
876\r
877 Status = Device->PciIo->Pci.Read (Device->PciIo, EfiPciIoWidthUint32, 0,\r
878 sizeof Pci / sizeof (UINT32), &Pci);\r
879 if (EFI_ERROR (Status)) {\r
880 goto ClosePciIo;\r
881 }\r
882\r
883 Device->VirtIo.SubSystemDeviceId = Pci.Hdr.DeviceId - 0x1040;\r
884\r
885 Status = ParseCapabilities (Device, Pci.Device.CapabilityPtr);\r
886 if (EFI_ERROR (Status)) {\r
887 goto ClosePciIo;\r
888 }\r
889\r
890 Status = Device->PciIo->Attributes (Device->PciIo,\r
891 EfiPciIoAttributeOperationGet, 0,\r
892 &Device->OriginalPciAttributes);\r
893 if (EFI_ERROR (Status)) {\r
894 goto ClosePciIo;\r
895 }\r
896\r
897 SetAttributes = 0;\r
898 UpdateAttributes (&Device->CommonConfig, &SetAttributes);\r
899 UpdateAttributes (&Device->NotifyConfig, &SetAttributes);\r
900 UpdateAttributes (&Device->SpecificConfig, &SetAttributes);\r
901 Status = Device->PciIo->Attributes (Device->PciIo,\r
902 EfiPciIoAttributeOperationEnable, SetAttributes,\r
903 NULL);\r
904 if (EFI_ERROR (Status)) {\r
905 goto ClosePciIo;\r
906 }\r
907\r
908 Status = gBS->InstallProtocolInterface (&DeviceHandle,\r
909 &gVirtioDeviceProtocolGuid, EFI_NATIVE_INTERFACE,\r
910 &Device->VirtIo);\r
911 if (EFI_ERROR (Status)) {\r
912 goto RestorePciAttributes;\r
913 }\r
914\r
915 return EFI_SUCCESS;\r
916\r
917RestorePciAttributes:\r
918 Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationSet,\r
919 Device->OriginalPciAttributes, NULL);\r
920\r
921ClosePciIo:\r
922 gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
923 This->DriverBindingHandle, DeviceHandle);\r
924\r
925FreeDevice:\r
926 FreePool (Device);\r
927\r
928 return Status;\r
929}\r
930\r
931\r
932STATIC\r
933EFI_STATUS\r
934EFIAPI\r
935Virtio10BindingStop (\r
936 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
937 IN EFI_HANDLE DeviceHandle,\r
938 IN UINTN NumberOfChildren,\r
939 IN EFI_HANDLE *ChildHandleBuffer\r
940 )\r
941{\r
942 EFI_STATUS Status;\r
943 VIRTIO_DEVICE_PROTOCOL *VirtIo;\r
944 VIRTIO_1_0_DEV *Device;\r
945\r
946 Status = gBS->OpenProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,\r
947 (VOID **)&VirtIo, This->DriverBindingHandle,\r
948 DeviceHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
949 if (EFI_ERROR (Status)) {\r
950 return Status;\r
951 }\r
952\r
953 Device = VIRTIO_1_0_FROM_VIRTIO_DEVICE (VirtIo);\r
954\r
955 Status = gBS->UninstallProtocolInterface (DeviceHandle,\r
956 &gVirtioDeviceProtocolGuid, &Device->VirtIo);\r
957 if (EFI_ERROR (Status)) {\r
958 return Status;\r
959 }\r
960\r
961 Device->PciIo->Attributes (Device->PciIo, EfiPciIoAttributeOperationSet,\r
962 Device->OriginalPciAttributes, NULL);\r
963 gBS->CloseProtocol (DeviceHandle, &gEfiPciIoProtocolGuid,\r
964 This->DriverBindingHandle, DeviceHandle);\r
965 FreePool (Device);\r
966\r
967 return EFI_SUCCESS;\r
968}\r
969\r
970\r
971STATIC EFI_DRIVER_BINDING_PROTOCOL mDriverBinding = {\r
972 &Virtio10BindingSupported,\r
973 &Virtio10BindingStart,\r
974 &Virtio10BindingStop,\r
975 0x10, // Version\r
976 NULL, // ImageHandle, to be overwritten\r
977 NULL // DriverBindingHandle, to be overwritten\r
978};\r
979\r
980\r
981//\r
982// EFI_COMPONENT_NAME_PROTOCOL and EFI_COMPONENT_NAME2_PROTOCOL\r
983// implementations\r
984//\r
985\r
986STATIC\r
987EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {\r
988 { "eng;en", L"Virtio 1.0 PCI Driver" },\r
989 { NULL, NULL }\r
990};\r
991\r
992STATIC\r
993EFI_COMPONENT_NAME_PROTOCOL mComponentName;\r
994\r
995STATIC\r
996EFI_STATUS\r
997EFIAPI\r
998Virtio10GetDriverName (\r
999 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
1000 IN CHAR8 *Language,\r
1001 OUT CHAR16 **DriverName\r
1002 )\r
1003{\r
1004 return LookupUnicodeString2 (\r
1005 Language,\r
1006 This->SupportedLanguages,\r
1007 mDriverNameTable,\r
1008 DriverName,\r
1009 (BOOLEAN)(This == &mComponentName) // Iso639Language\r
1010 );\r
1011}\r
1012\r
1013STATIC\r
1014EFI_STATUS\r
1015EFIAPI\r
1016Virtio10GetDeviceName (\r
1017 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
1018 IN EFI_HANDLE DeviceHandle,\r
1019 IN EFI_HANDLE ChildHandle,\r
1020 IN CHAR8 *Language,\r
1021 OUT CHAR16 **ControllerName\r
1022 )\r
1023{\r
1024 return EFI_UNSUPPORTED;\r
1025}\r
1026\r
1027STATIC\r
1028EFI_COMPONENT_NAME_PROTOCOL mComponentName = {\r
1029 &Virtio10GetDriverName,\r
1030 &Virtio10GetDeviceName,\r
1031 "eng"\r
1032};\r
1033\r
1034STATIC\r
1035EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {\r
1036 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &Virtio10GetDriverName,\r
1037 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &Virtio10GetDeviceName,\r
1038 "en"\r
1039};\r
1040\r
1041\r
1042//\r
1043// Entry point of this driver\r
1044//\r
1045\r
1046EFI_STATUS\r
1047EFIAPI\r
1048Virtio10EntryPoint (\r
1049 IN EFI_HANDLE ImageHandle,\r
1050 IN EFI_SYSTEM_TABLE *SystemTable\r
1051 )\r
1052{\r
1053 return EfiLibInstallDriverBindingComponentName2 (\r
1054 ImageHandle,\r
1055 SystemTable,\r
1056 &mDriverBinding,\r
1057 ImageHandle,\r
1058 &mComponentName,\r
1059 &mComponentName2\r
1060 );\r
1061}\r