]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/VirtioScsiDxe/VirtioScsi.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[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
ac0a286f 79#define VIRTIO_CFG_READ(Dev, Field, Pointer) ((Dev)->VirtIo->ReadDevice ( \\r
ece77e40
OM
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
37078a63 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
1b15eb06 93// ReportHostAdapterError() below.\r
37078a63 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
ac0a286f 155 UINTN Idx;\r
37078a63 156\r
157 if (\r
158 //\r
159 // bidirectional transfer was requested, but the host doesn't support it\r
160 //\r
ac0a286f 161 ((Packet->InTransferLength > 0) && (Packet->OutTransferLength > 0) &&\r
37078a63 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
ac0a286f 167 (Target > 0xFF) || (Lun >= 0x4000) ||\r
37078a63 168\r
169 //\r
170 // Command Descriptor Block bigger than VIRTIO_SCSI_CDB_SIZE\r
171 //\r
ac0a286f 172 (Packet->CdbLength > VIRTIO_SCSI_CDB_SIZE) ||\r
37078a63 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
ac0a286f
MK
178 ((UINT64)Packet->InTransferLength + Packet->OutTransferLength > SIZE_1GB)\r
179 )\r
180 {\r
37078a63 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
ac0a286f 191 (Target > Dev->MaxTarget) || (Lun > Dev->MaxLun) ||\r
37078a63 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
ac0a286f 197 (Packet->DataDirection > EFI_EXT_SCSI_DATA_DIRECTION_BIDIRECTIONAL) ||\r
37078a63 198\r
199 //\r
200 // trying to receive, but destination pointer is NULL, or contradicting\r
201 // transfer direction\r
202 //\r
ac0a286f
MK
203 ((Packet->InTransferLength > 0) &&\r
204 ((Packet->InDataBuffer == NULL) ||\r
205 (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_WRITE)\r
206 )\r
207 ) ||\r
37078a63 208\r
209 //\r
210 // trying to send, but source pointer is NULL, or contradicting transfer\r
211 // direction\r
212 //\r
ac0a286f
MK
213 ((Packet->OutTransferLength > 0) &&\r
214 ((Packet->OutDataBuffer == NULL) ||\r
215 (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ)\r
37078a63 216 )\r
ac0a286f
MK
217 )\r
218 )\r
219 {\r
37078a63 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
ac0a286f
MK
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
37078a63 236 Packet->InTransferLength = (Dev->MaxSectors / 2) * 512;\r
237 Packet->OutTransferLength = (Dev->MaxSectors / 2) * 512;\r
238 Packet->HostAdapterStatus =\r
ac0a286f
MK
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
37078a63 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
ac0a286f
MK
250 Request->Lun[1] = (UINT8)Target;\r
251 Request->Lun[2] = (UINT8)(((UINT32)Lun >> 8) | 0x40);\r
252 Request->Lun[3] = (UINT8)Lun;\r
37078a63 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
ac0a286f 259 Request->Cdb[Idx] = ((UINT8 *)Packet->Cdb)[Idx];\r
37078a63 260 }\r
261\r
262 return EFI_SUCCESS;\r
263}\r
264\r
37078a63 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
ac0a286f
MK
290 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,\r
291 IN CONST volatile VIRTIO_SCSI_RESP *Response\r
37078a63 292 )\r
293{\r
ac0a286f
MK
294 UINTN ResponseSenseLen;\r
295 UINTN Idx;\r
37078a63 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
ac0a286f 302 Packet->SenseDataLength = (UINT8)ResponseSenseLen;\r
37078a63 303 }\r
ac0a286f 304\r
37078a63 305 for (Idx = 0; Idx < Packet->SenseDataLength; ++Idx) {\r
ac0a286f 306 ((UINT8 *)Packet->SenseData)[Idx] = Response->Sense[Idx];\r
37078a63 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
ac0a286f
MK
324 Packet->InTransferLength -= Response->Residual;\r
325 } else {\r
37078a63 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
ac0a286f
MK
340 case VIRTIO_SCSI_S_OK:\r
341 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;\r
342 return EFI_SUCCESS;\r
37078a63 343\r
ac0a286f
MK
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
37078a63 348\r
ac0a286f
MK
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
37078a63 358\r
ac0a286f
MK
359 case VIRTIO_SCSI_S_RESET:\r
360 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET;\r
361 break;\r
37078a63 362\r
ac0a286f
MK
363 case VIRTIO_SCSI_S_BUSY:\r
364 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK;\r
365 return EFI_NOT_READY;\r
37078a63 366\r
ac0a286f
MK
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
37078a63 378 }\r
379\r
380 return EFI_DEVICE_ERROR;\r
381}\r
382\r
1b15eb06
BS
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
37078a63 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
ac0a286f
MK
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
37078a63 428 )\r
429{\r
ac0a286f
MK
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
37078a63 448\r
699a2c30
DB
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
ac0a286f 458 ZeroMem ((VOID *)&Request, sizeof (Request));\r
37078a63 459\r
460 Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);\r
461 CopyMem (&TargetValue, Target, sizeof TargetValue);\r
462\r
ac0a286f 463 InDataBuffer = NULL;\r
9d0d5050 464 OutDataBufferIsMapped = FALSE;\r
ac0a286f 465 InDataNumPages = 0;\r
9d0d5050 466\r
37078a63 467 Status = PopulateRequest (Dev, TargetValue, Lun, Packet, &Request);\r
468 if (EFI_ERROR (Status)) {\r
469 return Status;\r
470 }\r
471\r
9d0d5050
BS
472 //\r
473 // Map the virtio-scsi Request header buffer\r
474 //\r
475 Status = VirtioMapAllBytesInSharedBuffer (\r
476 Dev->VirtIo,\r
477 VirtioOperationBusMasterRead,\r
ac0a286f 478 (VOID *)&Request,\r
9d0d5050
BS
479 sizeof Request,\r
480 &RequestDeviceAddress,\r
ac0a286f
MK
481 &RequestMapping\r
482 );\r
9d0d5050
BS
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
9854561c 498 // In that case simply returning the EFI_DEVICE_ERROR is not sufficient. In\r
9d0d5050
BS
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
ac0a286f
MK
507 Status = Dev->VirtIo->AllocateSharedPages (\r
508 Dev->VirtIo,\r
509 InDataNumPages,\r
510 &InDataBuffer\r
511 );\r
9d0d5050
BS
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
37078a63 571\r
572 //\r
573 // preset a host status for ourselves that we do not accept as success\r
574 //\r
9d0d5050
BS
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
37078a63 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
9d0d5050
BS
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
37078a63 612\r
613 //\r
614 // enqueue "dataout" if any\r
615 //\r
616 if (Packet->OutTransferLength > 0) {\r
9d0d5050
BS
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
37078a63 624 }\r
625\r
626 //\r
627 // enqueue Response, to be written by the host\r
628 //\r
9d0d5050
BS
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
37078a63 636\r
637 //\r
638 // enqueue "datain" if any, to be written by the host\r
639 //\r
640 if (Packet->InTransferLength > 0) {\r
9d0d5050
BS
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
37078a63 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
ac0a286f
MK
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
9d0d5050
BS
662 Status = ReportHostAdapterError (Packet);\r
663 goto UnmapResponseBuffer;\r
37078a63 664 }\r
665\r
9d0d5050
BS
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
37078a63 706}\r
707\r
37078a63 708EFI_STATUS\r
709EFIAPI\r
710VirtioScsiGetNextTargetLun (\r
ac0a286f
MK
711 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
712 IN OUT UINT8 **TargetPointer,\r
713 IN OUT UINT64 *Lun\r
37078a63 714 )\r
715{\r
ac0a286f
MK
716 UINT8 *Target;\r
717 UINTN Idx;\r
718 UINT16 LastTarget;\r
719 VSCSI_DEV *Dev;\r
37078a63 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
ac0a286f
MK
729 for (Idx = 0; Idx < TARGET_MAX_BYTES && Target[Idx] == 0xFF; ++Idx) {\r
730 }\r
731\r
37078a63 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
ac0a286f 747 if ((LastTarget > Dev->MaxTarget) || (*Lun > Dev->MaxLun)) {\r
37078a63 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
37078a63 766EFI_STATUS\r
767EFIAPI\r
768VirtioScsiBuildDevicePath (\r
ac0a286f
MK
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
37078a63 773 )\r
774{\r
ac0a286f
MK
775 UINT16 TargetValue;\r
776 VSCSI_DEV *Dev;\r
777 SCSI_DEVICE_PATH *ScsiDevicePath;\r
37078a63 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
ac0a286f 785 if ((TargetValue > Dev->MaxTarget) || (Lun > Dev->MaxLun) || (Lun > 0xFFFF)) {\r
37078a63 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
ac0a286f
MK
796 ScsiDevicePath->Header.Length[0] = (UINT8)sizeof *ScsiDevicePath;\r
797 ScsiDevicePath->Header.Length[1] = (UINT8)(sizeof *ScsiDevicePath >> 8);\r
37078a63 798 ScsiDevicePath->Pun = TargetValue;\r
ac0a286f 799 ScsiDevicePath->Lun = (UINT16)Lun;\r
37078a63 800\r
801 *DevicePath = &ScsiDevicePath->Header;\r
802 return EFI_SUCCESS;\r
803}\r
804\r
37078a63 805EFI_STATUS\r
806EFIAPI\r
807VirtioScsiGetTargetLun (\r
ac0a286f
MK
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
37078a63 812 )\r
813{\r
ac0a286f
MK
814 SCSI_DEVICE_PATH *ScsiDevicePath;\r
815 VSCSI_DEV *Dev;\r
816 UINT8 *Target;\r
37078a63 817\r
ac0a286f
MK
818 if ((DevicePath == NULL) || (TargetPointer == NULL) || (*TargetPointer == NULL) ||\r
819 (Lun == NULL))\r
820 {\r
37078a63 821 return EFI_INVALID_PARAMETER;\r
822 }\r
823\r
ac0a286f
MK
824 if ((DevicePath->Type != MESSAGING_DEVICE_PATH) ||\r
825 (DevicePath->SubType != MSG_SCSI_DP))\r
826 {\r
37078a63 827 return EFI_UNSUPPORTED;\r
828 }\r
829\r
ac0a286f
MK
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
37078a63 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
37078a63 851EFI_STATUS\r
852EFIAPI\r
853VirtioScsiResetChannel (\r
ac0a286f 854 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This\r
37078a63 855 )\r
856{\r
857 return EFI_UNSUPPORTED;\r
858}\r
859\r
37078a63 860EFI_STATUS\r
861EFIAPI\r
862VirtioScsiResetTargetLun (\r
ac0a286f
MK
863 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
864 IN UINT8 *Target,\r
865 IN UINT64 Lun\r
37078a63 866 )\r
867{\r
868 return EFI_UNSUPPORTED;\r
869}\r
870\r
37078a63 871EFI_STATUS\r
872EFIAPI\r
873VirtioScsiGetNextTarget (\r
ac0a286f
MK
874 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,\r
875 IN OUT UINT8 **TargetPointer\r
37078a63 876 )\r
877{\r
ac0a286f
MK
878 UINT8 *Target;\r
879 UINTN Idx;\r
880 UINT16 LastTarget;\r
881 VSCSI_DEV *Dev;\r
37078a63 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
ac0a286f
MK
891 for (Idx = 0; Idx < TARGET_MAX_BYTES && Target[Idx] == 0xFF; ++Idx) {\r
892 }\r
893\r
37078a63 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
37078a63 921STATIC\r
922EFI_STATUS\r
923EFIAPI\r
924VirtioScsiInit (\r
ac0a286f 925 IN OUT VSCSI_DEV *Dev\r
37078a63 926 )\r
927{\r
ac0a286f
MK
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
37078a63 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
ac0a286f 940 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
37078a63 941 if (EFI_ERROR (Status)) {\r
942 goto Failed;\r
943 }\r
944\r
945 NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence\r
ac0a286f 946 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
37078a63 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
ac0a286f 952 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
56f65ed8
OM
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
37078a63 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
56f65ed8 968 Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);\r
37078a63 969 if (EFI_ERROR (Status)) {\r
970 goto Failed;\r
971 }\r
ac0a286f
MK
972\r
973 Dev->InOutSupported = (BOOLEAN)((Features & VIRTIO_SCSI_F_INOUT) != 0);\r
37078a63 974\r
56f65ed8 975 Status = VIRTIO_CFG_READ (Dev, MaxChannel, &MaxChannel);\r
37078a63 976 if (EFI_ERROR (Status)) {\r
977 goto Failed;\r
978 }\r
ac0a286f 979\r
37078a63 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
56f65ed8 988 Status = VIRTIO_CFG_READ (Dev, NumQueues, &NumQueues);\r
37078a63 989 if (EFI_ERROR (Status)) {\r
990 goto Failed;\r
991 }\r
ac0a286f 992\r
37078a63 993 if (NumQueues < 1) {\r
994 Status = EFI_UNSUPPORTED;\r
995 goto Failed;\r
996 }\r
997\r
56f65ed8 998 Status = VIRTIO_CFG_READ (Dev, MaxTarget, &Dev->MaxTarget);\r
37078a63 999 if (EFI_ERROR (Status)) {\r
1000 goto Failed;\r
1001 }\r
ac0a286f 1002\r
37078a63 1003 if (Dev->MaxTarget > PcdGet16 (PcdVirtioScsiMaxTargetLimit)) {\r
1004 Dev->MaxTarget = PcdGet16 (PcdVirtioScsiMaxTargetLimit);\r
1005 }\r
1006\r
56f65ed8 1007 Status = VIRTIO_CFG_READ (Dev, MaxLun, &Dev->MaxLun);\r
37078a63 1008 if (EFI_ERROR (Status)) {\r
1009 goto Failed;\r
1010 }\r
ac0a286f 1011\r
37078a63 1012 if (Dev->MaxLun > PcdGet32 (PcdVirtioScsiMaxLunLimit)) {\r
1013 Dev->MaxLun = PcdGet32 (PcdVirtioScsiMaxLunLimit);\r
1014 }\r
1015\r
56f65ed8 1016 Status = VIRTIO_CFG_READ (Dev, MaxSectors, &Dev->MaxSectors);\r
37078a63 1017 if (EFI_ERROR (Status)) {\r
1018 goto Failed;\r
1019 }\r
ac0a286f 1020\r
37078a63 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
17cbf735
BS
1030 Features &= VIRTIO_SCSI_F_INOUT | VIRTIO_F_VERSION_1 |\r
1031 VIRTIO_F_IOMMU_PLATFORM;\r
39c2d339
LE
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
37078a63 1044 //\r
1045 // step 4b -- allocate request virtqueue\r
1046 //\r
56f65ed8 1047 Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, VIRTIO_SCSI_REQUEST_QUEUE);\r
37078a63 1048 if (EFI_ERROR (Status)) {\r
1049 goto Failed;\r
1050 }\r
ac0a286f 1051\r
56f65ed8 1052 Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize);\r
37078a63 1053 if (EFI_ERROR (Status)) {\r
1054 goto Failed;\r
1055 }\r
ac0a286f 1056\r
37078a63 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
fc2c1543 1065 Status = VirtioRingInit (Dev->VirtIo, QueueSize, &Dev->Ring);\r
37078a63 1066 if (EFI_ERROR (Status)) {\r
1067 goto Failed;\r
1068 }\r
1069\r
fc2168fe
BS
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
37078a63 1083 //\r
56f65ed8 1084 // Additional steps for MMIO: align the queue appropriately, and set the\r
fc2168fe 1085 // size. If anything fails from here on, we must unmap the ring resources.\r
37078a63 1086 //\r
56f65ed8
OM
1087 Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);\r
1088 if (EFI_ERROR (Status)) {\r
fc2168fe 1089 goto UnmapQueue;\r
56f65ed8
OM
1090 }\r
1091\r
1092 Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);\r
1093 if (EFI_ERROR (Status)) {\r
fc2168fe 1094 goto UnmapQueue;\r
56f65ed8
OM
1095 }\r
1096\r
1097 //\r
1098 // step 4c -- Report GPFN (guest-physical frame number) of queue.\r
1099 //\r
fc2168fe
BS
1100 Status = Dev->VirtIo->SetQueueAddress (\r
1101 Dev->VirtIo,\r
1102 &Dev->Ring,\r
1103 RingBaseShift\r
1104 );\r
37078a63 1105 if (EFI_ERROR (Status)) {\r
fc2168fe 1106 goto UnmapQueue;\r
37078a63 1107 }\r
1108\r
1109 //\r
39c2d339 1110 // step 5 -- Report understood features and guest-tuneables.\r
37078a63 1111 //\r
39c2d339 1112 if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {\r
17cbf735 1113 Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM);\r
ac0a286f 1114 Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features);\r
39c2d339 1115 if (EFI_ERROR (Status)) {\r
fc2168fe 1116 goto UnmapQueue;\r
39c2d339 1117 }\r
37078a63 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
56f65ed8 1124 Status = VIRTIO_CFG_WRITE (Dev, CdbSize, VIRTIO_SCSI_CDB_SIZE);\r
37078a63 1125 if (EFI_ERROR (Status)) {\r
fc2168fe 1126 goto UnmapQueue;\r
37078a63 1127 }\r
ac0a286f 1128\r
56f65ed8 1129 Status = VIRTIO_CFG_WRITE (Dev, SenseSize, VIRTIO_SCSI_SENSE_SIZE);\r
37078a63 1130 if (EFI_ERROR (Status)) {\r
fc2168fe 1131 goto UnmapQueue;\r
37078a63 1132 }\r
1133\r
1134 //\r
1135 // step 6 -- initialization complete\r
1136 //\r
1137 NextDevStat |= VSTAT_DRIVER_OK;\r
ac0a286f 1138 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
37078a63 1139 if (EFI_ERROR (Status)) {\r
fc2168fe 1140 goto UnmapQueue;\r
37078a63 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
fc2168fe
BS
1176UnmapQueue:\r
1177 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap);\r
1178\r
37078a63 1179ReleaseQueue:\r
fc2c1543 1180 VirtioRingUninit (Dev->VirtIo, &Dev->Ring);\r
37078a63 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
56f65ed8 1185 // Status. VirtIo access failure here should not mask the original error.\r
37078a63 1186 //\r
1187 NextDevStat |= VSTAT_FAILED;\r
56f65ed8 1188 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
37078a63 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
37078a63 1198STATIC\r
1199VOID\r
1200EFIAPI\r
1201VirtioScsiUninit (\r
ac0a286f 1202 IN OUT VSCSI_DEV *Dev\r
37078a63 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
56f65ed8 1210 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);\r
37078a63 1211\r
1212 Dev->InOutSupported = FALSE;\r
1213 Dev->MaxTarget = 0;\r
1214 Dev->MaxLun = 0;\r
1215 Dev->MaxSectors = 0;\r
1216\r
fc2168fe 1217 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap);\r
fc2c1543 1218 VirtioRingUninit (Dev->VirtIo, &Dev->Ring);\r
37078a63 1219\r
ac0a286f 1220 SetMem (&Dev->PassThru, sizeof Dev->PassThru, 0x00);\r
37078a63 1221 SetMem (&Dev->PassThruMode, sizeof Dev->PassThruMode, 0x00);\r
1222}\r
1223\r
fbc80813
LE
1224//\r
1225// Event notification function enqueued by ExitBootServices().\r
1226//\r
1227\r
1228STATIC\r
1229VOID\r
1230EFIAPI\r
1231VirtioScsiExitBoot (\r
ac0a286f
MK
1232 IN EFI_EVENT Event,\r
1233 IN VOID *Context\r
fbc80813
LE
1234 )\r
1235{\r
ac0a286f 1236 VSCSI_DEV *Dev;\r
fbc80813 1237\r
bf99bdd1 1238 DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context));\r
fbc80813
LE
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
37078a63 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
37078a63 1261// - UEFI Spec 2.3.1 + Errata C\r
1262// - 6.3 Protocol Handler Services\r
37078a63 1263//\r
1264\r
1265EFI_STATUS\r
1266EFIAPI\r
1267VirtioScsiDriverBindingSupported (\r
ac0a286f
MK
1268 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
1269 IN EFI_HANDLE DeviceHandle,\r
1270 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
37078a63 1271 )\r
1272{\r
ac0a286f
MK
1273 EFI_STATUS Status;\r
1274 VIRTIO_DEVICE_PROTOCOL *VirtIo;\r
37078a63 1275\r
1276 //\r
56f65ed8
OM
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
37078a63 1279 // attempts (EFI_ALREADY_STARTED).\r
1280 //\r
1281 Status = gBS->OpenProtocol (\r
1282 DeviceHandle, // candidate device\r
56f65ed8
OM
1283 &gVirtioDeviceProtocolGuid, // for generic VirtIo access\r
1284 (VOID **)&VirtIo, // handle to instantiate\r
37078a63 1285 This->DriverBindingHandle, // requestor driver identity\r
1286 DeviceHandle, // ControllerHandle, according to\r
1287 // the UEFI Driver Model\r
56f65ed8 1288 EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive VirtIo access to\r
37078a63 1289 // the device; to be released\r
1290 );\r
1291 if (EFI_ERROR (Status)) {\r
1292 return Status;\r
1293 }\r
1294\r
56f65ed8
OM
1295 if (VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_SCSI_HOST) {\r
1296 Status = EFI_UNSUPPORTED;\r
37078a63 1297 }\r
1298\r
1299 //\r
56f65ed8 1300 // We needed VirtIo access only transitorily, to see whether we support the\r
37078a63 1301 // device or not.\r
1302 //\r
ac0a286f
MK
1303 gBS->CloseProtocol (\r
1304 DeviceHandle,\r
1305 &gVirtioDeviceProtocolGuid,\r
1306 This->DriverBindingHandle,\r
1307 DeviceHandle\r
1308 );\r
37078a63 1309 return Status;\r
1310}\r
1311\r
37078a63 1312EFI_STATUS\r
1313EFIAPI\r
1314VirtioScsiDriverBindingStart (\r
ac0a286f
MK
1315 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
1316 IN EFI_HANDLE DeviceHandle,\r
1317 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
37078a63 1318 )\r
1319{\r
ac0a286f
MK
1320 VSCSI_DEV *Dev;\r
1321 EFI_STATUS Status;\r
37078a63 1322\r
ac0a286f 1323 Dev = (VSCSI_DEV *)AllocateZeroPool (sizeof *Dev);\r
37078a63 1324 if (Dev == NULL) {\r
1325 return EFI_OUT_OF_RESOURCES;\r
1326 }\r
1327\r
ac0a286f
MK
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
37078a63 1336 if (EFI_ERROR (Status)) {\r
1337 goto FreeVirtioScsi;\r
1338 }\r
1339\r
1340 //\r
56f65ed8 1341 // VirtIo access granted, configure virtio-scsi device.\r
37078a63 1342 //\r
1343 Status = VirtioScsiInit (Dev);\r
1344 if (EFI_ERROR (Status)) {\r
56f65ed8 1345 goto CloseVirtIo;\r
37078a63 1346 }\r
1347\r
ac0a286f
MK
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
fbc80813
LE
1355 if (EFI_ERROR (Status)) {\r
1356 goto UninitDev;\r
1357 }\r
1358\r
37078a63 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
ac0a286f
MK
1364 Status = gBS->InstallProtocolInterface (\r
1365 &DeviceHandle,\r
1366 &gEfiExtScsiPassThruProtocolGuid,\r
1367 EFI_NATIVE_INTERFACE,\r
1368 &Dev->PassThru\r
1369 );\r
37078a63 1370 if (EFI_ERROR (Status)) {\r
fbc80813 1371 goto CloseExitBoot;\r
37078a63 1372 }\r
1373\r
1374 return EFI_SUCCESS;\r
1375\r
fbc80813
LE
1376CloseExitBoot:\r
1377 gBS->CloseEvent (Dev->ExitBoot);\r
1378\r
37078a63 1379UninitDev:\r
1380 VirtioScsiUninit (Dev);\r
1381\r
56f65ed8 1382CloseVirtIo:\r
ac0a286f
MK
1383 gBS->CloseProtocol (\r
1384 DeviceHandle,\r
1385 &gVirtioDeviceProtocolGuid,\r
1386 This->DriverBindingHandle,\r
1387 DeviceHandle\r
1388 );\r
37078a63 1389\r
1390FreeVirtioScsi:\r
1391 FreePool (Dev);\r
1392\r
1393 return Status;\r
1394}\r
1395\r
37078a63 1396EFI_STATUS\r
1397EFIAPI\r
1398VirtioScsiDriverBindingStop (\r
ac0a286f
MK
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
37078a63 1403 )\r
1404{\r
ac0a286f
MK
1405 EFI_STATUS Status;\r
1406 EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;\r
1407 VSCSI_DEV *Dev;\r
37078a63 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
ac0a286f
MK
1426 Status = gBS->UninstallProtocolInterface (\r
1427 DeviceHandle,\r
1428 &gEfiExtScsiPassThruProtocolGuid,\r
1429 &Dev->PassThru\r
1430 );\r
37078a63 1431 if (EFI_ERROR (Status)) {\r
1432 return Status;\r
1433 }\r
1434\r
fbc80813
LE
1435 gBS->CloseEvent (Dev->ExitBoot);\r
1436\r
37078a63 1437 VirtioScsiUninit (Dev);\r
1438\r
ac0a286f
MK
1439 gBS->CloseProtocol (\r
1440 DeviceHandle,\r
1441 &gVirtioDeviceProtocolGuid,\r
1442 This->DriverBindingHandle,\r
1443 DeviceHandle\r
1444 );\r
37078a63 1445\r
1446 FreePool (Dev);\r
1447\r
1448 return EFI_SUCCESS;\r
1449}\r
1450\r
37078a63 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
ac0a286f 1456STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {\r
37078a63 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
37078a63 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
ac0a286f 1479EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {\r
37078a63 1480 { "eng;en", L"Virtio SCSI Host Driver" },\r
ac0a286f 1481 { NULL, NULL }\r
37078a63 1482};\r
1483\r
1484STATIC\r
ac0a286f 1485EFI_COMPONENT_NAME_PROTOCOL gComponentName;\r
37078a63 1486\r
1487EFI_STATUS\r
1488EFIAPI\r
1489VirtioScsiGetDriverName (\r
ac0a286f
MK
1490 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
1491 IN CHAR8 *Language,\r
1492 OUT CHAR16 **DriverName\r
37078a63 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
ac0a286f
MK
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
37078a63 1512 )\r
1513{\r
1514 return EFI_UNSUPPORTED;\r
1515}\r
1516\r
1517STATIC\r
ac0a286f 1518EFI_COMPONENT_NAME_PROTOCOL gComponentName = {\r
37078a63 1519 &VirtioScsiGetDriverName,\r
1520 &VirtioScsiGetDeviceName,\r
1521 "eng" // SupportedLanguages, ISO 639-2 language codes\r
1522};\r
1523\r
1524STATIC\r
ac0a286f
MK
1525EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {\r
1526 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)&VirtioScsiGetDriverName,\r
1527 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)&VirtioScsiGetDeviceName,\r
37078a63 1528 "en" // SupportedLanguages, RFC 4646 language codes\r
1529};\r
1530\r
37078a63 1531//\r
1532// Entry point of this driver.\r
1533//\r
1534EFI_STATUS\r
1535EFIAPI\r
1536VirtioScsiEntryPoint (\r
ac0a286f
MK
1537 IN EFI_HANDLE ImageHandle,\r
1538 IN EFI_SYSTEM_TABLE *SystemTable\r
37078a63 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