VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/OvmfPkg/VirtioScsiDxe/VirtioScsi.c@ 109019

Last change on this file since 109019 was 108794, checked in by vboxsync, 4 weeks ago

Devices/EFI/FirmwareNew: Merge edk2-stable202502 from the vendor branch and make it build for the important platforms, bugref:4643

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