VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/OvmfPkg/LsiScsiDxe/LsiScsi.c@ 94264

Last change on this file since 94264 was 89983, checked in by vboxsync, 4 years ago

Devices/EFI: Merge edk-stable202105 and openssl 1.1.1j and make it build, bugref:4643

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette