]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/PvScsiDxe/PvScsi.c
OvmfPkg: Apply uncrustify changes
[mirror_edk2.git] / OvmfPkg / PvScsiDxe / PvScsi.c
CommitLineData
478c07d4
LA
1/** @file\r
2\r
3 This driver produces Extended SCSI Pass Thru Protocol instances for\r
4 pvscsi devices.\r
5\r
6 Copyright (C) 2020, Oracle and/or its affiliates.\r
7\r
8 SPDX-License-Identifier: BSD-2-Clause-Patent\r
9\r
10**/\r
11\r
a9f9d5cf
LA
12#include <IndustryStandard/Pci.h>\r
13#include <IndustryStandard/PvScsi.h>\r
b654edec 14#include <Library/BaseLib.h>\r
7efce2e5 15#include <Library/BaseMemoryLib.h>\r
e497432c 16#include <Library/MemoryAllocationLib.h>\r
a9f9d5cf 17#include <Library/UefiBootServicesTableLib.h>\r
ed08c571 18#include <Library/UefiLib.h>\r
a9f9d5cf 19#include <Protocol/PciIo.h>\r
b654edec 20#include <Protocol/PciRootBridgeIo.h>\r
478c07d4
LA
21#include <Uefi/UefiSpec.h>\r
22\r
e497432c
LA
23#include "PvScsi.h"\r
24\r
ed08c571
LA
25//\r
26// Higher versions will be used before lower, 0x10-0xffffffef is the version\r
27// range for IHV (Indie Hardware Vendors)\r
28//\r
ac0a286f 29#define PVSCSI_BINDING_VERSION 0x10\r
ed08c571 30\r
7efce2e5
LA
31//\r
32// Ext SCSI Pass Thru utilities\r
33//\r
34\r
c4c15b87
LA
35/**\r
36 Reads a 32-bit value into BAR0 using MMIO\r
37**/\r
38STATIC\r
39EFI_STATUS\r
40PvScsiMmioRead32 (\r
ac0a286f
MK
41 IN CONST PVSCSI_DEV *Dev,\r
42 IN UINT64 Offset,\r
43 OUT UINT32 *Value\r
c4c15b87
LA
44 )\r
45{\r
46 return Dev->PciIo->Mem.Read (\r
47 Dev->PciIo,\r
48 EfiPciIoWidthUint32,\r
49 PCI_BAR_IDX0,\r
50 Offset,\r
51 1, // Count\r
52 Value\r
53 );\r
54}\r
55\r
5269c26e
LA
56/**\r
57 Writes a 32-bit value into BAR0 using MMIO\r
58**/\r
59STATIC\r
60EFI_STATUS\r
61PvScsiMmioWrite32 (\r
ac0a286f
MK
62 IN CONST PVSCSI_DEV *Dev,\r
63 IN UINT64 Offset,\r
64 IN UINT32 Value\r
5269c26e
LA
65 )\r
66{\r
67 return Dev->PciIo->Mem.Write (\r
68 Dev->PciIo,\r
69 EfiPciIoWidthUint32,\r
70 PCI_BAR_IDX0,\r
71 Offset,\r
72 1, // Count\r
73 &Value\r
74 );\r
75}\r
76\r
77/**\r
78 Writes multiple words of data into BAR0 using MMIO\r
79**/\r
80STATIC\r
81EFI_STATUS\r
82PvScsiMmioWrite32Multiple (\r
ac0a286f
MK
83 IN CONST PVSCSI_DEV *Dev,\r
84 IN UINT64 Offset,\r
85 IN UINTN Count,\r
86 IN UINT32 *Words\r
5269c26e
LA
87 )\r
88{\r
89 return Dev->PciIo->Mem.Write (\r
90 Dev->PciIo,\r
91 EfiPciIoWidthFifoUint32,\r
92 PCI_BAR_IDX0,\r
93 Offset,\r
94 Count,\r
95 Words\r
96 );\r
97}\r
98\r
99/**\r
100 Send a PVSCSI command to device.\r
101\r
102 @param[in] Dev The pvscsi host device.\r
103 @param[in] Cmd The command to send to device.\r
104 @param[in] OPTIONAL DescWords An optional command descriptor (If command\r
105 have a descriptor). The descriptor is\r
106 provided as an array of UINT32 words and\r
107 is must be 32-bit aligned.\r
108 @param[in] DescWordsCount The number of words in command descriptor.\r
109 Caller must specify here 0 if DescWords\r
110 is not supplied (It is optional). In that\r
111 case, DescWords is ignored.\r
112\r
113 @return Status codes returned by Dev->PciIo->Mem.Write().\r
114\r
115**/\r
116STATIC\r
117EFI_STATUS\r
118PvScsiWriteCmdDesc (\r
ac0a286f
MK
119 IN CONST PVSCSI_DEV *Dev,\r
120 IN UINT32 Cmd,\r
121 IN UINT32 *DescWords OPTIONAL,\r
122 IN UINTN DescWordsCount\r
5269c26e
LA
123 )\r
124{\r
ac0a286f 125 EFI_STATUS Status;\r
5269c26e
LA
126\r
127 if (DescWordsCount > PVSCSI_MAX_CMD_DATA_WORDS) {\r
128 return EFI_INVALID_PARAMETER;\r
129 }\r
130\r
131 Status = PvScsiMmioWrite32 (Dev, PvScsiRegOffsetCommand, Cmd);\r
132 if (EFI_ERROR (Status)) {\r
133 return Status;\r
134 }\r
135\r
136 if (DescWordsCount > 0) {\r
137 return PvScsiMmioWrite32Multiple (\r
138 Dev,\r
139 PvScsiRegOffsetCommandData,\r
140 DescWordsCount,\r
141 DescWords\r
142 );\r
143 }\r
144\r
145 return EFI_SUCCESS;\r
146}\r
147\r
148STATIC\r
149EFI_STATUS\r
150PvScsiResetAdapter (\r
ac0a286f 151 IN CONST PVSCSI_DEV *Dev\r
5269c26e
LA
152 )\r
153{\r
154 return PvScsiWriteCmdDesc (Dev, PvScsiCmdAdapterReset, NULL, 0);\r
155}\r
156\r
c4c15b87
LA
157/**\r
158 Returns if PVSCSI request ring is full\r
159**/\r
160STATIC\r
161BOOLEAN\r
162PvScsiIsReqRingFull (\r
ac0a286f 163 IN CONST PVSCSI_DEV *Dev\r
c4c15b87
LA
164 )\r
165{\r
ac0a286f
MK
166 PVSCSI_RINGS_STATE *RingsState;\r
167 UINT32 ReqNumEntries;\r
c4c15b87 168\r
ac0a286f 169 RingsState = Dev->RingDesc.RingState;\r
c4c15b87
LA
170 ReqNumEntries = 1U << RingsState->ReqNumEntriesLog2;\r
171 return (RingsState->ReqProdIdx - RingsState->CmpConsIdx) >= ReqNumEntries;\r
172}\r
173\r
174/**\r
175 Returns pointer to current request descriptor to produce\r
176**/\r
177STATIC\r
178PVSCSI_RING_REQ_DESC *\r
179PvScsiGetCurrentRequest (\r
ac0a286f 180 IN CONST PVSCSI_DEV *Dev\r
c4c15b87
LA
181 )\r
182{\r
ac0a286f
MK
183 PVSCSI_RINGS_STATE *RingState;\r
184 UINT32 ReqNumEntries;\r
c4c15b87 185\r
ac0a286f 186 RingState = Dev->RingDesc.RingState;\r
c4c15b87
LA
187 ReqNumEntries = 1U << RingState->ReqNumEntriesLog2;\r
188 return Dev->RingDesc.RingReqs +\r
189 (RingState->ReqProdIdx & (ReqNumEntries - 1));\r
190}\r
191\r
192/**\r
193 Returns pointer to current completion descriptor to consume\r
194**/\r
195STATIC\r
196PVSCSI_RING_CMP_DESC *\r
197PvScsiGetCurrentResponse (\r
ac0a286f 198 IN CONST PVSCSI_DEV *Dev\r
c4c15b87
LA
199 )\r
200{\r
ac0a286f
MK
201 PVSCSI_RINGS_STATE *RingState;\r
202 UINT32 CmpNumEntries;\r
c4c15b87 203\r
ac0a286f 204 RingState = Dev->RingDesc.RingState;\r
c4c15b87
LA
205 CmpNumEntries = 1U << RingState->CmpNumEntriesLog2;\r
206 return Dev->RingDesc.RingCmps +\r
207 (RingState->CmpConsIdx & (CmpNumEntries - 1));\r
208}\r
209\r
210/**\r
211 Wait for device to signal completion of submitted requests\r
212**/\r
213STATIC\r
214EFI_STATUS\r
215PvScsiWaitForRequestCompletion (\r
ac0a286f 216 IN CONST PVSCSI_DEV *Dev\r
c4c15b87
LA
217 )\r
218{\r
ac0a286f
MK
219 EFI_STATUS Status;\r
220 UINT32 IntrStatus;\r
c4c15b87
LA
221\r
222 //\r
223 // Note: We don't yet support Timeout according to\r
224 // EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET.Timeout.\r
225 //\r
226 // This is consistent with some other Scsi PassThru drivers\r
227 // such as VirtioScsi.\r
228 //\r
ac0a286f 229 for ( ; ;) {\r
c4c15b87
LA
230 Status = PvScsiMmioRead32 (Dev, PvScsiRegOffsetIntrStatus, &IntrStatus);\r
231 if (EFI_ERROR (Status)) {\r
232 return Status;\r
233 }\r
234\r
235 //\r
236 // PVSCSI_INTR_CMPL_MASK is set if device completed submitted requests\r
237 //\r
238 if ((IntrStatus & PVSCSI_INTR_CMPL_MASK) != 0) {\r
239 break;\r
240 }\r
241\r
242 gBS->Stall (Dev->WaitForCmpStallInUsecs);\r
243 }\r
244\r
245 //\r
246 // Acknowledge PVSCSI_INTR_CMPL_MASK in device interrupt-status register\r
247 //\r
248 return PvScsiMmioWrite32 (\r
249 Dev,\r
250 PvScsiRegOffsetIntrStatus,\r
251 PVSCSI_INTR_CMPL_MASK\r
252 );\r
253}\r
254\r
255/**\r
256 Create a fake host adapter error\r
257**/\r
258STATIC\r
259EFI_STATUS\r
260ReportHostAdapterError (\r
ac0a286f 261 OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet\r
c4c15b87
LA
262 )\r
263{\r
ac0a286f 264 Packet->InTransferLength = 0;\r
c4c15b87 265 Packet->OutTransferLength = 0;\r
ac0a286f 266 Packet->SenseDataLength = 0;\r
c4c15b87 267 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;\r
ac0a286f 268 Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;\r
c4c15b87
LA
269 return EFI_DEVICE_ERROR;\r
270}\r
271\r
272/**\r
273 Create a fake host adapter overrun error\r
274**/\r
275STATIC\r
276EFI_STATUS\r
277ReportHostAdapterOverrunError (\r
ac0a286f 278 OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet\r
c4c15b87
LA
279 )\r
280{\r
ac0a286f 281 Packet->SenseDataLength = 0;\r
c4c15b87 282 Packet->HostAdapterStatus =\r
ac0a286f 283 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;\r
c4c15b87
LA
284 Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;\r
285 return EFI_BAD_BUFFER_SIZE;\r
286}\r
287\r
288/**\r
289 Populate a PVSCSI request descriptor from the Extended SCSI Pass Thru\r
290 Protocol packet.\r
291**/\r
292STATIC\r
293EFI_STATUS\r
294PopulateRequest (\r
ac0a286f
MK
295 IN CONST PVSCSI_DEV *Dev,\r
296 IN UINT8 *Target,\r
297 IN UINT64 Lun,\r
298 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,\r
299 OUT PVSCSI_RING_REQ_DESC *Request\r
c4c15b87
LA
300 )\r
301{\r
ac0a286f 302 UINT8 TargetValue;\r
c4c15b87
LA
303\r
304 //\r
305 // We only use first byte of target identifer\r
306 //\r
307 TargetValue = *Target;\r
308\r
309 //\r
310 // Check for unsupported requests\r
311 //\r
312 if (\r
313 //\r
314 // Bidirectional transfer was requested\r
315 //\r
ac0a286f 316 ((Packet->InTransferLength > 0) && (Packet->OutTransferLength > 0)) ||\r
c4c15b87
LA
317 (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL) ||\r
318 //\r
319 // Command Descriptor Block bigger than this constant should be considered\r
320 // out-of-band. We currently don't support these CDBs.\r
321 //\r
322 (Packet->CdbLength > PVSCSI_CDB_MAX_SIZE)\r
ac0a286f
MK
323 )\r
324 {\r
c4c15b87
LA
325 //\r
326 // This error code doesn't require updates to the Packet output fields\r
327 //\r
328 return EFI_UNSUPPORTED;\r
329 }\r
330\r
331 //\r
332 // Check for invalid parameters\r
333 //\r
334 if (\r
335 //\r
336 // Addressed invalid device\r
337 //\r
338 (TargetValue > Dev->MaxTarget) || (Lun > Dev->MaxLun) ||\r
339 //\r
340 // Invalid direction (there doesn't seem to be a macro for the "no data\r
341 // transferred" "direction", eg. for TEST UNIT READY)\r
342 //\r
343 (Packet->DataDirection > EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL) ||\r
344 //\r
345 // Trying to receive, but destination pointer is NULL, or contradicting\r
346 // transfer direction\r
347 //\r
348 ((Packet->InTransferLength > 0) &&\r
349 ((Packet->InDataBuffer == NULL) ||\r
350 (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_WRITE)\r
ac0a286f
MK
351 )\r
352 ) ||\r
c4c15b87
LA
353 //\r
354 // Trying to send, but source pointer is NULL, or contradicting\r
355 // transfer direction\r
356 //\r
357 ((Packet->OutTransferLength > 0) &&\r
358 ((Packet->OutDataBuffer == NULL) ||\r
359 (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ)\r
c4c15b87 360 )\r
ac0a286f
MK
361 )\r
362 )\r
363 {\r
c4c15b87
LA
364 //\r
365 // This error code doesn't require updates to the Packet output fields\r
366 //\r
367 return EFI_INVALID_PARAMETER;\r
368 }\r
369\r
370 //\r
371 // Check for input/output buffer too large for DMA communication buffer\r
372 //\r
373 if (Packet->InTransferLength > sizeof (Dev->DmaBuf->Data)) {\r
374 Packet->InTransferLength = sizeof (Dev->DmaBuf->Data);\r
375 return ReportHostAdapterOverrunError (Packet);\r
376 }\r
ac0a286f 377\r
c4c15b87
LA
378 if (Packet->OutTransferLength > sizeof (Dev->DmaBuf->Data)) {\r
379 Packet->OutTransferLength = sizeof (Dev->DmaBuf->Data);\r
380 return ReportHostAdapterOverrunError (Packet);\r
381 }\r
382\r
383 //\r
384 // Encode PVSCSI request\r
385 //\r
386 ZeroMem (Request, sizeof (*Request));\r
387\r
ac0a286f 388 Request->Bus = 0;\r
c4c15b87
LA
389 Request->Target = TargetValue;\r
390 //\r
391 // This cast is safe as PVSCSI_DEV.MaxLun is defined as UINT8\r
392 //\r
ac0a286f 393 Request->Lun[1] = (UINT8)Lun;\r
c4c15b87
LA
394 Request->SenseLen = Packet->SenseDataLength;\r
395 //\r
396 // DMA communication buffer SenseData overflow is not possible\r
397 // due to Packet->SenseDataLength defined as UINT8\r
398 //\r
399 Request->SenseAddr = PVSCSI_DMA_BUF_DEV_ADDR (Dev, SenseData);\r
ac0a286f 400 Request->CdbLen = Packet->CdbLength;\r
c4c15b87
LA
401 CopyMem (Request->Cdb, Packet->Cdb, Packet->CdbLength);\r
402 Request->VcpuHint = 0;\r
ac0a286f 403 Request->Tag = PVSCSI_SIMPLE_QUEUE_TAG;\r
c4c15b87 404 if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
ac0a286f 405 Request->Flags = PVSCSI_FLAG_CMD_DIR_TOHOST;\r
c4c15b87
LA
406 Request->DataLen = Packet->InTransferLength;\r
407 } else {\r
ac0a286f 408 Request->Flags = PVSCSI_FLAG_CMD_DIR_TODEVICE;\r
c4c15b87
LA
409 Request->DataLen = Packet->OutTransferLength;\r
410 CopyMem (\r
411 Dev->DmaBuf->Data,\r
412 Packet->OutDataBuffer,\r
413 Packet->OutTransferLength\r
414 );\r
415 }\r
ac0a286f 416\r
c4c15b87
LA
417 Request->DataAddr = PVSCSI_DMA_BUF_DEV_ADDR (Dev, Data);\r
418\r
419 return EFI_SUCCESS;\r
420}\r
421\r
422/**\r
423 Handle the PVSCSI device response:\r
424 - Copy returned data from DMA communication buffer.\r
425 - Update fields in Extended SCSI Pass Thru Protocol packet as required.\r
426 - Translate response code to EFI status code and host adapter status.\r
427**/\r
428STATIC\r
429EFI_STATUS\r
430HandleResponse (\r
ac0a286f
MK
431 IN PVSCSI_DEV *Dev,\r
432 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,\r
433 IN CONST PVSCSI_RING_CMP_DESC *Response\r
c4c15b87
LA
434 )\r
435{\r
436 //\r
437 // Fix SenseDataLength to amount of data returned\r
438 //\r
439 if (Packet->SenseDataLength > Response->SenseLen) {\r
440 Packet->SenseDataLength = (UINT8)Response->SenseLen;\r
441 }\r
ac0a286f 442\r
c4c15b87
LA
443 //\r
444 // Copy sense data from DMA communication buffer\r
445 //\r
446 CopyMem (\r
447 Packet->SenseData,\r
448 Dev->DmaBuf->SenseData,\r
449 Packet->SenseDataLength\r
450 );\r
451\r
452 //\r
453 // Copy device output from DMA communication buffer\r
454 //\r
455 if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
456 CopyMem (Packet->InDataBuffer, Dev->DmaBuf->Data, Packet->InTransferLength);\r
457 }\r
458\r
459 //\r
460 // Report target status\r
98936dc4
LA
461 // (Strangely, PVSCSI interface defines Response->ScsiStatus as UINT16.\r
462 // But it should de-facto always have a value that fits UINT8. To avoid\r
463 // unexpected behavior, verify value is in UINT8 bounds before casting)\r
c4c15b87 464 //\r
98936dc4
LA
465 ASSERT (Response->ScsiStatus <= MAX_UINT8);\r
466 Packet->TargetStatus = (UINT8)Response->ScsiStatus;\r
c4c15b87
LA
467\r
468 //\r
469 // Host adapter status and function return value depend on\r
470 // device response's host status\r
471 //\r
472 switch (Response->HostStatus) {\r
473 case PvScsiBtStatSuccess:\r
474 case PvScsiBtStatLinkedCommandCompleted:\r
475 case PvScsiBtStatLinkedCommandCompletedWithFlag:\r
476 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;\r
477 return EFI_SUCCESS;\r
478\r
479 case PvScsiBtStatDataUnderrun:\r
480 //\r
481 // Report transferred amount in underrun\r
482 //\r
483 if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
484 Packet->InTransferLength = (UINT32)Response->DataLen;\r
485 } else {\r
486 Packet->OutTransferLength = (UINT32)Response->DataLen;\r
487 }\r
ac0a286f 488\r
c4c15b87 489 Packet->HostAdapterStatus =\r
ac0a286f 490 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;\r
c4c15b87
LA
491 return EFI_SUCCESS;\r
492\r
493 case PvScsiBtStatDatarun:\r
494 Packet->HostAdapterStatus =\r
ac0a286f 495 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;\r
c4c15b87
LA
496 return EFI_SUCCESS;\r
497\r
498 case PvScsiBtStatSelTimeout:\r
499 Packet->HostAdapterStatus =\r
ac0a286f 500 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT;\r
c4c15b87
LA
501 return EFI_TIMEOUT;\r
502\r
503 case PvScsiBtStatBusFree:\r
504 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE;\r
505 break;\r
506\r
507 case PvScsiBtStatInvPhase:\r
508 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR;\r
509 break;\r
510\r
511 case PvScsiBtStatSensFailed:\r
512 Packet->HostAdapterStatus =\r
ac0a286f 513 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED;\r
c4c15b87
LA
514 break;\r
515\r
516 case PvScsiBtStatTagReject:\r
517 case PvScsiBtStatBadMsg:\r
518 Packet->HostAdapterStatus =\r
ac0a286f 519 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT;\r
c4c15b87
LA
520 break;\r
521\r
522 case PvScsiBtStatBusReset:\r
523 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET;\r
524 break;\r
525\r
526 case PvScsiBtStatHaTimeout:\r
527 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT;\r
528 return EFI_TIMEOUT;\r
529\r
530 case PvScsiBtStatScsiParity:\r
531 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR;\r
532 break;\r
533\r
534 default:\r
535 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;\r
536 break;\r
537 }\r
538\r
539 return EFI_DEVICE_ERROR;\r
540}\r
541\r
7efce2e5
LA
542/**\r
543 Check if Target argument to EXT_SCSI_PASS_THRU.GetNextTarget() and\r
544 EXT_SCSI_PASS_THRU.GetNextTargetLun() is initialized\r
545**/\r
546STATIC\r
547BOOLEAN\r
548IsTargetInitialized (\r
ac0a286f 549 IN UINT8 *Target\r
7efce2e5
LA
550 )\r
551{\r
ac0a286f 552 UINTN Idx;\r
7efce2e5
LA
553\r
554 for (Idx = 0; Idx < TARGET_MAX_BYTES; ++Idx) {\r
555 if (Target[Idx] != 0xFF) {\r
556 return TRUE;\r
557 }\r
558 }\r
ac0a286f 559\r
7efce2e5
LA
560 return FALSE;\r
561}\r
562\r
e497432c
LA
563//\r
564// Ext SCSI Pass Thru\r
565//\r
566\r
567STATIC\r
568EFI_STATUS\r
569EFIAPI\r
570PvScsiPassThru (\r
ac0a286f
MK
571 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
572 IN UINT8 *Target,\r
573 IN UINT64 Lun,\r
574 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,\r
575 IN EFI_EVENT Event OPTIONAL\r
e497432c
LA
576 )\r
577{\r
c4c15b87
LA
578 PVSCSI_DEV *Dev;\r
579 EFI_STATUS Status;\r
ac0a286f
MK
580 PVSCSI_RING_REQ_DESC *Request;\r
581 PVSCSI_RING_CMP_DESC *Response;\r
c4c15b87
LA
582\r
583 Dev = PVSCSI_FROM_PASS_THRU (This);\r
584\r
585 if (PvScsiIsReqRingFull (Dev)) {\r
586 return EFI_NOT_READY;\r
587 }\r
588\r
589 Request = PvScsiGetCurrentRequest (Dev);\r
590\r
591 Status = PopulateRequest (Dev, Target, Lun, Packet, Request);\r
592 if (EFI_ERROR (Status)) {\r
593 return Status;\r
594 }\r
595\r
596 //\r
597 // Writes to Request must be globally visible before making request\r
598 // available to device\r
599 //\r
600 MemoryFence ();\r
601 Dev->RingDesc.RingState->ReqProdIdx++;\r
602\r
603 Status = PvScsiMmioWrite32 (Dev, PvScsiRegOffsetKickRwIo, 0);\r
604 if (EFI_ERROR (Status)) {\r
605 //\r
606 // If kicking the host fails, we must fake a host adapter error.\r
607 // EFI_NOT_READY would save us the effort, but it would also suggest that\r
608 // the caller retry.\r
609 //\r
610 return ReportHostAdapterError (Packet);\r
611 }\r
612\r
613 Status = PvScsiWaitForRequestCompletion (Dev);\r
614 if (EFI_ERROR (Status)) {\r
615 //\r
616 // If waiting for request completion fails, we must fake a host adapter\r
617 // error. EFI_NOT_READY would save us the effort, but it would also suggest\r
618 // that the caller retry.\r
619 //\r
620 return ReportHostAdapterError (Packet);\r
621 }\r
622\r
623 Response = PvScsiGetCurrentResponse (Dev);\r
ac0a286f 624 Status = HandleResponse (Dev, Packet, Response);\r
c4c15b87
LA
625\r
626 //\r
627 // Reads from response must complete before releasing completion entry\r
628 // to device\r
629 //\r
630 MemoryFence ();\r
631 Dev->RingDesc.RingState->CmpConsIdx++;\r
632\r
633 return Status;\r
e497432c
LA
634}\r
635\r
636STATIC\r
637EFI_STATUS\r
638EFIAPI\r
639PvScsiGetNextTargetLun (\r
ac0a286f
MK
640 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
641 IN OUT UINT8 **Target,\r
642 IN OUT UINT64 *Lun\r
e497432c
LA
643 )\r
644{\r
ac0a286f
MK
645 UINT8 *TargetPtr;\r
646 UINT8 LastTarget;\r
647 PVSCSI_DEV *Dev;\r
7efce2e5
LA
648\r
649 if (Target == NULL) {\r
650 return EFI_INVALID_PARAMETER;\r
651 }\r
652\r
653 //\r
654 // The Target input parameter is unnecessarily a pointer-to-pointer\r
655 //\r
656 TargetPtr = *Target;\r
657\r
658 //\r
659 // If target not initialized, return first target & LUN\r
660 //\r
661 if (!IsTargetInitialized (TargetPtr)) {\r
662 ZeroMem (TargetPtr, TARGET_MAX_BYTES);\r
663 *Lun = 0;\r
664 return EFI_SUCCESS;\r
665 }\r
666\r
667 //\r
668 // We only use first byte of target identifer\r
669 //\r
670 LastTarget = *TargetPtr;\r
671\r
672 //\r
673 // Increment (target, LUN) pair if valid on input\r
674 //\r
675 Dev = PVSCSI_FROM_PASS_THRU (This);\r
ac0a286f 676 if ((LastTarget > Dev->MaxTarget) || (*Lun > Dev->MaxLun)) {\r
7efce2e5
LA
677 return EFI_INVALID_PARAMETER;\r
678 }\r
679\r
680 if (*Lun < Dev->MaxLun) {\r
681 ++*Lun;\r
682 return EFI_SUCCESS;\r
683 }\r
684\r
685 if (LastTarget < Dev->MaxTarget) {\r
686 *Lun = 0;\r
687 ++LastTarget;\r
688 *TargetPtr = LastTarget;\r
689 return EFI_SUCCESS;\r
690 }\r
691\r
692 return EFI_NOT_FOUND;\r
e497432c
LA
693}\r
694\r
695STATIC\r
696EFI_STATUS\r
697EFIAPI\r
698PvScsiBuildDevicePath (\r
ac0a286f
MK
699 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
700 IN UINT8 *Target,\r
701 IN UINT64 Lun,\r
702 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath\r
e497432c
LA
703 )\r
704{\r
9c2d8281
LA
705 UINT8 TargetValue;\r
706 PVSCSI_DEV *Dev;\r
707 SCSI_DEVICE_PATH *ScsiDevicePath;\r
708\r
709 if (DevicePath == NULL) {\r
710 return EFI_INVALID_PARAMETER;\r
711 }\r
712\r
713 //\r
714 // We only use first byte of target identifer\r
715 //\r
716 TargetValue = *Target;\r
717\r
718 Dev = PVSCSI_FROM_PASS_THRU (This);\r
ac0a286f 719 if ((TargetValue > Dev->MaxTarget) || (Lun > Dev->MaxLun)) {\r
9c2d8281
LA
720 return EFI_NOT_FOUND;\r
721 }\r
722\r
723 ScsiDevicePath = AllocatePool (sizeof (*ScsiDevicePath));\r
724 if (ScsiDevicePath == NULL) {\r
725 return EFI_OUT_OF_RESOURCES;\r
726 }\r
727\r
728 ScsiDevicePath->Header.Type = MESSAGING_DEVICE_PATH;\r
729 ScsiDevicePath->Header.SubType = MSG_SCSI_DP;\r
730 ScsiDevicePath->Header.Length[0] = (UINT8)sizeof (*ScsiDevicePath);\r
731 ScsiDevicePath->Header.Length[1] = (UINT8)(sizeof (*ScsiDevicePath) >> 8);\r
732 ScsiDevicePath->Pun = TargetValue;\r
733 ScsiDevicePath->Lun = (UINT16)Lun;\r
734\r
735 *DevicePath = &ScsiDevicePath->Header;\r
736 return EFI_SUCCESS;\r
e497432c
LA
737}\r
738\r
739STATIC\r
740EFI_STATUS\r
741EFIAPI\r
742PvScsiGetTargetLun (\r
ac0a286f
MK
743 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
744 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
745 OUT UINT8 **Target,\r
746 OUT UINT64 *Lun\r
e497432c
LA
747 )\r
748{\r
ac0a286f
MK
749 SCSI_DEVICE_PATH *ScsiDevicePath;\r
750 PVSCSI_DEV *Dev;\r
9c2d8281 751\r
ac0a286f 752 if ((DevicePath == NULL) || (Target == NULL) || (*Target == NULL) || (Lun == NULL)) {\r
9c2d8281
LA
753 return EFI_INVALID_PARAMETER;\r
754 }\r
755\r
ac0a286f
MK
756 if ((DevicePath->Type != MESSAGING_DEVICE_PATH) ||\r
757 (DevicePath->SubType != MSG_SCSI_DP))\r
758 {\r
9c2d8281
LA
759 return EFI_UNSUPPORTED;\r
760 }\r
761\r
762 ScsiDevicePath = (SCSI_DEVICE_PATH *)DevicePath;\r
ac0a286f
MK
763 Dev = PVSCSI_FROM_PASS_THRU (This);\r
764 if ((ScsiDevicePath->Pun > Dev->MaxTarget) ||\r
765 (ScsiDevicePath->Lun > Dev->MaxLun))\r
766 {\r
9c2d8281
LA
767 return EFI_NOT_FOUND;\r
768 }\r
769\r
770 //\r
771 // We only use first byte of target identifer\r
772 //\r
773 **Target = (UINT8)ScsiDevicePath->Pun;\r
774 ZeroMem (*Target + 1, TARGET_MAX_BYTES - 1);\r
775 *Lun = ScsiDevicePath->Lun;\r
776\r
777 return EFI_SUCCESS;\r
e497432c
LA
778}\r
779\r
780STATIC\r
781EFI_STATUS\r
782EFIAPI\r
783PvScsiResetChannel (\r
ac0a286f 784 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This\r
e497432c
LA
785 )\r
786{\r
787 return EFI_UNSUPPORTED;\r
788}\r
789\r
790STATIC\r
791EFI_STATUS\r
792EFIAPI\r
793PvScsiResetTargetLun (\r
ac0a286f
MK
794 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
795 IN UINT8 *Target,\r
796 IN UINT64 Lun\r
e497432c
LA
797 )\r
798{\r
799 return EFI_UNSUPPORTED;\r
800}\r
801\r
802STATIC\r
803EFI_STATUS\r
804EFIAPI\r
805PvScsiGetNextTarget (\r
ac0a286f
MK
806 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
807 IN OUT UINT8 **Target\r
e497432c
LA
808 )\r
809{\r
ac0a286f
MK
810 UINT8 *TargetPtr;\r
811 UINT8 LastTarget;\r
812 PVSCSI_DEV *Dev;\r
7efce2e5
LA
813\r
814 if (Target == NULL) {\r
815 return EFI_INVALID_PARAMETER;\r
816 }\r
817\r
818 //\r
819 // The Target input parameter is unnecessarily a pointer-to-pointer\r
820 //\r
821 TargetPtr = *Target;\r
822\r
823 //\r
824 // If target not initialized, return first target\r
825 //\r
826 if (!IsTargetInitialized (TargetPtr)) {\r
827 ZeroMem (TargetPtr, TARGET_MAX_BYTES);\r
828 return EFI_SUCCESS;\r
829 }\r
830\r
831 //\r
832 // We only use first byte of target identifer\r
833 //\r
834 LastTarget = *TargetPtr;\r
835\r
836 //\r
837 // Increment target if valid on input\r
838 //\r
839 Dev = PVSCSI_FROM_PASS_THRU (This);\r
840 if (LastTarget > Dev->MaxTarget) {\r
841 return EFI_INVALID_PARAMETER;\r
842 }\r
843\r
844 if (LastTarget < Dev->MaxTarget) {\r
845 ++LastTarget;\r
846 *TargetPtr = LastTarget;\r
847 return EFI_SUCCESS;\r
848 }\r
849\r
850 return EFI_NOT_FOUND;\r
e497432c
LA
851}\r
852\r
45098e8a
LA
853STATIC\r
854EFI_STATUS\r
855PvScsiSetPciAttributes (\r
ac0a286f 856 IN OUT PVSCSI_DEV *Dev\r
45098e8a
LA
857 )\r
858{\r
ac0a286f 859 EFI_STATUS Status;\r
45098e8a
LA
860\r
861 //\r
862 // Backup original PCI Attributes\r
863 //\r
864 Status = Dev->PciIo->Attributes (\r
865 Dev->PciIo,\r
866 EfiPciIoAttributeOperationGet,\r
867 0,\r
868 &Dev->OriginalPciAttributes\r
869 );\r
870 if (EFI_ERROR (Status)) {\r
871 return Status;\r
872 }\r
873\r
874 //\r
6672b3cf 875 // Enable MMIO-Space & Bus-Mastering\r
45098e8a 876 //\r
6672b3cf
LA
877 Status = Dev->PciIo->Attributes (\r
878 Dev->PciIo,\r
879 EfiPciIoAttributeOperationEnable,\r
880 (EFI_PCI_IO_ATTRIBUTE_MEMORY |\r
881 EFI_PCI_IO_ATTRIBUTE_BUS_MASTER),\r
882 NULL\r
883 );\r
884 if (EFI_ERROR (Status)) {\r
885 return Status;\r
886 }\r
45098e8a 887\r
f34c7645
LA
888 //\r
889 // Signal device supports 64-bit DMA addresses\r
890 //\r
891 Status = Dev->PciIo->Attributes (\r
892 Dev->PciIo,\r
893 EfiPciIoAttributeOperationEnable,\r
894 EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE,\r
895 NULL\r
896 );\r
897 if (EFI_ERROR (Status)) {\r
898 //\r
899 // Warn user that device will only be using 32-bit DMA addresses.\r
900 //\r
901 // Note that this does not prevent the device/driver from working\r
902 // and therefore we only warn and continue as usual.\r
903 //\r
904 DEBUG ((\r
905 DEBUG_WARN,\r
906 "%a: failed to enable 64-bit DMA addresses\n",\r
907 __FUNCTION__\r
908 ));\r
909 }\r
910\r
45098e8a
LA
911 return EFI_SUCCESS;\r
912}\r
913\r
914STATIC\r
915VOID\r
916PvScsiRestorePciAttributes (\r
ac0a286f 917 IN PVSCSI_DEV *Dev\r
45098e8a
LA
918 )\r
919{\r
920 Dev->PciIo->Attributes (\r
921 Dev->PciIo,\r
922 EfiPciIoAttributeOperationSet,\r
923 Dev->OriginalPciAttributes,\r
924 NULL\r
925 );\r
926}\r
927\r
b654edec
LA
928STATIC\r
929EFI_STATUS\r
930PvScsiAllocateSharedPages (\r
ac0a286f
MK
931 IN PVSCSI_DEV *Dev,\r
932 IN UINTN Pages,\r
933 OUT VOID **HostAddress,\r
934 OUT PVSCSI_DMA_DESC *DmaDesc\r
b654edec
LA
935 )\r
936{\r
ac0a286f
MK
937 EFI_STATUS Status;\r
938 UINTN NumberOfBytes;\r
b654edec
LA
939\r
940 Status = Dev->PciIo->AllocateBuffer (\r
941 Dev->PciIo,\r
942 AllocateAnyPages,\r
943 EfiBootServicesData,\r
944 Pages,\r
945 HostAddress,\r
946 EFI_PCI_ATTRIBUTE_MEMORY_CACHED\r
947 );\r
948 if (EFI_ERROR (Status)) {\r
949 return Status;\r
950 }\r
951\r
952 NumberOfBytes = EFI_PAGES_TO_SIZE (Pages);\r
ac0a286f
MK
953 Status = Dev->PciIo->Map (\r
954 Dev->PciIo,\r
955 EfiPciIoOperationBusMasterCommonBuffer,\r
956 *HostAddress,\r
957 &NumberOfBytes,\r
958 &DmaDesc->DeviceAddress,\r
959 &DmaDesc->Mapping\r
960 );\r
b654edec
LA
961 if (EFI_ERROR (Status)) {\r
962 goto FreeBuffer;\r
963 }\r
964\r
965 if (NumberOfBytes != EFI_PAGES_TO_SIZE (Pages)) {\r
966 Status = EFI_OUT_OF_RESOURCES;\r
967 goto Unmap;\r
968 }\r
969\r
970 return EFI_SUCCESS;\r
971\r
972Unmap:\r
973 Dev->PciIo->Unmap (Dev->PciIo, DmaDesc->Mapping);\r
974\r
975FreeBuffer:\r
976 Dev->PciIo->FreeBuffer (Dev->PciIo, Pages, *HostAddress);\r
977\r
978 return Status;\r
979}\r
980\r
981STATIC\r
982VOID\r
983PvScsiFreeSharedPages (\r
ac0a286f
MK
984 IN PVSCSI_DEV *Dev,\r
985 IN UINTN Pages,\r
986 IN VOID *HostAddress,\r
987 IN PVSCSI_DMA_DESC *DmaDesc\r
b654edec
LA
988 )\r
989{\r
990 Dev->PciIo->Unmap (Dev->PciIo, DmaDesc->Mapping);\r
991 Dev->PciIo->FreeBuffer (Dev->PciIo, Pages, HostAddress);\r
992}\r
993\r
994STATIC\r
995EFI_STATUS\r
996PvScsiInitRings (\r
ac0a286f 997 IN OUT PVSCSI_DEV *Dev\r
b654edec
LA
998 )\r
999{\r
ac0a286f 1000 EFI_STATUS Status;\r
b654edec
LA
1001\r
1002 Status = PvScsiAllocateSharedPages (\r
1003 Dev,\r
1004 1,\r
1005 (VOID **)&Dev->RingDesc.RingState,\r
1006 &Dev->RingDesc.RingStateDmaDesc\r
1007 );\r
1008 if (EFI_ERROR (Status)) {\r
1009 return Status;\r
1010 }\r
ac0a286f 1011\r
b654edec
LA
1012 ZeroMem (Dev->RingDesc.RingState, EFI_PAGE_SIZE);\r
1013\r
1014 Status = PvScsiAllocateSharedPages (\r
1015 Dev,\r
1016 1,\r
1017 (VOID **)&Dev->RingDesc.RingReqs,\r
1018 &Dev->RingDesc.RingReqsDmaDesc\r
1019 );\r
1020 if (EFI_ERROR (Status)) {\r
1021 goto FreeRingState;\r
1022 }\r
ac0a286f 1023\r
b654edec
LA
1024 ZeroMem (Dev->RingDesc.RingReqs, EFI_PAGE_SIZE);\r
1025\r
1026 Status = PvScsiAllocateSharedPages (\r
1027 Dev,\r
1028 1,\r
1029 (VOID **)&Dev->RingDesc.RingCmps,\r
1030 &Dev->RingDesc.RingCmpsDmaDesc\r
1031 );\r
1032 if (EFI_ERROR (Status)) {\r
1033 goto FreeRingReqs;\r
1034 }\r
ac0a286f 1035\r
b654edec
LA
1036 ZeroMem (Dev->RingDesc.RingCmps, EFI_PAGE_SIZE);\r
1037\r
b654edec
LA
1038 return EFI_SUCCESS;\r
1039\r
b654edec
LA
1040FreeRingReqs:\r
1041 PvScsiFreeSharedPages (\r
1042 Dev,\r
1043 1,\r
1044 Dev->RingDesc.RingReqs,\r
1045 &Dev->RingDesc.RingReqsDmaDesc\r
1046 );\r
1047\r
1048FreeRingState:\r
1049 PvScsiFreeSharedPages (\r
1050 Dev,\r
1051 1,\r
1052 Dev->RingDesc.RingState,\r
1053 &Dev->RingDesc.RingStateDmaDesc\r
1054 );\r
1055\r
1056 return Status;\r
1057}\r
1058\r
1059STATIC\r
1060VOID\r
1061PvScsiFreeRings (\r
ac0a286f 1062 IN OUT PVSCSI_DEV *Dev\r
b654edec
LA
1063 )\r
1064{\r
1065 PvScsiFreeSharedPages (\r
1066 Dev,\r
1067 1,\r
1068 Dev->RingDesc.RingCmps,\r
1069 &Dev->RingDesc.RingCmpsDmaDesc\r
1070 );\r
1071\r
1072 PvScsiFreeSharedPages (\r
1073 Dev,\r
1074 1,\r
1075 Dev->RingDesc.RingReqs,\r
1076 &Dev->RingDesc.RingReqsDmaDesc\r
1077 );\r
1078\r
1079 PvScsiFreeSharedPages (\r
1080 Dev,\r
1081 1,\r
1082 Dev->RingDesc.RingState,\r
1083 &Dev->RingDesc.RingStateDmaDesc\r
1084 );\r
1085}\r
1086\r
e210fc13
LA
1087STATIC\r
1088EFI_STATUS\r
1089PvScsiSetupRings (\r
ac0a286f 1090 IN OUT PVSCSI_DEV *Dev\r
e210fc13
LA
1091 )\r
1092{\r
1093 union {\r
ac0a286f
MK
1094 PVSCSI_CMD_DESC_SETUP_RINGS Cmd;\r
1095 UINT32 Uint32;\r
e210fc13 1096 } AlignedCmd;\r
ac0a286f 1097 PVSCSI_CMD_DESC_SETUP_RINGS *Cmd;\r
e210fc13
LA
1098\r
1099 Cmd = &AlignedCmd.Cmd;\r
1100\r
1101 ZeroMem (Cmd, sizeof (*Cmd));\r
1102 Cmd->ReqRingNumPages = 1;\r
1103 Cmd->CmpRingNumPages = 1;\r
ac0a286f
MK
1104 Cmd->RingsStatePPN = RShiftU64 (\r
1105 Dev->RingDesc.RingStateDmaDesc.DeviceAddress,\r
1106 EFI_PAGE_SHIFT\r
1107 );\r
e210fc13
LA
1108 Cmd->ReqRingPPNs[0] = RShiftU64 (\r
1109 Dev->RingDesc.RingReqsDmaDesc.DeviceAddress,\r
1110 EFI_PAGE_SHIFT\r
1111 );\r
1112 Cmd->CmpRingPPNs[0] = RShiftU64 (\r
1113 Dev->RingDesc.RingCmpsDmaDesc.DeviceAddress,\r
1114 EFI_PAGE_SHIFT\r
1115 );\r
1116\r
1117 STATIC_ASSERT (\r
1118 sizeof (*Cmd) % sizeof (UINT32) == 0,\r
1119 "Cmd must be multiple of 32-bit words"\r
1120 );\r
1121 return PvScsiWriteCmdDesc (\r
1122 Dev,\r
1123 PvScsiCmdSetupRings,\r
1124 (UINT32 *)Cmd,\r
1125 sizeof (*Cmd) / sizeof (UINT32)\r
1126 );\r
1127}\r
1128\r
e497432c
LA
1129STATIC\r
1130EFI_STATUS\r
1131PvScsiInit (\r
ac0a286f 1132 IN OUT PVSCSI_DEV *Dev\r
e497432c
LA
1133 )\r
1134{\r
ac0a286f 1135 EFI_STATUS Status;\r
45098e8a 1136\r
7efce2e5
LA
1137 //\r
1138 // Init configuration\r
1139 //\r
ac0a286f
MK
1140 Dev->MaxTarget = PcdGet8 (PcdPvScsiMaxTargetLimit);\r
1141 Dev->MaxLun = PcdGet8 (PcdPvScsiMaxLunLimit);\r
c4c15b87 1142 Dev->WaitForCmpStallInUsecs = PcdGet32 (PcdPvScsiWaitForCmpStallInUsecs);\r
7efce2e5 1143\r
45098e8a
LA
1144 //\r
1145 // Set PCI Attributes\r
1146 //\r
1147 Status = PvScsiSetPciAttributes (Dev);\r
1148 if (EFI_ERROR (Status)) {\r
1149 return Status;\r
1150 }\r
1151\r
5269c26e
LA
1152 //\r
1153 // Reset adapter\r
1154 //\r
1155 Status = PvScsiResetAdapter (Dev);\r
1156 if (EFI_ERROR (Status)) {\r
1157 goto RestorePciAttributes;\r
1158 }\r
1159\r
b654edec
LA
1160 //\r
1161 // Init PVSCSI rings\r
1162 //\r
1163 Status = PvScsiInitRings (Dev);\r
1164 if (EFI_ERROR (Status)) {\r
1165 goto RestorePciAttributes;\r
1166 }\r
1167\r
6510e197
LA
1168 //\r
1169 // Allocate DMA communication buffer\r
1170 //\r
1171 Status = PvScsiAllocateSharedPages (\r
1172 Dev,\r
1173 EFI_SIZE_TO_PAGES (sizeof (*Dev->DmaBuf)),\r
1174 (VOID **)&Dev->DmaBuf,\r
1175 &Dev->DmaBufDmaDesc\r
1176 );\r
1177 if (EFI_ERROR (Status)) {\r
1178 goto FreeRings;\r
1179 }\r
1180\r
e210fc13
LA
1181 //\r
1182 // Setup rings against device\r
1183 //\r
1184 Status = PvScsiSetupRings (Dev);\r
1185 if (EFI_ERROR (Status)) {\r
1186 goto FreeDmaCommBuffer;\r
1187 }\r
1188\r
e497432c
LA
1189 //\r
1190 // Populate the exported interface's attributes\r
1191 //\r
1192 Dev->PassThru.Mode = &Dev->PassThruMode;\r
1193 Dev->PassThru.PassThru = &PvScsiPassThru;\r
1194 Dev->PassThru.GetNextTargetLun = &PvScsiGetNextTargetLun;\r
1195 Dev->PassThru.BuildDevicePath = &PvScsiBuildDevicePath;\r
1196 Dev->PassThru.GetTargetLun = &PvScsiGetTargetLun;\r
1197 Dev->PassThru.ResetChannel = &PvScsiResetChannel;\r
1198 Dev->PassThru.ResetTargetLun = &PvScsiResetTargetLun;\r
1199 Dev->PassThru.GetNextTarget = &PvScsiGetNextTarget;\r
1200\r
1201 //\r
1202 // AdapterId is a target for which no handle will be created during bus scan.\r
1203 // Prevent any conflict with real devices.\r
1204 //\r
1205 Dev->PassThruMode.AdapterId = MAX_UINT32;\r
1206\r
1207 //\r
1208 // Set both physical and logical attributes for non-RAID SCSI channel\r
1209 //\r
1210 Dev->PassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL |\r
1211 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;\r
1212\r
1213 //\r
1214 // No restriction on transfer buffer alignment\r
1215 //\r
1216 Dev->PassThruMode.IoAlign = 0;\r
1217\r
1218 return EFI_SUCCESS;\r
5269c26e 1219\r
e210fc13
LA
1220FreeDmaCommBuffer:\r
1221 PvScsiFreeSharedPages (\r
1222 Dev,\r
1223 EFI_SIZE_TO_PAGES (sizeof (*Dev->DmaBuf)),\r
1224 Dev->DmaBuf,\r
1225 &Dev->DmaBufDmaDesc\r
1226 );\r
6510e197 1227\r
e210fc13 1228FreeRings:\r
6510e197
LA
1229 PvScsiFreeRings (Dev);\r
1230\r
5269c26e
LA
1231RestorePciAttributes:\r
1232 PvScsiRestorePciAttributes (Dev);\r
1233\r
1234 return Status;\r
e497432c
LA
1235}\r
1236\r
1237STATIC\r
1238VOID\r
1239PvScsiUninit (\r
ac0a286f 1240 IN OUT PVSCSI_DEV *Dev\r
e497432c
LA
1241 )\r
1242{\r
b654edec 1243 //\r
6510e197
LA
1244 // Reset device to:\r
1245 // - Make device stop processing all requests.\r
1246 // - Stop device usage of the rings.\r
1247 //\r
1248 // This is required to safely free the DMA communication buffer\r
1249 // and the rings.\r
b654edec
LA
1250 //\r
1251 PvScsiResetAdapter (Dev);\r
1252\r
6510e197
LA
1253 //\r
1254 // Free DMA communication buffer\r
1255 //\r
1256 PvScsiFreeSharedPages (\r
1257 Dev,\r
1258 EFI_SIZE_TO_PAGES (sizeof (*Dev->DmaBuf)),\r
1259 Dev->DmaBuf,\r
1260 &Dev->DmaBufDmaDesc\r
1261 );\r
1262\r
b654edec
LA
1263 PvScsiFreeRings (Dev);\r
1264\r
45098e8a 1265 PvScsiRestorePciAttributes (Dev);\r
e497432c
LA
1266}\r
1267\r
7d8a04e9
LA
1268/**\r
1269 Event notification called by ExitBootServices()\r
1270**/\r
1271STATIC\r
1272VOID\r
1273EFIAPI\r
1274PvScsiExitBoot (\r
ac0a286f
MK
1275 IN EFI_EVENT Event,\r
1276 IN VOID *Context\r
7d8a04e9
LA
1277 )\r
1278{\r
ac0a286f 1279 PVSCSI_DEV *Dev;\r
7d8a04e9
LA
1280\r
1281 Dev = Context;\r
1282 DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context));\r
1283\r
1284 //\r
1285 // Reset the device to stop device usage of the rings.\r
1286 //\r
1287 // We allocated said rings in EfiBootServicesData type memory, and code\r
1288 // executing after ExitBootServices() is permitted to overwrite it.\r
1289 //\r
1290 PvScsiResetAdapter (Dev);\r
1291}\r
1292\r
ed08c571
LA
1293//\r
1294// Driver Binding\r
1295//\r
1296\r
1297STATIC\r
1298EFI_STATUS\r
1299EFIAPI\r
1300PvScsiDriverBindingSupported (\r
ac0a286f
MK
1301 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
1302 IN EFI_HANDLE ControllerHandle,\r
1303 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
ed08c571
LA
1304 )\r
1305{\r
ac0a286f
MK
1306 EFI_STATUS Status;\r
1307 EFI_PCI_IO_PROTOCOL *PciIo;\r
1308 PCI_TYPE00 Pci;\r
a9f9d5cf
LA
1309\r
1310 Status = gBS->OpenProtocol (\r
1311 ControllerHandle,\r
1312 &gEfiPciIoProtocolGuid,\r
1313 (VOID **)&PciIo,\r
1314 This->DriverBindingHandle,\r
1315 ControllerHandle,\r
1316 EFI_OPEN_PROTOCOL_BY_DRIVER\r
1317 );\r
1318 if (EFI_ERROR (Status)) {\r
1319 return Status;\r
1320 }\r
1321\r
1322 Status = PciIo->Pci.Read (\r
1323 PciIo,\r
1324 EfiPciIoWidthUint32,\r
1325 0,\r
1326 sizeof (Pci) / sizeof (UINT32),\r
1327 &Pci\r
1328 );\r
1329 if (EFI_ERROR (Status)) {\r
1330 goto Done;\r
1331 }\r
1332\r
1333 if ((Pci.Hdr.VendorId != PCI_VENDOR_ID_VMWARE) ||\r
ac0a286f
MK
1334 (Pci.Hdr.DeviceId != PCI_DEVICE_ID_VMWARE_PVSCSI))\r
1335 {\r
a9f9d5cf
LA
1336 Status = EFI_UNSUPPORTED;\r
1337 goto Done;\r
1338 }\r
1339\r
1340 Status = EFI_SUCCESS;\r
1341\r
1342Done:\r
1343 gBS->CloseProtocol (\r
1344 ControllerHandle,\r
1345 &gEfiPciIoProtocolGuid,\r
1346 This->DriverBindingHandle,\r
1347 ControllerHandle\r
1348 );\r
1349\r
1350 return Status;\r
ed08c571
LA
1351}\r
1352\r
1353STATIC\r
1354EFI_STATUS\r
1355EFIAPI\r
1356PvScsiDriverBindingStart (\r
ac0a286f
MK
1357 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
1358 IN EFI_HANDLE ControllerHandle,\r
1359 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
ed08c571
LA
1360 )\r
1361{\r
ac0a286f
MK
1362 PVSCSI_DEV *Dev;\r
1363 EFI_STATUS Status;\r
e497432c 1364\r
ac0a286f 1365 Dev = (PVSCSI_DEV *)AllocateZeroPool (sizeof (*Dev));\r
e497432c
LA
1366 if (Dev == NULL) {\r
1367 return EFI_OUT_OF_RESOURCES;\r
1368 }\r
1369\r
c08eaaaf
LA
1370 Status = gBS->OpenProtocol (\r
1371 ControllerHandle,\r
1372 &gEfiPciIoProtocolGuid,\r
1373 (VOID **)&Dev->PciIo,\r
1374 This->DriverBindingHandle,\r
1375 ControllerHandle,\r
1376 EFI_OPEN_PROTOCOL_BY_DRIVER\r
1377 );\r
e497432c
LA
1378 if (EFI_ERROR (Status)) {\r
1379 goto FreePvScsi;\r
1380 }\r
1381\r
c08eaaaf
LA
1382 Status = PvScsiInit (Dev);\r
1383 if (EFI_ERROR (Status)) {\r
1384 goto ClosePciIo;\r
1385 }\r
1386\r
7d8a04e9
LA
1387 Status = gBS->CreateEvent (\r
1388 EVT_SIGNAL_EXIT_BOOT_SERVICES,\r
1389 TPL_CALLBACK,\r
1390 &PvScsiExitBoot,\r
1391 Dev,\r
1392 &Dev->ExitBoot\r
1393 );\r
1394 if (EFI_ERROR (Status)) {\r
1395 goto UninitDev;\r
1396 }\r
1397\r
e497432c
LA
1398 //\r
1399 // Setup complete, attempt to export the driver instance's PassThru interface\r
1400 //\r
1401 Dev->Signature = PVSCSI_SIG;\r
ac0a286f
MK
1402 Status = gBS->InstallProtocolInterface (\r
1403 &ControllerHandle,\r
1404 &gEfiExtScsiPassThruProtocolGuid,\r
1405 EFI_NATIVE_INTERFACE,\r
1406 &Dev->PassThru\r
1407 );\r
e497432c 1408 if (EFI_ERROR (Status)) {\r
7d8a04e9 1409 goto CloseExitBoot;\r
e497432c
LA
1410 }\r
1411\r
1412 return EFI_SUCCESS;\r
1413\r
7d8a04e9
LA
1414CloseExitBoot:\r
1415 gBS->CloseEvent (Dev->ExitBoot);\r
1416\r
e497432c
LA
1417UninitDev:\r
1418 PvScsiUninit (Dev);\r
1419\r
c08eaaaf
LA
1420ClosePciIo:\r
1421 gBS->CloseProtocol (\r
1422 ControllerHandle,\r
1423 &gEfiPciIoProtocolGuid,\r
1424 This->DriverBindingHandle,\r
1425 ControllerHandle\r
1426 );\r
1427\r
e497432c
LA
1428FreePvScsi:\r
1429 FreePool (Dev);\r
1430\r
1431 return Status;\r
ed08c571
LA
1432}\r
1433\r
1434STATIC\r
1435EFI_STATUS\r
1436EFIAPI\r
1437PvScsiDriverBindingStop (\r
ac0a286f
MK
1438 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
1439 IN EFI_HANDLE ControllerHandle,\r
1440 IN UINTN NumberOfChildren,\r
1441 IN EFI_HANDLE *ChildHandleBuffer\r
ed08c571
LA
1442 )\r
1443{\r
ac0a286f
MK
1444 EFI_STATUS Status;\r
1445 EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;\r
1446 PVSCSI_DEV *Dev;\r
e497432c
LA
1447\r
1448 Status = gBS->OpenProtocol (\r
1449 ControllerHandle,\r
1450 &gEfiExtScsiPassThruProtocolGuid,\r
1451 (VOID **)&PassThru,\r
1452 This->DriverBindingHandle,\r
1453 ControllerHandle,\r
1454 EFI_OPEN_PROTOCOL_GET_PROTOCOL // Lookup only\r
1455 );\r
1456 if (EFI_ERROR (Status)) {\r
1457 return Status;\r
1458 }\r
1459\r
1460 Dev = PVSCSI_FROM_PASS_THRU (PassThru);\r
1461\r
1462 Status = gBS->UninstallProtocolInterface (\r
1463 ControllerHandle,\r
1464 &gEfiExtScsiPassThruProtocolGuid,\r
1465 &Dev->PassThru\r
1466 );\r
1467 if (EFI_ERROR (Status)) {\r
1468 return Status;\r
1469 }\r
1470\r
7d8a04e9
LA
1471 gBS->CloseEvent (Dev->ExitBoot);\r
1472\r
e497432c
LA
1473 PvScsiUninit (Dev);\r
1474\r
c08eaaaf
LA
1475 gBS->CloseProtocol (\r
1476 ControllerHandle,\r
1477 &gEfiPciIoProtocolGuid,\r
1478 This->DriverBindingHandle,\r
1479 ControllerHandle\r
1480 );\r
1481\r
e497432c
LA
1482 FreePool (Dev);\r
1483\r
1484 return EFI_SUCCESS;\r
ed08c571
LA
1485}\r
1486\r
ac0a286f 1487STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding = {\r
ed08c571
LA
1488 &PvScsiDriverBindingSupported,\r
1489 &PvScsiDriverBindingStart,\r
1490 &PvScsiDriverBindingStop,\r
1491 PVSCSI_BINDING_VERSION,\r
1492 NULL, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2()\r
1493 NULL // DriverBindingHandle, filled as well\r
1494};\r
1495\r
419b30d6
LA
1496//\r
1497// Component Name\r
1498//\r
1499\r
ac0a286f 1500STATIC EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {\r
419b30d6
LA
1501 { "eng;en", L"PVSCSI Host Driver" },\r
1502 { NULL, NULL }\r
1503};\r
1504\r
ac0a286f 1505STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName;\r
419b30d6
LA
1506\r
1507STATIC\r
1508EFI_STATUS\r
1509EFIAPI\r
1510PvScsiGetDriverName (\r
ac0a286f
MK
1511 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
1512 IN CHAR8 *Language,\r
1513 OUT CHAR16 **DriverName\r
419b30d6
LA
1514 )\r
1515{\r
1516 return LookupUnicodeString2 (\r
1517 Language,\r
1518 This->SupportedLanguages,\r
1519 mDriverNameTable,\r
1520 DriverName,\r
1521 (BOOLEAN)(This == &mComponentName) // Iso639Language\r
1522 );\r
1523}\r
1524\r
1525STATIC\r
1526EFI_STATUS\r
1527EFIAPI\r
1528PvScsiGetDeviceName (\r
ac0a286f
MK
1529 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
1530 IN EFI_HANDLE DeviceHandle,\r
1531 IN EFI_HANDLE ChildHandle,\r
1532 IN CHAR8 *Language,\r
1533 OUT CHAR16 **ControllerName\r
419b30d6
LA
1534 )\r
1535{\r
1536 return EFI_UNSUPPORTED;\r
1537}\r
1538\r
ac0a286f 1539STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName = {\r
419b30d6
LA
1540 &PvScsiGetDriverName,\r
1541 &PvScsiGetDeviceName,\r
1542 "eng" // SupportedLanguages, ISO 639-2 language codes\r
1543};\r
1544\r
ac0a286f
MK
1545STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {\r
1546 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)&PvScsiGetDriverName,\r
1547 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)&PvScsiGetDeviceName,\r
419b30d6
LA
1548 "en" // SupportedLanguages, RFC 4646 language codes\r
1549};\r
1550\r
478c07d4
LA
1551//\r
1552// Entry Point\r
1553//\r
1554\r
1555EFI_STATUS\r
1556EFIAPI\r
1557PvScsiEntryPoint (\r
ac0a286f
MK
1558 IN EFI_HANDLE ImageHandle,\r
1559 IN EFI_SYSTEM_TABLE *SystemTable\r
478c07d4
LA
1560 )\r
1561{\r
ed08c571
LA
1562 return EfiLibInstallDriverBindingComponentName2 (\r
1563 ImageHandle,\r
1564 SystemTable,\r
1565 &mPvScsiDriverBinding,\r
1566 ImageHandle,\r
419b30d6
LA
1567 &mComponentName,\r
1568 &mComponentName2\r
ed08c571 1569 );\r
478c07d4 1570}\r