]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/PvScsiDxe/PvScsi.c
OvmfPkg/PvScsiDxe: Translate Target & LUN to/from DevicePath
[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
286STATIC\r
287EFI_STATUS\r
288PvScsiInit (\r
289 IN OUT PVSCSI_DEV *Dev\r
290 )\r
291{\r
7efce2e5
LA
292 //\r
293 // Init configuration\r
294 //\r
295 Dev->MaxTarget = PcdGet8 (PcdPvScsiMaxTargetLimit);\r
296 Dev->MaxLun = PcdGet8 (PcdPvScsiMaxLunLimit);\r
297\r
e497432c
LA
298 //\r
299 // Populate the exported interface's attributes\r
300 //\r
301 Dev->PassThru.Mode = &Dev->PassThruMode;\r
302 Dev->PassThru.PassThru = &PvScsiPassThru;\r
303 Dev->PassThru.GetNextTargetLun = &PvScsiGetNextTargetLun;\r
304 Dev->PassThru.BuildDevicePath = &PvScsiBuildDevicePath;\r
305 Dev->PassThru.GetTargetLun = &PvScsiGetTargetLun;\r
306 Dev->PassThru.ResetChannel = &PvScsiResetChannel;\r
307 Dev->PassThru.ResetTargetLun = &PvScsiResetTargetLun;\r
308 Dev->PassThru.GetNextTarget = &PvScsiGetNextTarget;\r
309\r
310 //\r
311 // AdapterId is a target for which no handle will be created during bus scan.\r
312 // Prevent any conflict with real devices.\r
313 //\r
314 Dev->PassThruMode.AdapterId = MAX_UINT32;\r
315\r
316 //\r
317 // Set both physical and logical attributes for non-RAID SCSI channel\r
318 //\r
319 Dev->PassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL |\r
320 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;\r
321\r
322 //\r
323 // No restriction on transfer buffer alignment\r
324 //\r
325 Dev->PassThruMode.IoAlign = 0;\r
326\r
327 return EFI_SUCCESS;\r
328}\r
329\r
330STATIC\r
331VOID\r
332PvScsiUninit (\r
333 IN OUT PVSCSI_DEV *Dev\r
334 )\r
335{\r
336 // Currently nothing to do here\r
337}\r
338\r
ed08c571
LA
339//\r
340// Driver Binding\r
341//\r
342\r
343STATIC\r
344EFI_STATUS\r
345EFIAPI\r
346PvScsiDriverBindingSupported (\r
347 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
348 IN EFI_HANDLE ControllerHandle,\r
349 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
350 )\r
351{\r
a9f9d5cf
LA
352 EFI_STATUS Status;\r
353 EFI_PCI_IO_PROTOCOL *PciIo;\r
354 PCI_TYPE00 Pci;\r
355\r
356 Status = gBS->OpenProtocol (\r
357 ControllerHandle,\r
358 &gEfiPciIoProtocolGuid,\r
359 (VOID **)&PciIo,\r
360 This->DriverBindingHandle,\r
361 ControllerHandle,\r
362 EFI_OPEN_PROTOCOL_BY_DRIVER\r
363 );\r
364 if (EFI_ERROR (Status)) {\r
365 return Status;\r
366 }\r
367\r
368 Status = PciIo->Pci.Read (\r
369 PciIo,\r
370 EfiPciIoWidthUint32,\r
371 0,\r
372 sizeof (Pci) / sizeof (UINT32),\r
373 &Pci\r
374 );\r
375 if (EFI_ERROR (Status)) {\r
376 goto Done;\r
377 }\r
378\r
379 if ((Pci.Hdr.VendorId != PCI_VENDOR_ID_VMWARE) ||\r
380 (Pci.Hdr.DeviceId != PCI_DEVICE_ID_VMWARE_PVSCSI)) {\r
381 Status = EFI_UNSUPPORTED;\r
382 goto Done;\r
383 }\r
384\r
385 Status = EFI_SUCCESS;\r
386\r
387Done:\r
388 gBS->CloseProtocol (\r
389 ControllerHandle,\r
390 &gEfiPciIoProtocolGuid,\r
391 This->DriverBindingHandle,\r
392 ControllerHandle\r
393 );\r
394\r
395 return Status;\r
ed08c571
LA
396}\r
397\r
398STATIC\r
399EFI_STATUS\r
400EFIAPI\r
401PvScsiDriverBindingStart (\r
402 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
403 IN EFI_HANDLE ControllerHandle,\r
404 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
405 )\r
406{\r
e497432c
LA
407 PVSCSI_DEV *Dev;\r
408 EFI_STATUS Status;\r
409\r
410 Dev = (PVSCSI_DEV *) AllocateZeroPool (sizeof (*Dev));\r
411 if (Dev == NULL) {\r
412 return EFI_OUT_OF_RESOURCES;\r
413 }\r
414\r
415 Status = PvScsiInit (Dev);\r
416 if (EFI_ERROR (Status)) {\r
417 goto FreePvScsi;\r
418 }\r
419\r
420 //\r
421 // Setup complete, attempt to export the driver instance's PassThru interface\r
422 //\r
423 Dev->Signature = PVSCSI_SIG;\r
424 Status = gBS->InstallProtocolInterface (\r
425 &ControllerHandle,\r
426 &gEfiExtScsiPassThruProtocolGuid,\r
427 EFI_NATIVE_INTERFACE,\r
428 &Dev->PassThru\r
429 );\r
430 if (EFI_ERROR (Status)) {\r
431 goto UninitDev;\r
432 }\r
433\r
434 return EFI_SUCCESS;\r
435\r
436UninitDev:\r
437 PvScsiUninit (Dev);\r
438\r
439FreePvScsi:\r
440 FreePool (Dev);\r
441\r
442 return Status;\r
ed08c571
LA
443}\r
444\r
445STATIC\r
446EFI_STATUS\r
447EFIAPI\r
448PvScsiDriverBindingStop (\r
449 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
450 IN EFI_HANDLE ControllerHandle,\r
451 IN UINTN NumberOfChildren,\r
452 IN EFI_HANDLE *ChildHandleBuffer\r
453 )\r
454{\r
e497432c
LA
455 EFI_STATUS Status;\r
456 EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;\r
457 PVSCSI_DEV *Dev;\r
458\r
459 Status = gBS->OpenProtocol (\r
460 ControllerHandle,\r
461 &gEfiExtScsiPassThruProtocolGuid,\r
462 (VOID **)&PassThru,\r
463 This->DriverBindingHandle,\r
464 ControllerHandle,\r
465 EFI_OPEN_PROTOCOL_GET_PROTOCOL // Lookup only\r
466 );\r
467 if (EFI_ERROR (Status)) {\r
468 return Status;\r
469 }\r
470\r
471 Dev = PVSCSI_FROM_PASS_THRU (PassThru);\r
472\r
473 Status = gBS->UninstallProtocolInterface (\r
474 ControllerHandle,\r
475 &gEfiExtScsiPassThruProtocolGuid,\r
476 &Dev->PassThru\r
477 );\r
478 if (EFI_ERROR (Status)) {\r
479 return Status;\r
480 }\r
481\r
482 PvScsiUninit (Dev);\r
483\r
484 FreePool (Dev);\r
485\r
486 return EFI_SUCCESS;\r
ed08c571
LA
487}\r
488\r
489STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding = {\r
490 &PvScsiDriverBindingSupported,\r
491 &PvScsiDriverBindingStart,\r
492 &PvScsiDriverBindingStop,\r
493 PVSCSI_BINDING_VERSION,\r
494 NULL, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2()\r
495 NULL // DriverBindingHandle, filled as well\r
496};\r
497\r
419b30d6
LA
498//\r
499// Component Name\r
500//\r
501\r
502STATIC EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {\r
503 { "eng;en", L"PVSCSI Host Driver" },\r
504 { NULL, NULL }\r
505};\r
506\r
507STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName;\r
508\r
509STATIC\r
510EFI_STATUS\r
511EFIAPI\r
512PvScsiGetDriverName (\r
513 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
514 IN CHAR8 *Language,\r
515 OUT CHAR16 **DriverName\r
516 )\r
517{\r
518 return LookupUnicodeString2 (\r
519 Language,\r
520 This->SupportedLanguages,\r
521 mDriverNameTable,\r
522 DriverName,\r
523 (BOOLEAN)(This == &mComponentName) // Iso639Language\r
524 );\r
525}\r
526\r
527STATIC\r
528EFI_STATUS\r
529EFIAPI\r
530PvScsiGetDeviceName (\r
531 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
532 IN EFI_HANDLE DeviceHandle,\r
533 IN EFI_HANDLE ChildHandle,\r
534 IN CHAR8 *Language,\r
535 OUT CHAR16 **ControllerName\r
536 )\r
537{\r
538 return EFI_UNSUPPORTED;\r
539}\r
540\r
541STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName = {\r
542 &PvScsiGetDriverName,\r
543 &PvScsiGetDeviceName,\r
544 "eng" // SupportedLanguages, ISO 639-2 language codes\r
545};\r
546\r
547STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {\r
548 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &PvScsiGetDriverName,\r
549 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &PvScsiGetDeviceName,\r
550 "en" // SupportedLanguages, RFC 4646 language codes\r
551};\r
552\r
478c07d4
LA
553//\r
554// Entry Point\r
555//\r
556\r
557EFI_STATUS\r
558EFIAPI\r
559PvScsiEntryPoint (\r
560 IN EFI_HANDLE ImageHandle,\r
561 IN EFI_SYSTEM_TABLE *SystemTable\r
562 )\r
563{\r
ed08c571
LA
564 return EfiLibInstallDriverBindingComponentName2 (\r
565 ImageHandle,\r
566 SystemTable,\r
567 &mPvScsiDriverBinding,\r
568 ImageHandle,\r
419b30d6
LA
569 &mComponentName,\r
570 &mComponentName2\r
ed08c571 571 );\r
478c07d4 572}\r