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