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