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