]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/LsiScsiDxe/LsiScsi.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[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 STATIC
29 EFI_STATUS
30 Out8 (
31 IN LSI_SCSI_DEV *Dev,
32 IN UINT32 Addr,
33 IN UINT8 Data
34 )
35 {
36 return Dev->PciIo->Io.Write (
37 Dev->PciIo,
38 EfiPciIoWidthUint8,
39 PCI_BAR_IDX0,
40 Addr,
41 1,
42 &Data
43 );
44 }
45
46 STATIC
47 EFI_STATUS
48 Out32 (
49 IN LSI_SCSI_DEV *Dev,
50 IN UINT32 Addr,
51 IN UINT32 Data
52 )
53 {
54 return Dev->PciIo->Io.Write (
55 Dev->PciIo,
56 EfiPciIoWidthUint32,
57 PCI_BAR_IDX0,
58 Addr,
59 1,
60 &Data
61 );
62 }
63
64 STATIC
65 EFI_STATUS
66 In8 (
67 IN LSI_SCSI_DEV *Dev,
68 IN UINT32 Addr,
69 OUT UINT8 *Data
70 )
71 {
72 return Dev->PciIo->Io.Read (
73 Dev->PciIo,
74 EfiPciIoWidthUint8,
75 PCI_BAR_IDX0,
76 Addr,
77 1,
78 Data
79 );
80 }
81
82 STATIC
83 EFI_STATUS
84 In32 (
85 IN LSI_SCSI_DEV *Dev,
86 IN UINT32 Addr,
87 OUT UINT32 *Data
88 )
89 {
90 return Dev->PciIo->Io.Read (
91 Dev->PciIo,
92 EfiPciIoWidthUint32,
93 PCI_BAR_IDX0,
94 Addr,
95 1,
96 Data
97 );
98 }
99
100 STATIC
101 EFI_STATUS
102 LsiScsiReset (
103 IN LSI_SCSI_DEV *Dev
104 )
105 {
106 return Out8 (Dev, LSI_REG_ISTAT0, LSI_ISTAT0_SRST);
107 }
108
109 STATIC
110 EFI_STATUS
111 ReportHostAdapterOverrunError (
112 OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
113 )
114 {
115 Packet->SenseDataLength = 0;
116 Packet->HostAdapterStatus =
117 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;
118 Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;
119 return EFI_BAD_BUFFER_SIZE;
120 }
121
122 /**
123
124 Check the request packet from the Extended SCSI Pass Thru Protocol. The
125 request packet is modified, to be forwarded outwards by LsiScsiPassThru(),
126 if invalid or unsupported parameters are detected.
127
128 @param[in] Dev The LSI 53C895A SCSI device the packet targets.
129
130 @param[in] Target The SCSI target controlled by the LSI 53C895A SCSI
131 device.
132
133 @param[in] Lun The Logical Unit Number under the SCSI target.
134
135 @param[in out] Packet The Extended SCSI Pass Thru Protocol packet.
136
137
138 @retval EFI_SUCCESS The Extended SCSI Pass Thru Protocol packet was valid.
139
140 @return Otherwise, invalid or unsupported parameters were
141 detected. Status codes are meant for direct forwarding
142 by the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru()
143 implementation.
144
145 **/
146 STATIC
147 EFI_STATUS
148 LsiScsiCheckRequest (
149 IN LSI_SCSI_DEV *Dev,
150 IN UINT8 Target,
151 IN UINT64 Lun,
152 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
153 )
154 {
155 if ((Target > Dev->MaxTarget) || (Lun > Dev->MaxLun) ||
156 (Packet->DataDirection > EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL) ||
157 //
158 // Trying to receive, but destination pointer is NULL, or contradicting
159 // transfer direction
160 //
161 ((Packet->InTransferLength > 0) &&
162 ((Packet->InDataBuffer == NULL) ||
163 (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_WRITE)
164 )
165 ) ||
166
167 //
168 // Trying to send, but source pointer is NULL, or contradicting transfer
169 // direction
170 //
171 ((Packet->OutTransferLength > 0) &&
172 ((Packet->OutDataBuffer == NULL) ||
173 (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ)
174 )
175 )
176 )
177 {
178 return EFI_INVALID_PARAMETER;
179 }
180
181 if ((Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL) ||
182 ((Packet->InTransferLength > 0) && (Packet->OutTransferLength > 0)) ||
183 (Packet->CdbLength > sizeof Dev->Dma->Cdb))
184 {
185 return EFI_UNSUPPORTED;
186 }
187
188 if (Packet->InTransferLength > sizeof Dev->Dma->Data) {
189 Packet->InTransferLength = sizeof Dev->Dma->Data;
190 return ReportHostAdapterOverrunError (Packet);
191 }
192
193 if (Packet->OutTransferLength > sizeof Dev->Dma->Data) {
194 Packet->OutTransferLength = sizeof Dev->Dma->Data;
195 return ReportHostAdapterOverrunError (Packet);
196 }
197
198 return EFI_SUCCESS;
199 }
200
201 /**
202
203 Interpret the request packet from the Extended SCSI Pass Thru Protocol and
204 compose the script to submit the command and data to the controller.
205
206 @param[in] Dev The LSI 53C895A SCSI device the packet targets.
207
208 @param[in] Target The SCSI target controlled by the LSI 53C895A SCSI
209 device.
210
211 @param[in] Lun The Logical Unit Number under the SCSI target.
212
213 @param[in out] Packet The Extended SCSI Pass Thru Protocol packet.
214
215
216 @retval EFI_SUCCESS The Extended SCSI Pass Thru Protocol packet was valid.
217
218 @return Otherwise, invalid or unsupported parameters were
219 detected. Status codes are meant for direct forwarding
220 by the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru()
221 implementation.
222
223 **/
224 STATIC
225 EFI_STATUS
226 LsiScsiProcessRequest (
227 IN LSI_SCSI_DEV *Dev,
228 IN UINT8 Target,
229 IN UINT64 Lun,
230 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
231 )
232 {
233 EFI_STATUS Status;
234 UINT32 *Script;
235 UINT8 *Cdb;
236 UINT8 *MsgOut;
237 UINT8 *MsgIn;
238 UINT8 *ScsiStatus;
239 UINT8 *Data;
240 UINT8 DStat;
241 UINT8 SIst0;
242 UINT8 SIst1;
243 UINT32 Csbc;
244 UINT32 CsbcBase;
245 UINT32 Transferred;
246
247 Script = Dev->Dma->Script;
248 Cdb = Dev->Dma->Cdb;
249 Data = Dev->Dma->Data;
250 MsgIn = Dev->Dma->MsgIn;
251 MsgOut = &Dev->Dma->MsgOut;
252 ScsiStatus = &Dev->Dma->Status;
253
254 *ScsiStatus = 0xFF;
255
256 DStat = 0;
257 SIst0 = 0;
258 SIst1 = 0;
259
260 SetMem (Cdb, sizeof Dev->Dma->Cdb, 0x00);
261 CopyMem (Cdb, Packet->Cdb, Packet->CdbLength);
262
263 //
264 // Fetch the first Cumulative SCSI Byte Count (CSBC).
265 //
266 // CSBC is a cumulative counter of the actual number of bytes that have been
267 // transferred across the SCSI bus during data phases, i.e. it will not
268 // count bytes sent in command, status, message in and out phases.
269 //
270 Status = In32 (Dev, LSI_REG_CSBC, &CsbcBase);
271 if (EFI_ERROR (Status)) {
272 goto Error;
273 }
274
275 //
276 // Clean up the DMA buffer for the script.
277 //
278 SetMem (Script, sizeof Dev->Dma->Script, 0x00);
279
280 //
281 // Compose the script to transfer data between the host and the device.
282 //
283 // References:
284 // * LSI53C895A PCI to Ultra2 SCSI Controller Version 2.2
285 // - Chapter 5 SCSI SCRIPT Instruction Set
286 // * SEABIOS lsi-scsi driver
287 //
288 // All instructions used here consist of 2 32bit words. The first word
289 // contains the command to execute. The second word is loaded into the
290 // DMA SCRIPTS Pointer Save (DSPS) register as either the DMA address
291 // for data transmission or the address/offset for the jump command.
292 // Some commands, such as the selection of the target, don't need to
293 // transfer data through DMA or jump to another instruction, then DSPS
294 // has to be zero.
295 //
296 // There are 3 major parts in this script. The first part (1~3) contains
297 // the instructions to select target and LUN and send the SCSI command
298 // from the request packet. The second part (4~7) is to handle the
299 // potential disconnection and prepare for the data transmission. The
300 // instructions in the third part (8~10) transmit the given data and
301 // collect the result. Instruction 11 raises the interrupt and marks the
302 // end of the script.
303 //
304
305 //
306 // 1. Select target.
307 //
308 *Script++ = LSI_INS_TYPE_IO | LSI_INS_IO_OPC_SEL | (UINT32)Target << 16;
309 *Script++ = 0x00000000;
310
311 //
312 // 2. Select LUN.
313 //
314 *MsgOut = 0x80 | (UINT8)Lun; // 0x80: Identify bit
315 *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_MSG_OUT |
316 (UINT32)sizeof Dev->Dma->MsgOut;
317 *Script++ = LSI_SCSI_DMA_ADDR (Dev, MsgOut);
318
319 //
320 // 3. Send the SCSI Command.
321 //
322 *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_CMD |
323 (UINT32)sizeof Dev->Dma->Cdb;
324 *Script++ = LSI_SCSI_DMA_ADDR (Dev, Cdb);
325
326 //
327 // 4. Check whether the current SCSI phase is "Message In" or not
328 // and jump to 7 if it is.
329 // Note: LSI_INS_TC_RA stands for "Relative Address Mode", so the
330 // offset 0x18 in the second word means jumping forward
331 // 3 (0x18/8) instructions.
332 //
333 *Script++ = LSI_INS_TYPE_TC | LSI_INS_TC_OPC_JMP |
334 LSI_INS_TC_SCSIP_MSG_IN | LSI_INS_TC_RA |
335 LSI_INS_TC_CP;
336 *Script++ = 0x00000018;
337
338 //
339 // 5. Read "Message" from the initiator to trigger reselect.
340 //
341 *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_MSG_IN |
342 (UINT32)sizeof Dev->Dma->MsgIn;
343 *Script++ = LSI_SCSI_DMA_ADDR (Dev, MsgIn);
344
345 //
346 // 6. Wait reselect.
347 //
348 *Script++ = LSI_INS_TYPE_IO | LSI_INS_IO_OPC_WAIT_RESEL;
349 *Script++ = 0x00000000;
350
351 //
352 // 7. Read "Message" from the initiator again
353 //
354 *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_MSG_IN |
355 (UINT32)sizeof Dev->Dma->MsgIn;
356 *Script++ = LSI_SCSI_DMA_ADDR (Dev, MsgIn);
357
358 //
359 // 8. Set the DMA command for the read/write operations.
360 // Note: Some requests, e.g. "TEST UNIT READY", do not come with
361 // allocated InDataBuffer or OutDataBuffer. We skip the DMA
362 // data command for those requests or this script would fail
363 // with LSI_SIST0_SGE due to the zero data length.
364 //
365 // LsiScsiCheckRequest() prevents both integer overflows in the command
366 // opcodes, and buffer overflows.
367 //
368 if (Packet->InTransferLength > 0) {
369 ASSERT (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ);
370 ASSERT (Packet->InTransferLength <= sizeof Dev->Dma->Data);
371 *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_DAT_IN |
372 Packet->InTransferLength;
373 *Script++ = LSI_SCSI_DMA_ADDR (Dev, Data);
374 } else if (Packet->OutTransferLength > 0) {
375 ASSERT (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_WRITE);
376 ASSERT (Packet->OutTransferLength <= sizeof Dev->Dma->Data);
377 CopyMem (Data, Packet->OutDataBuffer, Packet->OutTransferLength);
378 *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_DAT_OUT |
379 Packet->OutTransferLength;
380 *Script++ = LSI_SCSI_DMA_ADDR (Dev, Data);
381 }
382
383 //
384 // 9. Get the SCSI status.
385 //
386 *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_STAT |
387 (UINT32)sizeof Dev->Dma->Status;
388 *Script++ = LSI_SCSI_DMA_ADDR (Dev, Status);
389
390 //
391 // 10. Get the SCSI message.
392 //
393 *Script++ = LSI_INS_TYPE_BLK | LSI_INS_BLK_SCSIP_MSG_IN |
394 (UINT32)sizeof Dev->Dma->MsgIn;
395 *Script++ = LSI_SCSI_DMA_ADDR (Dev, MsgIn);
396
397 //
398 // 11. Raise the interrupt to end the script.
399 //
400 *Script++ = LSI_INS_TYPE_TC | LSI_INS_TC_OPC_INT |
401 LSI_INS_TC_SCSIP_DAT_OUT | LSI_INS_TC_JMP;
402 *Script++ = 0x00000000;
403
404 //
405 // Make sure the size of the script doesn't exceed the buffer.
406 //
407 ASSERT (Script <= Dev->Dma->Script + ARRAY_SIZE (Dev->Dma->Script));
408
409 //
410 // The controller starts to execute the script once the DMA Script
411 // Pointer (DSP) register is set.
412 //
413 Status = Out32 (Dev, LSI_REG_DSP, LSI_SCSI_DMA_ADDR (Dev, Script));
414 if (EFI_ERROR (Status)) {
415 goto Error;
416 }
417
418 //
419 // Poll the device registers (DSTAT, SIST0, and SIST1) until the SIR
420 // bit sets.
421 //
422 for ( ; ;) {
423 Status = In8 (Dev, LSI_REG_DSTAT, &DStat);
424 if (EFI_ERROR (Status)) {
425 goto Error;
426 }
427
428 Status = In8 (Dev, LSI_REG_SIST0, &SIst0);
429 if (EFI_ERROR (Status)) {
430 goto Error;
431 }
432
433 Status = In8 (Dev, LSI_REG_SIST1, &SIst1);
434 if (EFI_ERROR (Status)) {
435 goto Error;
436 }
437
438 if ((SIst0 != 0) || (SIst1 != 0)) {
439 goto Error;
440 }
441
442 //
443 // Check the SIR (SCRIPTS Interrupt Instruction Received) bit.
444 //
445 if (DStat & LSI_DSTAT_SIR) {
446 break;
447 }
448
449 gBS->Stall (Dev->StallPerPollUsec);
450 }
451
452 //
453 // Check if everything is good.
454 // SCSI Message Code 0x00: COMMAND COMPLETE
455 // SCSI Status Code 0x00: Good
456 //
457 if ((MsgIn[0] != 0) || (*ScsiStatus != 0)) {
458 goto Error;
459 }
460
461 //
462 // Fetch CSBC again to calculate the transferred bytes and update
463 // InTransferLength/OutTransferLength.
464 //
465 // Note: The number of transferred bytes is bounded by
466 // "sizeof Dev->Dma->Data", so it's safe to subtract CsbcBase
467 // from Csbc. If the CSBC register wraps around, the correct
468 // difference is ensured by the standard C modular arithmetic.
469 //
470 Status = In32 (Dev, LSI_REG_CSBC, &Csbc);
471 if (EFI_ERROR (Status)) {
472 goto Error;
473 }
474
475 Transferred = Csbc - CsbcBase;
476 if (Packet->InTransferLength > 0) {
477 if (Transferred <= Packet->InTransferLength) {
478 Packet->InTransferLength = Transferred;
479 } else {
480 goto Error;
481 }
482 } else if (Packet->OutTransferLength > 0) {
483 if (Transferred <= Packet->OutTransferLength) {
484 Packet->OutTransferLength = Transferred;
485 } else {
486 goto Error;
487 }
488 }
489
490 //
491 // Copy Data to InDataBuffer if necessary.
492 //
493 if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {
494 CopyMem (Packet->InDataBuffer, Data, Packet->InTransferLength);
495 }
496
497 //
498 // Always set SenseDataLength to 0.
499 // The instructions of LSI53C895A don't reply sense data. Instead, it
500 // relies on the SCSI command, "REQUEST SENSE", to get sense data. We set
501 // SenseDataLength to 0 to notify ScsiDiskDxe that there is no sense data
502 // written even if this request is processed successfully, so that It will
503 // issue "REQUEST SENSE" later to retrieve sense data.
504 //
505 Packet->SenseDataLength = 0;
506 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;
507 Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;
508
509 return EFI_SUCCESS;
510
511 Error:
512 DEBUG ((
513 DEBUG_VERBOSE,
514 "%a: dstat: %02X, sist0: %02X, sist1: %02X\n",
515 __FUNCTION__,
516 DStat,
517 SIst0,
518 SIst1
519 ));
520 //
521 // Update the request packet to reflect the status.
522 //
523 if (*ScsiStatus != 0xFF) {
524 Packet->TargetStatus = *ScsiStatus;
525 } else {
526 Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_TASK_ABORTED;
527 }
528
529 if (SIst0 & LSI_SIST0_PAR) {
530 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR;
531 } else if (SIst0 & LSI_SIST0_RST) {
532 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET;
533 } else if (SIst0 & LSI_SIST0_UDC) {
534 //
535 // The target device is disconnected unexpectedly. According to UEFI spec,
536 // this is TIMEOUT_COMMAND.
537 //
538 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND;
539 } else if (SIst0 & LSI_SIST0_SGE) {
540 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;
541 } else if (SIst1 & LSI_SIST1_HTH) {
542 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT;
543 } else if (SIst1 & LSI_SIST1_GEN) {
544 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT;
545 } else if (SIst1 & LSI_SIST1_STO) {
546 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT;
547 } else {
548 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;
549 }
550
551 //
552 // SenseData may be used to inspect the error. Since we don't set sense data,
553 // SenseDataLength has to be 0.
554 //
555 Packet->SenseDataLength = 0;
556
557 return EFI_DEVICE_ERROR;
558 }
559
560 //
561 // The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL
562 // for the LSI 53C895A SCSI Controller. Refer to UEFI Spec 2.3.1 + Errata C,
563 // sections
564 // - 14.1 SCSI Driver Model Overview,
565 // - 14.7 Extended SCSI Pass Thru Protocol.
566 //
567
568 EFI_STATUS
569 EFIAPI
570 LsiScsiPassThru (
571 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
572 IN UINT8 *Target,
573 IN UINT64 Lun,
574 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
575 IN EFI_EVENT Event OPTIONAL
576 )
577 {
578 EFI_STATUS Status;
579 LSI_SCSI_DEV *Dev;
580
581 Dev = LSI_SCSI_FROM_PASS_THRU (This);
582 Status = LsiScsiCheckRequest (Dev, *Target, Lun, Packet);
583 if (EFI_ERROR (Status)) {
584 return Status;
585 }
586
587 return LsiScsiProcessRequest (Dev, *Target, Lun, Packet);
588 }
589
590 EFI_STATUS
591 EFIAPI
592 LsiScsiGetNextTargetLun (
593 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
594 IN OUT UINT8 **TargetPointer,
595 IN OUT UINT64 *Lun
596 )
597 {
598 LSI_SCSI_DEV *Dev;
599 UINTN Idx;
600 UINT8 *Target;
601 UINT16 LastTarget;
602
603 //
604 // the TargetPointer input parameter is unnecessarily a pointer-to-pointer
605 //
606 Target = *TargetPointer;
607
608 //
609 // Search for first non-0xFF byte. If not found, return first target & LUN.
610 //
611 for (Idx = 0; Idx < TARGET_MAX_BYTES && Target[Idx] == 0xFF; ++Idx) {
612 }
613
614 if (Idx == TARGET_MAX_BYTES) {
615 SetMem (Target, TARGET_MAX_BYTES, 0x00);
616 *Lun = 0;
617 return EFI_SUCCESS;
618 }
619
620 CopyMem (&LastTarget, Target, sizeof LastTarget);
621
622 //
623 // increment (target, LUN) pair if valid on input
624 //
625 Dev = LSI_SCSI_FROM_PASS_THRU (This);
626 if ((LastTarget > Dev->MaxTarget) || (*Lun > Dev->MaxLun)) {
627 return EFI_INVALID_PARAMETER;
628 }
629
630 if (*Lun < Dev->MaxLun) {
631 ++*Lun;
632 return EFI_SUCCESS;
633 }
634
635 if (LastTarget < Dev->MaxTarget) {
636 *Lun = 0;
637 ++LastTarget;
638 CopyMem (Target, &LastTarget, sizeof LastTarget);
639 return EFI_SUCCESS;
640 }
641
642 return EFI_NOT_FOUND;
643 }
644
645 EFI_STATUS
646 EFIAPI
647 LsiScsiBuildDevicePath (
648 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
649 IN UINT8 *Target,
650 IN UINT64 Lun,
651 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
652 )
653 {
654 UINT16 TargetValue;
655 LSI_SCSI_DEV *Dev;
656 SCSI_DEVICE_PATH *ScsiDevicePath;
657
658 if (DevicePath == NULL) {
659 return EFI_INVALID_PARAMETER;
660 }
661
662 CopyMem (&TargetValue, Target, sizeof TargetValue);
663 Dev = LSI_SCSI_FROM_PASS_THRU (This);
664 if ((TargetValue > Dev->MaxTarget) || (Lun > Dev->MaxLun) || (Lun > 0xFFFF)) {
665 return EFI_NOT_FOUND;
666 }
667
668 ScsiDevicePath = AllocatePool (sizeof *ScsiDevicePath);
669 if (ScsiDevicePath == NULL) {
670 return EFI_OUT_OF_RESOURCES;
671 }
672
673 ScsiDevicePath->Header.Type = MESSAGING_DEVICE_PATH;
674 ScsiDevicePath->Header.SubType = MSG_SCSI_DP;
675 ScsiDevicePath->Header.Length[0] = (UINT8)sizeof *ScsiDevicePath;
676 ScsiDevicePath->Header.Length[1] = (UINT8)(sizeof *ScsiDevicePath >> 8);
677 ScsiDevicePath->Pun = TargetValue;
678 ScsiDevicePath->Lun = (UINT16)Lun;
679
680 *DevicePath = &ScsiDevicePath->Header;
681 return EFI_SUCCESS;
682 }
683
684 EFI_STATUS
685 EFIAPI
686 LsiScsiGetTargetLun (
687 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
688 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
689 OUT UINT8 **TargetPointer,
690 OUT UINT64 *Lun
691 )
692 {
693 SCSI_DEVICE_PATH *ScsiDevicePath;
694 LSI_SCSI_DEV *Dev;
695 UINT8 *Target;
696
697 if ((DevicePath == NULL) || (TargetPointer == NULL) || (*TargetPointer == NULL) ||
698 (Lun == NULL))
699 {
700 return EFI_INVALID_PARAMETER;
701 }
702
703 if ((DevicePath->Type != MESSAGING_DEVICE_PATH) ||
704 (DevicePath->SubType != MSG_SCSI_DP))
705 {
706 return EFI_UNSUPPORTED;
707 }
708
709 ScsiDevicePath = (SCSI_DEVICE_PATH *)DevicePath;
710 Dev = LSI_SCSI_FROM_PASS_THRU (This);
711 if ((ScsiDevicePath->Pun > Dev->MaxTarget) ||
712 (ScsiDevicePath->Lun > Dev->MaxLun))
713 {
714 return EFI_NOT_FOUND;
715 }
716
717 Target = *TargetPointer;
718 ZeroMem (Target, TARGET_MAX_BYTES);
719 CopyMem (Target, &ScsiDevicePath->Pun, sizeof ScsiDevicePath->Pun);
720 *Lun = ScsiDevicePath->Lun;
721
722 return EFI_SUCCESS;
723 }
724
725 EFI_STATUS
726 EFIAPI
727 LsiScsiResetChannel (
728 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This
729 )
730 {
731 return EFI_UNSUPPORTED;
732 }
733
734 EFI_STATUS
735 EFIAPI
736 LsiScsiResetTargetLun (
737 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
738 IN UINT8 *Target,
739 IN UINT64 Lun
740 )
741 {
742 return EFI_UNSUPPORTED;
743 }
744
745 EFI_STATUS
746 EFIAPI
747 LsiScsiGetNextTarget (
748 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
749 IN OUT UINT8 **TargetPointer
750 )
751 {
752 LSI_SCSI_DEV *Dev;
753 UINTN Idx;
754 UINT8 *Target;
755 UINT16 LastTarget;
756
757 //
758 // the TargetPointer input parameter is unnecessarily a pointer-to-pointer
759 //
760 Target = *TargetPointer;
761
762 //
763 // Search for first non-0xFF byte. If not found, return first target.
764 //
765 for (Idx = 0; Idx < TARGET_MAX_BYTES && Target[Idx] == 0xFF; ++Idx) {
766 }
767
768 if (Idx == TARGET_MAX_BYTES) {
769 SetMem (Target, TARGET_MAX_BYTES, 0x00);
770 return EFI_SUCCESS;
771 }
772
773 CopyMem (&LastTarget, Target, sizeof LastTarget);
774
775 //
776 // increment target if valid on input
777 //
778 Dev = LSI_SCSI_FROM_PASS_THRU (This);
779 if (LastTarget > Dev->MaxTarget) {
780 return EFI_INVALID_PARAMETER;
781 }
782
783 if (LastTarget < Dev->MaxTarget) {
784 ++LastTarget;
785 CopyMem (Target, &LastTarget, sizeof LastTarget);
786 return EFI_SUCCESS;
787 }
788
789 return EFI_NOT_FOUND;
790 }
791
792 STATIC
793 VOID
794 EFIAPI
795 LsiScsiExitBoot (
796 IN EFI_EVENT Event,
797 IN VOID *Context
798 )
799 {
800 LSI_SCSI_DEV *Dev;
801
802 Dev = Context;
803 DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context));
804 LsiScsiReset (Dev);
805 }
806
807 //
808 // Probe, start and stop functions of this driver, called by the DXE core for
809 // specific devices.
810 //
811 // The following specifications document these interfaces:
812 // - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol
813 // - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol
814 //
815
816 EFI_STATUS
817 EFIAPI
818 LsiScsiControllerSupported (
819 IN EFI_DRIVER_BINDING_PROTOCOL *This,
820 IN EFI_HANDLE ControllerHandle,
821 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
822 )
823 {
824 EFI_STATUS Status;
825 EFI_PCI_IO_PROTOCOL *PciIo;
826 PCI_TYPE00 Pci;
827
828 Status = gBS->OpenProtocol (
829 ControllerHandle,
830 &gEfiPciIoProtocolGuid,
831 (VOID **)&PciIo,
832 This->DriverBindingHandle,
833 ControllerHandle,
834 EFI_OPEN_PROTOCOL_BY_DRIVER
835 );
836 if (EFI_ERROR (Status)) {
837 return Status;
838 }
839
840 Status = PciIo->Pci.Read (
841 PciIo,
842 EfiPciIoWidthUint32,
843 0,
844 sizeof (Pci) / sizeof (UINT32),
845 &Pci
846 );
847 if (EFI_ERROR (Status)) {
848 goto Done;
849 }
850
851 if ((Pci.Hdr.VendorId == LSI_LOGIC_PCI_VENDOR_ID) &&
852 (Pci.Hdr.DeviceId == LSI_53C895A_PCI_DEVICE_ID))
853 {
854 Status = EFI_SUCCESS;
855 } else {
856 Status = EFI_UNSUPPORTED;
857 }
858
859 Done:
860 gBS->CloseProtocol (
861 ControllerHandle,
862 &gEfiPciIoProtocolGuid,
863 This->DriverBindingHandle,
864 ControllerHandle
865 );
866 return Status;
867 }
868
869 EFI_STATUS
870 EFIAPI
871 LsiScsiControllerStart (
872 IN EFI_DRIVER_BINDING_PROTOCOL *This,
873 IN EFI_HANDLE ControllerHandle,
874 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
875 )
876 {
877 EFI_STATUS Status;
878 LSI_SCSI_DEV *Dev;
879 UINTN Pages;
880 UINTN BytesMapped;
881
882 Dev = AllocateZeroPool (sizeof (*Dev));
883 if (Dev == NULL) {
884 return EFI_OUT_OF_RESOURCES;
885 }
886
887 Dev->Signature = LSI_SCSI_DEV_SIGNATURE;
888
889 STATIC_ASSERT (
890 FixedPcdGet8 (PcdLsiScsiMaxTargetLimit) < 8,
891 "LSI 53C895A supports targets [0..7]"
892 );
893 STATIC_ASSERT (
894 FixedPcdGet8 (PcdLsiScsiMaxLunLimit) < 128,
895 "LSI 53C895A supports LUNs [0..127]"
896 );
897 Dev->MaxTarget = PcdGet8 (PcdLsiScsiMaxTargetLimit);
898 Dev->MaxLun = PcdGet8 (PcdLsiScsiMaxLunLimit);
899 Dev->StallPerPollUsec = PcdGet32 (PcdLsiScsiStallPerPollUsec);
900
901 Status = gBS->OpenProtocol (
902 ControllerHandle,
903 &gEfiPciIoProtocolGuid,
904 (VOID **)&Dev->PciIo,
905 This->DriverBindingHandle,
906 ControllerHandle,
907 EFI_OPEN_PROTOCOL_BY_DRIVER
908 );
909 if (EFI_ERROR (Status)) {
910 goto FreePool;
911 }
912
913 Status = Dev->PciIo->Attributes (
914 Dev->PciIo,
915 EfiPciIoAttributeOperationGet,
916 0,
917 &Dev->OrigPciAttrs
918 );
919 if (EFI_ERROR (Status)) {
920 goto CloseProtocol;
921 }
922
923 //
924 // Enable I/O Space & Bus-Mastering
925 //
926 Status = Dev->PciIo->Attributes (
927 Dev->PciIo,
928 EfiPciIoAttributeOperationEnable,
929 (EFI_PCI_IO_ATTRIBUTE_IO |
930 EFI_PCI_IO_ATTRIBUTE_BUS_MASTER),
931 NULL
932 );
933 if (EFI_ERROR (Status)) {
934 goto CloseProtocol;
935 }
936
937 //
938 // Create buffers for data transfer
939 //
940 Pages = EFI_SIZE_TO_PAGES (sizeof (*Dev->Dma));
941 Status = Dev->PciIo->AllocateBuffer (
942 Dev->PciIo,
943 AllocateAnyPages,
944 EfiBootServicesData,
945 Pages,
946 (VOID **)&Dev->Dma,
947 EFI_PCI_ATTRIBUTE_MEMORY_CACHED
948 );
949 if (EFI_ERROR (Status)) {
950 goto RestoreAttributes;
951 }
952
953 BytesMapped = EFI_PAGES_TO_SIZE (Pages);
954 Status = Dev->PciIo->Map (
955 Dev->PciIo,
956 EfiPciIoOperationBusMasterCommonBuffer,
957 Dev->Dma,
958 &BytesMapped,
959 &Dev->DmaPhysical,
960 &Dev->DmaMapping
961 );
962 if (EFI_ERROR (Status)) {
963 goto FreeBuffer;
964 }
965
966 if (BytesMapped != EFI_PAGES_TO_SIZE (Pages)) {
967 Status = EFI_OUT_OF_RESOURCES;
968 goto Unmap;
969 }
970
971 Status = LsiScsiReset (Dev);
972 if (EFI_ERROR (Status)) {
973 goto Unmap;
974 }
975
976 Status = gBS->CreateEvent (
977 EVT_SIGNAL_EXIT_BOOT_SERVICES,
978 TPL_CALLBACK,
979 &LsiScsiExitBoot,
980 Dev,
981 &Dev->ExitBoot
982 );
983 if (EFI_ERROR (Status)) {
984 goto UninitDev;
985 }
986
987 //
988 // Host adapter channel, doesn't exist
989 //
990 Dev->PassThruMode.AdapterId = MAX_UINT32;
991 Dev->PassThruMode.Attributes =
992 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL |
993 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;
994
995 Dev->PassThru.Mode = &Dev->PassThruMode;
996 Dev->PassThru.PassThru = &LsiScsiPassThru;
997 Dev->PassThru.GetNextTargetLun = &LsiScsiGetNextTargetLun;
998 Dev->PassThru.BuildDevicePath = &LsiScsiBuildDevicePath;
999 Dev->PassThru.GetTargetLun = &LsiScsiGetTargetLun;
1000 Dev->PassThru.ResetChannel = &LsiScsiResetChannel;
1001 Dev->PassThru.ResetTargetLun = &LsiScsiResetTargetLun;
1002 Dev->PassThru.GetNextTarget = &LsiScsiGetNextTarget;
1003
1004 Status = gBS->InstallProtocolInterface (
1005 &ControllerHandle,
1006 &gEfiExtScsiPassThruProtocolGuid,
1007 EFI_NATIVE_INTERFACE,
1008 &Dev->PassThru
1009 );
1010 if (EFI_ERROR (Status)) {
1011 goto CloseExitBoot;
1012 }
1013
1014 return EFI_SUCCESS;
1015
1016 CloseExitBoot:
1017 gBS->CloseEvent (Dev->ExitBoot);
1018
1019 UninitDev:
1020 LsiScsiReset (Dev);
1021
1022 Unmap:
1023 Dev->PciIo->Unmap (
1024 Dev->PciIo,
1025 Dev->DmaMapping
1026 );
1027
1028 FreeBuffer:
1029 Dev->PciIo->FreeBuffer (
1030 Dev->PciIo,
1031 Pages,
1032 Dev->Dma
1033 );
1034
1035 RestoreAttributes:
1036 Dev->PciIo->Attributes (
1037 Dev->PciIo,
1038 EfiPciIoAttributeOperationSet,
1039 Dev->OrigPciAttrs,
1040 NULL
1041 );
1042
1043 CloseProtocol:
1044 gBS->CloseProtocol (
1045 ControllerHandle,
1046 &gEfiPciIoProtocolGuid,
1047 This->DriverBindingHandle,
1048 ControllerHandle
1049 );
1050
1051 FreePool:
1052 FreePool (Dev);
1053
1054 return Status;
1055 }
1056
1057 EFI_STATUS
1058 EFIAPI
1059 LsiScsiControllerStop (
1060 IN EFI_DRIVER_BINDING_PROTOCOL *This,
1061 IN EFI_HANDLE ControllerHandle,
1062 IN UINTN NumberOfChildren,
1063 IN EFI_HANDLE *ChildHandleBuffer
1064 )
1065 {
1066 EFI_STATUS Status;
1067 EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;
1068 LSI_SCSI_DEV *Dev;
1069
1070 Status = gBS->OpenProtocol (
1071 ControllerHandle,
1072 &gEfiExtScsiPassThruProtocolGuid,
1073 (VOID **)&PassThru,
1074 This->DriverBindingHandle,
1075 ControllerHandle,
1076 EFI_OPEN_PROTOCOL_GET_PROTOCOL // Lookup only
1077 );
1078 if (EFI_ERROR (Status)) {
1079 return Status;
1080 }
1081
1082 Dev = LSI_SCSI_FROM_PASS_THRU (PassThru);
1083
1084 Status = gBS->UninstallProtocolInterface (
1085 ControllerHandle,
1086 &gEfiExtScsiPassThruProtocolGuid,
1087 &Dev->PassThru
1088 );
1089 if (EFI_ERROR (Status)) {
1090 return Status;
1091 }
1092
1093 gBS->CloseEvent (Dev->ExitBoot);
1094
1095 LsiScsiReset (Dev);
1096
1097 Dev->PciIo->Unmap (
1098 Dev->PciIo,
1099 Dev->DmaMapping
1100 );
1101
1102 Dev->PciIo->FreeBuffer (
1103 Dev->PciIo,
1104 EFI_SIZE_TO_PAGES (sizeof (*Dev->Dma)),
1105 Dev->Dma
1106 );
1107
1108 Dev->PciIo->Attributes (
1109 Dev->PciIo,
1110 EfiPciIoAttributeOperationSet,
1111 Dev->OrigPciAttrs,
1112 NULL
1113 );
1114
1115 gBS->CloseProtocol (
1116 ControllerHandle,
1117 &gEfiPciIoProtocolGuid,
1118 This->DriverBindingHandle,
1119 ControllerHandle
1120 );
1121
1122 FreePool (Dev);
1123
1124 return Status;
1125 }
1126
1127 //
1128 // The static object that groups the Supported() (ie. probe), Start() and
1129 // Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata
1130 // C, 10.1 EFI Driver Binding Protocol.
1131 //
1132 STATIC
1133 EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {
1134 &LsiScsiControllerSupported,
1135 &LsiScsiControllerStart,
1136 &LsiScsiControllerStop,
1137 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers
1138 NULL, // ImageHandle, to be overwritten by
1139 // EfiLibInstallDriverBindingComponentName2() in LsiScsiEntryPoint()
1140 NULL // DriverBindingHandle, ditto
1141 };
1142
1143 //
1144 // The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and
1145 // EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name
1146 // in English, for display on standard console devices. This is recommended for
1147 // UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's
1148 // Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.
1149 //
1150 // Device type names ("LSI 53C895A SCSI Controller") are not formatted because
1151 // the driver supports only that device type. Therefore the driver name
1152 // suffices for unambiguous identification.
1153 //
1154
1155 STATIC
1156 EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
1157 { "eng;en", L"LSI 53C895A SCSI Controller Driver" },
1158 { NULL, NULL }
1159 };
1160
1161 STATIC
1162 EFI_COMPONENT_NAME_PROTOCOL gComponentName;
1163
1164 EFI_STATUS
1165 EFIAPI
1166 LsiScsiGetDriverName (
1167 IN EFI_COMPONENT_NAME_PROTOCOL *This,
1168 IN CHAR8 *Language,
1169 OUT CHAR16 **DriverName
1170 )
1171 {
1172 return LookupUnicodeString2 (
1173 Language,
1174 This->SupportedLanguages,
1175 mDriverNameTable,
1176 DriverName,
1177 (BOOLEAN)(This == &gComponentName) // Iso639Language
1178 );
1179 }
1180
1181 EFI_STATUS
1182 EFIAPI
1183 LsiScsiGetDeviceName (
1184 IN EFI_COMPONENT_NAME_PROTOCOL *This,
1185 IN EFI_HANDLE DeviceHandle,
1186 IN EFI_HANDLE ChildHandle,
1187 IN CHAR8 *Language,
1188 OUT CHAR16 **ControllerName
1189 )
1190 {
1191 return EFI_UNSUPPORTED;
1192 }
1193
1194 STATIC
1195 EFI_COMPONENT_NAME_PROTOCOL gComponentName = {
1196 &LsiScsiGetDriverName,
1197 &LsiScsiGetDeviceName,
1198 "eng" // SupportedLanguages, ISO 639-2 language codes
1199 };
1200
1201 STATIC
1202 EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {
1203 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)&LsiScsiGetDriverName,
1204 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)&LsiScsiGetDeviceName,
1205 "en" // SupportedLanguages, RFC 4646 language codes
1206 };
1207
1208 //
1209 // Entry point of this driver
1210 //
1211 EFI_STATUS
1212 EFIAPI
1213 LsiScsiEntryPoint (
1214 IN EFI_HANDLE ImageHandle,
1215 IN EFI_SYSTEM_TABLE *SystemTable
1216 )
1217 {
1218 return EfiLibInstallDriverBindingComponentName2 (
1219 ImageHandle,
1220 SystemTable,
1221 &gDriverBinding,
1222 ImageHandle, // The handle to install onto
1223 &gComponentName,
1224 &gComponentName2
1225 );
1226 }