VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/OvmfPkg/VirtioScsiDxe/VirtioScsi.c

Last change on this file was 101291, checked in by vboxsync, 18 months ago

EFI/FirmwareNew: Make edk2-stable202308 build on all supported platforms (using gcc at least, msvc not tested yet), bugref:4643

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette