OvmfPkg/Virtio: take RingBaseShift in SetQueueAddress()
[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
710\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
841 //\r
56f65ed8
OM
842 // Additional steps for MMIO: align the queue appropriately, and set the\r
843 // size. If anything fails from here on, we must release the ring resources.\r
37078a63 844 //\r
56f65ed8
OM
845 Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);\r
846 if (EFI_ERROR (Status)) {\r
847 goto ReleaseQueue;\r
848 }\r
849\r
850 Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);\r
851 if (EFI_ERROR (Status)) {\r
852 goto ReleaseQueue;\r
853 }\r
854\r
855 //\r
856 // step 4c -- Report GPFN (guest-physical frame number) of queue.\r
857 //\r
53a4c604 858 Status = Dev->VirtIo->SetQueueAddress (Dev->VirtIo, &Dev->Ring, 0);\r
37078a63 859 if (EFI_ERROR (Status)) {\r
860 goto ReleaseQueue;\r
861 }\r
862\r
863 //\r
39c2d339 864 // step 5 -- Report understood features and guest-tuneables.\r
37078a63 865 //\r
39c2d339
LE
866 if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {\r
867 Features &= ~(UINT64)VIRTIO_F_VERSION_1;\r
868 Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features);\r
869 if (EFI_ERROR (Status)) {\r
870 goto ReleaseQueue;\r
871 }\r
37078a63 872 }\r
873\r
874 //\r
875 // We expect these maximum sizes from the host. Since they are\r
876 // guest-negotiable, ask for them rather than just checking them.\r
877 //\r
56f65ed8 878 Status = VIRTIO_CFG_WRITE (Dev, CdbSize, VIRTIO_SCSI_CDB_SIZE);\r
37078a63 879 if (EFI_ERROR (Status)) {\r
880 goto ReleaseQueue;\r
881 }\r
56f65ed8 882 Status = VIRTIO_CFG_WRITE (Dev, SenseSize, VIRTIO_SCSI_SENSE_SIZE);\r
37078a63 883 if (EFI_ERROR (Status)) {\r
884 goto ReleaseQueue;\r
885 }\r
886\r
887 //\r
888 // step 6 -- initialization complete\r
889 //\r
890 NextDevStat |= VSTAT_DRIVER_OK;\r
56f65ed8 891 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
37078a63 892 if (EFI_ERROR (Status)) {\r
893 goto ReleaseQueue;\r
894 }\r
895\r
896 //\r
897 // populate the exported interface's attributes\r
898 //\r
899 Dev->PassThru.Mode = &Dev->PassThruMode;\r
900 Dev->PassThru.PassThru = &VirtioScsiPassThru;\r
901 Dev->PassThru.GetNextTargetLun = &VirtioScsiGetNextTargetLun;\r
902 Dev->PassThru.BuildDevicePath = &VirtioScsiBuildDevicePath;\r
903 Dev->PassThru.GetTargetLun = &VirtioScsiGetTargetLun;\r
904 Dev->PassThru.ResetChannel = &VirtioScsiResetChannel;\r
905 Dev->PassThru.ResetTargetLun = &VirtioScsiResetTargetLun;\r
906 Dev->PassThru.GetNextTarget = &VirtioScsiGetNextTarget;\r
907\r
908 //\r
909 // AdapterId is a target for which no handle will be created during bus scan.\r
910 // Prevent any conflict with real devices.\r
911 //\r
912 Dev->PassThruMode.AdapterId = 0xFFFFFFFF;\r
913\r
914 //\r
915 // Set both physical and logical attributes for non-RAID SCSI channel. See\r
916 // Driver Writer's Guide for UEFI 2.3.1 v1.01, 20.1.5 Implementing Extended\r
917 // SCSI Pass Thru Protocol.\r
918 //\r
919 Dev->PassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL |\r
920 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;\r
921\r
922 //\r
923 // no restriction on transfer buffer alignment\r
924 //\r
925 Dev->PassThruMode.IoAlign = 0;\r
926\r
927 return EFI_SUCCESS;\r
928\r
929ReleaseQueue:\r
fc2c1543 930 VirtioRingUninit (Dev->VirtIo, &Dev->Ring);\r
37078a63 931\r
932Failed:\r
933 //\r
934 // Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device\r
56f65ed8 935 // Status. VirtIo access failure here should not mask the original error.\r
37078a63 936 //\r
937 NextDevStat |= VSTAT_FAILED;\r
56f65ed8 938 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);\r
37078a63 939\r
940 Dev->InOutSupported = FALSE;\r
941 Dev->MaxTarget = 0;\r
942 Dev->MaxLun = 0;\r
943 Dev->MaxSectors = 0;\r
944\r
945 return Status; // reached only via Failed above\r
946}\r
947\r
948\r
37078a63 949STATIC\r
950VOID\r
951EFIAPI\r
952VirtioScsiUninit (\r
953 IN OUT VSCSI_DEV *Dev\r
954 )\r
955{\r
956 //\r
957 // Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. When\r
958 // VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from\r
959 // the old comms area.\r
960 //\r
56f65ed8 961 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);\r
37078a63 962\r
963 Dev->InOutSupported = FALSE;\r
964 Dev->MaxTarget = 0;\r
965 Dev->MaxLun = 0;\r
966 Dev->MaxSectors = 0;\r
967\r
fc2c1543 968 VirtioRingUninit (Dev->VirtIo, &Dev->Ring);\r
37078a63 969\r
970 SetMem (&Dev->PassThru, sizeof Dev->PassThru, 0x00);\r
971 SetMem (&Dev->PassThruMode, sizeof Dev->PassThruMode, 0x00);\r
972}\r
973\r
974\r
fbc80813
LE
975//\r
976// Event notification function enqueued by ExitBootServices().\r
977//\r
978\r
979STATIC\r
980VOID\r
981EFIAPI\r
982VirtioScsiExitBoot (\r
983 IN EFI_EVENT Event,\r
984 IN VOID *Context\r
985 )\r
986{\r
987 VSCSI_DEV *Dev;\r
988\r
989 //\r
990 // Reset the device. This causes the hypervisor to forget about the virtio\r
991 // ring.\r
992 //\r
993 // We allocated said ring in EfiBootServicesData type memory, and code\r
994 // executing after ExitBootServices() is permitted to overwrite it.\r
995 //\r
996 Dev = Context;\r
997 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);\r
998}\r
999\r
1000\r
37078a63 1001//\r
1002// Probe, start and stop functions of this driver, called by the DXE core for\r
1003// specific devices.\r
1004//\r
1005// The following specifications document these interfaces:\r
1006// - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol\r
1007// - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol\r
1008//\r
1009// The implementation follows:\r
1010// - Driver Writer's Guide for UEFI 2.3.1 v1.01\r
1011// - 5.1.3.4 OpenProtocol() and CloseProtocol()\r
37078a63 1012// - UEFI Spec 2.3.1 + Errata C\r
1013// - 6.3 Protocol Handler Services\r
37078a63 1014//\r
1015\r
1016EFI_STATUS\r
1017EFIAPI\r
1018VirtioScsiDriverBindingSupported (\r
1019 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
1020 IN EFI_HANDLE DeviceHandle,\r
1021 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
1022 )\r
1023{\r
56f65ed8
OM
1024 EFI_STATUS Status;\r
1025 VIRTIO_DEVICE_PROTOCOL *VirtIo;\r
37078a63 1026\r
1027 //\r
56f65ed8
OM
1028 // Attempt to open the device with the VirtIo set of interfaces. On success,\r
1029 // the protocol is "instantiated" for the VirtIo device. Covers duplicate open\r
37078a63 1030 // attempts (EFI_ALREADY_STARTED).\r
1031 //\r
1032 Status = gBS->OpenProtocol (\r
1033 DeviceHandle, // candidate device\r
56f65ed8
OM
1034 &gVirtioDeviceProtocolGuid, // for generic VirtIo access\r
1035 (VOID **)&VirtIo, // handle to instantiate\r
37078a63 1036 This->DriverBindingHandle, // requestor driver identity\r
1037 DeviceHandle, // ControllerHandle, according to\r
1038 // the UEFI Driver Model\r
56f65ed8 1039 EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive VirtIo access to\r
37078a63 1040 // the device; to be released\r
1041 );\r
1042 if (EFI_ERROR (Status)) {\r
1043 return Status;\r
1044 }\r
1045\r
56f65ed8
OM
1046 if (VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_SCSI_HOST) {\r
1047 Status = EFI_UNSUPPORTED;\r
37078a63 1048 }\r
1049\r
1050 //\r
56f65ed8 1051 // We needed VirtIo access only transitorily, to see whether we support the\r
37078a63 1052 // device or not.\r
1053 //\r
56f65ed8 1054 gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,\r
37078a63 1055 This->DriverBindingHandle, DeviceHandle);\r
1056 return Status;\r
1057}\r
1058\r
1059\r
1060EFI_STATUS\r
1061EFIAPI\r
1062VirtioScsiDriverBindingStart (\r
1063 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
1064 IN EFI_HANDLE DeviceHandle,\r
1065 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
1066 )\r
1067{\r
1068 VSCSI_DEV *Dev;\r
1069 EFI_STATUS Status;\r
1070\r
1071 Dev = (VSCSI_DEV *) AllocateZeroPool (sizeof *Dev);\r
1072 if (Dev == NULL) {\r
1073 return EFI_OUT_OF_RESOURCES;\r
1074 }\r
1075\r
56f65ed8
OM
1076 Status = gBS->OpenProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,\r
1077 (VOID **)&Dev->VirtIo, This->DriverBindingHandle,\r
37078a63 1078 DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);\r
1079 if (EFI_ERROR (Status)) {\r
1080 goto FreeVirtioScsi;\r
1081 }\r
1082\r
1083 //\r
56f65ed8 1084 // VirtIo access granted, configure virtio-scsi device.\r
37078a63 1085 //\r
1086 Status = VirtioScsiInit (Dev);\r
1087 if (EFI_ERROR (Status)) {\r
56f65ed8 1088 goto CloseVirtIo;\r
37078a63 1089 }\r
1090\r
fbc80813
LE
1091 Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,\r
1092 &VirtioScsiExitBoot, Dev, &Dev->ExitBoot);\r
1093 if (EFI_ERROR (Status)) {\r
1094 goto UninitDev;\r
1095 }\r
1096\r
37078a63 1097 //\r
1098 // Setup complete, attempt to export the driver instance's PassThru\r
1099 // interface.\r
1100 //\r
1101 Dev->Signature = VSCSI_SIG;\r
1102 Status = gBS->InstallProtocolInterface (&DeviceHandle,\r
1103 &gEfiExtScsiPassThruProtocolGuid, EFI_NATIVE_INTERFACE,\r
1104 &Dev->PassThru);\r
1105 if (EFI_ERROR (Status)) {\r
fbc80813 1106 goto CloseExitBoot;\r
37078a63 1107 }\r
1108\r
1109 return EFI_SUCCESS;\r
1110\r
fbc80813
LE
1111CloseExitBoot:\r
1112 gBS->CloseEvent (Dev->ExitBoot);\r
1113\r
37078a63 1114UninitDev:\r
1115 VirtioScsiUninit (Dev);\r
1116\r
56f65ed8
OM
1117CloseVirtIo:\r
1118 gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,\r
37078a63 1119 This->DriverBindingHandle, DeviceHandle);\r
1120\r
1121FreeVirtioScsi:\r
1122 FreePool (Dev);\r
1123\r
1124 return Status;\r
1125}\r
1126\r
1127\r
1128EFI_STATUS\r
1129EFIAPI\r
1130VirtioScsiDriverBindingStop (\r
1131 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
1132 IN EFI_HANDLE DeviceHandle,\r
1133 IN UINTN NumberOfChildren,\r
1134 IN EFI_HANDLE *ChildHandleBuffer\r
1135 )\r
1136{\r
1137 EFI_STATUS Status;\r
1138 EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;\r
1139 VSCSI_DEV *Dev;\r
1140\r
1141 Status = gBS->OpenProtocol (\r
1142 DeviceHandle, // candidate device\r
1143 &gEfiExtScsiPassThruProtocolGuid, // retrieve the SCSI iface\r
1144 (VOID **)&PassThru, // target pointer\r
1145 This->DriverBindingHandle, // requestor driver ident.\r
1146 DeviceHandle, // lookup req. for dev.\r
1147 EFI_OPEN_PROTOCOL_GET_PROTOCOL // lookup only, no new ref.\r
1148 );\r
1149 if (EFI_ERROR (Status)) {\r
1150 return Status;\r
1151 }\r
1152\r
1153 Dev = VIRTIO_SCSI_FROM_PASS_THRU (PassThru);\r
1154\r
1155 //\r
1156 // Handle Stop() requests for in-use driver instances gracefully.\r
1157 //\r
1158 Status = gBS->UninstallProtocolInterface (DeviceHandle,\r
1159 &gEfiExtScsiPassThruProtocolGuid, &Dev->PassThru);\r
1160 if (EFI_ERROR (Status)) {\r
1161 return Status;\r
1162 }\r
1163\r
fbc80813
LE
1164 gBS->CloseEvent (Dev->ExitBoot);\r
1165\r
37078a63 1166 VirtioScsiUninit (Dev);\r
1167\r
56f65ed8 1168 gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,\r
37078a63 1169 This->DriverBindingHandle, DeviceHandle);\r
1170\r
1171 FreePool (Dev);\r
1172\r
1173 return EFI_SUCCESS;\r
1174}\r
1175\r
1176\r
1177//\r
1178// The static object that groups the Supported() (ie. probe), Start() and\r
1179// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata\r
1180// C, 10.1 EFI Driver Binding Protocol.\r
1181//\r
1182STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {\r
1183 &VirtioScsiDriverBindingSupported,\r
1184 &VirtioScsiDriverBindingStart,\r
1185 &VirtioScsiDriverBindingStop,\r
1186 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers\r
1187 NULL, // ImageHandle, to be overwritten by\r
1188 // EfiLibInstallDriverBindingComponentName2() in VirtioScsiEntryPoint()\r
1189 NULL // DriverBindingHandle, ditto\r
1190};\r
1191\r
1192\r
1193//\r
1194// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and\r
1195// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name\r
1196// in English, for display on standard console devices. This is recommended for\r
1197// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's\r
1198// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.\r
1199//\r
1200// Device type names ("Virtio SCSI Host Device") are not formatted because the\r
1201// driver supports only that device type. Therefore the driver name suffices\r
1202// for unambiguous identification.\r
1203//\r
1204\r
1205STATIC\r
1206EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {\r
1207 { "eng;en", L"Virtio SCSI Host Driver" },\r
1208 { NULL, NULL }\r
1209};\r
1210\r
1211STATIC\r
1212EFI_COMPONENT_NAME_PROTOCOL gComponentName;\r
1213\r
1214EFI_STATUS\r
1215EFIAPI\r
1216VirtioScsiGetDriverName (\r
1217 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
1218 IN CHAR8 *Language,\r
1219 OUT CHAR16 **DriverName\r
1220 )\r
1221{\r
1222 return LookupUnicodeString2 (\r
1223 Language,\r
1224 This->SupportedLanguages,\r
1225 mDriverNameTable,\r
1226 DriverName,\r
1227 (BOOLEAN)(This == &gComponentName) // Iso639Language\r
1228 );\r
1229}\r
1230\r
1231EFI_STATUS\r
1232EFIAPI\r
1233VirtioScsiGetDeviceName (\r
1234 IN EFI_COMPONENT_NAME_PROTOCOL *This,\r
1235 IN EFI_HANDLE DeviceHandle,\r
1236 IN EFI_HANDLE ChildHandle,\r
1237 IN CHAR8 *Language,\r
1238 OUT CHAR16 **ControllerName\r
1239 )\r
1240{\r
1241 return EFI_UNSUPPORTED;\r
1242}\r
1243\r
1244STATIC\r
1245EFI_COMPONENT_NAME_PROTOCOL gComponentName = {\r
1246 &VirtioScsiGetDriverName,\r
1247 &VirtioScsiGetDeviceName,\r
1248 "eng" // SupportedLanguages, ISO 639-2 language codes\r
1249};\r
1250\r
1251STATIC\r
1252EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {\r
1253 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &VirtioScsiGetDriverName,\r
1254 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &VirtioScsiGetDeviceName,\r
1255 "en" // SupportedLanguages, RFC 4646 language codes\r
1256};\r
1257\r
1258\r
1259//\r
1260// Entry point of this driver.\r
1261//\r
1262EFI_STATUS\r
1263EFIAPI\r
1264VirtioScsiEntryPoint (\r
1265 IN EFI_HANDLE ImageHandle,\r
1266 IN EFI_SYSTEM_TABLE *SystemTable\r
1267 )\r
1268{\r
1269 return EfiLibInstallDriverBindingComponentName2 (\r
1270 ImageHandle,\r
1271 SystemTable,\r
1272 &gDriverBinding,\r
1273 ImageHandle,\r
1274 &gComponentName,\r
1275 &gComponentName2\r
1276 );\r
1277}\r