]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/LsiScsiDxe/LsiScsi.c
OvmfPkg/LsiScsiDxe: Report Targets and LUNs
[mirror_edk2.git] / OvmfPkg / LsiScsiDxe / LsiScsi.c
CommitLineData
e94d04a0
GL
1/** @file\r
2\r
3 This driver produces Extended SCSI Pass Thru Protocol instances for\r
4 LSI 53C895A SCSI devices.\r
5\r
6 Copyright (C) 2020, SUSE LLC.\r
7\r
8 SPDX-License-Identifier: BSD-2-Clause-Patent\r
9\r
10**/\r
11\r
79f802a5
GL
12#include <IndustryStandard/LsiScsi.h>\r
13#include <IndustryStandard/Pci.h>\r
23d982e2
GL
14#include <Library/BaseLib.h>\r
15#include <Library/BaseMemoryLib.h>\r
16#include <Library/DebugLib.h>\r
17#include <Library/MemoryAllocationLib.h>\r
12d99b8f 18#include <Library/PcdLib.h>\r
79f802a5 19#include <Library/UefiBootServicesTableLib.h>\r
5e6b870a 20#include <Library/UefiLib.h>\r
79f802a5
GL
21#include <Protocol/PciIo.h>\r
22#include <Protocol/PciRootBridgeIo.h>\r
23d982e2 23#include <Protocol/ScsiPassThruExt.h>\r
e94d04a0
GL
24#include <Uefi/UefiSpec.h>\r
25\r
5e6b870a
GL
26#include "LsiScsi.h"\r
27\r
23d982e2
GL
28//\r
29// The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL\r
30// for the LSI 53C895A SCSI Controller. Refer to UEFI Spec 2.3.1 + Errata C,\r
31// sections\r
32// - 14.1 SCSI Driver Model Overview,\r
33// - 14.7 Extended SCSI Pass Thru Protocol.\r
34//\r
35\r
36EFI_STATUS\r
37EFIAPI\r
38LsiScsiPassThru (\r
39 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
40 IN UINT8 *Target,\r
41 IN UINT64 Lun,\r
42 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,\r
43 IN EFI_EVENT Event OPTIONAL\r
44 )\r
45{\r
46 return EFI_UNSUPPORTED;\r
47}\r
48\r
49EFI_STATUS\r
50EFIAPI\r
51LsiScsiGetNextTargetLun (\r
52 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
53 IN OUT UINT8 **TargetPointer,\r
54 IN OUT UINT64 *Lun\r
55 )\r
56{\r
12d99b8f
GL
57 LSI_SCSI_DEV *Dev;\r
58 UINTN Idx;\r
59 UINT8 *Target;\r
60 UINT16 LastTarget;\r
61\r
62 //\r
63 // the TargetPointer input parameter is unnecessarily a pointer-to-pointer\r
64 //\r
65 Target = *TargetPointer;\r
66\r
67 //\r
68 // Search for first non-0xFF byte. If not found, return first target & LUN.\r
69 //\r
70 for (Idx = 0; Idx < TARGET_MAX_BYTES && Target[Idx] == 0xFF; ++Idx)\r
71 ;\r
72 if (Idx == TARGET_MAX_BYTES) {\r
73 SetMem (Target, TARGET_MAX_BYTES, 0x00);\r
74 *Lun = 0;\r
75 return EFI_SUCCESS;\r
76 }\r
77\r
78 CopyMem (&LastTarget, Target, sizeof LastTarget);\r
79\r
80 //\r
81 // increment (target, LUN) pair if valid on input\r
82 //\r
83 Dev = LSI_SCSI_FROM_PASS_THRU (This);\r
84 if (LastTarget > Dev->MaxTarget || *Lun > Dev->MaxLun) {\r
85 return EFI_INVALID_PARAMETER;\r
86 }\r
87\r
88 if (*Lun < Dev->MaxLun) {\r
89 ++*Lun;\r
90 return EFI_SUCCESS;\r
91 }\r
92\r
93 if (LastTarget < Dev->MaxTarget) {\r
94 *Lun = 0;\r
95 ++LastTarget;\r
96 CopyMem (Target, &LastTarget, sizeof LastTarget);\r
97 return EFI_SUCCESS;\r
98 }\r
99\r
23d982e2
GL
100 return EFI_NOT_FOUND;\r
101}\r
102\r
103EFI_STATUS\r
104EFIAPI\r
105LsiScsiBuildDevicePath (\r
106 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
107 IN UINT8 *Target,\r
108 IN UINT64 Lun,\r
109 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath\r
110 )\r
111{\r
12d99b8f
GL
112 UINT16 TargetValue;\r
113 LSI_SCSI_DEV *Dev;\r
114 SCSI_DEVICE_PATH *ScsiDevicePath;\r
115\r
116 if (DevicePath == NULL) {\r
117 return EFI_INVALID_PARAMETER;\r
118 }\r
119\r
120 CopyMem (&TargetValue, Target, sizeof TargetValue);\r
121 Dev = LSI_SCSI_FROM_PASS_THRU (This);\r
122 if (TargetValue > Dev->MaxTarget || Lun > Dev->MaxLun || Lun > 0xFFFF) {\r
123 return EFI_NOT_FOUND;\r
124 }\r
125\r
126 ScsiDevicePath = AllocatePool (sizeof *ScsiDevicePath);\r
127 if (ScsiDevicePath == NULL) {\r
128 return EFI_OUT_OF_RESOURCES;\r
129 }\r
130\r
131 ScsiDevicePath->Header.Type = MESSAGING_DEVICE_PATH;\r
132 ScsiDevicePath->Header.SubType = MSG_SCSI_DP;\r
133 ScsiDevicePath->Header.Length[0] = (UINT8) sizeof *ScsiDevicePath;\r
134 ScsiDevicePath->Header.Length[1] = (UINT8) (sizeof *ScsiDevicePath >> 8);\r
135 ScsiDevicePath->Pun = TargetValue;\r
136 ScsiDevicePath->Lun = (UINT16) Lun;\r
137\r
138 *DevicePath = &ScsiDevicePath->Header;\r
139 return EFI_SUCCESS;\r
23d982e2
GL
140}\r
141\r
142EFI_STATUS\r
143EFIAPI\r
144LsiScsiGetTargetLun (\r
145 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
146 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
147 OUT UINT8 **TargetPointer,\r
148 OUT UINT64 *Lun\r
149 )\r
150{\r
12d99b8f
GL
151 SCSI_DEVICE_PATH *ScsiDevicePath;\r
152 LSI_SCSI_DEV *Dev;\r
153 UINT8 *Target;\r
154\r
155 if (DevicePath == NULL || TargetPointer == NULL || *TargetPointer == NULL ||\r
156 Lun == NULL) {\r
157 return EFI_INVALID_PARAMETER;\r
158 }\r
159\r
160 if (DevicePath->Type != MESSAGING_DEVICE_PATH ||\r
161 DevicePath->SubType != MSG_SCSI_DP) {\r
162 return EFI_UNSUPPORTED;\r
163 }\r
164\r
165 ScsiDevicePath = (SCSI_DEVICE_PATH *) DevicePath;\r
166 Dev = LSI_SCSI_FROM_PASS_THRU (This);\r
167 if (ScsiDevicePath->Pun > Dev->MaxTarget ||\r
168 ScsiDevicePath->Lun > Dev->MaxLun) {\r
169 return EFI_NOT_FOUND;\r
170 }\r
171\r
172 Target = *TargetPointer;\r
173 ZeroMem (Target, TARGET_MAX_BYTES);\r
174 CopyMem (Target, &ScsiDevicePath->Pun, sizeof ScsiDevicePath->Pun);\r
175 *Lun = ScsiDevicePath->Lun;\r
176\r
177 return EFI_SUCCESS;\r
23d982e2
GL
178}\r
179\r
180EFI_STATUS\r
181EFIAPI\r
182LsiScsiResetChannel (\r
183 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This\r
184 )\r
185{\r
186 return EFI_UNSUPPORTED;\r
187}\r
188\r
189EFI_STATUS\r
190EFIAPI\r
191LsiScsiResetTargetLun (\r
192 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
193 IN UINT8 *Target,\r
194 IN UINT64 Lun\r
195 )\r
196{\r
197 return EFI_UNSUPPORTED;\r
198}\r
199\r
200EFI_STATUS\r
201EFIAPI\r
202LsiScsiGetNextTarget (\r
203 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
204 IN OUT UINT8 **TargetPointer\r
205 )\r
206{\r
12d99b8f
GL
207 LSI_SCSI_DEV *Dev;\r
208 UINTN Idx;\r
209 UINT8 *Target;\r
210 UINT16 LastTarget;\r
211\r
212 //\r
213 // the TargetPointer input parameter is unnecessarily a pointer-to-pointer\r
214 //\r
215 Target = *TargetPointer;\r
216\r
217 //\r
218 // Search for first non-0xFF byte. If not found, return first target.\r
219 //\r
220 for (Idx = 0; Idx < TARGET_MAX_BYTES && Target[Idx] == 0xFF; ++Idx)\r
221 ;\r
222 if (Idx == TARGET_MAX_BYTES) {\r
223 SetMem (Target, TARGET_MAX_BYTES, 0x00);\r
224 return EFI_SUCCESS;\r
225 }\r
226\r
227 CopyMem (&LastTarget, Target, sizeof LastTarget);\r
228\r
229 //\r
230 // increment target if valid on input\r
231 //\r
232 Dev = LSI_SCSI_FROM_PASS_THRU (This);\r
233 if (LastTarget > Dev->MaxTarget) {\r
234 return EFI_INVALID_PARAMETER;\r
235 }\r
236\r
237 if (LastTarget < Dev->MaxTarget) {\r
238 ++LastTarget;\r
239 CopyMem (Target, &LastTarget, sizeof LastTarget);\r
240 return EFI_SUCCESS;\r
241 }\r
242\r
23d982e2
GL
243 return EFI_NOT_FOUND;\r
244}\r
245\r
5e6b870a
GL
246//\r
247// Probe, start and stop functions of this driver, called by the DXE core for\r
248// specific devices.\r
249//\r
250// The following specifications document these interfaces:\r
251// - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol\r
252// - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol\r
253//\r
254\r
255EFI_STATUS\r
256EFIAPI\r
257LsiScsiControllerSupported (\r
258 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
259 IN EFI_HANDLE ControllerHandle,\r
260 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
261 )\r
262{\r
79f802a5
GL
263 EFI_STATUS Status;\r
264 EFI_PCI_IO_PROTOCOL *PciIo;\r
265 PCI_TYPE00 Pci;\r
266\r
267 Status = gBS->OpenProtocol (\r
268 ControllerHandle,\r
269 &gEfiPciIoProtocolGuid,\r
270 (VOID **)&PciIo,\r
271 This->DriverBindingHandle,\r
272 ControllerHandle,\r
273 EFI_OPEN_PROTOCOL_BY_DRIVER\r
274 );\r
275 if (EFI_ERROR (Status)) {\r
276 return Status;\r
277 }\r
278\r
279 Status = PciIo->Pci.Read (\r
280 PciIo,\r
281 EfiPciIoWidthUint32,\r
282 0,\r
283 sizeof (Pci) / sizeof (UINT32),\r
284 &Pci\r
285 );\r
286 if (EFI_ERROR (Status)) {\r
287 goto Done;\r
288 }\r
289\r
290 if (Pci.Hdr.VendorId == LSI_LOGIC_PCI_VENDOR_ID &&\r
291 Pci.Hdr.DeviceId == LSI_53C895A_PCI_DEVICE_ID) {\r
292 Status = EFI_SUCCESS;\r
293 } else {\r
294 Status = EFI_UNSUPPORTED;\r
295 }\r
296\r
297Done:\r
298 gBS->CloseProtocol (\r
299 ControllerHandle,\r
300 &gEfiPciIoProtocolGuid,\r
301 This->DriverBindingHandle,\r
302 ControllerHandle\r
303 );\r
304 return Status;\r
5e6b870a
GL
305}\r
306\r
307EFI_STATUS\r
308EFIAPI\r
309LsiScsiControllerStart (\r
310 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
311 IN EFI_HANDLE ControllerHandle,\r
312 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
313 )\r
314{\r
23d982e2
GL
315 EFI_STATUS Status;\r
316 LSI_SCSI_DEV *Dev;\r
317\r
318 Dev = AllocateZeroPool (sizeof (*Dev));\r
319 if (Dev == NULL) {\r
320 return EFI_OUT_OF_RESOURCES;\r
321 }\r
322\r
323 Dev->Signature = LSI_SCSI_DEV_SIGNATURE;\r
324\r
12d99b8f
GL
325 STATIC_ASSERT (\r
326 FixedPcdGet8 (PcdLsiScsiMaxTargetLimit) < 8,\r
327 "LSI 53C895A supports targets [0..7]"\r
328 );\r
329 STATIC_ASSERT (\r
330 FixedPcdGet8 (PcdLsiScsiMaxLunLimit) < 128,\r
331 "LSI 53C895A supports LUNs [0..127]"\r
332 );\r
333 Dev->MaxTarget = PcdGet8 (PcdLsiScsiMaxTargetLimit);\r
334 Dev->MaxLun = PcdGet8 (PcdLsiScsiMaxLunLimit);\r
335\r
23d982e2
GL
336 //\r
337 // Host adapter channel, doesn't exist\r
338 //\r
339 Dev->PassThruMode.AdapterId = MAX_UINT32;\r
340 Dev->PassThruMode.Attributes =\r
341 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL |\r
342 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;\r
343\r
344 Dev->PassThru.Mode = &Dev->PassThruMode;\r
345 Dev->PassThru.PassThru = &LsiScsiPassThru;\r
346 Dev->PassThru.GetNextTargetLun = &LsiScsiGetNextTargetLun;\r
347 Dev->PassThru.BuildDevicePath = &LsiScsiBuildDevicePath;\r
348 Dev->PassThru.GetTargetLun = &LsiScsiGetTargetLun;\r
349 Dev->PassThru.ResetChannel = &LsiScsiResetChannel;\r
350 Dev->PassThru.ResetTargetLun = &LsiScsiResetTargetLun;\r
351 Dev->PassThru.GetNextTarget = &LsiScsiGetNextTarget;\r
352\r
353 Status = gBS->InstallProtocolInterface (\r
354 &ControllerHandle,\r
355 &gEfiExtScsiPassThruProtocolGuid,\r
356 EFI_NATIVE_INTERFACE,\r
357 &Dev->PassThru\r
358 );\r
359 if (EFI_ERROR (Status)) {\r
360 goto FreePool;\r
361 }\r
362\r
5e6b870a 363 return EFI_SUCCESS;\r
23d982e2
GL
364\r
365FreePool:\r
366 FreePool (Dev);\r
367\r
368 return Status;\r
5e6b870a
GL
369}\r
370\r
371EFI_STATUS\r
372EFIAPI\r
373LsiScsiControllerStop (\r
374 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
375 IN EFI_HANDLE ControllerHandle,\r
376 IN UINTN NumberOfChildren,\r
377 IN EFI_HANDLE *ChildHandleBuffer\r
378 )\r
379{\r
23d982e2
GL
380 EFI_STATUS Status;\r
381 EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;\r
382 LSI_SCSI_DEV *Dev;\r
383\r
384 Status = gBS->OpenProtocol (\r
385 ControllerHandle,\r
386 &gEfiExtScsiPassThruProtocolGuid,\r
387 (VOID **)&PassThru,\r
388 This->DriverBindingHandle,\r
389 ControllerHandle,\r
390 EFI_OPEN_PROTOCOL_GET_PROTOCOL // Lookup only\r
391 );\r
392 if (EFI_ERROR (Status)) {\r
393 return Status;\r
394 }\r
395\r
396 Dev = LSI_SCSI_FROM_PASS_THRU (PassThru);\r
397\r
398 Status = gBS->UninstallProtocolInterface (\r
399 ControllerHandle,\r
400 &gEfiExtScsiPassThruProtocolGuid,\r
401 &Dev->PassThru\r
402 );\r
403 if (EFI_ERROR (Status)) {\r
404 return Status;\r
405 }\r
406\r
407 FreePool (Dev);\r
408\r
409 return Status;\r
5e6b870a
GL
410}\r
411\r
412//\r
413// The static object that groups the Supported() (ie. probe), Start() and\r
414// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata\r
415// C, 10.1 EFI Driver Binding Protocol.\r
416//\r
417STATIC\r
418EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {\r
419 &LsiScsiControllerSupported,\r
420 &LsiScsiControllerStart,\r
421 &LsiScsiControllerStop,\r
422 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers\r
423 NULL, // ImageHandle, to be overwritten by\r
424 // EfiLibInstallDriverBindingComponentName2() in LsiScsiEntryPoint()\r
425 NULL // DriverBindingHandle, ditto\r
426};\r
427\r
428\r
386ca8ab
GL
429//\r
430// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and\r
431// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name\r
432// in English, for display on standard console devices. This is recommended for\r
433// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's\r
434// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.\r
435//\r
436// Device type names ("LSI 53C895A SCSI Controller") are not formatted because\r
437// the driver supports only that device type. Therefore the driver name\r
438// suffices for unambiguous identification.\r
439//\r
440\r
441STATIC\r
442EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {\r
443 { "eng;en", L"LSI 53C895A SCSI Controller Driver" },\r
444 { NULL, NULL }\r
445};\r
446\r
447STATIC\r
448EFI_COMPONENT_NAME_PROTOCOL gComponentName;\r
449\r
450EFI_STATUS\r
451EFIAPI\r
452LsiScsiGetDriverName (\r
453 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
454 IN CHAR8 *Language,\r
455 OUT CHAR16 **DriverName\r
456 )\r
457{\r
458 return LookupUnicodeString2 (\r
459 Language,\r
460 This->SupportedLanguages,\r
461 mDriverNameTable,\r
462 DriverName,\r
463 (BOOLEAN)(This == &gComponentName) // Iso639Language\r
464 );\r
465}\r
466\r
467EFI_STATUS\r
468EFIAPI\r
469LsiScsiGetDeviceName (\r
470 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
471 IN EFI_HANDLE DeviceHandle,\r
472 IN EFI_HANDLE ChildHandle,\r
473 IN CHAR8 *Language,\r
474 OUT CHAR16 **ControllerName\r
475 )\r
476{\r
477 return EFI_UNSUPPORTED;\r
478}\r
479\r
480STATIC\r
481EFI_COMPONENT_NAME_PROTOCOL gComponentName = {\r
482 &LsiScsiGetDriverName,\r
483 &LsiScsiGetDeviceName,\r
484 "eng" // SupportedLanguages, ISO 639-2 language codes\r
485};\r
486\r
487STATIC\r
488EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {\r
489 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &LsiScsiGetDriverName,\r
490 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &LsiScsiGetDeviceName,\r
491 "en" // SupportedLanguages, RFC 4646 language codes\r
492};\r
493\r
e94d04a0
GL
494//\r
495// Entry point of this driver\r
496//\r
497EFI_STATUS\r
498EFIAPI\r
499LsiScsiEntryPoint (\r
500 IN EFI_HANDLE ImageHandle,\r
501 IN EFI_SYSTEM_TABLE *SystemTable\r
502 )\r
503{\r
5e6b870a
GL
504 return EfiLibInstallDriverBindingComponentName2 (\r
505 ImageHandle,\r
506 SystemTable,\r
507 &gDriverBinding,\r
508 ImageHandle, // The handle to install onto\r
386ca8ab
GL
509 &gComponentName,\r
510 &gComponentName2\r
5e6b870a 511 );\r
e94d04a0 512}\r