]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - OvmfPkg/VirtioScsiDxe/VirtioScsi.c
OvmfPkg/MemEncryptSevLib: add support to validate system RAM
[mirror_edk2.git] / OvmfPkg / VirtioScsiDxe / VirtioScsi.c
... / ...
CommitLineData
1/** @file\r
2\r
3 This driver produces Extended SCSI Pass Thru Protocol instances for\r
4 virtio-scsi devices.\r
5\r
6 The implementation is basic:\r
7\r
8 - No hotplug / hot-unplug.\r
9\r
10 - Although EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() could be a good match\r
11 for multiple in-flight virtio-scsi requests, we stick to synchronous\r
12 requests for now.\r
13\r
14 - Timeouts are not supported for EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru().\r
15\r
16 - Only one channel is supported. (At the time of this writing, host-side\r
17 virtio-scsi supports a single channel too.)\r
18\r
19 - Only one request queue is used (for the one synchronous request).\r
20\r
21 - The ResetChannel() and ResetTargetLun() functions of\r
22 EFI_EXT_SCSI_PASS_THRU_PROTOCOL are not supported (which is allowed by the\r
23 UEFI 2.3.1 Errata C specification), although\r
24 VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET could be a good match. That would\r
25 however require client code for the control queue, which is deemed\r
26 unreasonable for now.\r
27\r
28 Copyright (C) 2012, Red Hat, Inc.\r
29 Copyright (c) 2012 - 2018, Intel Corporation. All rights reserved.<BR>\r
30 Copyright (c) 2017, AMD Inc, All rights reserved.<BR>\r
31\r
32 SPDX-License-Identifier: BSD-2-Clause-Patent\r
33\r
34**/\r
35\r
36#include <IndustryStandard/VirtioScsi.h>\r
37#include <Library/BaseMemoryLib.h>\r
38#include <Library/DebugLib.h>\r
39#include <Library/MemoryAllocationLib.h>\r
40#include <Library/UefiBootServicesTableLib.h>\r
41#include <Library/UefiLib.h>\r
42#include <Library/VirtioLib.h>\r
43\r
44#include "VirtioScsi.h"\r
45\r
46/**\r
47\r
48 Convenience macros to read and write configuration elements of the\r
49 virtio-scsi VirtIo device.\r
50\r
51 The following macros make it possible to specify only the "core parameters"\r
52 for such accesses and to derive the rest. By the time VIRTIO_CFG_WRITE()\r
53 returns, the transaction will have been completed.\r
54\r
55 @param[in] Dev Pointer to the VSCSI_DEV structure.\r
56\r
57 @param[in] Field A field name from VSCSI_HDR, identifying the virtio-scsi\r
58 configuration item to access.\r
59\r
60 @param[in] Value (VIRTIO_CFG_WRITE() only.) The value to write to the\r
61 selected configuration item.\r
62\r
63 @param[out] Pointer (VIRTIO_CFG_READ() only.) The object to receive the\r
64 value read from the configuration item. Its type must be\r
65 one of UINT8, UINT16, UINT32, UINT64.\r
66\r
67\r
68 @return Status codes returned by Virtio->WriteDevice() / Virtio->ReadDevice().\r
69\r
70**/\r
71\r
72#define VIRTIO_CFG_WRITE(Dev, Field, Value) ((Dev)->VirtIo->WriteDevice ( \\r
73 (Dev)->VirtIo, \\r
74 OFFSET_OF_VSCSI (Field), \\r
75 SIZE_OF_VSCSI (Field), \\r
76 (Value) \\r
77 ))\r
78\r
79#define VIRTIO_CFG_READ(Dev, Field, Pointer) ((Dev)->VirtIo->ReadDevice ( \\r
80 (Dev)->VirtIo, \\r
81 OFFSET_OF_VSCSI (Field), \\r
82 SIZE_OF_VSCSI (Field), \\r
83 sizeof *(Pointer), \\r
84 (Pointer) \\r
85 ))\r
86\r
87//\r
88// UEFI Spec 2.3.1 + Errata C, 14.7 Extended SCSI Pass Thru Protocol specifies\r
89// the PassThru() interface. Beside returning a status code, the function must\r
90// set some fields in the EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET in/out\r
91// parameter on return. The following is a full list of those fields, for\r
92// easier validation of PopulateRequest(), ParseResponse(), and\r
93// ReportHostAdapterError() below.\r
94//\r
95// - InTransferLength\r
96// - OutTransferLength\r
97// - HostAdapterStatus\r
98// - TargetStatus\r
99// - SenseDataLength\r
100// - SenseData\r
101//\r
102// On any return from the PassThru() interface, these fields must be set,\r
103// except if the returned status code is explicitly exempt. (Actually the\r
104// implementation here conservatively sets these fields even in case not all\r
105// of them would be required by the specification.)\r
106//\r
107\r
108/**\r
109\r
110 Populate a virtio-scsi request from the Extended SCSI Pass Thru Protocol\r
111 packet.\r
112\r
113 The caller is responsible for pre-zeroing the virtio-scsi request. The\r
114 Extended SCSI Pass Thru Protocol packet is modified, to be forwarded outwards\r
115 by VirtioScsiPassThru(), if invalid or unsupported parameters are detected.\r
116\r
117 @param[in] Dev The virtio-scsi host device the packet targets.\r
118\r
119 @param[in] Target The SCSI target controlled by the virtio-scsi host\r
120 device.\r
121\r
122 @param[in] Lun The Logical Unit Number under the SCSI target.\r
123\r
124 @param[in out] Packet The Extended SCSI Pass Thru Protocol packet the\r
125 function translates to a virtio-scsi request. On\r
126 failure this parameter relays error contents.\r
127\r
128 @param[out] Request The pre-zeroed virtio-scsi request to populate. This\r
129 parameter is volatile-qualified because we expect the\r
130 caller to append it to a virtio ring, thus\r
131 assignments to Request must be visible when the\r
132 function returns.\r
133\r
134\r
135 @retval EFI_SUCCESS The Extended SCSI Pass Thru Protocol packet was valid,\r
136 Request has been populated.\r
137\r
138 @return Otherwise, invalid or unsupported parameters were\r
139 detected. Status codes are meant for direct forwarding\r
140 by the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru()\r
141 implementation.\r
142\r
143**/\r
144STATIC\r
145EFI_STATUS\r
146EFIAPI\r
147PopulateRequest (\r
148 IN CONST VSCSI_DEV *Dev,\r
149 IN UINT16 Target,\r
150 IN UINT64 Lun,\r
151 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,\r
152 OUT volatile VIRTIO_SCSI_REQ *Request\r
153 )\r
154{\r
155 UINTN Idx;\r
156\r
157 if (\r
158 //\r
159 // bidirectional transfer was requested, but the host doesn't support it\r
160 //\r
161 ((Packet->InTransferLength > 0) && (Packet->OutTransferLength > 0) &&\r
162 !Dev->InOutSupported) ||\r
163\r
164 //\r
165 // a target / LUN was addressed that's impossible to encode for the host\r
166 //\r
167 (Target > 0xFF) || (Lun >= 0x4000) ||\r
168\r
169 //\r
170 // Command Descriptor Block bigger than VIRTIO_SCSI_CDB_SIZE\r
171 //\r
172 (Packet->CdbLength > VIRTIO_SCSI_CDB_SIZE) ||\r
173\r
174 //\r
175 // From virtio-0.9.5, 2.3.2 Descriptor Table:\r
176 // "no descriptor chain may be more than 2^32 bytes long in total".\r
177 //\r
178 ((UINT64)Packet->InTransferLength + Packet->OutTransferLength > SIZE_1GB)\r
179 )\r
180 {\r
181 //\r
182 // this error code doesn't require updates to the Packet output fields\r
183 //\r
184 return EFI_UNSUPPORTED;\r
185 }\r
186\r
187 if (\r
188 //\r
189 // addressed invalid device\r
190 //\r
191 (Target > Dev->MaxTarget) || (Lun > Dev->MaxLun) ||\r
192\r
193 //\r
194 // invalid direction (there doesn't seem to be a macro for the "no data\r
195 // transferred" "direction", eg. for TEST UNIT READY)\r
196 //\r
197 (Packet->DataDirection > EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL) ||\r
198\r
199 //\r
200 // trying to receive, but destination pointer is NULL, or contradicting\r
201 // transfer direction\r
202 //\r
203 ((Packet->InTransferLength > 0) &&\r
204 ((Packet->InDataBuffer == NULL) ||\r
205 (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_WRITE)\r
206 )\r
207 ) ||\r
208\r
209 //\r
210 // trying to send, but source pointer is NULL, or contradicting transfer\r
211 // direction\r
212 //\r
213 ((Packet->OutTransferLength > 0) &&\r
214 ((Packet->OutDataBuffer == NULL) ||\r
215 (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ)\r
216 )\r
217 )\r
218 )\r
219 {\r
220 //\r
221 // this error code doesn't require updates to the Packet output fields\r
222 //\r
223 return EFI_INVALID_PARAMETER;\r
224 }\r
225\r
226 //\r
227 // Catch oversized requests eagerly. If this condition evaluates to false,\r
228 // then the combined size of a bidirectional request will not exceed the\r
229 // virtio-scsi device's transfer limit either.\r
230 //\r
231 if ((ALIGN_VALUE (Packet->OutTransferLength, 512) / 512\r
232 > Dev->MaxSectors / 2) ||\r
233 (ALIGN_VALUE (Packet->InTransferLength, 512) / 512\r
234 > Dev->MaxSectors / 2))\r
235 {\r
236 Packet->InTransferLength = (Dev->MaxSectors / 2) * 512;\r
237 Packet->OutTransferLength = (Dev->MaxSectors / 2) * 512;\r
238 Packet->HostAdapterStatus =\r
239 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;\r
240 Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;\r
241 Packet->SenseDataLength = 0;\r
242 return EFI_BAD_BUFFER_SIZE;\r
243 }\r
244\r
245 //\r
246 // target & LUN encoding: see virtio-0.9.5, Appendix I: SCSI Host Device,\r
247 // Device Operation: request queues\r
248 //\r
249 Request->Lun[0] = 1;\r
250 Request->Lun[1] = (UINT8)Target;\r
251 Request->Lun[2] = (UINT8)(((UINT32)Lun >> 8) | 0x40);\r
252 Request->Lun[3] = (UINT8)Lun;\r
253\r
254 //\r
255 // CopyMem() would cast away the "volatile" qualifier before access, which is\r
256 // undefined behavior (ISO C99 6.7.3p5)\r
257 //\r
258 for (Idx = 0; Idx < Packet->CdbLength; ++Idx) {\r
259 Request->Cdb[Idx] = ((UINT8 *)Packet->Cdb)[Idx];\r
260 }\r
261\r
262 return EFI_SUCCESS;\r
263}\r
264\r
265/**\r
266\r
267 Parse the virtio-scsi device's response, translate it to an EFI status code,\r
268 and update the Extended SCSI Pass Thru Protocol packet, to be returned by\r
269 the EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() implementation.\r
270\r
271 @param[in out] Packet The Extended SCSI Pass Thru Protocol packet that has\r
272 been translated to a virtio-scsi request with\r
273 PopulateRequest(), and processed by the host. On\r
274 output this parameter is updated with response or\r
275 error contents.\r
276\r
277 @param[in] Response The virtio-scsi response structure to parse. We expect\r
278 it to come from a virtio ring, thus it is qualified\r
279 volatile.\r
280\r
281\r
282 @return PassThru() status codes mandated by UEFI Spec 2.3.1 + Errata C, 14.7\r
283 Extended SCSI Pass Thru Protocol.\r
284\r
285**/\r
286STATIC\r
287EFI_STATUS\r
288EFIAPI\r
289ParseResponse (\r
290 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,\r
291 IN CONST volatile VIRTIO_SCSI_RESP *Response\r
292 )\r
293{\r
294 UINTN ResponseSenseLen;\r
295 UINTN Idx;\r
296\r
297 //\r
298 // return sense data (length and contents) in all cases, truncated if needed\r
299 //\r
300 ResponseSenseLen = MIN (Response->SenseLen, VIRTIO_SCSI_SENSE_SIZE);\r
301 if (Packet->SenseDataLength > ResponseSenseLen) {\r
302 Packet->SenseDataLength = (UINT8)ResponseSenseLen;\r
303 }\r
304\r
305 for (Idx = 0; Idx < Packet->SenseDataLength; ++Idx) {\r
306 ((UINT8 *)Packet->SenseData)[Idx] = Response->Sense[Idx];\r
307 }\r
308\r
309 //\r
310 // Report actual transfer lengths. The logic below covers all three\r
311 // DataDirections (read, write, bidirectional).\r
312 //\r
313 // -+- @ 0\r
314 // |\r
315 // | write ^ @ Residual (unprocessed)\r
316 // | |\r
317 // -+- @ OutTransferLength -+- @ InTransferLength\r
318 // | |\r
319 // | read |\r
320 // | |\r
321 // V @ OutTransferLength + InTransferLength -+- @ 0\r
322 //\r
323 if (Response->Residual <= Packet->InTransferLength) {\r
324 Packet->InTransferLength -= Response->Residual;\r
325 } else {\r
326 Packet->OutTransferLength -= Response->Residual - Packet->InTransferLength;\r
327 Packet->InTransferLength = 0;\r
328 }\r
329\r
330 //\r
331 // report target status in all cases\r
332 //\r
333 Packet->TargetStatus = Response->Status;\r
334\r
335 //\r
336 // host adapter status and function return value depend on virtio-scsi\r
337 // response code\r
338 //\r
339 switch (Response->Response) {\r
340 case VIRTIO_SCSI_S_OK:\r
341 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;\r
342 return EFI_SUCCESS;\r
343\r
344 case VIRTIO_SCSI_S_OVERRUN:\r
345 Packet->HostAdapterStatus =\r
346 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN;\r
347 break;\r
348\r
349 case VIRTIO_SCSI_S_BAD_TARGET:\r
350 //\r
351 // This is non-intuitive but explicitly required by the\r
352 // EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() specification for\r
353 // disconnected (but otherwise valid) target / LUN addresses.\r
354 //\r
355 Packet->HostAdapterStatus =\r
356 EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND;\r
357 return EFI_TIMEOUT;\r
358\r
359 case VIRTIO_SCSI_S_RESET:\r
360 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET;\r
361 break;\r
362\r
363 case VIRTIO_SCSI_S_BUSY:\r
364 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;\r
365 return EFI_NOT_READY;\r
366\r
367 //\r
368 // Lump together the rest. The mapping for VIRTIO_SCSI_S_ABORTED is\r
369 // intentional as well, not an oversight.\r
370 //\r
371 case VIRTIO_SCSI_S_ABORTED:\r
372 case VIRTIO_SCSI_S_TRANSPORT_FAILURE:\r
373 case VIRTIO_SCSI_S_TARGET_FAILURE:\r
374 case VIRTIO_SCSI_S_NEXUS_FAILURE:\r
375 case VIRTIO_SCSI_S_FAILURE:\r
376 default:\r
377 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;\r
378 }\r
379\r
380 return EFI_DEVICE_ERROR;\r
381}\r
382\r
383/**\r
384\r
385 The function can be used to create a fake host adapter error.\r
386\r
387 When VirtioScsiPassThru() is failed due to some reasons then this function\r
388 can be called to construct a host adapter error.\r
389\r
390 @param[out] Packet The Extended SCSI Pass Thru Protocol packet that the host\r
391 adapter error shall be placed in.\r
392\r
393\r
394 @retval EFI_DEVICE_ERROR The function returns this status code\r
395 unconditionally, to be propagated by\r
396 VirtioScsiPassThru().\r
397\r
398**/\r
399STATIC\r
400EFI_STATUS\r
401ReportHostAdapterError (\r
402 OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet\r
403 )\r
404{\r
405 Packet->InTransferLength = 0;\r
406 Packet->OutTransferLength = 0;\r
407 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;\r
408 Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;\r
409 Packet->SenseDataLength = 0;\r
410 return EFI_DEVICE_ERROR;\r
411}\r
412\r
413//\r
414// The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL\r
415// for the virtio-scsi HBA. Refer to UEFI Spec 2.3.1 + Errata C, sections\r
416// - 14.1 SCSI Driver Model Overview,\r
417// - 14.7 Extended SCSI Pass Thru Protocol.\r
418//\r
419\r
420EFI_STATUS\r
421EFIAPI\r
422VirtioScsiPassThru (\r
423 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
424 IN UINT8 *Target,\r
425 IN UINT64 Lun,\r
426 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,\r
427 IN EFI_EVENT Event OPTIONAL\r
428 )\r
429{\r
430 VSCSI_DEV *Dev;\r
431 UINT16 TargetValue;\r
432 EFI_STATUS Status;\r
433 volatile VIRTIO_SCSI_REQ Request;\r
434 volatile VIRTIO_SCSI_RESP *Response;\r
435 VOID *ResponseBuffer;\r
436 DESC_INDICES Indices;\r
437 VOID *RequestMapping;\r
438 VOID *ResponseMapping;\r
439 VOID *InDataMapping;\r
440 VOID *OutDataMapping;\r
441 EFI_PHYSICAL_ADDRESS RequestDeviceAddress;\r
442 EFI_PHYSICAL_ADDRESS ResponseDeviceAddress;\r
443 EFI_PHYSICAL_ADDRESS InDataDeviceAddress;\r
444 EFI_PHYSICAL_ADDRESS OutDataDeviceAddress;\r
445 VOID *InDataBuffer;\r
446 UINTN InDataNumPages;\r
447 BOOLEAN OutDataBufferIsMapped;\r
448\r
449 //\r
450 // Set InDataMapping,OutDataMapping,InDataDeviceAddress and OutDataDeviceAddress to\r
451 // suppress incorrect compiler/analyzer warnings.\r
452 //\r
453 InDataMapping = NULL;\r
454 OutDataMapping = NULL;\r
455 InDataDeviceAddress = 0;\r
456 OutDataDeviceAddress = 0;\r
457\r
458 ZeroMem ((VOID *)&Request, sizeof (Request));\r
459\r
460 Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);\r
461 CopyMem (&TargetValue, Target, sizeof TargetValue);\r
462\r
463 InDataBuffer = NULL;\r
464 OutDataBufferIsMapped = FALSE;\r
465 InDataNumPages = 0;\r
466\r
467 Status = PopulateRequest (Dev, TargetValue, Lun, Packet, &Request);\r
468 if (EFI_ERROR (Status)) {\r
469 return Status;\r
470 }\r
471\r
472 //\r
473 // Map the virtio-scsi Request header buffer\r
474 //\r
475 Status = VirtioMapAllBytesInSharedBuffer (\r
476 Dev->VirtIo,\r
477 VirtioOperationBusMasterRead,\r
478 (VOID *)&Request,\r
479 sizeof Request,\r
480 &RequestDeviceAddress,\r
481 &RequestMapping\r
482 );\r
483 if (EFI_ERROR (Status)) {\r
484 return ReportHostAdapterError (Packet);\r
485 }\r
486\r
487 //\r
488 // Map the input buffer\r
489 //\r
490 if (Packet->InTransferLength > 0) {\r
491 //\r
492 // Allocate a intermediate input buffer. This is mainly to handle the\r
493 // following case:\r
494 // * caller submits a bi-directional request\r
495 // * we perform the request fine\r
496 // * but we fail to unmap the "InDataMapping"\r
497 //\r
498 // In that case simply returning the EFI_DEVICE_ERROR is not sufficient. In\r
499 // addition to the error code we also need to update Packet fields\r
500 // accordingly so that we report the full loss of the incoming transfer.\r
501 //\r
502 // We allocate a temporary buffer and map it with BusMasterCommonBuffer. If\r
503 // the Virtio request is successful then we copy the data from temporary\r
504 // buffer into Packet->InDataBuffer.\r
505 //\r
506 InDataNumPages = EFI_SIZE_TO_PAGES ((UINTN)Packet->InTransferLength);\r
507 Status = Dev->VirtIo->AllocateSharedPages (\r
508 Dev->VirtIo,\r
509 InDataNumPages,\r
510 &InDataBuffer\r
511 );\r
512 if (EFI_ERROR (Status)) {\r
513 Status = ReportHostAdapterError (Packet);\r
514 goto UnmapRequestBuffer;\r
515 }\r
516\r
517 ZeroMem (InDataBuffer, Packet->InTransferLength);\r
518\r
519 Status = VirtioMapAllBytesInSharedBuffer (\r
520 Dev->VirtIo,\r
521 VirtioOperationBusMasterCommonBuffer,\r
522 InDataBuffer,\r
523 Packet->InTransferLength,\r
524 &InDataDeviceAddress,\r
525 &InDataMapping\r
526 );\r
527 if (EFI_ERROR (Status)) {\r
528 Status = ReportHostAdapterError (Packet);\r
529 goto FreeInDataBuffer;\r
530 }\r
531 }\r
532\r
533 //\r
534 // Map the output buffer\r
535 //\r
536 if (Packet->OutTransferLength > 0) {\r
537 Status = VirtioMapAllBytesInSharedBuffer (\r
538 Dev->VirtIo,\r
539 VirtioOperationBusMasterRead,\r
540 Packet->OutDataBuffer,\r
541 Packet->OutTransferLength,\r
542 &OutDataDeviceAddress,\r
543 &OutDataMapping\r
544 );\r
545 if (EFI_ERROR (Status)) {\r
546 Status = ReportHostAdapterError (Packet);\r
547 goto UnmapInDataBuffer;\r
548 }\r
549\r
550 OutDataBufferIsMapped = TRUE;\r
551 }\r
552\r
553 //\r
554 // Response header is bi-direction (we preset with host status and expect\r
555 // the device to update it). Allocate a response buffer which can be mapped\r
556 // to access equally by both processor and device.\r
557 //\r
558 Status = Dev->VirtIo->AllocateSharedPages (\r
559 Dev->VirtIo,\r
560 EFI_SIZE_TO_PAGES (sizeof *Response),\r
561 &ResponseBuffer\r
562 );\r
563 if (EFI_ERROR (Status)) {\r
564 Status = ReportHostAdapterError (Packet);\r
565 goto UnmapOutDataBuffer;\r
566 }\r
567\r
568 Response = ResponseBuffer;\r
569\r
570 ZeroMem ((VOID *)Response, sizeof (*Response));\r
571\r
572 //\r
573 // preset a host status for ourselves that we do not accept as success\r
574 //\r
575 Response->Response = VIRTIO_SCSI_S_FAILURE;\r
576\r
577 //\r
578 // Map the response buffer with BusMasterCommonBuffer so that response\r
579 // buffer can be accessed by both host and device.\r
580 //\r
581 Status = VirtioMapAllBytesInSharedBuffer (\r
582 Dev->VirtIo,\r
583 VirtioOperationBusMasterCommonBuffer,\r
584 ResponseBuffer,\r
585 sizeof (*Response),\r
586 &ResponseDeviceAddress,\r
587 &ResponseMapping\r
588 );\r
589 if (EFI_ERROR (Status)) {\r
590 Status = ReportHostAdapterError (Packet);\r
591 goto FreeResponseBuffer;\r
592 }\r
593\r
594 VirtioPrepare (&Dev->Ring, &Indices);\r
595\r
596 //\r
597 // ensured by VirtioScsiInit() -- this predicate, in combination with the\r
598 // lock-step progress, ensures we don't have to track free descriptors.\r
599 //\r
600 ASSERT (Dev->Ring.QueueSize >= 4);\r
601\r
602 //\r
603 // enqueue Request\r
604 //\r
605 VirtioAppendDesc (\r
606 &Dev->Ring,\r
607 RequestDeviceAddress,\r
608 sizeof Request,\r
609 VRING_DESC_F_NEXT,\r
610 &Indices\r
611 );\r
612\r
613 //\r
614 // enqueue "dataout" if any\r
615 //\r
616 if (Packet->OutTransferLength > 0) {\r
617 VirtioAppendDesc (\r
618 &Dev->Ring,\r
619 OutDataDeviceAddress,\r
620 Packet->OutTransferLength,\r
621 VRING_DESC_F_NEXT,\r
622 &Indices\r
623 );\r
624 }\r
625\r
626 //\r
627 // enqueue Response, to be written by the host\r
628 //\r
629 VirtioAppendDesc (\r
630 &Dev->Ring,\r
631 ResponseDeviceAddress,\r
632 sizeof *Response,\r
633 VRING_DESC_F_WRITE | (Packet->InTransferLength > 0 ? VRING_DESC_F_NEXT : 0),\r
634 &Indices\r
635 );\r
636\r
637 //\r
638 // enqueue "datain" if any, to be written by the host\r
639 //\r
640 if (Packet->InTransferLength > 0) {\r
641 VirtioAppendDesc (\r
642 &Dev->Ring,\r
643 InDataDeviceAddress,\r
644 Packet->InTransferLength,\r
645 VRING_DESC_F_WRITE,\r
646 &Indices\r
647 );\r
648 }\r
649\r
650 // If kicking the host fails, we must fake a host adapter error.\r
651 // EFI_NOT_READY would save us the effort, but it would also suggest that the\r
652 // caller retry.\r
653 //\r
654 if (VirtioFlush (\r
655 Dev->VirtIo,\r
656 VIRTIO_SCSI_REQUEST_QUEUE,\r
657 &Dev->Ring,\r
658 &Indices,\r
659 NULL\r
660 ) != EFI_SUCCESS)\r
661 {\r
662 Status = ReportHostAdapterError (Packet);\r
663 goto UnmapResponseBuffer;\r
664 }\r
665\r
666 Status = ParseResponse (Packet, Response);\r
667\r
668 //\r
669 // If virtio request was successful and it was a CPU read request then we\r
670 // have used an intermediate buffer. Copy the data from intermediate buffer\r
671 // to the final buffer.\r
672 //\r
673 if (InDataBuffer != NULL) {\r
674 CopyMem (Packet->InDataBuffer, InDataBuffer, Packet->InTransferLength);\r
675 }\r
676\r
677UnmapResponseBuffer:\r
678 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, ResponseMapping);\r
679\r
680FreeResponseBuffer:\r
681 Dev->VirtIo->FreeSharedPages (\r
682 Dev->VirtIo,\r
683 EFI_SIZE_TO_PAGES (sizeof *Response),\r
684 ResponseBuffer\r
685 );\r
686\r
687UnmapOutDataBuffer:\r
688 if (OutDataBufferIsMapped) {\r
689 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, OutDataMapping);\r
690 }\r
691\r
692UnmapInDataBuffer:\r
693 if (InDataBuffer != NULL) {\r
694 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, InDataMapping);\r
695 }\r
696\r
697FreeInDataBuffer:\r
698 if (InDataBuffer != NULL) {\r
699 Dev->VirtIo->FreeSharedPages (Dev->VirtIo, InDataNumPages, InDataBuffer);\r
700 }\r
701\r
702UnmapRequestBuffer:\r
703 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, RequestMapping);\r
704\r
705 return Status;\r
706}\r
707\r
708EFI_STATUS\r
709EFIAPI\r
710VirtioScsiGetNextTargetLun (\r
711 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
712 IN OUT UINT8 **TargetPointer,\r
713 IN OUT UINT64 *Lun\r
714 )\r
715{\r
716 UINT8 *Target;\r
717 UINTN Idx;\r
718 UINT16 LastTarget;\r
719 VSCSI_DEV *Dev;\r
720\r
721 //\r
722 // the TargetPointer input parameter is unnecessarily a pointer-to-pointer\r
723 //\r
724 Target = *TargetPointer;\r
725\r
726 //\r
727 // Search for first non-0xFF byte. If not found, return first target & LUN.\r
728 //\r
729 for (Idx = 0; Idx < TARGET_MAX_BYTES && Target[Idx] == 0xFF; ++Idx) {\r
730 }\r
731\r
732 if (Idx == TARGET_MAX_BYTES) {\r
733 SetMem (Target, TARGET_MAX_BYTES, 0x00);\r
734 *Lun = 0;\r
735 return EFI_SUCCESS;\r
736 }\r
737\r
738 //\r
739 // see the TARGET_MAX_BYTES check in "VirtioScsi.h"\r
740 //\r
741 CopyMem (&LastTarget, Target, sizeof LastTarget);\r
742\r
743 //\r
744 // increment (target, LUN) pair if valid on input\r
745 //\r
746 Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);\r
747 if ((LastTarget > Dev->MaxTarget) || (*Lun > Dev->MaxLun)) {\r
748 return EFI_INVALID_PARAMETER;\r
749 }\r
750\r
751 if (*Lun < Dev->MaxLun) {\r
752 ++*Lun;\r
753 return EFI_SUCCESS;\r
754 }\r
755\r
756 if (LastTarget < Dev->MaxTarget) {\r
757 *Lun = 0;\r
758 ++LastTarget;\r
759 CopyMem (Target, &LastTarget, sizeof LastTarget);\r
760 return EFI_SUCCESS;\r
761 }\r
762\r
763 return EFI_NOT_FOUND;\r
764}\r
765\r
766EFI_STATUS\r
767EFIAPI\r
768VirtioScsiBuildDevicePath (\r
769 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
770 IN UINT8 *Target,\r
771 IN UINT64 Lun,\r
772 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath\r
773 )\r
774{\r
775 UINT16 TargetValue;\r
776 VSCSI_DEV *Dev;\r
777 SCSI_DEVICE_PATH *ScsiDevicePath;\r
778\r
779 if (DevicePath == NULL) {\r
780 return EFI_INVALID_PARAMETER;\r
781 }\r
782\r
783 CopyMem (&TargetValue, Target, sizeof TargetValue);\r
784 Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);\r
785 if ((TargetValue > Dev->MaxTarget) || (Lun > Dev->MaxLun) || (Lun > 0xFFFF)) {\r
786 return EFI_NOT_FOUND;\r
787 }\r
788\r
789 ScsiDevicePath = AllocatePool (sizeof *ScsiDevicePath);\r
790 if (ScsiDevicePath == NULL) {\r
791 return EFI_OUT_OF_RESOURCES;\r
792 }\r
793\r
794 ScsiDevicePath->Header.Type = MESSAGING_DEVICE_PATH;\r
795 ScsiDevicePath->Header.SubType = MSG_SCSI_DP;\r
796 ScsiDevicePath->Header.Length[0] = (UINT8)sizeof *ScsiDevicePath;\r
797 ScsiDevicePath->Header.Length[1] = (UINT8)(sizeof *ScsiDevicePath >> 8);\r
798 ScsiDevicePath->Pun = TargetValue;\r
799 ScsiDevicePath->Lun = (UINT16)Lun;\r
800\r
801 *DevicePath = &ScsiDevicePath->Header;\r
802 return EFI_SUCCESS;\r
803}\r
804\r
805EFI_STATUS\r
806EFIAPI\r
807VirtioScsiGetTargetLun (\r
808 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
809 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
810 OUT UINT8 **TargetPointer,\r
811 OUT UINT64 *Lun\r
812 )\r
813{\r
814 SCSI_DEVICE_PATH *ScsiDevicePath;\r
815 VSCSI_DEV *Dev;\r
816 UINT8 *Target;\r
817\r
818 if ((DevicePath == NULL) || (TargetPointer == NULL) || (*TargetPointer == NULL) ||\r
819 (Lun == NULL))\r
820 {\r
821 return EFI_INVALID_PARAMETER;\r
822 }\r
823\r
824 if ((DevicePath->Type != MESSAGING_DEVICE_PATH) ||\r
825 (DevicePath->SubType != MSG_SCSI_DP))\r
826 {\r
827 return EFI_UNSUPPORTED;\r
828 }\r
829\r
830 ScsiDevicePath = (SCSI_DEVICE_PATH *)DevicePath;\r
831 Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);\r
832 if ((ScsiDevicePath->Pun > Dev->MaxTarget) ||\r
833 (ScsiDevicePath->Lun > Dev->MaxLun))\r
834 {\r
835 return EFI_NOT_FOUND;\r
836 }\r
837\r
838 //\r
839 // a) the TargetPointer input parameter is unnecessarily a pointer-to-pointer\r
840 // b) see the TARGET_MAX_BYTES check in "VirtioScsi.h"\r
841 // c) ScsiDevicePath->Pun is an UINT16\r
842 //\r
843 Target = *TargetPointer;\r
844 CopyMem (Target, &ScsiDevicePath->Pun, 2);\r
845 SetMem (Target + 2, TARGET_MAX_BYTES - 2, 0x00);\r
846\r
847 *Lun = ScsiDevicePath->Lun;\r
848 return EFI_SUCCESS;\r
849}\r
850\r
851EFI_STATUS\r
852EFIAPI\r
853VirtioScsiResetChannel (\r
854 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This\r
855 )\r
856{\r
857 return EFI_UNSUPPORTED;\r
858}\r
859\r
860EFI_STATUS\r
861EFIAPI\r
862VirtioScsiResetTargetLun (\r
863 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
864 IN UINT8 *Target,\r
865 IN UINT64 Lun\r
866 )\r
867{\r
868 return EFI_UNSUPPORTED;\r
869}\r
870\r
871EFI_STATUS\r
872EFIAPI\r
873VirtioScsiGetNextTarget (\r
874 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
875 IN OUT UINT8 **TargetPointer\r
876 )\r
877{\r
878 UINT8 *Target;\r
879 UINTN Idx;\r
880 UINT16 LastTarget;\r
881 VSCSI_DEV *Dev;\r
882\r
883 //\r
884 // the TargetPointer input parameter is unnecessarily a pointer-to-pointer\r
885 //\r
886 Target = *TargetPointer;\r
887\r
888 //\r
889 // Search for first non-0xFF byte. If not found, return first target.\r
890 //\r
891 for (Idx = 0; Idx < TARGET_MAX_BYTES && Target[Idx] == 0xFF; ++Idx) {\r
892 }\r
893\r
894 if (Idx == TARGET_MAX_BYTES) {\r
895 SetMem (Target, TARGET_MAX_BYTES, 0x00);\r
896 return EFI_SUCCESS;\r
897 }\r
898\r
899 //\r
900 // see the TARGET_MAX_BYTES check in "VirtioScsi.h"\r
901 //\r
902 CopyMem (&LastTarget, Target, sizeof LastTarget);\r
903\r
904 //\r
905 // increment target if valid on input\r
906 //\r
907 Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);\r
908 if (LastTarget > Dev->MaxTarget) {\r
909 return EFI_INVALID_PARAMETER;\r
910 }\r
911\r
912 if (LastTarget < Dev->MaxTarget) {\r
913 ++LastTarget;\r
914 CopyMem (Target, &LastTarget, sizeof LastTarget);\r
915 return EFI_SUCCESS;\r
916 }\r
917\r
918 return EFI_NOT_FOUND;\r
919}\r
920\r
921STATIC\r
922EFI_STATUS\r
923EFIAPI\r
924VirtioScsiInit (\r
925 IN OUT VSCSI_DEV *Dev\r
926 )\r
927{\r
928 UINT8 NextDevStat;\r
929 EFI_STATUS Status;\r
930 UINT64 RingBaseShift;\r
931 UINT64 Features;\r
932 UINT16 MaxChannel; // for validation only\r
933 UINT32 NumQueues; // for validation only\r
934 UINT16 QueueSize;\r
935\r
936 //\r
937 // Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence.\r
938 //\r
939 NextDevStat = 0; // step 1 -- reset device\r
940 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
941 if (EFI_ERROR (Status)) {\r
942 goto Failed;\r
943 }\r
944\r
945 NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence\r
946 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
947 if (EFI_ERROR (Status)) {\r
948 goto Failed;\r
949 }\r
950\r
951 NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it\r
952 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
953 if (EFI_ERROR (Status)) {\r
954 goto Failed;\r
955 }\r
956\r
957 //\r
958 // Set Page Size - MMIO VirtIo Specific\r
959 //\r
960 Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE);\r
961 if (EFI_ERROR (Status)) {\r
962 goto Failed;\r
963 }\r
964\r
965 //\r
966 // step 4a -- retrieve and validate features\r
967 //\r
968 Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);\r
969 if (EFI_ERROR (Status)) {\r
970 goto Failed;\r
971 }\r
972\r
973 Dev->InOutSupported = (BOOLEAN)((Features & VIRTIO_SCSI_F_INOUT) != 0);\r
974\r
975 Status = VIRTIO_CFG_READ (Dev, MaxChannel, &MaxChannel);\r
976 if (EFI_ERROR (Status)) {\r
977 goto Failed;\r
978 }\r
979\r
980 if (MaxChannel != 0) {\r
981 //\r
982 // this driver is for a single-channel virtio-scsi HBA\r
983 //\r
984 Status = EFI_UNSUPPORTED;\r
985 goto Failed;\r
986 }\r
987\r
988 Status = VIRTIO_CFG_READ (Dev, NumQueues, &NumQueues);\r
989 if (EFI_ERROR (Status)) {\r
990 goto Failed;\r
991 }\r
992\r
993 if (NumQueues < 1) {\r
994 Status = EFI_UNSUPPORTED;\r
995 goto Failed;\r
996 }\r
997\r
998 Status = VIRTIO_CFG_READ (Dev, MaxTarget, &Dev->MaxTarget);\r
999 if (EFI_ERROR (Status)) {\r
1000 goto Failed;\r
1001 }\r
1002\r
1003 if (Dev->MaxTarget > PcdGet16 (PcdVirtioScsiMaxTargetLimit)) {\r
1004 Dev->MaxTarget = PcdGet16 (PcdVirtioScsiMaxTargetLimit);\r
1005 }\r
1006\r
1007 Status = VIRTIO_CFG_READ (Dev, MaxLun, &Dev->MaxLun);\r
1008 if (EFI_ERROR (Status)) {\r
1009 goto Failed;\r
1010 }\r
1011\r
1012 if (Dev->MaxLun > PcdGet32 (PcdVirtioScsiMaxLunLimit)) {\r
1013 Dev->MaxLun = PcdGet32 (PcdVirtioScsiMaxLunLimit);\r
1014 }\r
1015\r
1016 Status = VIRTIO_CFG_READ (Dev, MaxSectors, &Dev->MaxSectors);\r
1017 if (EFI_ERROR (Status)) {\r
1018 goto Failed;\r
1019 }\r
1020\r
1021 if (Dev->MaxSectors < 2) {\r
1022 //\r
1023 // We must be able to halve it for bidirectional transfers\r
1024 // (see EFI_BAD_BUFFER_SIZE in PopulateRequest()).\r
1025 //\r
1026 Status = EFI_UNSUPPORTED;\r
1027 goto Failed;\r
1028 }\r
1029\r
1030 Features &= VIRTIO_SCSI_F_INOUT | VIRTIO_F_VERSION_1 |\r
1031 VIRTIO_F_IOMMU_PLATFORM;\r
1032\r
1033 //\r
1034 // In virtio-1.0, feature negotiation is expected to complete before queue\r
1035 // discovery, and the device can also reject the selected set of features.\r
1036 //\r
1037 if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) {\r
1038 Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat);\r
1039 if (EFI_ERROR (Status)) {\r
1040 goto Failed;\r
1041 }\r
1042 }\r
1043\r
1044 //\r
1045 // step 4b -- allocate request virtqueue\r
1046 //\r
1047 Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, VIRTIO_SCSI_REQUEST_QUEUE);\r
1048 if (EFI_ERROR (Status)) {\r
1049 goto Failed;\r
1050 }\r
1051\r
1052 Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize);\r
1053 if (EFI_ERROR (Status)) {\r
1054 goto Failed;\r
1055 }\r
1056\r
1057 //\r
1058 // VirtioScsiPassThru() uses at most four descriptors\r
1059 //\r
1060 if (QueueSize < 4) {\r
1061 Status = EFI_UNSUPPORTED;\r
1062 goto Failed;\r
1063 }\r
1064\r
1065 Status = VirtioRingInit (Dev->VirtIo, QueueSize, &Dev->Ring);\r
1066 if (EFI_ERROR (Status)) {\r
1067 goto Failed;\r
1068 }\r
1069\r
1070 //\r
1071 // If anything fails from here on, we must release the ring resources\r
1072 //\r
1073 Status = VirtioRingMap (\r
1074 Dev->VirtIo,\r
1075 &Dev->Ring,\r
1076 &RingBaseShift,\r
1077 &Dev->RingMap\r
1078 );\r
1079 if (EFI_ERROR (Status)) {\r
1080 goto ReleaseQueue;\r
1081 }\r
1082\r
1083 //\r
1084 // Additional steps for MMIO: align the queue appropriately, and set the\r
1085 // size. If anything fails from here on, we must unmap the ring resources.\r
1086 //\r
1087 Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);\r
1088 if (EFI_ERROR (Status)) {\r
1089 goto UnmapQueue;\r
1090 }\r
1091\r
1092 Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);\r
1093 if (EFI_ERROR (Status)) {\r
1094 goto UnmapQueue;\r
1095 }\r
1096\r
1097 //\r
1098 // step 4c -- Report GPFN (guest-physical frame number) of queue.\r
1099 //\r
1100 Status = Dev->VirtIo->SetQueueAddress (\r
1101 Dev->VirtIo,\r
1102 &Dev->Ring,\r
1103 RingBaseShift\r
1104 );\r
1105 if (EFI_ERROR (Status)) {\r
1106 goto UnmapQueue;\r
1107 }\r
1108\r
1109 //\r
1110 // step 5 -- Report understood features and guest-tuneables.\r
1111 //\r
1112 if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {\r
1113 Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM);\r
1114 Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features);\r
1115 if (EFI_ERROR (Status)) {\r
1116 goto UnmapQueue;\r
1117 }\r
1118 }\r
1119\r
1120 //\r
1121 // We expect these maximum sizes from the host. Since they are\r
1122 // guest-negotiable, ask for them rather than just checking them.\r
1123 //\r
1124 Status = VIRTIO_CFG_WRITE (Dev, CdbSize, VIRTIO_SCSI_CDB_SIZE);\r
1125 if (EFI_ERROR (Status)) {\r
1126 goto UnmapQueue;\r
1127 }\r
1128\r
1129 Status = VIRTIO_CFG_WRITE (Dev, SenseSize, VIRTIO_SCSI_SENSE_SIZE);\r
1130 if (EFI_ERROR (Status)) {\r
1131 goto UnmapQueue;\r
1132 }\r
1133\r
1134 //\r
1135 // step 6 -- initialization complete\r
1136 //\r
1137 NextDevStat |= VSTAT_DRIVER_OK;\r
1138 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
1139 if (EFI_ERROR (Status)) {\r
1140 goto UnmapQueue;\r
1141 }\r
1142\r
1143 //\r
1144 // populate the exported interface's attributes\r
1145 //\r
1146 Dev->PassThru.Mode = &Dev->PassThruMode;\r
1147 Dev->PassThru.PassThru = &VirtioScsiPassThru;\r
1148 Dev->PassThru.GetNextTargetLun = &VirtioScsiGetNextTargetLun;\r
1149 Dev->PassThru.BuildDevicePath = &VirtioScsiBuildDevicePath;\r
1150 Dev->PassThru.GetTargetLun = &VirtioScsiGetTargetLun;\r
1151 Dev->PassThru.ResetChannel = &VirtioScsiResetChannel;\r
1152 Dev->PassThru.ResetTargetLun = &VirtioScsiResetTargetLun;\r
1153 Dev->PassThru.GetNextTarget = &VirtioScsiGetNextTarget;\r
1154\r
1155 //\r
1156 // AdapterId is a target for which no handle will be created during bus scan.\r
1157 // Prevent any conflict with real devices.\r
1158 //\r
1159 Dev->PassThruMode.AdapterId = 0xFFFFFFFF;\r
1160\r
1161 //\r
1162 // Set both physical and logical attributes for non-RAID SCSI channel. See\r
1163 // Driver Writer's Guide for UEFI 2.3.1 v1.01, 20.1.5 Implementing Extended\r
1164 // SCSI Pass Thru Protocol.\r
1165 //\r
1166 Dev->PassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL |\r
1167 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;\r
1168\r
1169 //\r
1170 // no restriction on transfer buffer alignment\r
1171 //\r
1172 Dev->PassThruMode.IoAlign = 0;\r
1173\r
1174 return EFI_SUCCESS;\r
1175\r
1176UnmapQueue:\r
1177 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap);\r
1178\r
1179ReleaseQueue:\r
1180 VirtioRingUninit (Dev->VirtIo, &Dev->Ring);\r
1181\r
1182Failed:\r
1183 //\r
1184 // Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device\r
1185 // Status. VirtIo access failure here should not mask the original error.\r
1186 //\r
1187 NextDevStat |= VSTAT_FAILED;\r
1188 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
1189\r
1190 Dev->InOutSupported = FALSE;\r
1191 Dev->MaxTarget = 0;\r
1192 Dev->MaxLun = 0;\r
1193 Dev->MaxSectors = 0;\r
1194\r
1195 return Status; // reached only via Failed above\r
1196}\r
1197\r
1198STATIC\r
1199VOID\r
1200EFIAPI\r
1201VirtioScsiUninit (\r
1202 IN OUT VSCSI_DEV *Dev\r
1203 )\r
1204{\r
1205 //\r
1206 // Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. When\r
1207 // VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from\r
1208 // the old comms area.\r
1209 //\r
1210 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);\r
1211\r
1212 Dev->InOutSupported = FALSE;\r
1213 Dev->MaxTarget = 0;\r
1214 Dev->MaxLun = 0;\r
1215 Dev->MaxSectors = 0;\r
1216\r
1217 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap);\r
1218 VirtioRingUninit (Dev->VirtIo, &Dev->Ring);\r
1219\r
1220 SetMem (&Dev->PassThru, sizeof Dev->PassThru, 0x00);\r
1221 SetMem (&Dev->PassThruMode, sizeof Dev->PassThruMode, 0x00);\r
1222}\r
1223\r
1224//\r
1225// Event notification function enqueued by ExitBootServices().\r
1226//\r
1227\r
1228STATIC\r
1229VOID\r
1230EFIAPI\r
1231VirtioScsiExitBoot (\r
1232 IN EFI_EVENT Event,\r
1233 IN VOID *Context\r
1234 )\r
1235{\r
1236 VSCSI_DEV *Dev;\r
1237\r
1238 DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context));\r
1239 //\r
1240 // Reset the device. This causes the hypervisor to forget about the virtio\r
1241 // ring.\r
1242 //\r
1243 // We allocated said ring in EfiBootServicesData type memory, and code\r
1244 // executing after ExitBootServices() is permitted to overwrite it.\r
1245 //\r
1246 Dev = Context;\r
1247 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);\r
1248}\r
1249\r
1250//\r
1251// Probe, start and stop functions of this driver, called by the DXE core for\r
1252// specific devices.\r
1253//\r
1254// The following specifications document these interfaces:\r
1255// - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol\r
1256// - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol\r
1257//\r
1258// The implementation follows:\r
1259// - Driver Writer's Guide for UEFI 2.3.1 v1.01\r
1260// - 5.1.3.4 OpenProtocol() and CloseProtocol()\r
1261// - UEFI Spec 2.3.1 + Errata C\r
1262// - 6.3 Protocol Handler Services\r
1263//\r
1264\r
1265EFI_STATUS\r
1266EFIAPI\r
1267VirtioScsiDriverBindingSupported (\r
1268 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
1269 IN EFI_HANDLE DeviceHandle,\r
1270 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
1271 )\r
1272{\r
1273 EFI_STATUS Status;\r
1274 VIRTIO_DEVICE_PROTOCOL *VirtIo;\r
1275\r
1276 //\r
1277 // Attempt to open the device with the VirtIo set of interfaces. On success,\r
1278 // the protocol is "instantiated" for the VirtIo device. Covers duplicate open\r
1279 // attempts (EFI_ALREADY_STARTED).\r
1280 //\r
1281 Status = gBS->OpenProtocol (\r
1282 DeviceHandle, // candidate device\r
1283 &gVirtioDeviceProtocolGuid, // for generic VirtIo access\r
1284 (VOID **)&VirtIo, // handle to instantiate\r
1285 This->DriverBindingHandle, // requestor driver identity\r
1286 DeviceHandle, // ControllerHandle, according to\r
1287 // the UEFI Driver Model\r
1288 EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive VirtIo access to\r
1289 // the device; to be released\r
1290 );\r
1291 if (EFI_ERROR (Status)) {\r
1292 return Status;\r
1293 }\r
1294\r
1295 if (VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_SCSI_HOST) {\r
1296 Status = EFI_UNSUPPORTED;\r
1297 }\r
1298\r
1299 //\r
1300 // We needed VirtIo access only transitorily, to see whether we support the\r
1301 // device or not.\r
1302 //\r
1303 gBS->CloseProtocol (\r
1304 DeviceHandle,\r
1305 &gVirtioDeviceProtocolGuid,\r
1306 This->DriverBindingHandle,\r
1307 DeviceHandle\r
1308 );\r
1309 return Status;\r
1310}\r
1311\r
1312EFI_STATUS\r
1313EFIAPI\r
1314VirtioScsiDriverBindingStart (\r
1315 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
1316 IN EFI_HANDLE DeviceHandle,\r
1317 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
1318 )\r
1319{\r
1320 VSCSI_DEV *Dev;\r
1321 EFI_STATUS Status;\r
1322\r
1323 Dev = (VSCSI_DEV *)AllocateZeroPool (sizeof *Dev);\r
1324 if (Dev == NULL) {\r
1325 return EFI_OUT_OF_RESOURCES;\r
1326 }\r
1327\r
1328 Status = gBS->OpenProtocol (\r
1329 DeviceHandle,\r
1330 &gVirtioDeviceProtocolGuid,\r
1331 (VOID **)&Dev->VirtIo,\r
1332 This->DriverBindingHandle,\r
1333 DeviceHandle,\r
1334 EFI_OPEN_PROTOCOL_BY_DRIVER\r
1335 );\r
1336 if (EFI_ERROR (Status)) {\r
1337 goto FreeVirtioScsi;\r
1338 }\r
1339\r
1340 //\r
1341 // VirtIo access granted, configure virtio-scsi device.\r
1342 //\r
1343 Status = VirtioScsiInit (Dev);\r
1344 if (EFI_ERROR (Status)) {\r
1345 goto CloseVirtIo;\r
1346 }\r
1347\r
1348 Status = gBS->CreateEvent (\r
1349 EVT_SIGNAL_EXIT_BOOT_SERVICES,\r
1350 TPL_CALLBACK,\r
1351 &VirtioScsiExitBoot,\r
1352 Dev,\r
1353 &Dev->ExitBoot\r
1354 );\r
1355 if (EFI_ERROR (Status)) {\r
1356 goto UninitDev;\r
1357 }\r
1358\r
1359 //\r
1360 // Setup complete, attempt to export the driver instance's PassThru\r
1361 // interface.\r
1362 //\r
1363 Dev->Signature = VSCSI_SIG;\r
1364 Status = gBS->InstallProtocolInterface (\r
1365 &DeviceHandle,\r
1366 &gEfiExtScsiPassThruProtocolGuid,\r
1367 EFI_NATIVE_INTERFACE,\r
1368 &Dev->PassThru\r
1369 );\r
1370 if (EFI_ERROR (Status)) {\r
1371 goto CloseExitBoot;\r
1372 }\r
1373\r
1374 return EFI_SUCCESS;\r
1375\r
1376CloseExitBoot:\r
1377 gBS->CloseEvent (Dev->ExitBoot);\r
1378\r
1379UninitDev:\r
1380 VirtioScsiUninit (Dev);\r
1381\r
1382CloseVirtIo:\r
1383 gBS->CloseProtocol (\r
1384 DeviceHandle,\r
1385 &gVirtioDeviceProtocolGuid,\r
1386 This->DriverBindingHandle,\r
1387 DeviceHandle\r
1388 );\r
1389\r
1390FreeVirtioScsi:\r
1391 FreePool (Dev);\r
1392\r
1393 return Status;\r
1394}\r
1395\r
1396EFI_STATUS\r
1397EFIAPI\r
1398VirtioScsiDriverBindingStop (\r
1399 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
1400 IN EFI_HANDLE DeviceHandle,\r
1401 IN UINTN NumberOfChildren,\r
1402 IN EFI_HANDLE *ChildHandleBuffer\r
1403 )\r
1404{\r
1405 EFI_STATUS Status;\r
1406 EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;\r
1407 VSCSI_DEV *Dev;\r
1408\r
1409 Status = gBS->OpenProtocol (\r
1410 DeviceHandle, // candidate device\r
1411 &gEfiExtScsiPassThruProtocolGuid, // retrieve the SCSI iface\r
1412 (VOID **)&PassThru, // target pointer\r
1413 This->DriverBindingHandle, // requestor driver ident.\r
1414 DeviceHandle, // lookup req. for dev.\r
1415 EFI_OPEN_PROTOCOL_GET_PROTOCOL // lookup only, no new ref.\r
1416 );\r
1417 if (EFI_ERROR (Status)) {\r
1418 return Status;\r
1419 }\r
1420\r
1421 Dev = VIRTIO_SCSI_FROM_PASS_THRU (PassThru);\r
1422\r
1423 //\r
1424 // Handle Stop() requests for in-use driver instances gracefully.\r
1425 //\r
1426 Status = gBS->UninstallProtocolInterface (\r
1427 DeviceHandle,\r
1428 &gEfiExtScsiPassThruProtocolGuid,\r
1429 &Dev->PassThru\r
1430 );\r
1431 if (EFI_ERROR (Status)) {\r
1432 return Status;\r
1433 }\r
1434\r
1435 gBS->CloseEvent (Dev->ExitBoot);\r
1436\r
1437 VirtioScsiUninit (Dev);\r
1438\r
1439 gBS->CloseProtocol (\r
1440 DeviceHandle,\r
1441 &gVirtioDeviceProtocolGuid,\r
1442 This->DriverBindingHandle,\r
1443 DeviceHandle\r
1444 );\r
1445\r
1446 FreePool (Dev);\r
1447\r
1448 return EFI_SUCCESS;\r
1449}\r
1450\r
1451//\r
1452// The static object that groups the Supported() (ie. probe), Start() and\r
1453// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata\r
1454// C, 10.1 EFI Driver Binding Protocol.\r
1455//\r
1456STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {\r
1457 &VirtioScsiDriverBindingSupported,\r
1458 &VirtioScsiDriverBindingStart,\r
1459 &VirtioScsiDriverBindingStop,\r
1460 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers\r
1461 NULL, // ImageHandle, to be overwritten by\r
1462 // EfiLibInstallDriverBindingComponentName2() in VirtioScsiEntryPoint()\r
1463 NULL // DriverBindingHandle, ditto\r
1464};\r
1465\r
1466//\r
1467// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and\r
1468// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name\r
1469// in English, for display on standard console devices. This is recommended for\r
1470// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's\r
1471// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.\r
1472//\r
1473// Device type names ("Virtio SCSI Host Device") are not formatted because the\r
1474// driver supports only that device type. Therefore the driver name suffices\r
1475// for unambiguous identification.\r
1476//\r
1477\r
1478STATIC\r
1479EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {\r
1480 { "eng;en", L"Virtio SCSI Host Driver" },\r
1481 { NULL, NULL }\r
1482};\r
1483\r
1484STATIC\r
1485EFI_COMPONENT_NAME_PROTOCOL gComponentName;\r
1486\r
1487EFI_STATUS\r
1488EFIAPI\r
1489VirtioScsiGetDriverName (\r
1490 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
1491 IN CHAR8 *Language,\r
1492 OUT CHAR16 **DriverName\r
1493 )\r
1494{\r
1495 return LookupUnicodeString2 (\r
1496 Language,\r
1497 This->SupportedLanguages,\r
1498 mDriverNameTable,\r
1499 DriverName,\r
1500 (BOOLEAN)(This == &gComponentName) // Iso639Language\r
1501 );\r
1502}\r
1503\r
1504EFI_STATUS\r
1505EFIAPI\r
1506VirtioScsiGetDeviceName (\r
1507 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
1508 IN EFI_HANDLE DeviceHandle,\r
1509 IN EFI_HANDLE ChildHandle,\r
1510 IN CHAR8 *Language,\r
1511 OUT CHAR16 **ControllerName\r
1512 )\r
1513{\r
1514 return EFI_UNSUPPORTED;\r
1515}\r
1516\r
1517STATIC\r
1518EFI_COMPONENT_NAME_PROTOCOL gComponentName = {\r
1519 &VirtioScsiGetDriverName,\r
1520 &VirtioScsiGetDeviceName,\r
1521 "eng" // SupportedLanguages, ISO 639-2 language codes\r
1522};\r
1523\r
1524STATIC\r
1525EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {\r
1526 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)&VirtioScsiGetDriverName,\r
1527 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)&VirtioScsiGetDeviceName,\r
1528 "en" // SupportedLanguages, RFC 4646 language codes\r
1529};\r
1530\r
1531//\r
1532// Entry point of this driver.\r
1533//\r
1534EFI_STATUS\r
1535EFIAPI\r
1536VirtioScsiEntryPoint (\r
1537 IN EFI_HANDLE ImageHandle,\r
1538 IN EFI_SYSTEM_TABLE *SystemTable\r
1539 )\r
1540{\r
1541 return EfiLibInstallDriverBindingComponentName2 (\r
1542 ImageHandle,\r
1543 SystemTable,\r
1544 &gDriverBinding,\r
1545 ImageHandle,\r
1546 &gComponentName,\r
1547 &gComponentName2\r
1548 );\r
1549}\r