1 /** @file
2 
3   This driver produces Extended SCSI Pass Thru Protocol instances for
4   LSI Fusion MPT SCSI devices.
5 
6   Copyright (C) 2020, Oracle and/or its affiliates.
7 
8   SPDX-License-Identifier: BSD-2-Clause-Patent
9 
10 **/
11 
12 #include <IndustryStandard/FusionMptScsi.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 //
27 // Higher versions will be used before lower, 0x10-0xffffffef is the version
28 // range for IVH (Indie Hardware Vendors)
29 //
30 #define MPT_SCSI_BINDING_VERSION 0x10
31 
32 //
33 // Runtime Structures
34 //
35 
36 typedef struct {
37   MPT_SCSI_REQUEST_ALIGNED        IoRequest;
38   MPT_SCSI_IO_REPLY_ALIGNED       IoReply;
39   //
40   // As EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET.SenseDataLength is defined
41   // as UINT8, defining here SenseData size to MAX_UINT8 will guarantee it
42   // cannot overflow when passed to device.
43   //
44   UINT8                           Sense[MAX_UINT8];
45   //
46   // This size of the data is arbitrarily chosen.
47   // It seems to be sufficient for all I/O requests sent through
48   // EFI_SCSI_PASS_THRU_PROTOCOL.PassThru() for common boot scenarios.
49   //
50   UINT8                           Data[0x2000];
51 } MPT_SCSI_DMA_BUFFER;
52 
53 #define MPT_SCSI_DEV_SIGNATURE SIGNATURE_32 ('M','P','T','S')
54 typedef struct {
55   UINT32                          Signature;
56   EFI_EXT_SCSI_PASS_THRU_PROTOCOL PassThru;
57   EFI_EXT_SCSI_PASS_THRU_MODE     PassThruMode;
58   UINT8                           MaxTarget;
59   UINT32                          StallPerPollUsec;
60   EFI_PCI_IO_PROTOCOL             *PciIo;
61   UINT64                          OriginalPciAttributes;
62   EFI_EVENT                       ExitBoot;
63   MPT_SCSI_DMA_BUFFER             *Dma;
64   EFI_PHYSICAL_ADDRESS            DmaPhysical;
65   VOID                            *DmaMapping;
66   BOOLEAN                         IoReplyEnqueued;
67 } MPT_SCSI_DEV;
68 
69 #define MPT_SCSI_FROM_PASS_THRU(PassThruPtr) \
70   CR (PassThruPtr, MPT_SCSI_DEV, PassThru, MPT_SCSI_DEV_SIGNATURE)
71 
72 #define MPT_SCSI_DMA_ADDR(Dev, MemberName) \
73   (Dev->DmaPhysical + OFFSET_OF (MPT_SCSI_DMA_BUFFER, MemberName))
74 
75 #define MPT_SCSI_DMA_ADDR_HIGH(Dev, MemberName) \
76   ((UINT32)RShiftU64 (MPT_SCSI_DMA_ADDR (Dev, MemberName), 32))
77 
78 #define MPT_SCSI_DMA_ADDR_LOW(Dev, MemberName) \
79   ((UINT32)MPT_SCSI_DMA_ADDR (Dev, MemberName))
80 
81 //
82 // Hardware functions
83 //
84 
85 STATIC
86 EFI_STATUS
Out32(IN MPT_SCSI_DEV * Dev,IN UINT32 Addr,IN UINT32 Data)87 Out32 (
88   IN MPT_SCSI_DEV       *Dev,
89   IN UINT32             Addr,
90   IN UINT32             Data
91   )
92 {
93   return Dev->PciIo->Io.Write (
94                           Dev->PciIo,
95                           EfiPciIoWidthUint32,
96                           PCI_BAR_IDX0,
97                           Addr,
98                           1,
99                           &Data
100                           );
101 }
102 
103 STATIC
104 EFI_STATUS
In32(IN MPT_SCSI_DEV * Dev,IN UINT32 Addr,OUT UINT32 * Data)105 In32 (
106   IN  MPT_SCSI_DEV       *Dev,
107   IN  UINT32             Addr,
108   OUT UINT32             *Data
109   )
110 {
111   return Dev->PciIo->Io.Read (
112                           Dev->PciIo,
113                           EfiPciIoWidthUint32,
114                           PCI_BAR_IDX0,
115                           Addr,
116                           1,
117                           Data
118                           );
119 }
120 
121 STATIC
122 EFI_STATUS
MptDoorbell(IN MPT_SCSI_DEV * Dev,IN UINT8 DoorbellFunc,IN UINT8 DoorbellArg)123 MptDoorbell (
124   IN MPT_SCSI_DEV       *Dev,
125   IN UINT8              DoorbellFunc,
126   IN UINT8              DoorbellArg
127   )
128 {
129   return Out32 (
130            Dev,
131            MPT_REG_DOORBELL,
132            (((UINT32)DoorbellFunc) << 24) | (DoorbellArg << 16)
133            );
134 }
135 
136 STATIC
137 EFI_STATUS
MptScsiReset(IN MPT_SCSI_DEV * Dev)138 MptScsiReset (
139   IN MPT_SCSI_DEV       *Dev
140   )
141 {
142   EFI_STATUS Status;
143 
144   //
145   // Reset hardware
146   //
147   Status = MptDoorbell (Dev, MPT_DOORBELL_RESET, 0);
148   if (EFI_ERROR (Status)) {
149     return Status;
150   }
151   //
152   // Mask interrupts
153   //
154   Status = Out32 (Dev, MPT_REG_IMASK, MPT_IMASK_DOORBELL | MPT_IMASK_REPLY);
155   if (EFI_ERROR (Status)) {
156     return Status;
157   }
158   //
159   // Clear interrupt status
160   //
161   Status = Out32 (Dev, MPT_REG_ISTATUS, 0);
162   if (EFI_ERROR (Status)) {
163     return Status;
164   }
165 
166   return EFI_SUCCESS;
167 }
168 
169 STATIC
170 EFI_STATUS
MptScsiInit(IN MPT_SCSI_DEV * Dev)171 MptScsiInit (
172   IN MPT_SCSI_DEV       *Dev
173   )
174 {
175   EFI_STATUS                       Status;
176   union {
177     MPT_IO_CONTROLLER_INIT_REQUEST Data;
178     UINT32                         Uint32;
179   } AlignedReq;
180   MPT_IO_CONTROLLER_INIT_REQUEST   *Req;
181   MPT_IO_CONTROLLER_INIT_REPLY     Reply;
182   UINT8                            *ReplyBytes;
183   UINT32                           ReplyWord;
184 
185   Req = &AlignedReq.Data;
186 
187   Status = MptScsiReset (Dev);
188   if (EFI_ERROR (Status)) {
189     return Status;
190   }
191 
192   ZeroMem (Req, sizeof (*Req));
193   ZeroMem (&Reply, sizeof (Reply));
194   Req->WhoInit = MPT_IOC_WHOINIT_ROM_BIOS;
195   Req->Function = MPT_MESSAGE_HDR_FUNCTION_IOC_INIT;
196   STATIC_ASSERT (
197     FixedPcdGet8 (PcdMptScsiMaxTargetLimit) < 255,
198     "Req supports 255 targets only (max target is 254)"
199     );
200   Req->MaxDevices = Dev->MaxTarget + 1;
201   Req->MaxBuses = 1;
202   Req->ReplyFrameSize = sizeof Dev->Dma->IoReply.Data;
203   Req->HostMfaHighAddr = MPT_SCSI_DMA_ADDR_HIGH (Dev, IoRequest);
204   Req->SenseBufferHighAddr = MPT_SCSI_DMA_ADDR_HIGH (Dev, Sense);
205 
206   //
207   // Send controller init through doorbell
208   //
209   STATIC_ASSERT (
210     sizeof (*Req) % sizeof (UINT32) == 0,
211     "Req must be multiple of UINT32"
212     );
213   STATIC_ASSERT (
214     sizeof (*Req) / sizeof (UINT32) <= MAX_UINT8,
215     "Req must fit in MAX_UINT8 Dwords"
216     );
217   Status = MptDoorbell (
218              Dev,
219              MPT_DOORBELL_HANDSHAKE,
220              (UINT8)(sizeof (*Req) / sizeof (UINT32))
221              );
222   if (EFI_ERROR (Status)) {
223     return Status;
224   }
225   Status = Dev->PciIo->Io.Write (
226                             Dev->PciIo,
227                             EfiPciIoWidthFifoUint32,
228                             PCI_BAR_IDX0,
229                             MPT_REG_DOORBELL,
230                             sizeof (*Req) / sizeof (UINT32),
231                             Req
232                             );
233   if (EFI_ERROR (Status)) {
234     return Status;
235   }
236 
237   //
238   // Read reply through doorbell
239   // Each 32bit (Dword) read produces 16bit (Word) of data
240   //
241   // The reply is read back to complete the doorbell function but it
242   // isn't useful because it doesn't contain relevant data or status
243   // codes.
244   //
245   STATIC_ASSERT (
246     sizeof (Reply) % sizeof (UINT16) == 0,
247     "Reply must be multiple of UINT16"
248     );
249   ReplyBytes = (UINT8 *)&Reply;
250   while (ReplyBytes != (UINT8 *)(&Reply + 1)) {
251     Status = In32 (Dev, MPT_REG_DOORBELL, &ReplyWord);
252     if (EFI_ERROR (Status)) {
253       return Status;
254     }
255     CopyMem (ReplyBytes, &ReplyWord, sizeof (UINT16));
256     ReplyBytes += sizeof (UINT16);
257   }
258 
259   //
260   // Clear interrupts generated by doorbell reply
261   //
262   Status = Out32 (Dev, MPT_REG_ISTATUS, 0);
263   if (EFI_ERROR (Status)) {
264     return Status;
265   }
266 
267   return EFI_SUCCESS;
268 }
269 
270 STATIC
271 EFI_STATUS
ReportHostAdapterError(OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET * Packet)272 ReportHostAdapterError (
273   OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
274   )
275 {
276   DEBUG ((DEBUG_ERROR, "%a: fatal error in scsi request\n", __FUNCTION__));
277   Packet->InTransferLength  = 0;
278   Packet->OutTransferLength = 0;
279   Packet->SenseDataLength   = 0;
280   Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;
281   Packet->TargetStatus      = EFI_EXT_SCSI_STATUS_TARGET_TASK_ABORTED;
282   return EFI_DEVICE_ERROR;
283 }
284 
285 STATIC
286 EFI_STATUS
ReportHostAdapterOverrunError(OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET * Packet)287 ReportHostAdapterOverrunError (
288   OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
289   )
290 {
291   Packet->SenseDataLength = 0;
292   Packet->HostAdapterStatus =
293             EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;
294   Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;
295   return EFI_BAD_BUFFER_SIZE;
296 }
297 
298 STATIC
299 EFI_STATUS
MptScsiPopulateRequest(IN MPT_SCSI_DEV * Dev,IN UINT8 Target,IN UINT64 Lun,IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET * Packet)300 MptScsiPopulateRequest (
301   IN MPT_SCSI_DEV                                   *Dev,
302   IN UINT8                                          Target,
303   IN UINT64                                         Lun,
304   IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
305   )
306 {
307   MPT_SCSI_REQUEST_WITH_SG *Request;
308 
309   Request = &Dev->Dma->IoRequest.Data;
310 
311   if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL ||
312       (Packet->InTransferLength > 0 && Packet->OutTransferLength > 0) ||
313       Packet->CdbLength > sizeof (Request->Header.Cdb)) {
314     return EFI_UNSUPPORTED;
315   }
316 
317   if (Target > Dev->MaxTarget || Lun > 0 ||
318       Packet->DataDirection > EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL ||
319       //
320       // Trying to receive, but destination pointer is NULL, or contradicting
321       // transfer direction
322       //
323       (Packet->InTransferLength > 0 &&
324        (Packet->InDataBuffer == NULL ||
325         Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_WRITE
326          )
327         ) ||
328 
329       //
330       // Trying to send, but source pointer is NULL, or contradicting transfer
331       // direction
332       //
333       (Packet->OutTransferLength > 0 &&
334        (Packet->OutDataBuffer == NULL ||
335         Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ
336          )
337         )
338     ) {
339     return EFI_INVALID_PARAMETER;
340   }
341 
342   if (Packet->InTransferLength > sizeof (Dev->Dma->Data)) {
343     Packet->InTransferLength = sizeof (Dev->Dma->Data);
344     return ReportHostAdapterOverrunError (Packet);
345   }
346   if (Packet->OutTransferLength > sizeof (Dev->Dma->Data)) {
347     Packet->OutTransferLength = sizeof (Dev->Dma->Data);
348     return ReportHostAdapterOverrunError (Packet);
349   }
350 
351   ZeroMem (Request, sizeof (*Request));
352   Request->Header.TargetId = Target;
353   //
354   // Only LUN 0 is currently supported, hence the cast is safe
355   //
356   Request->Header.Lun[1] = (UINT8)Lun;
357   Request->Header.Function = MPT_MESSAGE_HDR_FUNCTION_SCSI_IO_REQUEST;
358   Request->Header.MessageContext = 1; // We handle one request at a time
359 
360   Request->Header.CdbLength = Packet->CdbLength;
361   CopyMem (Request->Header.Cdb, Packet->Cdb, Packet->CdbLength);
362 
363   //
364   // SenseDataLength is UINT8, Sense[] is MAX_UINT8, so we can't overflow
365   //
366   ZeroMem (Dev->Dma->Sense, Packet->SenseDataLength);
367   Request->Header.SenseBufferLength = Packet->SenseDataLength;
368   Request->Header.SenseBufferLowAddress = MPT_SCSI_DMA_ADDR_LOW (Dev, Sense);
369 
370   Request->Sg.EndOfList = 1;
371   Request->Sg.EndOfBuffer = 1;
372   Request->Sg.LastElement = 1;
373   Request->Sg.ElementType = MPT_SG_ENTRY_TYPE_SIMPLE;
374   Request->Sg.Is64BitAddress = 1;
375   Request->Sg.DataBufferAddress = MPT_SCSI_DMA_ADDR (Dev, Data);
376 
377   //
378   // "MPT_SG_ENTRY_SIMPLE.Length" is a 24-bit quantity.
379   //
380   STATIC_ASSERT (
381     sizeof (Dev->Dma->Data) < SIZE_16MB,
382     "MPT_SCSI_DMA_BUFFER.Data must be smaller than 16MB"
383     );
384 
385   if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
386     Request->Header.DataLength = Packet->InTransferLength;
387     Request->Sg.Length = Packet->InTransferLength;
388     Request->Header.Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_READ;
389   } else {
390     Request->Header.DataLength = Packet->OutTransferLength;
391     Request->Sg.Length = Packet->OutTransferLength;
392     Request->Header.Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_WRITE;
393 
394     CopyMem (Dev->Dma->Data, Packet->OutDataBuffer, Packet->OutTransferLength);
395     Request->Sg.BufferContainsData = 1;
396   }
397 
398   if (Request->Header.DataLength == 0) {
399     Request->Header.Control = MPT_SCSIIO_REQUEST_CONTROL_TXDIR_NONE;
400   }
401 
402   return EFI_SUCCESS;
403 }
404 
405 STATIC
406 EFI_STATUS
MptScsiSendRequest(IN MPT_SCSI_DEV * Dev,IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET * Packet)407 MptScsiSendRequest (
408   IN MPT_SCSI_DEV                                   *Dev,
409   IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
410   )
411 {
412   EFI_STATUS Status;
413 
414   if (!Dev->IoReplyEnqueued) {
415     //
416     // Put one free reply frame on the reply queue, the hardware may use it to
417     // report an error to us.
418     //
419     Status = Out32 (Dev, MPT_REG_REP_Q, MPT_SCSI_DMA_ADDR_LOW (Dev, IoReply));
420     if (EFI_ERROR (Status)) {
421       return EFI_DEVICE_ERROR;
422     }
423     Dev->IoReplyEnqueued = TRUE;
424   }
425 
426   Status = Out32 (Dev, MPT_REG_REQ_Q, MPT_SCSI_DMA_ADDR_LOW (Dev, IoRequest));
427   if (EFI_ERROR (Status)) {
428     return EFI_DEVICE_ERROR;
429   }
430 
431   return EFI_SUCCESS;
432 }
433 
434 STATIC
435 EFI_STATUS
MptScsiGetReply(IN MPT_SCSI_DEV * Dev,OUT UINT32 * Reply)436 MptScsiGetReply (
437   IN MPT_SCSI_DEV                                   *Dev,
438   OUT UINT32                                        *Reply
439   )
440 {
441   EFI_STATUS Status;
442   UINT32     Istatus;
443   UINT32     EmptyReply;
444 
445   //
446   // Timeouts are not supported for
447   // EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() in this implementation.
448   //
449   for (;;) {
450     Status = In32 (Dev, MPT_REG_ISTATUS, &Istatus);
451     if (EFI_ERROR (Status)) {
452       return Status;
453     }
454 
455     //
456     // Interrupt raised
457     //
458     if (Istatus & MPT_IMASK_REPLY) {
459       break;
460     }
461 
462     gBS->Stall (Dev->StallPerPollUsec);
463   }
464 
465   Status = In32 (Dev, MPT_REG_REP_Q, Reply);
466   if (EFI_ERROR (Status)) {
467     return Status;
468   }
469 
470   //
471   // The driver is supposed to fetch replies until 0xffffffff is returned, which
472   // will reset the interrupt status. We put only one request, so we expect the
473   // next read reply to be the last.
474   //
475   Status = In32 (Dev, MPT_REG_REP_Q, &EmptyReply);
476   if (EFI_ERROR (Status) || EmptyReply != MAX_UINT32) {
477     return EFI_DEVICE_ERROR;
478   }
479 
480   return EFI_SUCCESS;
481 }
482 
483 STATIC
484 EFI_STATUS
MptScsiHandleReply(IN MPT_SCSI_DEV * Dev,IN UINT32 Reply,OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET * Packet)485 MptScsiHandleReply (
486   IN MPT_SCSI_DEV                                   *Dev,
487   IN UINT32                                         Reply,
488   OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET    *Packet
489   )
490 {
491   if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
492     CopyMem (Packet->InDataBuffer, Dev->Dma->Data, Packet->InTransferLength);
493   }
494 
495   if (Reply == Dev->Dma->IoRequest.Data.Header.MessageContext) {
496     //
497     // This is a turbo reply, everything is good
498     //
499     Packet->SenseDataLength = 0;
500     Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;
501     Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;
502 
503   } else if ((Reply & BIT31) != 0) {
504     DEBUG ((DEBUG_INFO, "%a: Full reply returned\n", __FUNCTION__));
505     //
506     // When reply MSB is set, we got a full reply. Since we submitted only one
507     // reply frame, we know it's IoReply.
508     //
509     Dev->IoReplyEnqueued = FALSE;
510 
511     Packet->TargetStatus = Dev->Dma->IoReply.Data.ScsiStatus;
512     //
513     // Make sure device only lowers SenseDataLength before copying sense
514     //
515     ASSERT (Dev->Dma->IoReply.Data.SenseCount <= Packet->SenseDataLength);
516     Packet->SenseDataLength =
517       (UINT8)MIN (Dev->Dma->IoReply.Data.SenseCount, Packet->SenseDataLength);
518     CopyMem (Packet->SenseData, Dev->Dma->Sense, Packet->SenseDataLength);
519 
520     if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
521       Packet->InTransferLength = Dev->Dma->IoReply.Data.TransferCount;
522     } else {
523       Packet->OutTransferLength = Dev->Dma->IoReply.Data.TransferCount;
524     }
525 
526     switch (Dev->Dma->IoReply.Data.IocStatus) {
527     case MPT_SCSI_IOCSTATUS_SUCCESS:
528       Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;
529       break;
530     case MPT_SCSI_IOCSTATUS_DEVICE_NOT_THERE:
531       Packet->HostAdapterStatus =
532         EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT;
533       return EFI_TIMEOUT;
534     case MPT_SCSI_IOCSTATUS_DATA_UNDERRUN:
535     case MPT_SCSI_IOCSTATUS_DATA_OVERRUN:
536       Packet->HostAdapterStatus =
537         EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;
538       break;
539     default:
540       Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;
541       return EFI_DEVICE_ERROR;
542     }
543 
544   } else {
545     DEBUG ((DEBUG_ERROR, "%a: unexpected reply (%x)\n", __FUNCTION__, Reply));
546     return ReportHostAdapterError (Packet);
547   }
548 
549   return EFI_SUCCESS;
550 }
551 
552 //
553 // Ext SCSI Pass Thru
554 //
555 
556 STATIC
557 EFI_STATUS
558 EFIAPI
MptScsiPassThru(IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL * This,IN UINT8 * Target,IN UINT64 Lun,IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET * Packet,IN EFI_EVENT Event OPTIONAL)559 MptScsiPassThru (
560   IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                *This,
561   IN UINT8                                          *Target,
562   IN UINT64                                         Lun,
563   IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
564   IN EFI_EVENT                                      Event     OPTIONAL
565   )
566 {
567   EFI_STATUS   Status;
568   MPT_SCSI_DEV *Dev;
569   UINT32       Reply;
570 
571   Dev = MPT_SCSI_FROM_PASS_THRU (This);
572   //
573   // We only use first byte of target identifer
574   //
575   Status = MptScsiPopulateRequest (Dev, *Target, Lun, Packet);
576   if (EFI_ERROR (Status)) {
577     //
578     // MptScsiPopulateRequest modified packet according to the error
579     //
580     return Status;
581   }
582 
583   Status = MptScsiSendRequest (Dev, Packet);
584   if (EFI_ERROR (Status)) {
585     return ReportHostAdapterError (Packet);
586   }
587 
588   Status = MptScsiGetReply (Dev, &Reply);
589   if (EFI_ERROR (Status)) {
590     return ReportHostAdapterError (Packet);
591   }
592 
593   return MptScsiHandleReply (Dev, Reply, Packet);
594 }
595 
596 STATIC
597 BOOLEAN
IsTargetInitialized(IN UINT8 * Target)598 IsTargetInitialized (
599   IN UINT8                                          *Target
600   )
601 {
602   UINTN Idx;
603 
604   for (Idx = 0; Idx < TARGET_MAX_BYTES; ++Idx) {
605     if (Target[Idx] != 0xFF) {
606       return TRUE;
607     }
608   }
609   return FALSE;
610 }
611 
612 STATIC
613 EFI_STATUS
614 EFIAPI
MptScsiGetNextTargetLun(IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL * This,IN OUT UINT8 ** Target,IN OUT UINT64 * Lun)615 MptScsiGetNextTargetLun (
616   IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                *This,
617   IN OUT UINT8                                      **Target,
618   IN OUT UINT64                                     *Lun
619   )
620 {
621   MPT_SCSI_DEV *Dev;
622 
623   Dev = MPT_SCSI_FROM_PASS_THRU (This);
624   //
625   // Currently support only LUN 0, so hardcode it
626   //
627   if (!IsTargetInitialized (*Target)) {
628     ZeroMem (*Target, TARGET_MAX_BYTES);
629     *Lun = 0;
630   } else if (**Target > Dev->MaxTarget || *Lun > 0) {
631     return EFI_INVALID_PARAMETER;
632   } else if (**Target < Dev->MaxTarget) {
633     //
634     // This device interface support 256 targets only, so it's enough to
635     // increment the LSB of Target, as it will never overflow.
636     //
637     **Target += 1;
638   } else {
639     return EFI_NOT_FOUND;
640   }
641 
642   return EFI_SUCCESS;
643 }
644 
645 STATIC
646 EFI_STATUS
647 EFIAPI
MptScsiGetNextTarget(IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL * This,IN OUT UINT8 ** Target)648 MptScsiGetNextTarget (
649   IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL               *This,
650   IN OUT UINT8                                     **Target
651   )
652 {
653   MPT_SCSI_DEV *Dev;
654 
655   Dev = MPT_SCSI_FROM_PASS_THRU (This);
656   if (!IsTargetInitialized (*Target)) {
657     ZeroMem (*Target, TARGET_MAX_BYTES);
658   } else if (**Target > Dev->MaxTarget) {
659     return EFI_INVALID_PARAMETER;
660   } else if (**Target < Dev->MaxTarget) {
661     //
662     // This device interface support 256 targets only, so it's enough to
663     // increment the LSB of Target, as it will never overflow.
664     //
665     **Target += 1;
666   } else {
667     return EFI_NOT_FOUND;
668   }
669 
670   return EFI_SUCCESS;
671 }
672 
673 STATIC
674 EFI_STATUS
675 EFIAPI
MptScsiBuildDevicePath(IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL * This,IN UINT8 * Target,IN UINT64 Lun,IN OUT EFI_DEVICE_PATH_PROTOCOL ** DevicePath)676 MptScsiBuildDevicePath (
677   IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL               *This,
678   IN UINT8                                         *Target,
679   IN UINT64                                        Lun,
680   IN OUT EFI_DEVICE_PATH_PROTOCOL                  **DevicePath
681   )
682 {
683   MPT_SCSI_DEV     *Dev;
684   SCSI_DEVICE_PATH *ScsiDevicePath;
685 
686   if (DevicePath == NULL) {
687     return EFI_INVALID_PARAMETER;
688   }
689 
690   //
691   // This device support 256 targets only, so it's enough to dereference
692   // the LSB of Target.
693   //
694   Dev = MPT_SCSI_FROM_PASS_THRU (This);
695   if (*Target > Dev->MaxTarget || Lun > 0) {
696     return EFI_NOT_FOUND;
697   }
698 
699   ScsiDevicePath = AllocateZeroPool (sizeof (*ScsiDevicePath));
700   if (ScsiDevicePath == NULL) {
701     return EFI_OUT_OF_RESOURCES;
702   }
703 
704   ScsiDevicePath->Header.Type      = MESSAGING_DEVICE_PATH;
705   ScsiDevicePath->Header.SubType   = MSG_SCSI_DP;
706   ScsiDevicePath->Header.Length[0] = (UINT8)sizeof (*ScsiDevicePath);
707   ScsiDevicePath->Header.Length[1] = (UINT8)(sizeof (*ScsiDevicePath) >> 8);
708   ScsiDevicePath->Pun              = *Target;
709   ScsiDevicePath->Lun              = (UINT16)Lun;
710 
711   *DevicePath = &ScsiDevicePath->Header;
712   return EFI_SUCCESS;
713 }
714 
715 STATIC
716 EFI_STATUS
717 EFIAPI
MptScsiGetTargetLun(IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL * This,IN EFI_DEVICE_PATH_PROTOCOL * DevicePath,OUT UINT8 ** Target,OUT UINT64 * Lun)718 MptScsiGetTargetLun (
719   IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL               *This,
720   IN EFI_DEVICE_PATH_PROTOCOL                      *DevicePath,
721   OUT UINT8                                        **Target,
722   OUT UINT64                                       *Lun
723   )
724 {
725   MPT_SCSI_DEV     *Dev;
726   SCSI_DEVICE_PATH *ScsiDevicePath;
727 
728   if (DevicePath == NULL ||
729       Target == NULL || *Target == NULL || Lun == NULL) {
730     return EFI_INVALID_PARAMETER;
731   }
732 
733   if (DevicePath->Type    != MESSAGING_DEVICE_PATH ||
734       DevicePath->SubType != MSG_SCSI_DP) {
735     return EFI_UNSUPPORTED;
736   }
737 
738   Dev = MPT_SCSI_FROM_PASS_THRU (This);
739   ScsiDevicePath = (SCSI_DEVICE_PATH *)DevicePath;
740   if (ScsiDevicePath->Pun > Dev->MaxTarget ||
741       ScsiDevicePath->Lun > 0) {
742     return EFI_NOT_FOUND;
743   }
744 
745   ZeroMem (*Target, TARGET_MAX_BYTES);
746   //
747   // This device support 256 targets only, so it's enough to set the LSB
748   // of Target.
749   //
750   **Target = (UINT8)ScsiDevicePath->Pun;
751   *Lun = ScsiDevicePath->Lun;
752 
753   return EFI_SUCCESS;
754 }
755 
756 STATIC
757 EFI_STATUS
758 EFIAPI
MptScsiResetChannel(IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL * This)759 MptScsiResetChannel (
760   IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL               *This
761   )
762 {
763   return EFI_UNSUPPORTED;
764 }
765 
766 STATIC
767 VOID
768 EFIAPI
MptScsiExitBoot(IN EFI_EVENT Event,IN VOID * Context)769 MptScsiExitBoot (
770   IN  EFI_EVENT Event,
771   IN  VOID      *Context
772   )
773 {
774   MPT_SCSI_DEV *Dev;
775 
776   Dev = Context;
777   DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context));
778   MptScsiReset (Dev);
779 }
780 STATIC
781 EFI_STATUS
782 EFIAPI
MptScsiResetTargetLun(IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL * This,IN UINT8 * Target,IN UINT64 Lun)783 MptScsiResetTargetLun (
784   IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL               *This,
785   IN UINT8                                         *Target,
786   IN UINT64                                        Lun
787   )
788 {
789   return EFI_UNSUPPORTED;
790 }
791 
792 //
793 // Driver Binding
794 //
795 
796 STATIC
797 EFI_STATUS
798 EFIAPI
MptScsiControllerSupported(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE ControllerHandle,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL)799 MptScsiControllerSupported (
800   IN EFI_DRIVER_BINDING_PROTOCOL            *This,
801   IN EFI_HANDLE                             ControllerHandle,
802   IN EFI_DEVICE_PATH_PROTOCOL               *RemainingDevicePath OPTIONAL
803   )
804 {
805   EFI_STATUS          Status;
806   EFI_PCI_IO_PROTOCOL *PciIo;
807   PCI_TYPE00          Pci;
808 
809   Status = gBS->OpenProtocol (
810                   ControllerHandle,
811                   &gEfiPciIoProtocolGuid,
812                   (VOID **)&PciIo,
813                   This->DriverBindingHandle,
814                   ControllerHandle,
815                   EFI_OPEN_PROTOCOL_BY_DRIVER
816                   );
817   if (EFI_ERROR (Status)) {
818     return Status;
819   }
820 
821   Status = PciIo->Pci.Read (
822                         PciIo,
823                         EfiPciIoWidthUint32,
824                         0,
825                         sizeof (Pci) / sizeof (UINT32),
826                         &Pci
827                         );
828   if (EFI_ERROR (Status)) {
829     goto Done;
830   }
831 
832   if (Pci.Hdr.VendorId == LSI_LOGIC_PCI_VENDOR_ID &&
833       (Pci.Hdr.DeviceId == LSI_53C1030_PCI_DEVICE_ID ||
834        Pci.Hdr.DeviceId == LSI_SAS1068_PCI_DEVICE_ID ||
835        Pci.Hdr.DeviceId == LSI_SAS1068E_PCI_DEVICE_ID)) {
836     Status = EFI_SUCCESS;
837   } else {
838     Status = EFI_UNSUPPORTED;
839   }
840 
841 Done:
842   gBS->CloseProtocol (
843          ControllerHandle,
844          &gEfiPciIoProtocolGuid,
845          This->DriverBindingHandle,
846          ControllerHandle
847          );
848   return Status;
849 }
850 
851 STATIC
852 EFI_STATUS
853 EFIAPI
MptScsiControllerStart(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE ControllerHandle,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL)854 MptScsiControllerStart (
855   IN EFI_DRIVER_BINDING_PROTOCOL            *This,
856   IN EFI_HANDLE                             ControllerHandle,
857   IN EFI_DEVICE_PATH_PROTOCOL               *RemainingDevicePath OPTIONAL
858   )
859 {
860   EFI_STATUS           Status;
861   MPT_SCSI_DEV         *Dev;
862   UINTN                Pages;
863   UINTN                BytesMapped;
864 
865   Dev = AllocateZeroPool (sizeof (*Dev));
866   if (Dev == NULL) {
867     return EFI_OUT_OF_RESOURCES;
868   }
869 
870   Dev->Signature = MPT_SCSI_DEV_SIGNATURE;
871 
872   Dev->MaxTarget = PcdGet8 (PcdMptScsiMaxTargetLimit);
873   Dev->StallPerPollUsec = PcdGet32 (PcdMptScsiStallPerPollUsec);
874 
875   Status = gBS->OpenProtocol (
876                   ControllerHandle,
877                   &gEfiPciIoProtocolGuid,
878                   (VOID **)&Dev->PciIo,
879                   This->DriverBindingHandle,
880                   ControllerHandle,
881                   EFI_OPEN_PROTOCOL_BY_DRIVER
882                   );
883   if (EFI_ERROR (Status)) {
884     goto FreePool;
885   }
886 
887   Status = Dev->PciIo->Attributes (
888                          Dev->PciIo,
889                          EfiPciIoAttributeOperationGet,
890                          0,
891                          &Dev->OriginalPciAttributes
892                          );
893   if (EFI_ERROR (Status)) {
894     goto CloseProtocol;
895   }
896 
897   //
898   // Enable I/O Space & Bus-Mastering
899   //
900   Status = Dev->PciIo->Attributes (
901                          Dev->PciIo,
902                          EfiPciIoAttributeOperationEnable,
903                          (EFI_PCI_IO_ATTRIBUTE_IO |
904                           EFI_PCI_IO_ATTRIBUTE_BUS_MASTER),
905                          NULL
906                          );
907   if (EFI_ERROR (Status)) {
908     goto CloseProtocol;
909   }
910 
911   //
912   // Signal device supports 64-bit DMA addresses
913   //
914   Status = Dev->PciIo->Attributes (
915                          Dev->PciIo,
916                          EfiPciIoAttributeOperationEnable,
917                          EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE,
918                          NULL
919                          );
920   if (EFI_ERROR (Status)) {
921     //
922     // Warn user that device will only be using 32-bit DMA addresses.
923     //
924     // Note that this does not prevent the device/driver from working
925     // and therefore we only warn and continue as usual.
926     //
927     DEBUG ((
928       DEBUG_WARN,
929       "%a: failed to enable 64-bit DMA addresses\n",
930       __FUNCTION__
931       ));
932   }
933 
934   //
935   // Create buffers for data transfer
936   //
937   Pages = EFI_SIZE_TO_PAGES (sizeof (*Dev->Dma));
938   Status = Dev->PciIo->AllocateBuffer (
939                          Dev->PciIo,
940                          AllocateAnyPages,
941                          EfiBootServicesData,
942                          Pages,
943                          (VOID **)&Dev->Dma,
944                          EFI_PCI_ATTRIBUTE_MEMORY_CACHED
945                          );
946   if (EFI_ERROR (Status)) {
947     goto RestoreAttributes;
948   }
949 
950   BytesMapped = EFI_PAGES_TO_SIZE (Pages);
951   Status = Dev->PciIo->Map (
952                          Dev->PciIo,
953                          EfiPciIoOperationBusMasterCommonBuffer,
954                          Dev->Dma,
955                          &BytesMapped,
956                          &Dev->DmaPhysical,
957                          &Dev->DmaMapping
958                          );
959   if (EFI_ERROR (Status)) {
960     goto FreeBuffer;
961   }
962 
963   if (BytesMapped != EFI_PAGES_TO_SIZE (Pages)) {
964     Status = EFI_OUT_OF_RESOURCES;
965     goto Unmap;
966   }
967 
968   Status = MptScsiInit (Dev);
969   if (EFI_ERROR (Status)) {
970     goto Unmap;
971   }
972 
973   Status = gBS->CreateEvent (
974                   EVT_SIGNAL_EXIT_BOOT_SERVICES,
975                   TPL_CALLBACK,
976                   &MptScsiExitBoot,
977                   Dev,
978                   &Dev->ExitBoot
979                   );
980   if (EFI_ERROR (Status)) {
981     goto UninitDev;
982   }
983 
984   //
985   // Host adapter channel, doesn't exist
986   //
987   Dev->PassThruMode.AdapterId = MAX_UINT32;
988   Dev->PassThruMode.Attributes =
989     EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL |
990     EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;
991 
992   Dev->PassThru.Mode = &Dev->PassThruMode;
993   Dev->PassThru.PassThru = &MptScsiPassThru;
994   Dev->PassThru.GetNextTargetLun = &MptScsiGetNextTargetLun;
995   Dev->PassThru.BuildDevicePath = &MptScsiBuildDevicePath;
996   Dev->PassThru.GetTargetLun = &MptScsiGetTargetLun;
997   Dev->PassThru.ResetChannel = &MptScsiResetChannel;
998   Dev->PassThru.ResetTargetLun = &MptScsiResetTargetLun;
999   Dev->PassThru.GetNextTarget = &MptScsiGetNextTarget;
1000 
1001   Status = gBS->InstallProtocolInterface (
1002                   &ControllerHandle,
1003                   &gEfiExtScsiPassThruProtocolGuid,
1004                   EFI_NATIVE_INTERFACE,
1005                   &Dev->PassThru
1006                   );
1007   if (EFI_ERROR (Status)) {
1008     goto CloseExitBoot;
1009   }
1010 
1011   return EFI_SUCCESS;
1012 
1013 CloseExitBoot:
1014   gBS->CloseEvent (Dev->ExitBoot);
1015 
1016 UninitDev:
1017   MptScsiReset (Dev);
1018 
1019 Unmap:
1020   Dev->PciIo->Unmap (
1021                 Dev->PciIo,
1022                 Dev->DmaMapping
1023                 );
1024 
1025 FreeBuffer:
1026   Dev->PciIo->FreeBuffer (
1027                 Dev->PciIo,
1028                 Pages,
1029                 Dev->Dma
1030                 );
1031 
1032 RestoreAttributes:
1033   Dev->PciIo->Attributes (
1034                 Dev->PciIo,
1035                 EfiPciIoAttributeOperationSet,
1036                 Dev->OriginalPciAttributes,
1037                 NULL
1038                 );
1039 
1040 CloseProtocol:
1041   gBS->CloseProtocol (
1042          ControllerHandle,
1043          &gEfiPciIoProtocolGuid,
1044          This->DriverBindingHandle,
1045          ControllerHandle
1046          );
1047 
1048 FreePool:
1049   FreePool (Dev);
1050 
1051   return Status;
1052 }
1053 
1054 STATIC
1055 EFI_STATUS
1056 EFIAPI
MptScsiControllerStop(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE ControllerHandle,IN UINTN NumberOfChildren,IN EFI_HANDLE * ChildHandleBuffer)1057 MptScsiControllerStop (
1058   IN EFI_DRIVER_BINDING_PROTOCOL            *This,
1059   IN  EFI_HANDLE                            ControllerHandle,
1060   IN  UINTN                                 NumberOfChildren,
1061   IN  EFI_HANDLE                            *ChildHandleBuffer
1062   )
1063 {
1064   EFI_STATUS                      Status;
1065   EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;
1066   MPT_SCSI_DEV                    *Dev;
1067 
1068   Status = gBS->OpenProtocol (
1069                   ControllerHandle,
1070                   &gEfiExtScsiPassThruProtocolGuid,
1071                   (VOID **)&PassThru,
1072                   This->DriverBindingHandle,
1073                   ControllerHandle,
1074                   EFI_OPEN_PROTOCOL_GET_PROTOCOL // Lookup only
1075                   );
1076   if (EFI_ERROR (Status)) {
1077     return Status;
1078   }
1079 
1080   Dev = MPT_SCSI_FROM_PASS_THRU (PassThru);
1081 
1082   Status = gBS->UninstallProtocolInterface (
1083                   ControllerHandle,
1084                   &gEfiExtScsiPassThruProtocolGuid,
1085                   &Dev->PassThru
1086                   );
1087   if (EFI_ERROR (Status)) {
1088     return Status;
1089   }
1090 
1091   gBS->CloseEvent (Dev->ExitBoot);
1092 
1093   MptScsiReset (Dev);
1094 
1095   Dev->PciIo->Unmap (
1096                 Dev->PciIo,
1097                 Dev->DmaMapping
1098                 );
1099 
1100   Dev->PciIo->FreeBuffer (
1101                 Dev->PciIo,
1102                 EFI_SIZE_TO_PAGES (sizeof (*Dev->Dma)),
1103                 Dev->Dma
1104                 );
1105 
1106   Dev->PciIo->Attributes (
1107                 Dev->PciIo,
1108                 EfiPciIoAttributeOperationSet,
1109                 Dev->OriginalPciAttributes,
1110                 NULL
1111                 );
1112 
1113   gBS->CloseProtocol (
1114          ControllerHandle,
1115          &gEfiPciIoProtocolGuid,
1116          This->DriverBindingHandle,
1117          ControllerHandle
1118          );
1119 
1120   FreePool (Dev);
1121 
1122   return Status;
1123 }
1124 
1125 STATIC
1126 EFI_DRIVER_BINDING_PROTOCOL mMptScsiDriverBinding = {
1127   &MptScsiControllerSupported,
1128   &MptScsiControllerStart,
1129   &MptScsiControllerStop,
1130   MPT_SCSI_BINDING_VERSION,
1131   NULL, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2
1132   NULL, // DriverBindingHandle, filled as well
1133 };
1134 
1135 //
1136 // Component Name
1137 //
1138 
1139 STATIC
1140 EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
1141   { "eng;en", L"LSI Fusion MPT SCSI Driver" },
1142   { NULL,     NULL                   }
1143 };
1144 
1145 STATIC
1146 EFI_COMPONENT_NAME_PROTOCOL mComponentName;
1147 
1148 EFI_STATUS
1149 EFIAPI
MptScsiGetDriverName(IN EFI_COMPONENT_NAME_PROTOCOL * This,IN CHAR8 * Language,OUT CHAR16 ** DriverName)1150 MptScsiGetDriverName (
1151   IN  EFI_COMPONENT_NAME_PROTOCOL *This,
1152   IN  CHAR8                       *Language,
1153   OUT CHAR16                      **DriverName
1154   )
1155 {
1156   return LookupUnicodeString2 (
1157            Language,
1158            This->SupportedLanguages,
1159            mDriverNameTable,
1160            DriverName,
1161            (BOOLEAN)(This == &mComponentName) // Iso639Language
1162            );
1163 }
1164 
1165 EFI_STATUS
1166 EFIAPI
MptScsiGetDeviceName(IN EFI_COMPONENT_NAME_PROTOCOL * This,IN EFI_HANDLE DeviceHandle,IN EFI_HANDLE ChildHandle,IN CHAR8 * Language,OUT CHAR16 ** ControllerName)1167 MptScsiGetDeviceName (
1168   IN  EFI_COMPONENT_NAME_PROTOCOL *This,
1169   IN  EFI_HANDLE                  DeviceHandle,
1170   IN  EFI_HANDLE                  ChildHandle,
1171   IN  CHAR8                       *Language,
1172   OUT CHAR16                      **ControllerName
1173   )
1174 {
1175   return EFI_UNSUPPORTED;
1176 }
1177 
1178 STATIC
1179 EFI_COMPONENT_NAME_PROTOCOL mComponentName = {
1180   &MptScsiGetDriverName,
1181   &MptScsiGetDeviceName,
1182   "eng" // SupportedLanguages, ISO 639-2 language codes
1183 };
1184 
1185 STATIC
1186 EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {
1187   (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)     &MptScsiGetDriverName,
1188   (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &MptScsiGetDeviceName,
1189   "en" // SupportedLanguages, RFC 4646 language codes
1190 };
1191 
1192 //
1193 // Entry Point
1194 //
1195 
1196 EFI_STATUS
1197 EFIAPI
MptScsiEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)1198 MptScsiEntryPoint (
1199   IN EFI_HANDLE       ImageHandle,
1200   IN EFI_SYSTEM_TABLE *SystemTable
1201   )
1202 {
1203   return EfiLibInstallDriverBindingComponentName2 (
1204            ImageHandle,
1205            SystemTable,
1206            &mMptScsiDriverBinding,
1207            ImageHandle, // The handle to install onto
1208            &mComponentName,
1209            &mComponentName2
1210            );
1211 }
1212