VirtualBox

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

Last change on this file since 81367 was 81367, checked in by vboxsync, 5 years ago

Storage/DevVirtioSCSI.cpp: While trying to diagnose issue seen with EFI boot, fixed problem it had working on Windows. At least Windows was able to find and format a disk

  • Property svn:eol-style set to native
File size: 43.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
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//
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 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
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 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 }
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
389 The function can be used to create a fake host adapter error.
390
391 When VirtioScsiPassThru() is failed due to some reasons then this function
392 can be called to construct a host adapter error.
393
394 @param[out] Packet The Extended SCSI Pass Thru Protocol packet that the host
395 adapter error shall be placed in.
396
397
398 @retval EFI_DEVICE_ERROR The function returns this status code
399 unconditionally, to be propagated by
400 VirtioScsiPassThru().
401
402**/
403STATIC
404EFI_STATUS
405ReportHostAdapterError (
406 OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
407 )
408{
409 Packet->InTransferLength = 0;
410 Packet->OutTransferLength = 0;
411 Packet->HostAdapterStatus = EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OTHER;
412 Packet->TargetStatus = EFI_EXT_SCSI_STATUS_TARGET_GOOD;
413 Packet->SenseDataLength = 0;
414 return EFI_DEVICE_ERROR;
415}
416
417
418//
419// The next seven functions implement EFI_EXT_SCSI_PASS_THRU_PROTOCOL
420// for the virtio-scsi HBA. Refer to UEFI Spec 2.3.1 + Errata C, sections
421// - 14.1 SCSI Driver Model Overview,
422// - 14.7 Extended SCSI Pass Thru Protocol.
423//
424
425EFI_STATUS
426EFIAPI
427VirtioScsiPassThru (
428 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
429 IN UINT8 *Target,
430 IN UINT64 Lun,
431 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
432 IN EFI_EVENT Event OPTIONAL
433 )
434{
435 VSCSI_DEV *Dev;
436 UINT16 TargetValue;
437 EFI_STATUS Status;
438 volatile VIRTIO_SCSI_REQ Request;
439 volatile VIRTIO_SCSI_RESP *Response;
440 VOID *ResponseBuffer;
441 DESC_INDICES Indices;
442#ifndef VBOX
443 VOID *RequestMapping;
444 VOID *ResponseMapping;
445 VOID *InDataMapping;
446 VOID *OutDataMapping;
447#else
448 VOID *RequestMapping = NULL; /**< Initialize or cl.exe fails (gets confused by goto's). */
449 VOID *ResponseMapping = NULL;
450 VOID *InDataMapping = NULL;
451 VOID *OutDataMapping = NULL;
452#endif
453 EFI_PHYSICAL_ADDRESS RequestDeviceAddress;
454 EFI_PHYSICAL_ADDRESS ResponseDeviceAddress;
455#ifndef VBOX
456 EFI_PHYSICAL_ADDRESS InDataDeviceAddress;
457 EFI_PHYSICAL_ADDRESS OutDataDeviceAddress;
458#else
459 EFI_PHYSICAL_ADDRESS InDataDeviceAddress = 0; /**< Initialize or cl.exe fails (gets confused by goto's). */
460 EFI_PHYSICAL_ADDRESS OutDataDeviceAddress = 0;
461#endif
462 VOID *InDataBuffer;
463 UINTN InDataNumPages;
464 BOOLEAN OutDataBufferIsMapped;
465
466 //
467 // Set InDataMapping,OutDataMapping,InDataDeviceAddress and OutDataDeviceAddress to
468 // suppress incorrect compiler/analyzer warnings.
469 //
470 InDataMapping = NULL;
471 OutDataMapping = NULL;
472 InDataDeviceAddress = 0;
473 OutDataDeviceAddress = 0;
474
475 ZeroMem ((VOID*) &Request, sizeof (Request));
476
477 Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);
478 CopyMem (&TargetValue, Target, sizeof TargetValue);
479
480 InDataBuffer = NULL;
481 OutDataBufferIsMapped = FALSE;
482 InDataNumPages = 0;
483
484 Status = PopulateRequest (Dev, TargetValue, Lun, Packet, &Request);
485 if (EFI_ERROR (Status)) {
486 return Status;
487 }
488
489 //
490 // Map the virtio-scsi Request header buffer
491 //
492 Status = VirtioMapAllBytesInSharedBuffer (
493 Dev->VirtIo,
494 VirtioOperationBusMasterRead,
495 (VOID *) &Request,
496 sizeof Request,
497 &RequestDeviceAddress,
498 &RequestMapping);
499 if (EFI_ERROR (Status)) {
500 return ReportHostAdapterError (Packet);
501 }
502
503 //
504 // Map the input buffer
505 //
506 if (Packet->InTransferLength > 0) {
507 //
508 // Allocate a intermediate input buffer. This is mainly to handle the
509 // following case:
510 // * caller submits a bi-directional request
511 // * we perform the request fine
512 // * but we fail to unmap the "InDataMapping"
513 //
514 // In that case simply returing the EFI_DEVICE_ERROR is not sufficient. In
515 // addition to the error code we also need to update Packet fields
516 // accordingly so that we report the full loss of the incoming transfer.
517 //
518 // We allocate a temporary buffer and map it with BusMasterCommonBuffer. If
519 // the Virtio request is successful then we copy the data from temporary
520 // buffer into Packet->InDataBuffer.
521 //
522 InDataNumPages = EFI_SIZE_TO_PAGES ((UINTN)Packet->InTransferLength);
523 Status = Dev->VirtIo->AllocateSharedPages (
524 Dev->VirtIo,
525 InDataNumPages,
526 &InDataBuffer
527 );
528 if (EFI_ERROR (Status)) {
529 Status = ReportHostAdapterError (Packet);
530 goto UnmapRequestBuffer;
531 }
532
533 ZeroMem (InDataBuffer, Packet->InTransferLength);
534
535 Status = VirtioMapAllBytesInSharedBuffer (
536 Dev->VirtIo,
537 VirtioOperationBusMasterCommonBuffer,
538 InDataBuffer,
539 Packet->InTransferLength,
540 &InDataDeviceAddress,
541 &InDataMapping
542 );
543 if (EFI_ERROR (Status)) {
544 Status = ReportHostAdapterError (Packet);
545 goto FreeInDataBuffer;
546 }
547 }
548
549 //
550 // Map the output buffer
551 //
552 if (Packet->OutTransferLength > 0) {
553 Status = VirtioMapAllBytesInSharedBuffer (
554 Dev->VirtIo,
555 VirtioOperationBusMasterRead,
556 Packet->OutDataBuffer,
557 Packet->OutTransferLength,
558 &OutDataDeviceAddress,
559 &OutDataMapping
560 );
561 if (EFI_ERROR (Status)) {
562 Status = ReportHostAdapterError (Packet);
563 goto UnmapInDataBuffer;
564 }
565
566 OutDataBufferIsMapped = TRUE;
567 }
568
569 //
570 // Response header is bi-direction (we preset with host status and expect
571 // the device to update it). Allocate a response buffer which can be mapped
572 // to access equally by both processor and device.
573 //
574 Status = Dev->VirtIo->AllocateSharedPages (
575 Dev->VirtIo,
576 EFI_SIZE_TO_PAGES (sizeof *Response),
577 &ResponseBuffer
578 );
579 if (EFI_ERROR (Status)) {
580 Status = ReportHostAdapterError (Packet);
581 goto UnmapOutDataBuffer;
582 }
583
584 Response = ResponseBuffer;
585
586 ZeroMem ((VOID *)Response, sizeof (*Response));
587
588 //
589 // preset a host status for ourselves that we do not accept as success
590 //
591 Response->Response = VIRTIO_SCSI_S_FAILURE;
592
593 //
594 // Map the response buffer with BusMasterCommonBuffer so that response
595 // buffer can be accessed by both host and device.
596 //
597 Status = VirtioMapAllBytesInSharedBuffer (
598 Dev->VirtIo,
599 VirtioOperationBusMasterCommonBuffer,
600 ResponseBuffer,
601 sizeof (*Response),
602 &ResponseDeviceAddress,
603 &ResponseMapping
604 );
605 if (EFI_ERROR (Status)) {
606 Status = ReportHostAdapterError (Packet);
607 goto FreeResponseBuffer;
608 }
609
610 VirtioPrepare (&Dev->Ring, &Indices);
611
612 //
613 // ensured by VirtioScsiInit() -- this predicate, in combination with the
614 // lock-step progress, ensures we don't have to track free descriptors.
615 //
616 ASSERT (Dev->Ring.QueueSize >= 4);
617
618 //
619 // enqueue Request
620 //
621 VirtioAppendDesc (
622 &Dev->Ring,
623 RequestDeviceAddress,
624 sizeof Request,
625 VRING_DESC_F_NEXT,
626 &Indices
627 );
628
629 //
630 // enqueue "dataout" if any
631 //
632 if (Packet->OutTransferLength > 0) {
633 VirtioAppendDesc (
634 &Dev->Ring,
635 OutDataDeviceAddress,
636 Packet->OutTransferLength,
637 VRING_DESC_F_NEXT,
638 &Indices
639 );
640 }
641
642 //
643 // enqueue Response, to be written by the host
644 //
645 VirtioAppendDesc (
646 &Dev->Ring,
647 ResponseDeviceAddress,
648 sizeof *Response,
649 VRING_DESC_F_WRITE | (Packet->InTransferLength > 0 ? VRING_DESC_F_NEXT : 0),
650 &Indices
651 );
652
653 //
654 // enqueue "datain" if any, to be written by the host
655 //
656 if (Packet->InTransferLength > 0) {
657 VirtioAppendDesc (
658 &Dev->Ring,
659 InDataDeviceAddress,
660 Packet->InTransferLength,
661 VRING_DESC_F_WRITE,
662 &Indices
663 );
664 }
665
666 // If kicking the host fails, we must fake a host adapter error.
667 // EFI_NOT_READY would save us the effort, but it would also suggest that the
668 // caller retry.
669 //
670 if (VirtioFlush (Dev->VirtIo, VIRTIO_SCSI_REQUEST_QUEUE, &Dev->Ring,
671 &Indices, NULL) != EFI_SUCCESS) {
672 Status = ReportHostAdapterError (Packet);
673 goto UnmapResponseBuffer;
674 }
675
676 Status = ParseResponse (Packet, Response);
677
678 //
679 // If virtio request was successful and it was a CPU read request then we
680 // have used an intermediate buffer. Copy the data from intermediate buffer
681 // to the final buffer.
682 //
683 if (InDataBuffer != NULL) {
684 CopyMem (Packet->InDataBuffer, InDataBuffer, Packet->InTransferLength);
685 }
686
687UnmapResponseBuffer:
688 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, ResponseMapping);
689
690FreeResponseBuffer:
691 Dev->VirtIo->FreeSharedPages (
692 Dev->VirtIo,
693 EFI_SIZE_TO_PAGES (sizeof *Response),
694 ResponseBuffer
695 );
696
697UnmapOutDataBuffer:
698 if (OutDataBufferIsMapped) {
699 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, OutDataMapping);
700 }
701
702UnmapInDataBuffer:
703 if (InDataBuffer != NULL) {
704 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, InDataMapping);
705 }
706
707FreeInDataBuffer:
708 if (InDataBuffer != NULL) {
709 Dev->VirtIo->FreeSharedPages (Dev->VirtIo, InDataNumPages, InDataBuffer);
710 }
711
712UnmapRequestBuffer:
713 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, RequestMapping);
714
715 return Status;
716}
717
718
719EFI_STATUS
720EFIAPI
721VirtioScsiGetNextTargetLun (
722 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
723 IN OUT UINT8 **TargetPointer,
724 IN OUT UINT64 *Lun
725 )
726{
727 UINT8 *Target;
728 UINTN Idx;
729 UINT16 LastTarget;
730 VSCSI_DEV *Dev;
731
732 //
733 // the TargetPointer input parameter is unnecessarily a pointer-to-pointer
734 //
735 Target = *TargetPointer;
736
737 //
738 // Search for first non-0xFF byte. If not found, return first target & LUN.
739 //
740 for (Idx = 0; Idx < TARGET_MAX_BYTES && Target[Idx] == 0xFF; ++Idx)
741 ;
742 if (Idx == TARGET_MAX_BYTES) {
743 SetMem (Target, TARGET_MAX_BYTES, 0x00);
744 *Lun = 0;
745 return EFI_SUCCESS;
746 }
747
748 //
749 // see the TARGET_MAX_BYTES check in "VirtioScsi.h"
750 //
751 CopyMem (&LastTarget, Target, sizeof LastTarget);
752
753 //
754 // increment (target, LUN) pair if valid on input
755 //
756 Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);
757 if (LastTarget > Dev->MaxTarget || *Lun > Dev->MaxLun) {
758 return EFI_INVALID_PARAMETER;
759 }
760
761 if (*Lun < Dev->MaxLun) {
762 ++*Lun;
763 return EFI_SUCCESS;
764 }
765
766 if (LastTarget < Dev->MaxTarget) {
767 *Lun = 0;
768 ++LastTarget;
769 CopyMem (Target, &LastTarget, sizeof LastTarget);
770 return EFI_SUCCESS;
771 }
772
773 return EFI_NOT_FOUND;
774}
775
776
777EFI_STATUS
778EFIAPI
779VirtioScsiBuildDevicePath (
780 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
781 IN UINT8 *Target,
782 IN UINT64 Lun,
783 IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
784 )
785{
786 UINT16 TargetValue;
787 VSCSI_DEV *Dev;
788 SCSI_DEVICE_PATH *ScsiDevicePath;
789
790 if (DevicePath == NULL) {
791 return EFI_INVALID_PARAMETER;
792 }
793
794 CopyMem (&TargetValue, Target, sizeof TargetValue);
795 Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);
796 if (TargetValue > Dev->MaxTarget || Lun > Dev->MaxLun || Lun > 0xFFFF) {
797 return EFI_NOT_FOUND;
798 }
799
800 ScsiDevicePath = AllocatePool (sizeof *ScsiDevicePath);
801 if (ScsiDevicePath == NULL) {
802 return EFI_OUT_OF_RESOURCES;
803 }
804
805 ScsiDevicePath->Header.Type = MESSAGING_DEVICE_PATH;
806 ScsiDevicePath->Header.SubType = MSG_SCSI_DP;
807 ScsiDevicePath->Header.Length[0] = (UINT8) sizeof *ScsiDevicePath;
808 ScsiDevicePath->Header.Length[1] = (UINT8) (sizeof *ScsiDevicePath >> 8);
809 ScsiDevicePath->Pun = TargetValue;
810 ScsiDevicePath->Lun = (UINT16) Lun;
811
812 *DevicePath = &ScsiDevicePath->Header;
813 return EFI_SUCCESS;
814}
815
816
817EFI_STATUS
818EFIAPI
819VirtioScsiGetTargetLun (
820 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
821 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
822 OUT UINT8 **TargetPointer,
823 OUT UINT64 *Lun
824 )
825{
826 SCSI_DEVICE_PATH *ScsiDevicePath;
827 VSCSI_DEV *Dev;
828 UINT8 *Target;
829
830 if (DevicePath == NULL || TargetPointer == NULL || *TargetPointer == NULL ||
831 Lun == NULL) {
832 return EFI_INVALID_PARAMETER;
833 }
834
835 if (DevicePath->Type != MESSAGING_DEVICE_PATH ||
836 DevicePath->SubType != MSG_SCSI_DP) {
837 return EFI_UNSUPPORTED;
838 }
839
840 ScsiDevicePath = (SCSI_DEVICE_PATH *) DevicePath;
841 Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);
842 if (ScsiDevicePath->Pun > Dev->MaxTarget ||
843 ScsiDevicePath->Lun > Dev->MaxLun) {
844 return EFI_NOT_FOUND;
845 }
846
847 //
848 // a) the TargetPointer input parameter is unnecessarily a pointer-to-pointer
849 // b) see the TARGET_MAX_BYTES check in "VirtioScsi.h"
850 // c) ScsiDevicePath->Pun is an UINT16
851 //
852 Target = *TargetPointer;
853 CopyMem (Target, &ScsiDevicePath->Pun, 2);
854 SetMem (Target + 2, TARGET_MAX_BYTES - 2, 0x00);
855
856 *Lun = ScsiDevicePath->Lun;
857 return EFI_SUCCESS;
858}
859
860
861EFI_STATUS
862EFIAPI
863VirtioScsiResetChannel (
864 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This
865 )
866{
867 return EFI_UNSUPPORTED;
868}
869
870
871EFI_STATUS
872EFIAPI
873VirtioScsiResetTargetLun (
874 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
875 IN UINT8 *Target,
876 IN UINT64 Lun
877 )
878{
879 return EFI_UNSUPPORTED;
880}
881
882
883EFI_STATUS
884EFIAPI
885VirtioScsiGetNextTarget (
886 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
887 IN OUT UINT8 **TargetPointer
888 )
889{
890 UINT8 *Target;
891 UINTN Idx;
892 UINT16 LastTarget;
893 VSCSI_DEV *Dev;
894
895 //
896 // the TargetPointer input parameter is unnecessarily a pointer-to-pointer
897 //
898 Target = *TargetPointer;
899
900 //
901 // Search for first non-0xFF byte. If not found, return first target.
902 //
903 for (Idx = 0; Idx < TARGET_MAX_BYTES && Target[Idx] == 0xFF; ++Idx)
904 ;
905 if (Idx == TARGET_MAX_BYTES) {
906 SetMem (Target, TARGET_MAX_BYTES, 0x00);
907 return EFI_SUCCESS;
908 }
909
910 //
911 // see the TARGET_MAX_BYTES check in "VirtioScsi.h"
912 //
913 CopyMem (&LastTarget, Target, sizeof LastTarget);
914
915 //
916 // increment target if valid on input
917 //
918 Dev = VIRTIO_SCSI_FROM_PASS_THRU (This);
919 if (LastTarget > Dev->MaxTarget) {
920 return EFI_INVALID_PARAMETER;
921 }
922
923 if (LastTarget < Dev->MaxTarget) {
924 ++LastTarget;
925 CopyMem (Target, &LastTarget, sizeof LastTarget);
926 return EFI_SUCCESS;
927 }
928
929 return EFI_NOT_FOUND;
930}
931
932
933STATIC
934EFI_STATUS
935EFIAPI
936VirtioScsiInit (
937 IN OUT VSCSI_DEV *Dev
938 )
939{
940 UINT8 NextDevStat;
941 EFI_STATUS Status;
942 UINT64 RingBaseShift;
943 UINT64 Features;
944 UINT16 MaxChannel; // for validation only
945 UINT32 NumQueues; // for validation only
946 UINT16 QueueSize;
947
948 //
949 // Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence.
950 //
951 NextDevStat = 0; // step 1 -- reset device
952 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
953 if (EFI_ERROR (Status)) {
954 goto Failed;
955 }
956
957 NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence
958 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
959 if (EFI_ERROR (Status)) {
960 goto Failed;
961 }
962
963 NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it
964 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
965 if (EFI_ERROR (Status)) {
966 goto Failed;
967 }
968
969 //
970 // Set Page Size - MMIO VirtIo Specific
971 //
972 Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE);
973 if (EFI_ERROR (Status)) {
974 goto Failed;
975 }
976
977 //
978 // step 4a -- retrieve and validate features
979 //
980 Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features);
981 if (EFI_ERROR (Status)) {
982 goto Failed;
983 }
984 Dev->InOutSupported = (BOOLEAN) ((Features & VIRTIO_SCSI_F_INOUT) != 0);
985
986 Status = VIRTIO_CFG_READ (Dev, MaxChannel, &MaxChannel);
987 if (EFI_ERROR (Status)) {
988 goto Failed;
989 }
990 if (MaxChannel != 0) {
991 //
992 // this driver is for a single-channel virtio-scsi HBA
993 //
994 Status = EFI_UNSUPPORTED;
995 goto Failed;
996 }
997
998 Status = VIRTIO_CFG_READ (Dev, NumQueues, &NumQueues);
999 if (EFI_ERROR (Status)) {
1000 goto Failed;
1001 }
1002 if (NumQueues < 1) {
1003 Status = EFI_UNSUPPORTED;
1004 goto Failed;
1005 }
1006
1007 Status = VIRTIO_CFG_READ (Dev, MaxTarget, &Dev->MaxTarget);
1008 if (EFI_ERROR (Status)) {
1009 goto Failed;
1010 }
1011 if (Dev->MaxTarget > PcdGet16 (PcdVirtioScsiMaxTargetLimit)) {
1012 Dev->MaxTarget = PcdGet16 (PcdVirtioScsiMaxTargetLimit);
1013 }
1014
1015 Status = VIRTIO_CFG_READ (Dev, MaxLun, &Dev->MaxLun);
1016 if (EFI_ERROR (Status)) {
1017 goto Failed;
1018 }
1019 if (Dev->MaxLun > PcdGet32 (PcdVirtioScsiMaxLunLimit)) {
1020 Dev->MaxLun = PcdGet32 (PcdVirtioScsiMaxLunLimit);
1021 }
1022
1023 Status = VIRTIO_CFG_READ (Dev, MaxSectors, &Dev->MaxSectors);
1024 if (EFI_ERROR (Status)) {
1025 goto Failed;
1026 }
1027 if (Dev->MaxSectors < 2) {
1028 //
1029 // We must be able to halve it for bidirectional transfers
1030 // (see EFI_BAD_BUFFER_SIZE in PopulateRequest()).
1031 //
1032 Status = EFI_UNSUPPORTED;
1033 goto Failed;
1034 }
1035
1036 Features &= VIRTIO_SCSI_F_INOUT | VIRTIO_F_VERSION_1 |
1037 VIRTIO_F_IOMMU_PLATFORM;
1038
1039 //
1040 // In virtio-1.0, feature negotiation is expected to complete before queue
1041 // discovery, and the device can also reject the selected set of features.
1042 //
1043 if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) {
1044 Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat);
1045 if (EFI_ERROR (Status)) {
1046 goto Failed;
1047 }
1048 }
1049
1050 //
1051 // step 4b -- allocate request virtqueue
1052 //
1053 Status = Dev->VirtIo->SetQueueSel (Dev->VirtIo, VIRTIO_SCSI_REQUEST_QUEUE);
1054 if (EFI_ERROR (Status)) {
1055 goto Failed;
1056 }
1057 Status = Dev->VirtIo->GetQueueNumMax (Dev->VirtIo, &QueueSize);
1058 if (EFI_ERROR (Status)) {
1059 goto Failed;
1060 }
1061 //
1062 // VirtioScsiPassThru() uses at most four descriptors
1063 //
1064 if (QueueSize < 4) {
1065 Status = EFI_UNSUPPORTED;
1066 goto Failed;
1067 }
1068
1069 Status = VirtioRingInit (Dev->VirtIo, QueueSize, &Dev->Ring);
1070 if (EFI_ERROR (Status)) {
1071 goto Failed;
1072 }
1073
1074 //
1075 // If anything fails from here on, we must release the ring resources
1076 //
1077 Status = VirtioRingMap (
1078 Dev->VirtIo,
1079 &Dev->Ring,
1080 &RingBaseShift,
1081 &Dev->RingMap
1082 );
1083 if (EFI_ERROR (Status)) {
1084 goto ReleaseQueue;
1085 }
1086
1087 //
1088 // Additional steps for MMIO: align the queue appropriately, and set the
1089 // size. If anything fails from here on, we must unmap the ring resources.
1090 //
1091 Status = Dev->VirtIo->SetQueueNum (Dev->VirtIo, QueueSize);
1092 if (EFI_ERROR (Status)) {
1093 goto UnmapQueue;
1094 }
1095
1096 Status = Dev->VirtIo->SetQueueAlign (Dev->VirtIo, EFI_PAGE_SIZE);
1097 if (EFI_ERROR (Status)) {
1098 goto UnmapQueue;
1099 }
1100
1101 //
1102 // step 4c -- Report GPFN (guest-physical frame number) of queue.
1103 //
1104 Status = Dev->VirtIo->SetQueueAddress (
1105 Dev->VirtIo,
1106 &Dev->Ring,
1107 RingBaseShift
1108 );
1109 if (EFI_ERROR (Status)) {
1110 goto UnmapQueue;
1111 }
1112
1113 //
1114 // step 5 -- Report understood features and guest-tuneables.
1115 //
1116 if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) {
1117 Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM);
1118 Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features);
1119 if (EFI_ERROR (Status)) {
1120 goto UnmapQueue;
1121 }
1122 }
1123
1124 //
1125 // We expect these maximum sizes from the host. Since they are
1126 // guest-negotiable, ask for them rather than just checking them.
1127 //
1128 Status = VIRTIO_CFG_WRITE (Dev, CdbSize, VIRTIO_SCSI_CDB_SIZE);
1129 if (EFI_ERROR (Status)) {
1130 goto UnmapQueue;
1131 }
1132 Status = VIRTIO_CFG_WRITE (Dev, SenseSize, VIRTIO_SCSI_SENSE_SIZE);
1133 if (EFI_ERROR (Status)) {
1134 goto UnmapQueue;
1135 }
1136
1137 //
1138 // step 6 -- initialization complete
1139 //
1140 NextDevStat |= VSTAT_DRIVER_OK;
1141 Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
1142 if (EFI_ERROR (Status)) {
1143 goto UnmapQueue;
1144 }
1145
1146 //
1147 // populate the exported interface's attributes
1148 //
1149 Dev->PassThru.Mode = &Dev->PassThruMode;
1150 Dev->PassThru.PassThru = &VirtioScsiPassThru;
1151 Dev->PassThru.GetNextTargetLun = &VirtioScsiGetNextTargetLun;
1152 Dev->PassThru.BuildDevicePath = &VirtioScsiBuildDevicePath;
1153 Dev->PassThru.GetTargetLun = &VirtioScsiGetTargetLun;
1154 Dev->PassThru.ResetChannel = &VirtioScsiResetChannel;
1155 Dev->PassThru.ResetTargetLun = &VirtioScsiResetTargetLun;
1156 Dev->PassThru.GetNextTarget = &VirtioScsiGetNextTarget;
1157
1158 //
1159 // AdapterId is a target for which no handle will be created during bus scan.
1160 // Prevent any conflict with real devices.
1161 //
1162 Dev->PassThruMode.AdapterId = 0xFFFFFFFF;
1163
1164 //
1165 // Set both physical and logical attributes for non-RAID SCSI channel. See
1166 // Driver Writer's Guide for UEFI 2.3.1 v1.01, 20.1.5 Implementing Extended
1167 // SCSI Pass Thru Protocol.
1168 //
1169 Dev->PassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL |
1170 EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;
1171
1172 //
1173 // no restriction on transfer buffer alignment
1174 //
1175 Dev->PassThruMode.IoAlign = 0;
1176
1177 return EFI_SUCCESS;
1178
1179UnmapQueue:
1180 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap);
1181
1182ReleaseQueue:
1183 VirtioRingUninit (Dev->VirtIo, &Dev->Ring);
1184
1185Failed:
1186 //
1187 // Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device
1188 // Status. VirtIo access failure here should not mask the original error.
1189 //
1190 NextDevStat |= VSTAT_FAILED;
1191 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat);
1192
1193 Dev->InOutSupported = FALSE;
1194 Dev->MaxTarget = 0;
1195 Dev->MaxLun = 0;
1196 Dev->MaxSectors = 0;
1197
1198 return Status; // reached only via Failed above
1199}
1200
1201
1202STATIC
1203VOID
1204EFIAPI
1205VirtioScsiUninit (
1206 IN OUT VSCSI_DEV *Dev
1207 )
1208{
1209 //
1210 // Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. When
1211 // VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from
1212 // the old comms area.
1213 //
1214 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
1215
1216 Dev->InOutSupported = FALSE;
1217 Dev->MaxTarget = 0;
1218 Dev->MaxLun = 0;
1219 Dev->MaxSectors = 0;
1220
1221 Dev->VirtIo->UnmapSharedBuffer (Dev->VirtIo, Dev->RingMap);
1222 VirtioRingUninit (Dev->VirtIo, &Dev->Ring);
1223
1224 SetMem (&Dev->PassThru, sizeof Dev->PassThru, 0x00);
1225 SetMem (&Dev->PassThruMode, sizeof Dev->PassThruMode, 0x00);
1226}
1227
1228
1229//
1230// Event notification function enqueued by ExitBootServices().
1231//
1232
1233STATIC
1234VOID
1235EFIAPI
1236VirtioScsiExitBoot (
1237 IN EFI_EVENT Event,
1238 IN VOID *Context
1239 )
1240{
1241 VSCSI_DEV *Dev;
1242
1243 DEBUG ((DEBUG_VERBOSE, "%a: Context=0x%p\n", __FUNCTION__, Context));
1244 //
1245 // Reset the device. This causes the hypervisor to forget about the virtio
1246 // ring.
1247 //
1248 // We allocated said ring in EfiBootServicesData type memory, and code
1249 // executing after ExitBootServices() is permitted to overwrite it.
1250 //
1251 Dev = Context;
1252 Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0);
1253}
1254
1255
1256//
1257// Probe, start and stop functions of this driver, called by the DXE core for
1258// specific devices.
1259//
1260// The following specifications document these interfaces:
1261// - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol
1262// - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol
1263//
1264// The implementation follows:
1265// - Driver Writer's Guide for UEFI 2.3.1 v1.01
1266// - 5.1.3.4 OpenProtocol() and CloseProtocol()
1267// - UEFI Spec 2.3.1 + Errata C
1268// - 6.3 Protocol Handler Services
1269//
1270
1271EFI_STATUS
1272EFIAPI
1273VirtioScsiDriverBindingSupported (
1274 IN EFI_DRIVER_BINDING_PROTOCOL *This,
1275 IN EFI_HANDLE DeviceHandle,
1276 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
1277 )
1278{
1279 EFI_STATUS Status;
1280 VIRTIO_DEVICE_PROTOCOL *VirtIo;
1281
1282 //
1283 // Attempt to open the device with the VirtIo set of interfaces. On success,
1284 // the protocol is "instantiated" for the VirtIo device. Covers duplicate open
1285 // attempts (EFI_ALREADY_STARTED).
1286 //
1287 Status = gBS->OpenProtocol (
1288 DeviceHandle, // candidate device
1289 &gVirtioDeviceProtocolGuid, // for generic VirtIo access
1290 (VOID **)&VirtIo, // handle to instantiate
1291 This->DriverBindingHandle, // requestor driver identity
1292 DeviceHandle, // ControllerHandle, according to
1293 // the UEFI Driver Model
1294 EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive VirtIo access to
1295 // the device; to be released
1296 );
1297 if (EFI_ERROR (Status)) {
1298 return Status;
1299 }
1300
1301 if (VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_SCSI_HOST) {
1302 Status = EFI_UNSUPPORTED;
1303 }
1304
1305 //
1306 // We needed VirtIo access only transitorily, to see whether we support the
1307 // device or not.
1308 //
1309 gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
1310 This->DriverBindingHandle, DeviceHandle);
1311 return Status;
1312}
1313
1314
1315EFI_STATUS
1316EFIAPI
1317VirtioScsiDriverBindingStart (
1318 IN EFI_DRIVER_BINDING_PROTOCOL *This,
1319 IN EFI_HANDLE DeviceHandle,
1320 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
1321 )
1322{
1323 VSCSI_DEV *Dev;
1324 EFI_STATUS Status;
1325
1326 Dev = (VSCSI_DEV *) AllocateZeroPool (sizeof *Dev);
1327 if (Dev == NULL) {
1328 return EFI_OUT_OF_RESOURCES;
1329 }
1330
1331 Status = gBS->OpenProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
1332 (VOID **)&Dev->VirtIo, This->DriverBindingHandle,
1333 DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
1334 if (EFI_ERROR (Status)) {
1335 goto FreeVirtioScsi;
1336 }
1337
1338 //
1339 // VirtIo access granted, configure virtio-scsi device.
1340 //
1341 Status = VirtioScsiInit (Dev);
1342 if (EFI_ERROR (Status)) {
1343 goto CloseVirtIo;
1344 }
1345
1346 Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
1347 &VirtioScsiExitBoot, Dev, &Dev->ExitBoot);
1348 if (EFI_ERROR (Status)) {
1349 goto UninitDev;
1350 }
1351
1352 //
1353 // Setup complete, attempt to export the driver instance's PassThru
1354 // interface.
1355 //
1356 Dev->Signature = VSCSI_SIG;
1357 Status = gBS->InstallProtocolInterface (&DeviceHandle,
1358 &gEfiExtScsiPassThruProtocolGuid, EFI_NATIVE_INTERFACE,
1359 &Dev->PassThru);
1360 if (EFI_ERROR (Status)) {
1361 goto CloseExitBoot;
1362 }
1363
1364 return EFI_SUCCESS;
1365
1366CloseExitBoot:
1367 gBS->CloseEvent (Dev->ExitBoot);
1368
1369UninitDev:
1370 VirtioScsiUninit (Dev);
1371
1372CloseVirtIo:
1373 gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
1374 This->DriverBindingHandle, DeviceHandle);
1375
1376FreeVirtioScsi:
1377 FreePool (Dev);
1378
1379 return Status;
1380}
1381
1382
1383EFI_STATUS
1384EFIAPI
1385VirtioScsiDriverBindingStop (
1386 IN EFI_DRIVER_BINDING_PROTOCOL *This,
1387 IN EFI_HANDLE DeviceHandle,
1388 IN UINTN NumberOfChildren,
1389 IN EFI_HANDLE *ChildHandleBuffer
1390 )
1391{
1392 EFI_STATUS Status;
1393 EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;
1394 VSCSI_DEV *Dev;
1395
1396 Status = gBS->OpenProtocol (
1397 DeviceHandle, // candidate device
1398 &gEfiExtScsiPassThruProtocolGuid, // retrieve the SCSI iface
1399 (VOID **)&PassThru, // target pointer
1400 This->DriverBindingHandle, // requestor driver ident.
1401 DeviceHandle, // lookup req. for dev.
1402 EFI_OPEN_PROTOCOL_GET_PROTOCOL // lookup only, no new ref.
1403 );
1404 if (EFI_ERROR (Status)) {
1405 return Status;
1406 }
1407
1408 Dev = VIRTIO_SCSI_FROM_PASS_THRU (PassThru);
1409
1410 //
1411 // Handle Stop() requests for in-use driver instances gracefully.
1412 //
1413 Status = gBS->UninstallProtocolInterface (DeviceHandle,
1414 &gEfiExtScsiPassThruProtocolGuid, &Dev->PassThru);
1415 if (EFI_ERROR (Status)) {
1416 return Status;
1417 }
1418
1419 gBS->CloseEvent (Dev->ExitBoot);
1420
1421 VirtioScsiUninit (Dev);
1422
1423 gBS->CloseProtocol (DeviceHandle, &gVirtioDeviceProtocolGuid,
1424 This->DriverBindingHandle, DeviceHandle);
1425
1426 FreePool (Dev);
1427
1428 return EFI_SUCCESS;
1429}
1430
1431
1432//
1433// The static object that groups the Supported() (ie. probe), Start() and
1434// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata
1435// C, 10.1 EFI Driver Binding Protocol.
1436//
1437STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {
1438 &VirtioScsiDriverBindingSupported,
1439 &VirtioScsiDriverBindingStart,
1440 &VirtioScsiDriverBindingStop,
1441 0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers
1442 NULL, // ImageHandle, to be overwritten by
1443 // EfiLibInstallDriverBindingComponentName2() in VirtioScsiEntryPoint()
1444 NULL // DriverBindingHandle, ditto
1445};
1446
1447
1448//
1449// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and
1450// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name
1451// in English, for display on standard console devices. This is recommended for
1452// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's
1453// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.
1454//
1455// Device type names ("Virtio SCSI Host Device") are not formatted because the
1456// driver supports only that device type. Therefore the driver name suffices
1457// for unambiguous identification.
1458//
1459
1460STATIC
1461EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
1462 { "eng;en", L"Virtio SCSI Host Driver" },
1463 { NULL, NULL }
1464};
1465
1466STATIC
1467EFI_COMPONENT_NAME_PROTOCOL gComponentName;
1468
1469EFI_STATUS
1470EFIAPI
1471VirtioScsiGetDriverName (
1472 IN EFI_COMPONENT_NAME_PROTOCOL *This,
1473 IN CHAR8 *Language,
1474 OUT CHAR16 **DriverName
1475 )
1476{
1477 return LookupUnicodeString2 (
1478 Language,
1479 This->SupportedLanguages,
1480 mDriverNameTable,
1481 DriverName,
1482 (BOOLEAN)(This == &gComponentName) // Iso639Language
1483 );
1484}
1485
1486EFI_STATUS
1487EFIAPI
1488VirtioScsiGetDeviceName (
1489 IN EFI_COMPONENT_NAME_PROTOCOL *This,
1490 IN EFI_HANDLE DeviceHandle,
1491 IN EFI_HANDLE ChildHandle,
1492 IN CHAR8 *Language,
1493 OUT CHAR16 **ControllerName
1494 )
1495{
1496 return EFI_UNSUPPORTED;
1497}
1498
1499STATIC
1500EFI_COMPONENT_NAME_PROTOCOL gComponentName = {
1501 &VirtioScsiGetDriverName,
1502 &VirtioScsiGetDeviceName,
1503 "eng" // SupportedLanguages, ISO 639-2 language codes
1504};
1505
1506STATIC
1507EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {
1508 (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) &VirtioScsiGetDriverName,
1509 (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &VirtioScsiGetDeviceName,
1510 "en" // SupportedLanguages, RFC 4646 language codes
1511};
1512
1513
1514//
1515// Entry point of this driver.
1516//
1517EFI_STATUS
1518EFIAPI
1519VirtioScsiEntryPoint (
1520 IN EFI_HANDLE ImageHandle,
1521 IN EFI_SYSTEM_TABLE *SystemTable
1522 )
1523{
1524 return EfiLibInstallDriverBindingComponentName2 (
1525 ImageHandle,
1526 SystemTable,
1527 &gDriverBinding,
1528 ImageHandle,
1529 &gComponentName,
1530 &gComponentName2
1531 );
1532}
Note: See TracBrowser for help on using the repository browser.

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