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