]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/MptScsiDxe/MptScsi.c
OvmfPkg/MptScsiDxe: Reset device on ExitBootServices()
[mirror_edk2.git] / OvmfPkg / MptScsiDxe / MptScsi.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }