]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/PvScsiDxe/PvScsi.c
cf75884350eeef056765133d12b7e2e147d8af5f
[mirror_edk2.git] / OvmfPkg / PvScsiDxe / PvScsi.c
1 /** @file
2
3 This driver produces Extended SCSI Pass Thru Protocol instances for
4 pvscsi devices.
5
6 Copyright (C) 2020, Oracle and/or its affiliates.
7
8 SPDX-License-Identifier: BSD-2-Clause-Patent
9
10 **/
11
12 #include <IndustryStandard/Pci.h>
13 #include <IndustryStandard/PvScsi.h>
14 #include <Library/BaseMemoryLib.h>
15 #include <Library/MemoryAllocationLib.h>
16 #include <Library/UefiBootServicesTableLib.h>
17 #include <Library/UefiLib.h>
18 #include <Protocol/PciIo.h>
19 #include <Uefi/UefiSpec.h>
20
21 #include "PvScsi.h"
22
23 //
24 // Higher versions will be used before lower, 0x10-0xffffffef is the version
25 // range for IHV (Indie Hardware Vendors)
26 //
27 #define PVSCSI_BINDING_VERSION 0x10
28
29 //
30 // Ext SCSI Pass Thru utilities
31 //
32
33 /**
34 Writes a 32-bit value into BAR0 using MMIO
35 **/
36 STATIC
37 EFI_STATUS
38 PvScsiMmioWrite32 (
39 IN CONST PVSCSI_DEV *Dev,
40 IN UINT64 Offset,
41 IN UINT32 Value
42 )
43 {
44 return Dev->PciIo->Mem.Write (
45 Dev->PciIo,
46 EfiPciIoWidthUint32,
47 PCI_BAR_IDX0,
48 Offset,
49 1, // Count
50 &Value
51 );
52 }
53
54 /**
55 Writes multiple words of data into BAR0 using MMIO
56 **/
57 STATIC
58 EFI_STATUS
59 PvScsiMmioWrite32Multiple (
60 IN CONST PVSCSI_DEV *Dev,
61 IN UINT64 Offset,
62 IN UINTN Count,
63 IN UINT32 *Words
64 )
65 {
66 return Dev->PciIo->Mem.Write (
67 Dev->PciIo,
68 EfiPciIoWidthFifoUint32,
69 PCI_BAR_IDX0,
70 Offset,
71 Count,
72 Words
73 );
74 }
75
76 /**
77 Send a PVSCSI command to device.
78
79 @param[in] Dev The pvscsi host device.
80 @param[in] Cmd The command to send to device.
81 @param[in] OPTIONAL DescWords An optional command descriptor (If command
82 have a descriptor). The descriptor is
83 provided as an array of UINT32 words and
84 is must be 32-bit aligned.
85 @param[in] DescWordsCount The number of words in command descriptor.
86 Caller must specify here 0 if DescWords
87 is not supplied (It is optional). In that
88 case, DescWords is ignored.
89
90 @return Status codes returned by Dev->PciIo->Mem.Write().
91
92 **/
93 STATIC
94 EFI_STATUS
95 PvScsiWriteCmdDesc (
96 IN CONST PVSCSI_DEV *Dev,
97 IN UINT32 Cmd,
98 IN UINT32 *DescWords OPTIONAL,
99 IN UINTN DescWordsCount
100 )
101 {
102 EFI_STATUS Status;
103
104 if (DescWordsCount > PVSCSI_MAX_CMD_DATA_WORDS) {
105 return EFI_INVALID_PARAMETER;
106 }
107
108 Status = PvScsiMmioWrite32 (Dev, PvScsiRegOffsetCommand, Cmd);
109 if (EFI_ERROR (Status)) {
110 return Status;
111 }
112
113 if (DescWordsCount > 0) {
114 return PvScsiMmioWrite32Multiple (
115 Dev,
116 PvScsiRegOffsetCommandData,
117 DescWordsCount,
118 DescWords
119 );
120 }
121
122 return EFI_SUCCESS;
123 }
124
125 STATIC
126 EFI_STATUS
127 PvScsiResetAdapter (
128 IN CONST PVSCSI_DEV *Dev
129 )
130 {
131 return PvScsiWriteCmdDesc (Dev, PvScsiCmdAdapterReset, NULL, 0);
132 }
133
134 /**
135 Check if Target argument to EXT_SCSI_PASS_THRU.GetNextTarget() and
136 EXT_SCSI_PASS_THRU.GetNextTargetLun() is initialized
137 **/
138 STATIC
139 BOOLEAN
140 IsTargetInitialized (
141 IN UINT8 *Target
142 )
143 {
144 UINTN Idx;
145
146 for (Idx = 0; Idx < TARGET_MAX_BYTES; ++Idx) {
147 if (Target[Idx] != 0xFF) {
148 return TRUE;
149 }
150 }
151 return FALSE;
152 }
153
154 //
155 // Ext SCSI Pass Thru
156 //
157
158 STATIC
159 EFI_STATUS
160 EFIAPI
161 PvScsiPassThru (
162 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
163 IN UINT8 *Target,
164 IN UINT64 Lun,
165 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
166 IN EFI_EVENT Event OPTIONAL
167 )
168 {
169 return EFI_UNSUPPORTED;
170 }
171
172 STATIC
173 EFI_STATUS
174 EFIAPI
175 PvScsiGetNextTargetLun (
176 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
177 IN OUT UINT8 **Target,
178 IN OUT UINT64 *Lun
179 )
180 {
181 UINT8 *TargetPtr;
182 UINT8 LastTarget;
183 PVSCSI_DEV *Dev;
184
185 if (Target == NULL) {
186 return EFI_INVALID_PARAMETER;
187 }
188
189 //
190 // The Target input parameter is unnecessarily a pointer-to-pointer
191 //
192 TargetPtr = *Target;
193
194 //
195 // If target not initialized, return first target & LUN
196 //
197 if (!IsTargetInitialized (TargetPtr)) {
198 ZeroMem (TargetPtr, TARGET_MAX_BYTES);
199 *Lun = 0;
200 return EFI_SUCCESS;
201 }
202
203 //
204 // We only use first byte of target identifer
205 //
206 LastTarget = *TargetPtr;
207
208 //
209 // Increment (target, LUN) pair if valid on input
210 //
211 Dev = PVSCSI_FROM_PASS_THRU (This);
212 if (LastTarget > Dev->MaxTarget || *Lun > Dev->MaxLun) {
213 return EFI_INVALID_PARAMETER;
214 }
215
216 if (*Lun < Dev->MaxLun) {
217 ++*Lun;
218 return EFI_SUCCESS;
219 }
220
221 if (LastTarget < Dev->MaxTarget) {
222 *Lun = 0;
223 ++LastTarget;
224 *TargetPtr = LastTarget;
225 return EFI_SUCCESS;
226 }
227
228 return EFI_NOT_FOUND;
229 }
230
231 STATIC
232 EFI_STATUS
233 EFIAPI
234 PvScsiBuildDevicePath (
235 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
236 IN UINT8 *Target,
237 IN UINT64 Lun,
238 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
239 )
240 {
241 UINT8 TargetValue;
242 PVSCSI_DEV *Dev;
243 SCSI_DEVICE_PATH *ScsiDevicePath;
244
245 if (DevicePath == NULL) {
246 return EFI_INVALID_PARAMETER;
247 }
248
249 //
250 // We only use first byte of target identifer
251 //
252 TargetValue = *Target;
253
254 Dev = PVSCSI_FROM_PASS_THRU (This);
255 if (TargetValue > Dev->MaxTarget || Lun > Dev->MaxLun) {
256 return EFI_NOT_FOUND;
257 }
258
259 ScsiDevicePath = AllocatePool (sizeof (*ScsiDevicePath));
260 if (ScsiDevicePath == NULL) {
261 return EFI_OUT_OF_RESOURCES;
262 }
263
264 ScsiDevicePath->Header.Type = MESSAGING_DEVICE_PATH;
265 ScsiDevicePath->Header.SubType = MSG_SCSI_DP;
266 ScsiDevicePath->Header.Length[0] = (UINT8)sizeof (*ScsiDevicePath);
267 ScsiDevicePath->Header.Length[1] = (UINT8)(sizeof (*ScsiDevicePath) >> 8);
268 ScsiDevicePath->Pun = TargetValue;
269 ScsiDevicePath->Lun = (UINT16)Lun;
270
271 *DevicePath = &ScsiDevicePath->Header;
272 return EFI_SUCCESS;
273 }
274
275 STATIC
276 EFI_STATUS
277 EFIAPI
278 PvScsiGetTargetLun (
279 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
280 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
281 OUT UINT8 **Target,
282 OUT UINT64 *Lun
283 )
284 {
285 SCSI_DEVICE_PATH *ScsiDevicePath;
286 PVSCSI_DEV *Dev;
287
288 if (DevicePath == NULL || Target == NULL || *Target == NULL || Lun == NULL) {
289 return EFI_INVALID_PARAMETER;
290 }
291
292 if (DevicePath->Type != MESSAGING_DEVICE_PATH ||
293 DevicePath->SubType != MSG_SCSI_DP) {
294 return EFI_UNSUPPORTED;
295 }
296
297 ScsiDevicePath = (SCSI_DEVICE_PATH *)DevicePath;
298 Dev = PVSCSI_FROM_PASS_THRU (This);
299 if (ScsiDevicePath->Pun > Dev->MaxTarget ||
300 ScsiDevicePath->Lun > Dev->MaxLun) {
301 return EFI_NOT_FOUND;
302 }
303
304 //
305 // We only use first byte of target identifer
306 //
307 **Target = (UINT8)ScsiDevicePath->Pun;
308 ZeroMem (*Target + 1, TARGET_MAX_BYTES - 1);
309 *Lun = ScsiDevicePath->Lun;
310
311 return EFI_SUCCESS;
312 }
313
314 STATIC
315 EFI_STATUS
316 EFIAPI
317 PvScsiResetChannel (
318 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This
319 )
320 {
321 return EFI_UNSUPPORTED;
322 }
323
324 STATIC
325 EFI_STATUS
326 EFIAPI
327 PvScsiResetTargetLun (
328 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
329 IN UINT8 *Target,
330 IN UINT64 Lun
331 )
332 {
333 return EFI_UNSUPPORTED;
334 }
335
336 STATIC
337 EFI_STATUS
338 EFIAPI
339 PvScsiGetNextTarget (
340 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
341 IN OUT UINT8 **Target
342 )
343 {
344 UINT8 *TargetPtr;
345 UINT8 LastTarget;
346 PVSCSI_DEV *Dev;
347
348 if (Target == NULL) {
349 return EFI_INVALID_PARAMETER;
350 }
351
352 //
353 // The Target input parameter is unnecessarily a pointer-to-pointer
354 //
355 TargetPtr = *Target;
356
357 //
358 // If target not initialized, return first target
359 //
360 if (!IsTargetInitialized (TargetPtr)) {
361 ZeroMem (TargetPtr, TARGET_MAX_BYTES);
362 return EFI_SUCCESS;
363 }
364
365 //
366 // We only use first byte of target identifer
367 //
368 LastTarget = *TargetPtr;
369
370 //
371 // Increment target if valid on input
372 //
373 Dev = PVSCSI_FROM_PASS_THRU (This);
374 if (LastTarget > Dev->MaxTarget) {
375 return EFI_INVALID_PARAMETER;
376 }
377
378 if (LastTarget < Dev->MaxTarget) {
379 ++LastTarget;
380 *TargetPtr = LastTarget;
381 return EFI_SUCCESS;
382 }
383
384 return EFI_NOT_FOUND;
385 }
386
387 STATIC
388 EFI_STATUS
389 PvScsiSetPciAttributes (
390 IN OUT PVSCSI_DEV *Dev
391 )
392 {
393 EFI_STATUS Status;
394
395 //
396 // Backup original PCI Attributes
397 //
398 Status = Dev->PciIo->Attributes (
399 Dev->PciIo,
400 EfiPciIoAttributeOperationGet,
401 0,
402 &Dev->OriginalPciAttributes
403 );
404 if (EFI_ERROR (Status)) {
405 return Status;
406 }
407
408 //
409 // Enable MMIO-Space & Bus-Mastering
410 //
411 Status = Dev->PciIo->Attributes (
412 Dev->PciIo,
413 EfiPciIoAttributeOperationEnable,
414 (EFI_PCI_IO_ATTRIBUTE_MEMORY |
415 EFI_PCI_IO_ATTRIBUTE_BUS_MASTER),
416 NULL
417 );
418 if (EFI_ERROR (Status)) {
419 return Status;
420 }
421
422 return EFI_SUCCESS;
423 }
424
425 STATIC
426 VOID
427 PvScsiRestorePciAttributes (
428 IN PVSCSI_DEV *Dev
429 )
430 {
431 Dev->PciIo->Attributes (
432 Dev->PciIo,
433 EfiPciIoAttributeOperationSet,
434 Dev->OriginalPciAttributes,
435 NULL
436 );
437 }
438
439 STATIC
440 EFI_STATUS
441 PvScsiInit (
442 IN OUT PVSCSI_DEV *Dev
443 )
444 {
445 EFI_STATUS Status;
446
447 //
448 // Init configuration
449 //
450 Dev->MaxTarget = PcdGet8 (PcdPvScsiMaxTargetLimit);
451 Dev->MaxLun = PcdGet8 (PcdPvScsiMaxLunLimit);
452
453 //
454 // Set PCI Attributes
455 //
456 Status = PvScsiSetPciAttributes (Dev);
457 if (EFI_ERROR (Status)) {
458 return Status;
459 }
460
461 //
462 // Reset adapter
463 //
464 Status = PvScsiResetAdapter (Dev);
465 if (EFI_ERROR (Status)) {
466 goto RestorePciAttributes;
467 }
468
469 //
470 // Populate the exported interface's attributes
471 //
472 Dev->PassThru.Mode = &Dev->PassThruMode;
473 Dev->PassThru.PassThru = &PvScsiPassThru;
474 Dev->PassThru.GetNextTargetLun = &PvScsiGetNextTargetLun;
475 Dev->PassThru.BuildDevicePath = &PvScsiBuildDevicePath;
476 Dev->PassThru.GetTargetLun = &PvScsiGetTargetLun;
477 Dev->PassThru.ResetChannel = &PvScsiResetChannel;
478 Dev->PassThru.ResetTargetLun = &PvScsiResetTargetLun;
479 Dev->PassThru.GetNextTarget = &PvScsiGetNextTarget;
480
481 //
482 // AdapterId is a target for which no handle will be created during bus scan.
483 // Prevent any conflict with real devices.
484 //
485 Dev->PassThruMode.AdapterId = MAX_UINT32;
486
487 //
488 // Set both physical and logical attributes for non-RAID SCSI channel
489 //
490 Dev->PassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL |
491 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;
492
493 //
494 // No restriction on transfer buffer alignment
495 //
496 Dev->PassThruMode.IoAlign = 0;
497
498 return EFI_SUCCESS;
499
500 RestorePciAttributes:
501 PvScsiRestorePciAttributes (Dev);
502
503 return Status;
504 }
505
506 STATIC
507 VOID
508 PvScsiUninit (
509 IN OUT PVSCSI_DEV *Dev
510 )
511 {
512 PvScsiRestorePciAttributes (Dev);
513 }
514
515 //
516 // Driver Binding
517 //
518
519 STATIC
520 EFI_STATUS
521 EFIAPI
522 PvScsiDriverBindingSupported (
523 IN EFI_DRIVER_BINDING_PROTOCOL *This,
524 IN EFI_HANDLE ControllerHandle,
525 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
526 )
527 {
528 EFI_STATUS Status;
529 EFI_PCI_IO_PROTOCOL *PciIo;
530 PCI_TYPE00 Pci;
531
532 Status = gBS->OpenProtocol (
533 ControllerHandle,
534 &gEfiPciIoProtocolGuid,
535 (VOID **)&PciIo,
536 This->DriverBindingHandle,
537 ControllerHandle,
538 EFI_OPEN_PROTOCOL_BY_DRIVER
539 );
540 if (EFI_ERROR (Status)) {
541 return Status;
542 }
543
544 Status = PciIo->Pci.Read (
545 PciIo,
546 EfiPciIoWidthUint32,
547 0,
548 sizeof (Pci) / sizeof (UINT32),
549 &Pci
550 );
551 if (EFI_ERROR (Status)) {
552 goto Done;
553 }
554
555 if ((Pci.Hdr.VendorId != PCI_VENDOR_ID_VMWARE) ||
556 (Pci.Hdr.DeviceId != PCI_DEVICE_ID_VMWARE_PVSCSI)) {
557 Status = EFI_UNSUPPORTED;
558 goto Done;
559 }
560
561 Status = EFI_SUCCESS;
562
563 Done:
564 gBS->CloseProtocol (
565 ControllerHandle,
566 &gEfiPciIoProtocolGuid,
567 This->DriverBindingHandle,
568 ControllerHandle
569 );
570
571 return Status;
572 }
573
574 STATIC
575 EFI_STATUS
576 EFIAPI
577 PvScsiDriverBindingStart (
578 IN EFI_DRIVER_BINDING_PROTOCOL *This,
579 IN EFI_HANDLE ControllerHandle,
580 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
581 )
582 {
583 PVSCSI_DEV *Dev;
584 EFI_STATUS Status;
585
586 Dev = (PVSCSI_DEV *) AllocateZeroPool (sizeof (*Dev));
587 if (Dev == NULL) {
588 return EFI_OUT_OF_RESOURCES;
589 }
590
591 Status = gBS->OpenProtocol (
592 ControllerHandle,
593 &gEfiPciIoProtocolGuid,
594 (VOID **)&Dev->PciIo,
595 This->DriverBindingHandle,
596 ControllerHandle,
597 EFI_OPEN_PROTOCOL_BY_DRIVER
598 );
599 if (EFI_ERROR (Status)) {
600 goto FreePvScsi;
601 }
602
603 Status = PvScsiInit (Dev);
604 if (EFI_ERROR (Status)) {
605 goto ClosePciIo;
606 }
607
608 //
609 // Setup complete, attempt to export the driver instance's PassThru interface
610 //
611 Dev->Signature = PVSCSI_SIG;
612 Status = gBS->InstallProtocolInterface (
613 &ControllerHandle,
614 &gEfiExtScsiPassThruProtocolGuid,
615 EFI_NATIVE_INTERFACE,
616 &Dev->PassThru
617 );
618 if (EFI_ERROR (Status)) {
619 goto UninitDev;
620 }
621
622 return EFI_SUCCESS;
623
624 UninitDev:
625 PvScsiUninit (Dev);
626
627 ClosePciIo:
628 gBS->CloseProtocol (
629 ControllerHandle,
630 &gEfiPciIoProtocolGuid,
631 This->DriverBindingHandle,
632 ControllerHandle
633 );
634
635 FreePvScsi:
636 FreePool (Dev);
637
638 return Status;
639 }
640
641 STATIC
642 EFI_STATUS
643 EFIAPI
644 PvScsiDriverBindingStop (
645 IN EFI_DRIVER_BINDING_PROTOCOL *This,
646 IN EFI_HANDLE ControllerHandle,
647 IN UINTN NumberOfChildren,
648 IN EFI_HANDLE *ChildHandleBuffer
649 )
650 {
651 EFI_STATUS Status;
652 EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;
653 PVSCSI_DEV *Dev;
654
655 Status = gBS->OpenProtocol (
656 ControllerHandle,
657 &gEfiExtScsiPassThruProtocolGuid,
658 (VOID **)&PassThru,
659 This->DriverBindingHandle,
660 ControllerHandle,
661 EFI_OPEN_PROTOCOL_GET_PROTOCOL // Lookup only
662 );
663 if (EFI_ERROR (Status)) {
664 return Status;
665 }
666
667 Dev = PVSCSI_FROM_PASS_THRU (PassThru);
668
669 Status = gBS->UninstallProtocolInterface (
670 ControllerHandle,
671 &gEfiExtScsiPassThruProtocolGuid,
672 &Dev->PassThru
673 );
674 if (EFI_ERROR (Status)) {
675 return Status;
676 }
677
678 PvScsiUninit (Dev);
679
680 gBS->CloseProtocol (
681 ControllerHandle,
682 &gEfiPciIoProtocolGuid,
683 This->DriverBindingHandle,
684 ControllerHandle
685 );
686
687 FreePool (Dev);
688
689 return EFI_SUCCESS;
690 }
691
692 STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding = {
693 &PvScsiDriverBindingSupported,
694 &PvScsiDriverBindingStart,
695 &PvScsiDriverBindingStop,
696 PVSCSI_BINDING_VERSION,
697 NULL, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2()
698 NULL // DriverBindingHandle, filled as well
699 };
700
701 //
702 // Component Name
703 //
704
705 STATIC EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
706 { "eng;en", L"PVSCSI Host Driver" },
707 { NULL, NULL }
708 };
709
710 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName;
711
712 STATIC
713 EFI_STATUS
714 EFIAPI
715 PvScsiGetDriverName (
716 IN EFI_COMPONENT_NAME_PROTOCOL *This,
717 IN CHAR8 *Language,
718 OUT CHAR16 **DriverName
719 )
720 {
721 return LookupUnicodeString2 (
722 Language,
723 This->SupportedLanguages,
724 mDriverNameTable,
725 DriverName,
726 (BOOLEAN)(This == &mComponentName) // Iso639Language
727 );
728 }
729
730 STATIC
731 EFI_STATUS
732 EFIAPI
733 PvScsiGetDeviceName (
734 IN EFI_COMPONENT_NAME_PROTOCOL *This,
735 IN EFI_HANDLE DeviceHandle,
736 IN EFI_HANDLE ChildHandle,
737 IN CHAR8 *Language,
738 OUT CHAR16 **ControllerName
739 )
740 {
741 return EFI_UNSUPPORTED;
742 }
743
744 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName = {
745 &PvScsiGetDriverName,
746 &PvScsiGetDeviceName,
747 "eng" // SupportedLanguages, ISO 639-2 language codes
748 };
749
750 STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {
751 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &PvScsiGetDriverName,
752 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &PvScsiGetDeviceName,
753 "en" // SupportedLanguages, RFC 4646 language codes
754 };
755
756 //
757 // Entry Point
758 //
759
760 EFI_STATUS
761 EFIAPI
762 PvScsiEntryPoint (
763 IN EFI_HANDLE ImageHandle,
764 IN EFI_SYSTEM_TABLE *SystemTable
765 )
766 {
767 return EfiLibInstallDriverBindingComponentName2 (
768 ImageHandle,
769 SystemTable,
770 &mPvScsiDriverBinding,
771 ImageHandle,
772 &mComponentName,
773 &mComponentName2
774 );
775 }