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