OvmfPkg/MptScsiDxe: Build and decode DevicePath
[mirror_edk2.git] / OvmfPkg / MptScsiDxe / MptScsi.c
CommitLineData
feec20b2
NL
1/** @file\r
2\r
3 This driver produces Extended SCSI Pass Thru Protocol instances for\r
4 LSI Fusion MPT SCSI 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
f4707442
NL
12#include <IndustryStandard/FusionMptScsi.h>\r
13#include <IndustryStandard/Pci.h>\r
093cceaf 14#include <Library/BaseMemoryLib.h>\r
a53e5b41
NL
15#include <Library/DebugLib.h>\r
16#include <Library/MemoryAllocationLib.h>\r
093cceaf 17#include <Library/PcdLib.h>\r
f4707442 18#include <Library/UefiBootServicesTableLib.h>\r
ad8f2d6b 19#include <Library/UefiLib.h>\r
f4707442 20#include <Protocol/PciIo.h>\r
a53e5b41 21#include <Protocol/ScsiPassThruExt.h>\r
feec20b2
NL
22#include <Uefi/UefiSpec.h>\r
23\r
ad8f2d6b
NL
24//\r
25// Higher versions will be used before lower, 0x10-0xffffffef is the version\r
26// range for IVH (Indie Hardware Vendors)\r
27//\r
28#define MPT_SCSI_BINDING_VERSION 0x10\r
29\r
a53e5b41
NL
30//\r
31// Runtime Structures\r
32//\r
33\r
34#define MPT_SCSI_DEV_SIGNATURE SIGNATURE_32 ('M','P','T','S')\r
35typedef struct {\r
36 UINT32 Signature;\r
37 EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru;\r
38 EFI_EXT_SCSI_PASS_THRU_MODE PassThruMode;\r
093cceaf 39 UINT8 MaxTarget;\r
a53e5b41
NL
40} MPT_SCSI_DEV;\r
41\r
42#define MPT_SCSI_FROM_PASS_THRU(PassThruPtr) \\r
43 CR (PassThruPtr, MPT_SCSI_DEV, PassThru, MPT_SCSI_DEV_SIGNATURE)\r
44\r
45//\r
46// Ext SCSI Pass Thru\r
47//\r
48\r
49STATIC\r
50EFI_STATUS\r
51EFIAPI\r
52MptScsiPassThru (\r
53 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
54 IN UINT8 *Target,\r
55 IN UINT64 Lun,\r
56 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,\r
57 IN EFI_EVENT Event OPTIONAL\r
58 )\r
59{\r
60 return EFI_UNSUPPORTED;\r
61}\r
62\r
093cceaf
NL
63STATIC\r
64BOOLEAN\r
65IsTargetInitialized (\r
66 IN UINT8 *Target\r
67 )\r
68{\r
69 UINTN Idx;\r
70\r
71 for (Idx = 0; Idx < TARGET_MAX_BYTES; ++Idx) {\r
72 if (Target[Idx] != 0xFF) {\r
73 return TRUE;\r
74 }\r
75 }\r
76 return FALSE;\r
77}\r
78\r
a53e5b41
NL
79STATIC\r
80EFI_STATUS\r
81EFIAPI\r
82MptScsiGetNextTargetLun (\r
83 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
84 IN OUT UINT8 **Target,\r
85 IN OUT UINT64 *Lun\r
86 )\r
87{\r
093cceaf
NL
88 MPT_SCSI_DEV *Dev;\r
89\r
90 Dev = MPT_SCSI_FROM_PASS_THRU (This);\r
91 //\r
92 // Currently support only LUN 0, so hardcode it\r
93 //\r
94 if (!IsTargetInitialized (*Target)) {\r
95 ZeroMem (*Target, TARGET_MAX_BYTES);\r
96 *Lun = 0;\r
97 } else if (**Target > Dev->MaxTarget || *Lun > 0) {\r
98 return EFI_INVALID_PARAMETER;\r
99 } else if (**Target < Dev->MaxTarget) {\r
100 //\r
101 // This device interface support 256 targets only, so it's enough to\r
102 // increment the LSB of Target, as it will never overflow.\r
103 //\r
104 **Target += 1;\r
105 } else {\r
106 return EFI_NOT_FOUND;\r
107 }\r
108\r
109 return EFI_SUCCESS;\r
a53e5b41
NL
110}\r
111\r
112STATIC\r
113EFI_STATUS\r
114EFIAPI\r
115MptScsiGetNextTarget (\r
116 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
117 IN OUT UINT8 **Target\r
118 )\r
119{\r
093cceaf
NL
120 MPT_SCSI_DEV *Dev;\r
121\r
122 Dev = MPT_SCSI_FROM_PASS_THRU (This);\r
123 if (!IsTargetInitialized (*Target)) {\r
124 ZeroMem (*Target, TARGET_MAX_BYTES);\r
125 } else if (**Target > Dev->MaxTarget) {\r
126 return EFI_INVALID_PARAMETER;\r
127 } else if (**Target < Dev->MaxTarget) {\r
128 //\r
129 // This device interface support 256 targets only, so it's enough to\r
130 // increment the LSB of Target, as it will never overflow.\r
131 //\r
132 **Target += 1;\r
133 } else {\r
134 return EFI_NOT_FOUND;\r
135 }\r
136\r
137 return EFI_SUCCESS;\r
a53e5b41
NL
138}\r
139\r
140STATIC\r
141EFI_STATUS\r
142EFIAPI\r
143MptScsiBuildDevicePath (\r
144 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
145 IN UINT8 *Target,\r
146 IN UINT64 Lun,\r
147 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath\r
148 )\r
149{\r
f9941d31
NL
150 MPT_SCSI_DEV *Dev;\r
151 SCSI_DEVICE_PATH *ScsiDevicePath;\r
152\r
153 if (DevicePath == NULL) {\r
154 return EFI_INVALID_PARAMETER;\r
155 }\r
156\r
157 //\r
158 // This device support 256 targets only, so it's enough to dereference\r
159 // the LSB of Target.\r
160 //\r
161 Dev = MPT_SCSI_FROM_PASS_THRU (This);\r
162 if (*Target > Dev->MaxTarget || Lun > 0) {\r
163 return EFI_NOT_FOUND;\r
164 }\r
165\r
166 ScsiDevicePath = AllocateZeroPool (sizeof (*ScsiDevicePath));\r
167 if (ScsiDevicePath == NULL) {\r
168 return EFI_OUT_OF_RESOURCES;\r
169 }\r
170\r
171 ScsiDevicePath->Header.Type = MESSAGING_DEVICE_PATH;\r
172 ScsiDevicePath->Header.SubType = MSG_SCSI_DP;\r
173 ScsiDevicePath->Header.Length[0] = (UINT8)sizeof (*ScsiDevicePath);\r
174 ScsiDevicePath->Header.Length[1] = (UINT8)(sizeof (*ScsiDevicePath) >> 8);\r
175 ScsiDevicePath->Pun = *Target;\r
176 ScsiDevicePath->Lun = (UINT16)Lun;\r
177\r
178 *DevicePath = &ScsiDevicePath->Header;\r
179 return EFI_SUCCESS;\r
a53e5b41
NL
180}\r
181\r
182STATIC\r
183EFI_STATUS\r
184EFIAPI\r
185MptScsiGetTargetLun (\r
186 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
187 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
188 OUT UINT8 **Target,\r
189 OUT UINT64 *Lun\r
190 )\r
191{\r
f9941d31
NL
192 MPT_SCSI_DEV *Dev;\r
193 SCSI_DEVICE_PATH *ScsiDevicePath;\r
194\r
195 if (DevicePath == NULL ||\r
196 Target == NULL || *Target == NULL || Lun == NULL) {\r
197 return EFI_INVALID_PARAMETER;\r
198 }\r
199\r
200 if (DevicePath->Type != MESSAGING_DEVICE_PATH ||\r
201 DevicePath->SubType != MSG_SCSI_DP) {\r
202 return EFI_UNSUPPORTED;\r
203 }\r
204\r
205 Dev = MPT_SCSI_FROM_PASS_THRU (This);\r
206 ScsiDevicePath = (SCSI_DEVICE_PATH *)DevicePath;\r
207 if (ScsiDevicePath->Pun > Dev->MaxTarget ||\r
208 ScsiDevicePath->Lun > 0) {\r
209 return EFI_NOT_FOUND;\r
210 }\r
211\r
212 ZeroMem (*Target, TARGET_MAX_BYTES);\r
213 //\r
214 // This device support 256 targets only, so it's enough to set the LSB\r
215 // of Target.\r
216 //\r
217 **Target = (UINT8)ScsiDevicePath->Pun;\r
218 *Lun = ScsiDevicePath->Lun;\r
219\r
220 return EFI_SUCCESS;\r
a53e5b41
NL
221}\r
222\r
223STATIC\r
224EFI_STATUS\r
225EFIAPI\r
226MptScsiResetChannel (\r
227 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This\r
228 )\r
229{\r
230 return EFI_UNSUPPORTED;\r
231}\r
232\r
233STATIC\r
234EFI_STATUS\r
235EFIAPI\r
236MptScsiResetTargetLun (\r
237 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
238 IN UINT8 *Target,\r
239 IN UINT64 Lun\r
240 )\r
241{\r
242 return EFI_UNSUPPORTED;\r
243}\r
244\r
ad8f2d6b
NL
245//\r
246// Driver Binding\r
247//\r
248\r
249STATIC\r
250EFI_STATUS\r
251EFIAPI\r
252MptScsiControllerSupported (\r
253 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
254 IN EFI_HANDLE ControllerHandle,\r
255 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
256 )\r
257{\r
f4707442
NL
258 EFI_STATUS Status;\r
259 EFI_PCI_IO_PROTOCOL *PciIo;\r
260 PCI_TYPE00 Pci;\r
261\r
262 Status = gBS->OpenProtocol (\r
263 ControllerHandle,\r
264 &gEfiPciIoProtocolGuid,\r
265 (VOID **)&PciIo,\r
266 This->DriverBindingHandle,\r
267 ControllerHandle,\r
268 EFI_OPEN_PROTOCOL_BY_DRIVER\r
269 );\r
270 if (EFI_ERROR (Status)) {\r
271 return Status;\r
272 }\r
273\r
274 Status = PciIo->Pci.Read (\r
275 PciIo,\r
276 EfiPciIoWidthUint32,\r
277 0,\r
278 sizeof (Pci) / sizeof (UINT32),\r
279 &Pci\r
280 );\r
281 if (EFI_ERROR (Status)) {\r
282 goto Done;\r
283 }\r
284\r
285 if (Pci.Hdr.VendorId == LSI_LOGIC_PCI_VENDOR_ID &&\r
286 (Pci.Hdr.DeviceId == LSI_53C1030_PCI_DEVICE_ID ||\r
287 Pci.Hdr.DeviceId == LSI_SAS1068_PCI_DEVICE_ID ||\r
288 Pci.Hdr.DeviceId == LSI_SAS1068E_PCI_DEVICE_ID)) {\r
289 Status = EFI_SUCCESS;\r
290 } else {\r
291 Status = EFI_UNSUPPORTED;\r
292 }\r
293\r
294Done:\r
295 gBS->CloseProtocol (\r
296 ControllerHandle,\r
297 &gEfiPciIoProtocolGuid,\r
298 This->DriverBindingHandle,\r
299 ControllerHandle\r
300 );\r
301 return Status;\r
ad8f2d6b
NL
302}\r
303\r
304STATIC\r
305EFI_STATUS\r
306EFIAPI\r
307MptScsiControllerStart (\r
308 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
309 IN EFI_HANDLE ControllerHandle,\r
310 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
311 )\r
312{\r
a53e5b41
NL
313 EFI_STATUS Status;\r
314 MPT_SCSI_DEV *Dev;\r
315\r
316 Dev = AllocateZeroPool (sizeof (*Dev));\r
317 if (Dev == NULL) {\r
318 return EFI_OUT_OF_RESOURCES;\r
319 }\r
320\r
321 Dev->Signature = MPT_SCSI_DEV_SIGNATURE;\r
322\r
093cceaf
NL
323 Dev->MaxTarget = PcdGet8 (PcdMptScsiMaxTargetLimit);\r
324\r
a53e5b41
NL
325 //\r
326 // Host adapter channel, doesn't exist\r
327 //\r
328 Dev->PassThruMode.AdapterId = MAX_UINT32;\r
329 Dev->PassThruMode.Attributes =\r
330 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL |\r
331 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;\r
332\r
333 Dev->PassThru.Mode = &Dev->PassThruMode;\r
334 Dev->PassThru.PassThru = &MptScsiPassThru;\r
335 Dev->PassThru.GetNextTargetLun = &MptScsiGetNextTargetLun;\r
336 Dev->PassThru.BuildDevicePath = &MptScsiBuildDevicePath;\r
337 Dev->PassThru.GetTargetLun = &MptScsiGetTargetLun;\r
338 Dev->PassThru.ResetChannel = &MptScsiResetChannel;\r
339 Dev->PassThru.ResetTargetLun = &MptScsiResetTargetLun;\r
340 Dev->PassThru.GetNextTarget = &MptScsiGetNextTarget;\r
341\r
342 Status = gBS->InstallProtocolInterface (\r
343 &ControllerHandle,\r
344 &gEfiExtScsiPassThruProtocolGuid,\r
345 EFI_NATIVE_INTERFACE,\r
346 &Dev->PassThru\r
347 );\r
348 if (EFI_ERROR (Status)) {\r
349 goto FreePool;\r
350 }\r
351\r
352 return EFI_SUCCESS;\r
353\r
354FreePool:\r
355 FreePool (Dev);\r
356\r
357 return Status;\r
ad8f2d6b
NL
358}\r
359\r
360STATIC\r
361EFI_STATUS\r
362EFIAPI\r
363MptScsiControllerStop (\r
364 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
365 IN EFI_HANDLE ControllerHandle,\r
366 IN UINTN NumberOfChildren,\r
367 IN EFI_HANDLE *ChildHandleBuffer\r
368 )\r
369{\r
a53e5b41
NL
370 EFI_STATUS Status;\r
371 EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;\r
372 MPT_SCSI_DEV *Dev;\r
373\r
374 Status = gBS->OpenProtocol (\r
375 ControllerHandle,\r
376 &gEfiExtScsiPassThruProtocolGuid,\r
377 (VOID **)&PassThru,\r
378 This->DriverBindingHandle,\r
379 ControllerHandle,\r
380 EFI_OPEN_PROTOCOL_GET_PROTOCOL // Lookup only\r
381 );\r
382 if (EFI_ERROR (Status)) {\r
383 return Status;\r
384 }\r
385\r
386 Dev = MPT_SCSI_FROM_PASS_THRU (PassThru);\r
387\r
388 Status = gBS->UninstallProtocolInterface (\r
389 ControllerHandle,\r
390 &gEfiExtScsiPassThruProtocolGuid,\r
391 &Dev->PassThru\r
392 );\r
393 if (EFI_ERROR (Status)) {\r
394 return Status;\r
395 }\r
396\r
397 FreePool (Dev);\r
398\r
399 return Status;\r
ad8f2d6b
NL
400}\r
401\r
402STATIC\r
403EFI_DRIVER_BINDING_PROTOCOL mMptScsiDriverBinding = {\r
404 &MptScsiControllerSupported,\r
405 &MptScsiControllerStart,\r
406 &MptScsiControllerStop,\r
407 MPT_SCSI_BINDING_VERSION,\r
408 NULL, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2\r
409 NULL, // DriverBindingHandle, filled as well\r
410};\r
411\r
be7fcaa1
NL
412//\r
413// Component Name\r
414//\r
415\r
416STATIC\r
417EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {\r
418 { "eng;en", L"LSI Fusion MPT SCSI Driver" },\r
419 { NULL, NULL }\r
420};\r
421\r
422STATIC\r
423EFI_COMPONENT_NAME_PROTOCOL mComponentName;\r
424\r
425EFI_STATUS\r
426EFIAPI\r
427MptScsiGetDriverName (\r
428 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
429 IN CHAR8 *Language,\r
430 OUT CHAR16 **DriverName\r
431 )\r
432{\r
433 return LookupUnicodeString2 (\r
434 Language,\r
435 This->SupportedLanguages,\r
436 mDriverNameTable,\r
437 DriverName,\r
438 (BOOLEAN)(This == &mComponentName) // Iso639Language\r
439 );\r
440}\r
441\r
442EFI_STATUS\r
443EFIAPI\r
444MptScsiGetDeviceName (\r
445 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
446 IN EFI_HANDLE DeviceHandle,\r
447 IN EFI_HANDLE ChildHandle,\r
448 IN CHAR8 *Language,\r
449 OUT CHAR16 **ControllerName\r
450 )\r
451{\r
452 return EFI_UNSUPPORTED;\r
453}\r
454\r
455STATIC\r
456EFI_COMPONENT_NAME_PROTOCOL mComponentName = {\r
457 &MptScsiGetDriverName,\r
458 &MptScsiGetDeviceName,\r
459 "eng" // SupportedLanguages, ISO 639-2 language codes\r
460};\r
461\r
462STATIC\r
463EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {\r
464 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &MptScsiGetDriverName,\r
465 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &MptScsiGetDeviceName,\r
466 "en" // SupportedLanguages, RFC 4646 language codes\r
467};\r
468\r
feec20b2
NL
469//\r
470// Entry Point\r
471//\r
472\r
473EFI_STATUS\r
474EFIAPI\r
475MptScsiEntryPoint (\r
476 IN EFI_HANDLE ImageHandle,\r
477 IN EFI_SYSTEM_TABLE *SystemTable\r
478 )\r
479{\r
ad8f2d6b
NL
480 return EfiLibInstallDriverBindingComponentName2 (\r
481 ImageHandle,\r
482 SystemTable,\r
483 &mMptScsiDriverBinding,\r
484 ImageHandle, // The handle to install onto\r
be7fcaa1
NL
485 &mComponentName,\r
486 &mComponentName2\r
ad8f2d6b 487 );\r
feec20b2 488}\r