]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/PvScsiDxe/PvScsi.c
OvmfPkg/PvScsiDxe: Refactor setup of rings to separate function
[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
29#define PVSCSI_BINDING_VERSION 0x10\r
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
41 IN CONST PVSCSI_DEV *Dev,\r
42 IN UINT64 Offset,\r
43 OUT UINT32 *Value\r
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
62 IN CONST PVSCSI_DEV *Dev,\r
63 IN UINT64 Offset,\r
64 IN UINT32 Value\r
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
83 IN CONST PVSCSI_DEV *Dev,\r
84 IN UINT64 Offset,\r
85 IN UINTN Count,\r
86 IN UINT32 *Words\r
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
119 IN CONST PVSCSI_DEV *Dev,\r
120 IN UINT32 Cmd,\r
121 IN UINT32 *DescWords OPTIONAL,\r
122 IN UINTN DescWordsCount\r
123 )\r
124{\r
125 EFI_STATUS Status;\r
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
151 IN CONST PVSCSI_DEV *Dev\r
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
163 IN CONST PVSCSI_DEV *Dev\r
164 )\r
165{\r
166 PVSCSI_RINGS_STATE *RingsState;\r
167 UINT32 ReqNumEntries;\r
168\r
169 RingsState = Dev->RingDesc.RingState;\r
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
180 IN CONST PVSCSI_DEV *Dev\r
181 )\r
182{\r
183 PVSCSI_RINGS_STATE *RingState;\r
184 UINT32 ReqNumEntries;\r
185\r
186 RingState = Dev->RingDesc.RingState;\r
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
198 IN CONST PVSCSI_DEV *Dev\r
199 )\r
200{\r
201 PVSCSI_RINGS_STATE *RingState;\r
202 UINT32 CmpNumEntries;\r
203\r
204 RingState = Dev->RingDesc.RingState;\r
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
216 IN CONST PVSCSI_DEV *Dev\r
217 )\r
218{\r
219 EFI_STATUS Status;\r
220 UINT32 IntrStatus;\r
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
229 for (;;) {\r
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
261 OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet\r
262 )\r
263{\r
264 Packet->InTransferLength = 0;\r
265 Packet->OutTransferLength = 0;\r
266 Packet->SenseDataLength = 0;\r
267 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;\r
268 Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;\r
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
278 OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet\r
279 )\r
280{\r
281 Packet->SenseDataLength = 0;\r
282 Packet->HostAdapterStatus =\r
283 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;\r
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
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
300 )\r
301{\r
302 UINT8 TargetValue;\r
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
316 (Packet->InTransferLength > 0 && Packet->OutTransferLength > 0) ||\r
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
323 ) {\r
324\r
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
351 )\r
352 ) ||\r
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
360 )\r
361 )\r
362 ) {\r
363\r
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
377 if (Packet->OutTransferLength > sizeof (Dev->DmaBuf->Data)) {\r
378 Packet->OutTransferLength = sizeof (Dev->DmaBuf->Data);\r
379 return ReportHostAdapterOverrunError (Packet);\r
380 }\r
381\r
382 //\r
383 // Encode PVSCSI request\r
384 //\r
385 ZeroMem (Request, sizeof (*Request));\r
386\r
387 Request->Bus = 0;\r
388 Request->Target = TargetValue;\r
389 //\r
390 // This cast is safe as PVSCSI_DEV.MaxLun is defined as UINT8\r
391 //\r
392 Request->Lun[1] = (UINT8)Lun;\r
393 Request->SenseLen = Packet->SenseDataLength;\r
394 //\r
395 // DMA communication buffer SenseData overflow is not possible\r
396 // due to Packet->SenseDataLength defined as UINT8\r
397 //\r
398 Request->SenseAddr = PVSCSI_DMA_BUF_DEV_ADDR (Dev, SenseData);\r
399 Request->CdbLen = Packet->CdbLength;\r
400 CopyMem (Request->Cdb, Packet->Cdb, Packet->CdbLength);\r
401 Request->VcpuHint = 0;\r
402 Request->Tag = PVSCSI_SIMPLE_QUEUE_TAG;\r
403 if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
404 Request->Flags = PVSCSI_FLAG_CMD_DIR_TOHOST;\r
405 Request->DataLen = Packet->InTransferLength;\r
406 } else {\r
407 Request->Flags = PVSCSI_FLAG_CMD_DIR_TODEVICE;\r
408 Request->DataLen = Packet->OutTransferLength;\r
409 CopyMem (\r
410 Dev->DmaBuf->Data,\r
411 Packet->OutDataBuffer,\r
412 Packet->OutTransferLength\r
413 );\r
414 }\r
415 Request->DataAddr = PVSCSI_DMA_BUF_DEV_ADDR (Dev, Data);\r
416\r
417 return EFI_SUCCESS;\r
418}\r
419\r
420/**\r
421 Handle the PVSCSI device response:\r
422 - Copy returned data from DMA communication buffer.\r
423 - Update fields in Extended SCSI Pass Thru Protocol packet as required.\r
424 - Translate response code to EFI status code and host adapter status.\r
425**/\r
426STATIC\r
427EFI_STATUS\r
428HandleResponse (\r
429 IN PVSCSI_DEV *Dev,\r
430 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,\r
431 IN CONST PVSCSI_RING_CMP_DESC *Response\r
432 )\r
433{\r
434 //\r
435 // Fix SenseDataLength to amount of data returned\r
436 //\r
437 if (Packet->SenseDataLength > Response->SenseLen) {\r
438 Packet->SenseDataLength = (UINT8)Response->SenseLen;\r
439 }\r
440 //\r
441 // Copy sense data from DMA communication buffer\r
442 //\r
443 CopyMem (\r
444 Packet->SenseData,\r
445 Dev->DmaBuf->SenseData,\r
446 Packet->SenseDataLength\r
447 );\r
448\r
449 //\r
450 // Copy device output from DMA communication buffer\r
451 //\r
452 if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
453 CopyMem (Packet->InDataBuffer, Dev->DmaBuf->Data, Packet->InTransferLength);\r
454 }\r
455\r
456 //\r
457 // Report target status\r
98936dc4
LA
458 // (Strangely, PVSCSI interface defines Response->ScsiStatus as UINT16.\r
459 // But it should de-facto always have a value that fits UINT8. To avoid\r
460 // unexpected behavior, verify value is in UINT8 bounds before casting)\r
c4c15b87 461 //\r
98936dc4
LA
462 ASSERT (Response->ScsiStatus <= MAX_UINT8);\r
463 Packet->TargetStatus = (UINT8)Response->ScsiStatus;\r
c4c15b87
LA
464\r
465 //\r
466 // Host adapter status and function return value depend on\r
467 // device response's host status\r
468 //\r
469 switch (Response->HostStatus) {\r
470 case PvScsiBtStatSuccess:\r
471 case PvScsiBtStatLinkedCommandCompleted:\r
472 case PvScsiBtStatLinkedCommandCompletedWithFlag:\r
473 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;\r
474 return EFI_SUCCESS;\r
475\r
476 case PvScsiBtStatDataUnderrun:\r
477 //\r
478 // Report transferred amount in underrun\r
479 //\r
480 if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) {\r
481 Packet->InTransferLength = (UINT32)Response->DataLen;\r
482 } else {\r
483 Packet->OutTransferLength = (UINT32)Response->DataLen;\r
484 }\r
485 Packet->HostAdapterStatus =\r
486 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;\r
487 return EFI_SUCCESS;\r
488\r
489 case PvScsiBtStatDatarun:\r
490 Packet->HostAdapterStatus =\r
491 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;\r
492 return EFI_SUCCESS;\r
493\r
494 case PvScsiBtStatSelTimeout:\r
495 Packet->HostAdapterStatus =\r
496 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT;\r
497 return EFI_TIMEOUT;\r
498\r
499 case PvScsiBtStatBusFree:\r
500 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE;\r
501 break;\r
502\r
503 case PvScsiBtStatInvPhase:\r
504 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR;\r
505 break;\r
506\r
507 case PvScsiBtStatSensFailed:\r
508 Packet->HostAdapterStatus =\r
509 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED;\r
510 break;\r
511\r
512 case PvScsiBtStatTagReject:\r
513 case PvScsiBtStatBadMsg:\r
514 Packet->HostAdapterStatus =\r
515 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT;\r
516 break;\r
517\r
518 case PvScsiBtStatBusReset:\r
519 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET;\r
520 break;\r
521\r
522 case PvScsiBtStatHaTimeout:\r
523 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT;\r
524 return EFI_TIMEOUT;\r
525\r
526 case PvScsiBtStatScsiParity:\r
527 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR;\r
528 break;\r
529\r
530 default:\r
531 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;\r
532 break;\r
533 }\r
534\r
535 return EFI_DEVICE_ERROR;\r
536}\r
537\r
7efce2e5
LA
538/**\r
539 Check if Target argument to EXT_SCSI_PASS_THRU.GetNextTarget() and\r
540 EXT_SCSI_PASS_THRU.GetNextTargetLun() is initialized\r
541**/\r
542STATIC\r
543BOOLEAN\r
544IsTargetInitialized (\r
545 IN UINT8 *Target\r
546 )\r
547{\r
548 UINTN Idx;\r
549\r
550 for (Idx = 0; Idx < TARGET_MAX_BYTES; ++Idx) {\r
551 if (Target[Idx] != 0xFF) {\r
552 return TRUE;\r
553 }\r
554 }\r
555 return FALSE;\r
556}\r
557\r
e497432c
LA
558//\r
559// Ext SCSI Pass Thru\r
560//\r
561\r
562STATIC\r
563EFI_STATUS\r
564EFIAPI\r
565PvScsiPassThru (\r
566 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
567 IN UINT8 *Target,\r
568 IN UINT64 Lun,\r
569 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,\r
570 IN EFI_EVENT Event OPTIONAL\r
571 )\r
572{\r
c4c15b87
LA
573 PVSCSI_DEV *Dev;\r
574 EFI_STATUS Status;\r
575 PVSCSI_RING_REQ_DESC *Request;\r
576 PVSCSI_RING_CMP_DESC *Response;\r
577\r
578 Dev = PVSCSI_FROM_PASS_THRU (This);\r
579\r
580 if (PvScsiIsReqRingFull (Dev)) {\r
581 return EFI_NOT_READY;\r
582 }\r
583\r
584 Request = PvScsiGetCurrentRequest (Dev);\r
585\r
586 Status = PopulateRequest (Dev, Target, Lun, Packet, Request);\r
587 if (EFI_ERROR (Status)) {\r
588 return Status;\r
589 }\r
590\r
591 //\r
592 // Writes to Request must be globally visible before making request\r
593 // available to device\r
594 //\r
595 MemoryFence ();\r
596 Dev->RingDesc.RingState->ReqProdIdx++;\r
597\r
598 Status = PvScsiMmioWrite32 (Dev, PvScsiRegOffsetKickRwIo, 0);\r
599 if (EFI_ERROR (Status)) {\r
600 //\r
601 // If kicking the host fails, we must fake a host adapter error.\r
602 // EFI_NOT_READY would save us the effort, but it would also suggest that\r
603 // the caller retry.\r
604 //\r
605 return ReportHostAdapterError (Packet);\r
606 }\r
607\r
608 Status = PvScsiWaitForRequestCompletion (Dev);\r
609 if (EFI_ERROR (Status)) {\r
610 //\r
611 // If waiting for request completion fails, we must fake a host adapter\r
612 // error. EFI_NOT_READY would save us the effort, but it would also suggest\r
613 // that the caller retry.\r
614 //\r
615 return ReportHostAdapterError (Packet);\r
616 }\r
617\r
618 Response = PvScsiGetCurrentResponse (Dev);\r
619 Status = HandleResponse (Dev, Packet, Response);\r
620\r
621 //\r
622 // Reads from response must complete before releasing completion entry\r
623 // to device\r
624 //\r
625 MemoryFence ();\r
626 Dev->RingDesc.RingState->CmpConsIdx++;\r
627\r
628 return Status;\r
e497432c
LA
629}\r
630\r
631STATIC\r
632EFI_STATUS\r
633EFIAPI\r
634PvScsiGetNextTargetLun (\r
635 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
636 IN OUT UINT8 **Target,\r
637 IN OUT UINT64 *Lun\r
638 )\r
639{\r
7efce2e5
LA
640 UINT8 *TargetPtr;\r
641 UINT8 LastTarget;\r
642 PVSCSI_DEV *Dev;\r
643\r
644 if (Target == NULL) {\r
645 return EFI_INVALID_PARAMETER;\r
646 }\r
647\r
648 //\r
649 // The Target input parameter is unnecessarily a pointer-to-pointer\r
650 //\r
651 TargetPtr = *Target;\r
652\r
653 //\r
654 // If target not initialized, return first target & LUN\r
655 //\r
656 if (!IsTargetInitialized (TargetPtr)) {\r
657 ZeroMem (TargetPtr, TARGET_MAX_BYTES);\r
658 *Lun = 0;\r
659 return EFI_SUCCESS;\r
660 }\r
661\r
662 //\r
663 // We only use first byte of target identifer\r
664 //\r
665 LastTarget = *TargetPtr;\r
666\r
667 //\r
668 // Increment (target, LUN) pair if valid on input\r
669 //\r
670 Dev = PVSCSI_FROM_PASS_THRU (This);\r
671 if (LastTarget > Dev->MaxTarget || *Lun > Dev->MaxLun) {\r
672 return EFI_INVALID_PARAMETER;\r
673 }\r
674\r
675 if (*Lun < Dev->MaxLun) {\r
676 ++*Lun;\r
677 return EFI_SUCCESS;\r
678 }\r
679\r
680 if (LastTarget < Dev->MaxTarget) {\r
681 *Lun = 0;\r
682 ++LastTarget;\r
683 *TargetPtr = LastTarget;\r
684 return EFI_SUCCESS;\r
685 }\r
686\r
687 return EFI_NOT_FOUND;\r
e497432c
LA
688}\r
689\r
690STATIC\r
691EFI_STATUS\r
692EFIAPI\r
693PvScsiBuildDevicePath (\r
694 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
695 IN UINT8 *Target,\r
696 IN UINT64 Lun,\r
697 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath\r
698 )\r
699{\r
9c2d8281
LA
700 UINT8 TargetValue;\r
701 PVSCSI_DEV *Dev;\r
702 SCSI_DEVICE_PATH *ScsiDevicePath;\r
703\r
704 if (DevicePath == NULL) {\r
705 return EFI_INVALID_PARAMETER;\r
706 }\r
707\r
708 //\r
709 // We only use first byte of target identifer\r
710 //\r
711 TargetValue = *Target;\r
712\r
713 Dev = PVSCSI_FROM_PASS_THRU (This);\r
714 if (TargetValue > Dev->MaxTarget || Lun > Dev->MaxLun) {\r
715 return EFI_NOT_FOUND;\r
716 }\r
717\r
718 ScsiDevicePath = AllocatePool (sizeof (*ScsiDevicePath));\r
719 if (ScsiDevicePath == NULL) {\r
720 return EFI_OUT_OF_RESOURCES;\r
721 }\r
722\r
723 ScsiDevicePath->Header.Type = MESSAGING_DEVICE_PATH;\r
724 ScsiDevicePath->Header.SubType = MSG_SCSI_DP;\r
725 ScsiDevicePath->Header.Length[0] = (UINT8)sizeof (*ScsiDevicePath);\r
726 ScsiDevicePath->Header.Length[1] = (UINT8)(sizeof (*ScsiDevicePath) >> 8);\r
727 ScsiDevicePath->Pun = TargetValue;\r
728 ScsiDevicePath->Lun = (UINT16)Lun;\r
729\r
730 *DevicePath = &ScsiDevicePath->Header;\r
731 return EFI_SUCCESS;\r
e497432c
LA
732}\r
733\r
734STATIC\r
735EFI_STATUS\r
736EFIAPI\r
737PvScsiGetTargetLun (\r
738 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
739 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
740 OUT UINT8 **Target,\r
741 OUT UINT64 *Lun\r
742 )\r
743{\r
9c2d8281
LA
744 SCSI_DEVICE_PATH *ScsiDevicePath;\r
745 PVSCSI_DEV *Dev;\r
746\r
747 if (DevicePath == NULL || Target == NULL || *Target == NULL || Lun == NULL) {\r
748 return EFI_INVALID_PARAMETER;\r
749 }\r
750\r
751 if (DevicePath->Type != MESSAGING_DEVICE_PATH ||\r
752 DevicePath->SubType != MSG_SCSI_DP) {\r
753 return EFI_UNSUPPORTED;\r
754 }\r
755\r
756 ScsiDevicePath = (SCSI_DEVICE_PATH *)DevicePath;\r
757 Dev = PVSCSI_FROM_PASS_THRU (This);\r
758 if (ScsiDevicePath->Pun > Dev->MaxTarget ||\r
759 ScsiDevicePath->Lun > Dev->MaxLun) {\r
760 return EFI_NOT_FOUND;\r
761 }\r
762\r
763 //\r
764 // We only use first byte of target identifer\r
765 //\r
766 **Target = (UINT8)ScsiDevicePath->Pun;\r
767 ZeroMem (*Target + 1, TARGET_MAX_BYTES - 1);\r
768 *Lun = ScsiDevicePath->Lun;\r
769\r
770 return EFI_SUCCESS;\r
e497432c
LA
771}\r
772\r
773STATIC\r
774EFI_STATUS\r
775EFIAPI\r
776PvScsiResetChannel (\r
777 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This\r
778 )\r
779{\r
780 return EFI_UNSUPPORTED;\r
781}\r
782\r
783STATIC\r
784EFI_STATUS\r
785EFIAPI\r
786PvScsiResetTargetLun (\r
787 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
788 IN UINT8 *Target,\r
789 IN UINT64 Lun\r
790 )\r
791{\r
792 return EFI_UNSUPPORTED;\r
793}\r
794\r
795STATIC\r
796EFI_STATUS\r
797EFIAPI\r
798PvScsiGetNextTarget (\r
799 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
800 IN OUT UINT8 **Target\r
801 )\r
802{\r
7efce2e5
LA
803 UINT8 *TargetPtr;\r
804 UINT8 LastTarget;\r
805 PVSCSI_DEV *Dev;\r
806\r
807 if (Target == NULL) {\r
808 return EFI_INVALID_PARAMETER;\r
809 }\r
810\r
811 //\r
812 // The Target input parameter is unnecessarily a pointer-to-pointer\r
813 //\r
814 TargetPtr = *Target;\r
815\r
816 //\r
817 // If target not initialized, return first target\r
818 //\r
819 if (!IsTargetInitialized (TargetPtr)) {\r
820 ZeroMem (TargetPtr, TARGET_MAX_BYTES);\r
821 return EFI_SUCCESS;\r
822 }\r
823\r
824 //\r
825 // We only use first byte of target identifer\r
826 //\r
827 LastTarget = *TargetPtr;\r
828\r
829 //\r
830 // Increment target if valid on input\r
831 //\r
832 Dev = PVSCSI_FROM_PASS_THRU (This);\r
833 if (LastTarget > Dev->MaxTarget) {\r
834 return EFI_INVALID_PARAMETER;\r
835 }\r
836\r
837 if (LastTarget < Dev->MaxTarget) {\r
838 ++LastTarget;\r
839 *TargetPtr = LastTarget;\r
840 return EFI_SUCCESS;\r
841 }\r
842\r
843 return EFI_NOT_FOUND;\r
e497432c
LA
844}\r
845\r
45098e8a
LA
846STATIC\r
847EFI_STATUS\r
848PvScsiSetPciAttributes (\r
849 IN OUT PVSCSI_DEV *Dev\r
850 )\r
851{\r
852 EFI_STATUS Status;\r
853\r
854 //\r
855 // Backup original PCI Attributes\r
856 //\r
857 Status = Dev->PciIo->Attributes (\r
858 Dev->PciIo,\r
859 EfiPciIoAttributeOperationGet,\r
860 0,\r
861 &Dev->OriginalPciAttributes\r
862 );\r
863 if (EFI_ERROR (Status)) {\r
864 return Status;\r
865 }\r
866\r
867 //\r
6672b3cf 868 // Enable MMIO-Space & Bus-Mastering\r
45098e8a 869 //\r
6672b3cf
LA
870 Status = Dev->PciIo->Attributes (\r
871 Dev->PciIo,\r
872 EfiPciIoAttributeOperationEnable,\r
873 (EFI_PCI_IO_ATTRIBUTE_MEMORY |\r
874 EFI_PCI_IO_ATTRIBUTE_BUS_MASTER),\r
875 NULL\r
876 );\r
877 if (EFI_ERROR (Status)) {\r
878 return Status;\r
879 }\r
45098e8a 880\r
f34c7645
LA
881 //\r
882 // Signal device supports 64-bit DMA addresses\r
883 //\r
884 Status = Dev->PciIo->Attributes (\r
885 Dev->PciIo,\r
886 EfiPciIoAttributeOperationEnable,\r
887 EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE,\r
888 NULL\r
889 );\r
890 if (EFI_ERROR (Status)) {\r
891 //\r
892 // Warn user that device will only be using 32-bit DMA addresses.\r
893 //\r
894 // Note that this does not prevent the device/driver from working\r
895 // and therefore we only warn and continue as usual.\r
896 //\r
897 DEBUG ((\r
898 DEBUG_WARN,\r
899 "%a: failed to enable 64-bit DMA addresses\n",\r
900 __FUNCTION__\r
901 ));\r
902 }\r
903\r
45098e8a
LA
904 return EFI_SUCCESS;\r
905}\r
906\r
907STATIC\r
908VOID\r
909PvScsiRestorePciAttributes (\r
910 IN PVSCSI_DEV *Dev\r
911 )\r
912{\r
913 Dev->PciIo->Attributes (\r
914 Dev->PciIo,\r
915 EfiPciIoAttributeOperationSet,\r
916 Dev->OriginalPciAttributes,\r
917 NULL\r
918 );\r
919}\r
920\r
b654edec
LA
921STATIC\r
922EFI_STATUS\r
923PvScsiAllocateSharedPages (\r
924 IN PVSCSI_DEV *Dev,\r
925 IN UINTN Pages,\r
926 OUT VOID **HostAddress,\r
927 OUT PVSCSI_DMA_DESC *DmaDesc\r
928 )\r
929{\r
930 EFI_STATUS Status;\r
931 UINTN NumberOfBytes;\r
932\r
933 Status = Dev->PciIo->AllocateBuffer (\r
934 Dev->PciIo,\r
935 AllocateAnyPages,\r
936 EfiBootServicesData,\r
937 Pages,\r
938 HostAddress,\r
939 EFI_PCI_ATTRIBUTE_MEMORY_CACHED\r
940 );\r
941 if (EFI_ERROR (Status)) {\r
942 return Status;\r
943 }\r
944\r
945 NumberOfBytes = EFI_PAGES_TO_SIZE (Pages);\r
946 Status = Dev->PciIo->Map (\r
947 Dev->PciIo,\r
948 EfiPciIoOperationBusMasterCommonBuffer,\r
949 *HostAddress,\r
950 &NumberOfBytes,\r
951 &DmaDesc->DeviceAddress,\r
952 &DmaDesc->Mapping\r
953 );\r
954 if (EFI_ERROR (Status)) {\r
955 goto FreeBuffer;\r
956 }\r
957\r
958 if (NumberOfBytes != EFI_PAGES_TO_SIZE (Pages)) {\r
959 Status = EFI_OUT_OF_RESOURCES;\r
960 goto Unmap;\r
961 }\r
962\r
963 return EFI_SUCCESS;\r
964\r
965Unmap:\r
966 Dev->PciIo->Unmap (Dev->PciIo, DmaDesc->Mapping);\r
967\r
968FreeBuffer:\r
969 Dev->PciIo->FreeBuffer (Dev->PciIo, Pages, *HostAddress);\r
970\r
971 return Status;\r
972}\r
973\r
974STATIC\r
975VOID\r
976PvScsiFreeSharedPages (\r
977 IN PVSCSI_DEV *Dev,\r
978 IN UINTN Pages,\r
979 IN VOID *HostAddress,\r
980 IN PVSCSI_DMA_DESC *DmaDesc\r
981 )\r
982{\r
983 Dev->PciIo->Unmap (Dev->PciIo, DmaDesc->Mapping);\r
984 Dev->PciIo->FreeBuffer (Dev->PciIo, Pages, HostAddress);\r
985}\r
986\r
987STATIC\r
988EFI_STATUS\r
989PvScsiInitRings (\r
990 IN OUT PVSCSI_DEV *Dev\r
991 )\r
992{\r
993 EFI_STATUS Status;\r
b654edec
LA
994\r
995 Status = PvScsiAllocateSharedPages (\r
996 Dev,\r
997 1,\r
998 (VOID **)&Dev->RingDesc.RingState,\r
999 &Dev->RingDesc.RingStateDmaDesc\r
1000 );\r
1001 if (EFI_ERROR (Status)) {\r
1002 return Status;\r
1003 }\r
1004 ZeroMem (Dev->RingDesc.RingState, EFI_PAGE_SIZE);\r
1005\r
1006 Status = PvScsiAllocateSharedPages (\r
1007 Dev,\r
1008 1,\r
1009 (VOID **)&Dev->RingDesc.RingReqs,\r
1010 &Dev->RingDesc.RingReqsDmaDesc\r
1011 );\r
1012 if (EFI_ERROR (Status)) {\r
1013 goto FreeRingState;\r
1014 }\r
1015 ZeroMem (Dev->RingDesc.RingReqs, EFI_PAGE_SIZE);\r
1016\r
1017 Status = PvScsiAllocateSharedPages (\r
1018 Dev,\r
1019 1,\r
1020 (VOID **)&Dev->RingDesc.RingCmps,\r
1021 &Dev->RingDesc.RingCmpsDmaDesc\r
1022 );\r
1023 if (EFI_ERROR (Status)) {\r
1024 goto FreeRingReqs;\r
1025 }\r
1026 ZeroMem (Dev->RingDesc.RingCmps, EFI_PAGE_SIZE);\r
1027\r
b654edec
LA
1028 return EFI_SUCCESS;\r
1029\r
b654edec
LA
1030FreeRingReqs:\r
1031 PvScsiFreeSharedPages (\r
1032 Dev,\r
1033 1,\r
1034 Dev->RingDesc.RingReqs,\r
1035 &Dev->RingDesc.RingReqsDmaDesc\r
1036 );\r
1037\r
1038FreeRingState:\r
1039 PvScsiFreeSharedPages (\r
1040 Dev,\r
1041 1,\r
1042 Dev->RingDesc.RingState,\r
1043 &Dev->RingDesc.RingStateDmaDesc\r
1044 );\r
1045\r
1046 return Status;\r
1047}\r
1048\r
1049STATIC\r
1050VOID\r
1051PvScsiFreeRings (\r
1052 IN OUT PVSCSI_DEV *Dev\r
1053 )\r
1054{\r
1055 PvScsiFreeSharedPages (\r
1056 Dev,\r
1057 1,\r
1058 Dev->RingDesc.RingCmps,\r
1059 &Dev->RingDesc.RingCmpsDmaDesc\r
1060 );\r
1061\r
1062 PvScsiFreeSharedPages (\r
1063 Dev,\r
1064 1,\r
1065 Dev->RingDesc.RingReqs,\r
1066 &Dev->RingDesc.RingReqsDmaDesc\r
1067 );\r
1068\r
1069 PvScsiFreeSharedPages (\r
1070 Dev,\r
1071 1,\r
1072 Dev->RingDesc.RingState,\r
1073 &Dev->RingDesc.RingStateDmaDesc\r
1074 );\r
1075}\r
1076\r
e210fc13
LA
1077STATIC\r
1078EFI_STATUS\r
1079PvScsiSetupRings (\r
1080 IN OUT PVSCSI_DEV *Dev\r
1081 )\r
1082{\r
1083 union {\r
1084 PVSCSI_CMD_DESC_SETUP_RINGS Cmd;\r
1085 UINT32 Uint32;\r
1086 } AlignedCmd;\r
1087 PVSCSI_CMD_DESC_SETUP_RINGS *Cmd;\r
1088\r
1089 Cmd = &AlignedCmd.Cmd;\r
1090\r
1091 ZeroMem (Cmd, sizeof (*Cmd));\r
1092 Cmd->ReqRingNumPages = 1;\r
1093 Cmd->CmpRingNumPages = 1;\r
1094 Cmd->RingsStatePPN = RShiftU64 (\r
1095 Dev->RingDesc.RingStateDmaDesc.DeviceAddress,\r
1096 EFI_PAGE_SHIFT\r
1097 );\r
1098 Cmd->ReqRingPPNs[0] = RShiftU64 (\r
1099 Dev->RingDesc.RingReqsDmaDesc.DeviceAddress,\r
1100 EFI_PAGE_SHIFT\r
1101 );\r
1102 Cmd->CmpRingPPNs[0] = RShiftU64 (\r
1103 Dev->RingDesc.RingCmpsDmaDesc.DeviceAddress,\r
1104 EFI_PAGE_SHIFT\r
1105 );\r
1106\r
1107 STATIC_ASSERT (\r
1108 sizeof (*Cmd) % sizeof (UINT32) == 0,\r
1109 "Cmd must be multiple of 32-bit words"\r
1110 );\r
1111 return PvScsiWriteCmdDesc (\r
1112 Dev,\r
1113 PvScsiCmdSetupRings,\r
1114 (UINT32 *)Cmd,\r
1115 sizeof (*Cmd) / sizeof (UINT32)\r
1116 );\r
1117}\r
1118\r
e497432c
LA
1119STATIC\r
1120EFI_STATUS\r
1121PvScsiInit (\r
1122 IN OUT PVSCSI_DEV *Dev\r
1123 )\r
1124{\r
45098e8a
LA
1125 EFI_STATUS Status;\r
1126\r
7efce2e5
LA
1127 //\r
1128 // Init configuration\r
1129 //\r
1130 Dev->MaxTarget = PcdGet8 (PcdPvScsiMaxTargetLimit);\r
1131 Dev->MaxLun = PcdGet8 (PcdPvScsiMaxLunLimit);\r
c4c15b87 1132 Dev->WaitForCmpStallInUsecs = PcdGet32 (PcdPvScsiWaitForCmpStallInUsecs);\r
7efce2e5 1133\r
45098e8a
LA
1134 //\r
1135 // Set PCI Attributes\r
1136 //\r
1137 Status = PvScsiSetPciAttributes (Dev);\r
1138 if (EFI_ERROR (Status)) {\r
1139 return Status;\r
1140 }\r
1141\r
5269c26e
LA
1142 //\r
1143 // Reset adapter\r
1144 //\r
1145 Status = PvScsiResetAdapter (Dev);\r
1146 if (EFI_ERROR (Status)) {\r
1147 goto RestorePciAttributes;\r
1148 }\r
1149\r
b654edec
LA
1150 //\r
1151 // Init PVSCSI rings\r
1152 //\r
1153 Status = PvScsiInitRings (Dev);\r
1154 if (EFI_ERROR (Status)) {\r
1155 goto RestorePciAttributes;\r
1156 }\r
1157\r
6510e197
LA
1158 //\r
1159 // Allocate DMA communication buffer\r
1160 //\r
1161 Status = PvScsiAllocateSharedPages (\r
1162 Dev,\r
1163 EFI_SIZE_TO_PAGES (sizeof (*Dev->DmaBuf)),\r
1164 (VOID **)&Dev->DmaBuf,\r
1165 &Dev->DmaBufDmaDesc\r
1166 );\r
1167 if (EFI_ERROR (Status)) {\r
1168 goto FreeRings;\r
1169 }\r
1170\r
e210fc13
LA
1171 //\r
1172 // Setup rings against device\r
1173 //\r
1174 Status = PvScsiSetupRings (Dev);\r
1175 if (EFI_ERROR (Status)) {\r
1176 goto FreeDmaCommBuffer;\r
1177 }\r
1178\r
e497432c
LA
1179 //\r
1180 // Populate the exported interface's attributes\r
1181 //\r
1182 Dev->PassThru.Mode = &Dev->PassThruMode;\r
1183 Dev->PassThru.PassThru = &PvScsiPassThru;\r
1184 Dev->PassThru.GetNextTargetLun = &PvScsiGetNextTargetLun;\r
1185 Dev->PassThru.BuildDevicePath = &PvScsiBuildDevicePath;\r
1186 Dev->PassThru.GetTargetLun = &PvScsiGetTargetLun;\r
1187 Dev->PassThru.ResetChannel = &PvScsiResetChannel;\r
1188 Dev->PassThru.ResetTargetLun = &PvScsiResetTargetLun;\r
1189 Dev->PassThru.GetNextTarget = &PvScsiGetNextTarget;\r
1190\r
1191 //\r
1192 // AdapterId is a target for which no handle will be created during bus scan.\r
1193 // Prevent any conflict with real devices.\r
1194 //\r
1195 Dev->PassThruMode.AdapterId = MAX_UINT32;\r
1196\r
1197 //\r
1198 // Set both physical and logical attributes for non-RAID SCSI channel\r
1199 //\r
1200 Dev->PassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL |\r
1201 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;\r
1202\r
1203 //\r
1204 // No restriction on transfer buffer alignment\r
1205 //\r
1206 Dev->PassThruMode.IoAlign = 0;\r
1207\r
1208 return EFI_SUCCESS;\r
5269c26e 1209\r
e210fc13
LA
1210FreeDmaCommBuffer:\r
1211 PvScsiFreeSharedPages (\r
1212 Dev,\r
1213 EFI_SIZE_TO_PAGES (sizeof (*Dev->DmaBuf)),\r
1214 Dev->DmaBuf,\r
1215 &Dev->DmaBufDmaDesc\r
1216 );\r
6510e197 1217\r
e210fc13 1218FreeRings:\r
6510e197
LA
1219 PvScsiFreeRings (Dev);\r
1220\r
5269c26e
LA
1221RestorePciAttributes:\r
1222 PvScsiRestorePciAttributes (Dev);\r
1223\r
1224 return Status;\r
e497432c
LA
1225}\r
1226\r
1227STATIC\r
1228VOID\r
1229PvScsiUninit (\r
1230 IN OUT PVSCSI_DEV *Dev\r
1231 )\r
1232{\r
b654edec 1233 //\r
6510e197
LA
1234 // Reset device to:\r
1235 // - Make device stop processing all requests.\r
1236 // - Stop device usage of the rings.\r
1237 //\r
1238 // This is required to safely free the DMA communication buffer\r
1239 // and the rings.\r
b654edec
LA
1240 //\r
1241 PvScsiResetAdapter (Dev);\r
1242\r
6510e197
LA
1243 //\r
1244 // Free DMA communication buffer\r
1245 //\r
1246 PvScsiFreeSharedPages (\r
1247 Dev,\r
1248 EFI_SIZE_TO_PAGES (sizeof (*Dev->DmaBuf)),\r
1249 Dev->DmaBuf,\r
1250 &Dev->DmaBufDmaDesc\r
1251 );\r
1252\r
b654edec
LA
1253 PvScsiFreeRings (Dev);\r
1254\r
45098e8a 1255 PvScsiRestorePciAttributes (Dev);\r
e497432c
LA
1256}\r
1257\r
7d8a04e9
LA
1258/**\r
1259 Event notification called by ExitBootServices()\r
1260**/\r
1261STATIC\r
1262VOID\r
1263EFIAPI\r
1264PvScsiExitBoot (\r
1265 IN EFI_EVENT Event,\r
1266 IN VOID *Context\r
1267 )\r
1268{\r
1269 PVSCSI_DEV *Dev;\r
1270\r
1271 Dev = Context;\r
1272 DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context));\r
1273\r
1274 //\r
1275 // Reset the device to stop device usage of the rings.\r
1276 //\r
1277 // We allocated said rings in EfiBootServicesData type memory, and code\r
1278 // executing after ExitBootServices() is permitted to overwrite it.\r
1279 //\r
1280 PvScsiResetAdapter (Dev);\r
1281}\r
1282\r
ed08c571
LA
1283//\r
1284// Driver Binding\r
1285//\r
1286\r
1287STATIC\r
1288EFI_STATUS\r
1289EFIAPI\r
1290PvScsiDriverBindingSupported (\r
1291 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
1292 IN EFI_HANDLE ControllerHandle,\r
1293 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
1294 )\r
1295{\r
a9f9d5cf
LA
1296 EFI_STATUS Status;\r
1297 EFI_PCI_IO_PROTOCOL *PciIo;\r
1298 PCI_TYPE00 Pci;\r
1299\r
1300 Status = gBS->OpenProtocol (\r
1301 ControllerHandle,\r
1302 &gEfiPciIoProtocolGuid,\r
1303 (VOID **)&PciIo,\r
1304 This->DriverBindingHandle,\r
1305 ControllerHandle,\r
1306 EFI_OPEN_PROTOCOL_BY_DRIVER\r
1307 );\r
1308 if (EFI_ERROR (Status)) {\r
1309 return Status;\r
1310 }\r
1311\r
1312 Status = PciIo->Pci.Read (\r
1313 PciIo,\r
1314 EfiPciIoWidthUint32,\r
1315 0,\r
1316 sizeof (Pci) / sizeof (UINT32),\r
1317 &Pci\r
1318 );\r
1319 if (EFI_ERROR (Status)) {\r
1320 goto Done;\r
1321 }\r
1322\r
1323 if ((Pci.Hdr.VendorId != PCI_VENDOR_ID_VMWARE) ||\r
1324 (Pci.Hdr.DeviceId != PCI_DEVICE_ID_VMWARE_PVSCSI)) {\r
1325 Status = EFI_UNSUPPORTED;\r
1326 goto Done;\r
1327 }\r
1328\r
1329 Status = EFI_SUCCESS;\r
1330\r
1331Done:\r
1332 gBS->CloseProtocol (\r
1333 ControllerHandle,\r
1334 &gEfiPciIoProtocolGuid,\r
1335 This->DriverBindingHandle,\r
1336 ControllerHandle\r
1337 );\r
1338\r
1339 return Status;\r
ed08c571
LA
1340}\r
1341\r
1342STATIC\r
1343EFI_STATUS\r
1344EFIAPI\r
1345PvScsiDriverBindingStart (\r
1346 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
1347 IN EFI_HANDLE ControllerHandle,\r
1348 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
1349 )\r
1350{\r
e497432c
LA
1351 PVSCSI_DEV *Dev;\r
1352 EFI_STATUS Status;\r
1353\r
1354 Dev = (PVSCSI_DEV *) AllocateZeroPool (sizeof (*Dev));\r
1355 if (Dev == NULL) {\r
1356 return EFI_OUT_OF_RESOURCES;\r
1357 }\r
1358\r
c08eaaaf
LA
1359 Status = gBS->OpenProtocol (\r
1360 ControllerHandle,\r
1361 &gEfiPciIoProtocolGuid,\r
1362 (VOID **)&Dev->PciIo,\r
1363 This->DriverBindingHandle,\r
1364 ControllerHandle,\r
1365 EFI_OPEN_PROTOCOL_BY_DRIVER\r
1366 );\r
e497432c
LA
1367 if (EFI_ERROR (Status)) {\r
1368 goto FreePvScsi;\r
1369 }\r
1370\r
c08eaaaf
LA
1371 Status = PvScsiInit (Dev);\r
1372 if (EFI_ERROR (Status)) {\r
1373 goto ClosePciIo;\r
1374 }\r
1375\r
7d8a04e9
LA
1376 Status = gBS->CreateEvent (\r
1377 EVT_SIGNAL_EXIT_BOOT_SERVICES,\r
1378 TPL_CALLBACK,\r
1379 &PvScsiExitBoot,\r
1380 Dev,\r
1381 &Dev->ExitBoot\r
1382 );\r
1383 if (EFI_ERROR (Status)) {\r
1384 goto UninitDev;\r
1385 }\r
1386\r
e497432c
LA
1387 //\r
1388 // Setup complete, attempt to export the driver instance's PassThru interface\r
1389 //\r
1390 Dev->Signature = PVSCSI_SIG;\r
1391 Status = gBS->InstallProtocolInterface (\r
1392 &ControllerHandle,\r
1393 &gEfiExtScsiPassThruProtocolGuid,\r
1394 EFI_NATIVE_INTERFACE,\r
1395 &Dev->PassThru\r
1396 );\r
1397 if (EFI_ERROR (Status)) {\r
7d8a04e9 1398 goto CloseExitBoot;\r
e497432c
LA
1399 }\r
1400\r
1401 return EFI_SUCCESS;\r
1402\r
7d8a04e9
LA
1403CloseExitBoot:\r
1404 gBS->CloseEvent (Dev->ExitBoot);\r
1405\r
e497432c
LA
1406UninitDev:\r
1407 PvScsiUninit (Dev);\r
1408\r
c08eaaaf
LA
1409ClosePciIo:\r
1410 gBS->CloseProtocol (\r
1411 ControllerHandle,\r
1412 &gEfiPciIoProtocolGuid,\r
1413 This->DriverBindingHandle,\r
1414 ControllerHandle\r
1415 );\r
1416\r
e497432c
LA
1417FreePvScsi:\r
1418 FreePool (Dev);\r
1419\r
1420 return Status;\r
ed08c571
LA
1421}\r
1422\r
1423STATIC\r
1424EFI_STATUS\r
1425EFIAPI\r
1426PvScsiDriverBindingStop (\r
1427 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
1428 IN EFI_HANDLE ControllerHandle,\r
1429 IN UINTN NumberOfChildren,\r
1430 IN EFI_HANDLE *ChildHandleBuffer\r
1431 )\r
1432{\r
e497432c
LA
1433 EFI_STATUS Status;\r
1434 EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;\r
1435 PVSCSI_DEV *Dev;\r
1436\r
1437 Status = gBS->OpenProtocol (\r
1438 ControllerHandle,\r
1439 &gEfiExtScsiPassThruProtocolGuid,\r
1440 (VOID **)&PassThru,\r
1441 This->DriverBindingHandle,\r
1442 ControllerHandle,\r
1443 EFI_OPEN_PROTOCOL_GET_PROTOCOL // Lookup only\r
1444 );\r
1445 if (EFI_ERROR (Status)) {\r
1446 return Status;\r
1447 }\r
1448\r
1449 Dev = PVSCSI_FROM_PASS_THRU (PassThru);\r
1450\r
1451 Status = gBS->UninstallProtocolInterface (\r
1452 ControllerHandle,\r
1453 &gEfiExtScsiPassThruProtocolGuid,\r
1454 &Dev->PassThru\r
1455 );\r
1456 if (EFI_ERROR (Status)) {\r
1457 return Status;\r
1458 }\r
1459\r
7d8a04e9
LA
1460 gBS->CloseEvent (Dev->ExitBoot);\r
1461\r
e497432c
LA
1462 PvScsiUninit (Dev);\r
1463\r
c08eaaaf
LA
1464 gBS->CloseProtocol (\r
1465 ControllerHandle,\r
1466 &gEfiPciIoProtocolGuid,\r
1467 This->DriverBindingHandle,\r
1468 ControllerHandle\r
1469 );\r
1470\r
e497432c
LA
1471 FreePool (Dev);\r
1472\r
1473 return EFI_SUCCESS;\r
ed08c571
LA
1474}\r
1475\r
1476STATIC EFI_DRIVER_BINDING_PROTOCOL mPvScsiDriverBinding = {\r
1477 &PvScsiDriverBindingSupported,\r
1478 &PvScsiDriverBindingStart,\r
1479 &PvScsiDriverBindingStop,\r
1480 PVSCSI_BINDING_VERSION,\r
1481 NULL, // ImageHandle, filled by EfiLibInstallDriverBindingComponentName2()\r
1482 NULL // DriverBindingHandle, filled as well\r
1483};\r
1484\r
419b30d6
LA
1485//\r
1486// Component Name\r
1487//\r
1488\r
1489STATIC EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {\r
1490 { "eng;en", L"PVSCSI Host Driver" },\r
1491 { NULL, NULL }\r
1492};\r
1493\r
1494STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName;\r
1495\r
1496STATIC\r
1497EFI_STATUS\r
1498EFIAPI\r
1499PvScsiGetDriverName (\r
1500 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
1501 IN CHAR8 *Language,\r
1502 OUT CHAR16 **DriverName\r
1503 )\r
1504{\r
1505 return LookupUnicodeString2 (\r
1506 Language,\r
1507 This->SupportedLanguages,\r
1508 mDriverNameTable,\r
1509 DriverName,\r
1510 (BOOLEAN)(This == &mComponentName) // Iso639Language\r
1511 );\r
1512}\r
1513\r
1514STATIC\r
1515EFI_STATUS\r
1516EFIAPI\r
1517PvScsiGetDeviceName (\r
1518 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
1519 IN EFI_HANDLE DeviceHandle,\r
1520 IN EFI_HANDLE ChildHandle,\r
1521 IN CHAR8 *Language,\r
1522 OUT CHAR16 **ControllerName\r
1523 )\r
1524{\r
1525 return EFI_UNSUPPORTED;\r
1526}\r
1527\r
1528STATIC EFI_COMPONENT_NAME_PROTOCOL mComponentName = {\r
1529 &PvScsiGetDriverName,\r
1530 &PvScsiGetDeviceName,\r
1531 "eng" // SupportedLanguages, ISO 639-2 language codes\r
1532};\r
1533\r
1534STATIC EFI_COMPONENT_NAME2_PROTOCOL mComponentName2 = {\r
1535 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &PvScsiGetDriverName,\r
1536 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &PvScsiGetDeviceName,\r
1537 "en" // SupportedLanguages, RFC 4646 language codes\r
1538};\r
1539\r
478c07d4
LA
1540//\r
1541// Entry Point\r
1542//\r
1543\r
1544EFI_STATUS\r
1545EFIAPI\r
1546PvScsiEntryPoint (\r
1547 IN EFI_HANDLE ImageHandle,\r
1548 IN EFI_SYSTEM_TABLE *SystemTable\r
1549 )\r
1550{\r
ed08c571
LA
1551 return EfiLibInstallDriverBindingComponentName2 (\r
1552 ImageHandle,\r
1553 SystemTable,\r
1554 &mPvScsiDriverBinding,\r
1555 ImageHandle,\r
419b30d6
LA
1556 &mComponentName,\r
1557 &mComponentName2\r
ed08c571 1558 );\r
478c07d4 1559}\r