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