]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/PvScsiDxe/PvScsi.c
OvmfPkg/PvScsiDxe: Backup/Restore PCI attributes on Init/UnInit
[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 Check if Target argument to EXT_SCSI_PASS_THRU.GetNextTarget() and
35 EXT_SCSI_PASS_THRU.GetNextTargetLun() is initialized
36 **/
37 STATIC
38 BOOLEAN
39 IsTargetInitialized (
40 IN UINT8 *Target
41 )
42 {
43 UINTN Idx;
44
45 for (Idx = 0; Idx < TARGET_MAX_BYTES; ++Idx) {
46 if (Target[Idx] != 0xFF) {
47 return TRUE;
48 }
49 }
50 return FALSE;
51 }
52
53 //
54 // Ext SCSI Pass Thru
55 //
56
57 STATIC
58 EFI_STATUS
59 EFIAPI
60 PvScsiPassThru (
61 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
62 IN UINT8 *Target,
63 IN UINT64 Lun,
64 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
65 IN EFI_EVENT Event OPTIONAL
66 )
67 {
68 return EFI_UNSUPPORTED;
69 }
70
71 STATIC
72 EFI_STATUS
73 EFIAPI
74 PvScsiGetNextTargetLun (
75 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
76 IN OUT UINT8 **Target,
77 IN OUT UINT64 *Lun
78 )
79 {
80 UINT8 *TargetPtr;
81 UINT8 LastTarget;
82 PVSCSI_DEV *Dev;
83
84 if (Target == NULL) {
85 return EFI_INVALID_PARAMETER;
86 }
87
88 //
89 // The Target input parameter is unnecessarily a pointer-to-pointer
90 //
91 TargetPtr = *Target;
92
93 //
94 // If target not initialized, return first target & LUN
95 //
96 if (!IsTargetInitialized (TargetPtr)) {
97 ZeroMem (TargetPtr, TARGET_MAX_BYTES);
98 *Lun = 0;
99 return EFI_SUCCESS;
100 }
101
102 //
103 // We only use first byte of target identifer
104 //
105 LastTarget = *TargetPtr;
106
107 //
108 // Increment (target, LUN) pair if valid on input
109 //
110 Dev = PVSCSI_FROM_PASS_THRU (This);
111 if (LastTarget > Dev->MaxTarget || *Lun > Dev->MaxLun) {
112 return EFI_INVALID_PARAMETER;
113 }
114
115 if (*Lun < Dev->MaxLun) {
116 ++*Lun;
117 return EFI_SUCCESS;
118 }
119
120 if (LastTarget < Dev->MaxTarget) {
121 *Lun = 0;
122 ++LastTarget;
123 *TargetPtr = LastTarget;
124 return EFI_SUCCESS;
125 }
126
127 return EFI_NOT_FOUND;
128 }
129
130 STATIC
131 EFI_STATUS
132 EFIAPI
133 PvScsiBuildDevicePath (
134 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
135 IN UINT8 *Target,
136 IN UINT64 Lun,
137 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
138 )
139 {
140 UINT8 TargetValue;
141 PVSCSI_DEV *Dev;
142 SCSI_DEVICE_PATH *ScsiDevicePath;
143
144 if (DevicePath == NULL) {
145 return EFI_INVALID_PARAMETER;
146 }
147
148 //
149 // We only use first byte of target identifer
150 //
151 TargetValue = *Target;
152
153 Dev = PVSCSI_FROM_PASS_THRU (This);
154 if (TargetValue > Dev->MaxTarget || Lun > Dev->MaxLun) {
155 return EFI_NOT_FOUND;
156 }
157
158 ScsiDevicePath = AllocatePool (sizeof (*ScsiDevicePath));
159 if (ScsiDevicePath == NULL) {
160 return EFI_OUT_OF_RESOURCES;
161 }
162
163 ScsiDevicePath->Header.Type = MESSAGING_DEVICE_PATH;
164 ScsiDevicePath->Header.SubType = MSG_SCSI_DP;
165 ScsiDevicePath->Header.Length[0] = (UINT8)sizeof (*ScsiDevicePath);
166 ScsiDevicePath->Header.Length[1] = (UINT8)(sizeof (*ScsiDevicePath) >> 8);
167 ScsiDevicePath->Pun = TargetValue;
168 ScsiDevicePath->Lun = (UINT16)Lun;
169
170 *DevicePath = &ScsiDevicePath->Header;
171 return EFI_SUCCESS;
172 }
173
174 STATIC
175 EFI_STATUS
176 EFIAPI
177 PvScsiGetTargetLun (
178 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
179 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
180 OUT UINT8 **Target,
181 OUT UINT64 *Lun
182 )
183 {
184 SCSI_DEVICE_PATH *ScsiDevicePath;
185 PVSCSI_DEV *Dev;
186
187 if (DevicePath == NULL || Target == NULL || *Target == NULL || Lun == NULL) {
188 return EFI_INVALID_PARAMETER;
189 }
190
191 if (DevicePath->Type != MESSAGING_DEVICE_PATH ||
192 DevicePath->SubType != MSG_SCSI_DP) {
193 return EFI_UNSUPPORTED;
194 }
195
196 ScsiDevicePath = (SCSI_DEVICE_PATH *)DevicePath;
197 Dev = PVSCSI_FROM_PASS_THRU (This);
198 if (ScsiDevicePath->Pun > Dev->MaxTarget ||
199 ScsiDevicePath->Lun > Dev->MaxLun) {
200 return EFI_NOT_FOUND;
201 }
202
203 //
204 // We only use first byte of target identifer
205 //
206 **Target = (UINT8)ScsiDevicePath->Pun;
207 ZeroMem (*Target + 1, TARGET_MAX_BYTES - 1);
208 *Lun = ScsiDevicePath->Lun;
209
210 return EFI_SUCCESS;
211 }
212
213 STATIC
214 EFI_STATUS
215 EFIAPI
216 PvScsiResetChannel (
217 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This
218 )
219 {
220 return EFI_UNSUPPORTED;
221 }
222
223 STATIC
224 EFI_STATUS
225 EFIAPI
226 PvScsiResetTargetLun (
227 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
228 IN UINT8 *Target,
229 IN UINT64 Lun
230 )
231 {
232 return EFI_UNSUPPORTED;
233 }
234
235 STATIC
236 EFI_STATUS
237 EFIAPI
238 PvScsiGetNextTarget (
239 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
240 IN OUT UINT8 **Target
241 )
242 {
243 UINT8 *TargetPtr;
244 UINT8 LastTarget;
245 PVSCSI_DEV *Dev;
246
247 if (Target == NULL) {
248 return EFI_INVALID_PARAMETER;
249 }
250
251 //
252 // The Target input parameter is unnecessarily a pointer-to-pointer
253 //
254 TargetPtr = *Target;
255
256 //
257 // If target not initialized, return first target
258 //
259 if (!IsTargetInitialized (TargetPtr)) {
260 ZeroMem (TargetPtr, TARGET_MAX_BYTES);
261 return EFI_SUCCESS;
262 }
263
264 //
265 // We only use first byte of target identifer
266 //
267 LastTarget = *TargetPtr;
268
269 //
270 // Increment target if valid on input
271 //
272 Dev = PVSCSI_FROM_PASS_THRU (This);
273 if (LastTarget > Dev->MaxTarget) {
274 return EFI_INVALID_PARAMETER;
275 }
276
277 if (LastTarget < Dev->MaxTarget) {
278 ++LastTarget;
279 *TargetPtr = LastTarget;
280 return EFI_SUCCESS;
281 }
282
283 return EFI_NOT_FOUND;
284 }
285
286 STATIC
287 EFI_STATUS
288 PvScsiSetPciAttributes (
289 IN OUT PVSCSI_DEV *Dev
290 )
291 {
292 EFI_STATUS Status;
293
294 //
295 // Backup original PCI Attributes
296 //
297 Status = Dev->PciIo->Attributes (
298 Dev->PciIo,
299 EfiPciIoAttributeOperationGet,
300 0,
301 &Dev->OriginalPciAttributes
302 );
303 if (EFI_ERROR (Status)) {
304 return Status;
305 }
306
307 //
308 // TODO: Change PCI Attributes
309 //
310
311 return EFI_SUCCESS;
312 }
313
314 STATIC
315 VOID
316 PvScsiRestorePciAttributes (
317 IN PVSCSI_DEV *Dev
318 )
319 {
320 Dev->PciIo->Attributes (
321 Dev->PciIo,
322 EfiPciIoAttributeOperationSet,
323 Dev->OriginalPciAttributes,
324 NULL
325 );
326 }
327
328 STATIC
329 EFI_STATUS
330 PvScsiInit (
331 IN OUT PVSCSI_DEV *Dev
332 )
333 {
334 EFI_STATUS Status;
335
336 //
337 // Init configuration
338 //
339 Dev->MaxTarget = PcdGet8 (PcdPvScsiMaxTargetLimit);
340 Dev->MaxLun = PcdGet8 (PcdPvScsiMaxLunLimit);
341
342 //
343 // Set PCI Attributes
344 //
345 Status = PvScsiSetPciAttributes (Dev);
346 if (EFI_ERROR (Status)) {
347 return Status;
348 }
349
350 //
351 // Populate the exported interface's attributes
352 //
353 Dev->PassThru.Mode = &Dev->PassThruMode;
354 Dev->PassThru.PassThru = &PvScsiPassThru;
355 Dev->PassThru.GetNextTargetLun = &PvScsiGetNextTargetLun;
356 Dev->PassThru.BuildDevicePath = &PvScsiBuildDevicePath;
357 Dev->PassThru.GetTargetLun = &PvScsiGetTargetLun;
358 Dev->PassThru.ResetChannel = &PvScsiResetChannel;
359 Dev->PassThru.ResetTargetLun = &PvScsiResetTargetLun;
360 Dev->PassThru.GetNextTarget = &PvScsiGetNextTarget;
361
362 //
363 // AdapterId is a target for which no handle will be created during bus scan.
364 // Prevent any conflict with real devices.
365 //
366 Dev->PassThruMode.AdapterId = MAX_UINT32;
367
368 //
369 // Set both physical and logical attributes for non-RAID SCSI channel
370 //
371 Dev->PassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL |
372 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;
373
374 //
375 // No restriction on transfer buffer alignment
376 //
377 Dev->PassThruMode.IoAlign = 0;
378
379 return EFI_SUCCESS;
380 }
381
382 STATIC
383 VOID
384 PvScsiUninit (
385 IN OUT PVSCSI_DEV *Dev
386 )
387 {
388 PvScsiRestorePciAttributes (Dev);
389 }
390
391 //
392 // Driver Binding
393 //
394
395 STATIC
396 EFI_STATUS
397 EFIAPI
398 PvScsiDriverBindingSupported (
399 IN EFI_DRIVER_BINDING_PROTOCOL *This,
400 IN EFI_HANDLE ControllerHandle,
401 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
402 )
403 {
404 EFI_STATUS Status;
405 EFI_PCI_IO_PROTOCOL *PciIo;
406 PCI_TYPE00 Pci;
407
408 Status = gBS->OpenProtocol (
409 ControllerHandle,
410 &gEfiPciIoProtocolGuid,
411 (VOID **)&PciIo,
412 This->DriverBindingHandle,
413 ControllerHandle,
414 EFI_OPEN_PROTOCOL_BY_DRIVER
415 );
416 if (EFI_ERROR (Status)) {
417 return Status;
418 }
419
420 Status = PciIo->Pci.Read (
421 PciIo,
422 EfiPciIoWidthUint32,
423 0,
424 sizeof (Pci) / sizeof (UINT32),
425 &Pci
426 );
427 if (EFI_ERROR (Status)) {
428 goto Done;
429 }
430
431 if ((Pci.Hdr.VendorId != PCI_VENDOR_ID_VMWARE) ||
432 (Pci.Hdr.DeviceId != PCI_DEVICE_ID_VMWARE_PVSCSI)) {
433 Status = EFI_UNSUPPORTED;
434 goto Done;
435 }
436
437 Status = EFI_SUCCESS;
438
439 Done:
440 gBS->CloseProtocol (
441 ControllerHandle,
442 &gEfiPciIoProtocolGuid,
443 This->DriverBindingHandle,
444 ControllerHandle
445 );
446
447 return Status;
448 }
449
450 STATIC
451 EFI_STATUS
452 EFIAPI
453 PvScsiDriverBindingStart (
454 IN EFI_DRIVER_BINDING_PROTOCOL *This,
455 IN EFI_HANDLE ControllerHandle,
456 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
457 )
458 {
459 PVSCSI_DEV *Dev;
460 EFI_STATUS Status;
461
462 Dev = (PVSCSI_DEV *) AllocateZeroPool (sizeof (*Dev));
463 if (Dev == NULL) {
464 return EFI_OUT_OF_RESOURCES;
465 }
466
467 Status = gBS->OpenProtocol (
468 ControllerHandle,
469 &gEfiPciIoProtocolGuid,
470 (VOID **)&Dev->PciIo,
471 This->DriverBindingHandle,
472 ControllerHandle,
473 EFI_OPEN_PROTOCOL_BY_DRIVER
474 );
475 if (EFI_ERROR (Status)) {
476 goto FreePvScsi;
477 }
478
479 Status = PvScsiInit (Dev);
480 if (EFI_ERROR (Status)) {
481 goto ClosePciIo;
482 }
483
484 //
485 // Setup complete, attempt to export the driver instance's PassThru interface
486 //
487 Dev->Signature = PVSCSI_SIG;
488 Status = gBS->InstallProtocolInterface (
489 &ControllerHandle,
490 &gEfiExtScsiPassThruProtocolGuid,
491 EFI_NATIVE_INTERFACE,
492 &Dev->PassThru
493 );
494 if (EFI_ERROR (Status)) {
495 goto UninitDev;
496 }
497
498 return EFI_SUCCESS;
499
500 UninitDev:
501 PvScsiUninit (Dev);
502
503 ClosePciIo:
504 gBS->CloseProtocol (
505 ControllerHandle,
506 &gEfiPciIoProtocolGuid,
507 This->DriverBindingHandle,
508 ControllerHandle
509 );
510
511 FreePvScsi:
512 FreePool (Dev);
513
514 return Status;
515 }
516
517 STATIC
518 EFI_STATUS
519 EFIAPI
520 PvScsiDriverBindingStop (
521 IN EFI_DRIVER_BINDING_PROTOCOL *This,
522 IN EFI_HANDLE ControllerHandle,
523 IN UINTN NumberOfChildren,
524 IN EFI_HANDLE *ChildHandleBuffer
525 )
526 {
527 EFI_STATUS Status;
528 EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;
529 PVSCSI_DEV *Dev;
530
531 Status = gBS->OpenProtocol (
532 ControllerHandle,
533 &gEfiExtScsiPassThruProtocolGuid,
534 (VOID **)&PassThru,
535 This->DriverBindingHandle,
536 ControllerHandle,
537 EFI_OPEN_PROTOCOL_GET_PROTOCOL // Lookup only
538 );
539 if (EFI_ERROR (Status)) {
540 return Status;
541 }
542
543 Dev = PVSCSI_FROM_PASS_THRU (PassThru);
544
545 Status = gBS->UninstallProtocolInterface (
546 ControllerHandle,
547 &gEfiExtScsiPassThruProtocolGuid,
548 &Dev->PassThru
549 );
550 if (EFI_ERROR (Status)) {
551 return Status;
552 }
553
554 PvScsiUninit (Dev);
555
556 gBS->CloseProtocol (
557 ControllerHandle,
558 &gEfiPciIoProtocolGuid,
559 This->DriverBindingHandle,
560 ControllerHandle
561 );
562
563 FreePool (Dev);
564
565 return EFI_SUCCESS;
566 }
567
568 STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding = {
569 &PvScsiDriverBindingSupported,
570 &PvScsiDriverBindingStart,
571 &PvScsiDriverBindingStop,
572 PVSCSI_BINDING_VERSION,
573 NULL, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2()
574 NULL // DriverBindingHandle, filled as well
575 };
576
577 //
578 // Component Name
579 //
580
581 STATIC EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
582 { "eng;en", L"PVSCSI Host Driver" },
583 { NULL, NULL }
584 };
585
586 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName;
587
588 STATIC
589 EFI_STATUS
590 EFIAPI
591 PvScsiGetDriverName (
592 IN EFI_COMPONENT_NAME_PROTOCOL *This,
593 IN CHAR8 *Language,
594 OUT CHAR16 **DriverName
595 )
596 {
597 return LookupUnicodeString2 (
598 Language,
599 This->SupportedLanguages,
600 mDriverNameTable,
601 DriverName,
602 (BOOLEAN)(This == &mComponentName) // Iso639Language
603 );
604 }
605
606 STATIC
607 EFI_STATUS
608 EFIAPI
609 PvScsiGetDeviceName (
610 IN EFI_COMPONENT_NAME_PROTOCOL *This,
611 IN EFI_HANDLE DeviceHandle,
612 IN EFI_HANDLE ChildHandle,
613 IN CHAR8 *Language,
614 OUT CHAR16 **ControllerName
615 )
616 {
617 return EFI_UNSUPPORTED;
618 }
619
620 STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName = {
621 &PvScsiGetDriverName,
622 &PvScsiGetDeviceName,
623 "eng" // SupportedLanguages, ISO 639-2 language codes
624 };
625
626 STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {
627 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &PvScsiGetDriverName,
628 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &PvScsiGetDeviceName,
629 "en" // SupportedLanguages, RFC 4646 language codes
630 };
631
632 //
633 // Entry Point
634 //
635
636 EFI_STATUS
637 EFIAPI
638 PvScsiEntryPoint (
639 IN EFI_HANDLE ImageHandle,
640 IN EFI_SYSTEM_TABLE *SystemTable
641 )
642 {
643 return EfiLibInstallDriverBindingComponentName2 (
644 ImageHandle,
645 SystemTable,
646 &mPvScsiDriverBinding,
647 ImageHandle,
648 &mComponentName,
649 &mComponentName2
650 );
651 }