1 /** @file
2 
3   This driver produces Virtio Device Protocol instances for Virtio Mmio devices.
4 
5   Copyright (C) 2013, ARM Ltd.
6   Copyright (C) 2017, AMD Inc. All rights reserved.<BR>
7 
8   SPDX-License-Identifier: BSD-2-Clause-Patent
9 
10 **/
11 
12 #include <Library/BaseMemoryLib.h>
13 #include <Library/UefiBootServicesTableLib.h>
14 
15 #include "VirtioMmioDevice.h"
16 
17 STATIC CONST VIRTIO_DEVICE_PROTOCOL mMmioDeviceProtocolTemplate = {
18     0,                                     // Revision
19     0,                                     // SubSystemDeviceId
20     VirtioMmioGetDeviceFeatures,           // GetDeviceFeatures
21     VirtioMmioSetGuestFeatures,            // SetGuestFeatures
22     VirtioMmioSetQueueAddress,             // SetQueueAddress
23     VirtioMmioSetQueueSel,                 // SetQueueSel
24     VirtioMmioSetQueueNotify,              // SetQueueNotify
25     VirtioMmioSetQueueAlignment,           // SetQueueAlign
26     VirtioMmioSetPageSize,                 // SetPageSize
27     VirtioMmioGetQueueSize,                // GetQueueNumMax
28     VirtioMmioSetQueueSize,                // SetQueueNum
29     VirtioMmioGetDeviceStatus,             // GetDeviceStatus
30     VirtioMmioSetDeviceStatus,             // SetDeviceStatus
31     VirtioMmioDeviceWrite,                 // WriteDevice
32     VirtioMmioDeviceRead,                  // ReadDevice
33     VirtioMmioAllocateSharedPages,         // AllocateSharedPages
34     VirtioMmioFreeSharedPages,             // FreeSharedPages
35     VirtioMmioMapSharedBuffer,             // MapSharedBuffer
36     VirtioMmioUnmapSharedBuffer            // UnmapSharedBuffer
37 };
38 
39 /**
40 
41   Initialize the VirtIo MMIO Device
42 
43   @param[in] BaseAddress   Base Address of the VirtIo MMIO Device
44 
45   @param[in, out] Device   The driver instance to configure.
46 
47   @retval EFI_SUCCESS      Setup complete.
48 
49   @retval EFI_UNSUPPORTED  The driver is not a VirtIo MMIO device.
50 
51 **/
52 STATIC
53 EFI_STATUS
54 EFIAPI
VirtioMmioInit(IN PHYSICAL_ADDRESS BaseAddress,IN OUT VIRTIO_MMIO_DEVICE * Device)55 VirtioMmioInit (
56   IN PHYSICAL_ADDRESS        BaseAddress,
57   IN OUT VIRTIO_MMIO_DEVICE *Device
58   )
59 {
60   UINT32     MagicValue;
61   UINT32     VendorId;
62   UINT32     Version;
63 
64   //
65   // Initialize VirtIo Mmio Device
66   //
67   CopyMem (&Device->VirtioDevice, &mMmioDeviceProtocolTemplate,
68         sizeof (VIRTIO_DEVICE_PROTOCOL));
69   Device->BaseAddress = BaseAddress;
70   Device->VirtioDevice.Revision = VIRTIO_SPEC_REVISION (0, 9, 5);
71   Device->VirtioDevice.SubSystemDeviceId =
72           MmioRead32 (BaseAddress + VIRTIO_MMIO_OFFSET_DEVICE_ID);
73 
74   //
75   // Double-check MMIO-specific values
76   //
77   MagicValue = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_MAGIC);
78   if (MagicValue != VIRTIO_MMIO_MAGIC) {
79     return EFI_UNSUPPORTED;
80   }
81 
82   Version = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_VERSION);
83   if (Version != 1) {
84     return EFI_UNSUPPORTED;
85   }
86 
87   //
88   // Double-check MMIO-specific values
89   //
90   VendorId = VIRTIO_CFG_READ (Device, VIRTIO_MMIO_OFFSET_VENDOR_ID);
91   if (VendorId != VIRTIO_VENDOR_ID) {
92     //
93     // The ARM Base and Foundation Models do not report a valid VirtIo VendorId.
94     // They return a value of 0x0 for the VendorId.
95     //
96     DEBUG((EFI_D_WARN, "VirtioMmioInit: Warning: The VendorId (0x%X) does not "
97                        "match the VirtIo VendorId (0x%X).\n",
98                        VendorId, VIRTIO_VENDOR_ID));
99   }
100 
101   return EFI_SUCCESS;
102 }
103 
104 
105 /**
106 
107   Uninitialize the internals of a virtio-mmio device that has been successfully
108   set up with VirtioMmioInit().
109 
110   @param[in, out]  Device  The device to clean up.
111 
112 **/
113 
114 STATIC
115 VOID
116 EFIAPI
VirtioMmioUninit(IN VIRTIO_MMIO_DEVICE * Device)117 VirtioMmioUninit (
118   IN VIRTIO_MMIO_DEVICE *Device
119   )
120 {
121   //
122   // Note: This function mirrors VirtioMmioInit() that does not allocate any
123   //       resources - there's nothing to free here.
124   //
125 }
126 
127 EFI_STATUS
VirtioMmioInstallDevice(IN PHYSICAL_ADDRESS BaseAddress,IN EFI_HANDLE Handle)128 VirtioMmioInstallDevice (
129   IN PHYSICAL_ADDRESS       BaseAddress,
130   IN EFI_HANDLE             Handle
131   )
132 {
133   EFI_STATUS          Status;
134   VIRTIO_MMIO_DEVICE *VirtIo;
135 
136   if (!BaseAddress) {
137     return EFI_INVALID_PARAMETER;
138   }
139   if (Handle == NULL) {
140     return EFI_INVALID_PARAMETER;
141   }
142 
143   //
144   // Allocate VIRTIO_MMIO_DEVICE
145   //
146   VirtIo = AllocateZeroPool (sizeof (VIRTIO_MMIO_DEVICE));
147   if (VirtIo == NULL) {
148     return EFI_OUT_OF_RESOURCES;
149   }
150 
151   VirtIo->Signature = VIRTIO_MMIO_DEVICE_SIGNATURE;
152 
153   Status = VirtioMmioInit (BaseAddress, VirtIo);
154   if (EFI_ERROR (Status)) {
155     goto FreeVirtioMem;
156   }
157 
158   //
159   // Install VIRTIO_DEVICE_PROTOCOL to Handle
160   //
161   Status = gBS->InstallProtocolInterface (&Handle,
162                   &gVirtioDeviceProtocolGuid, EFI_NATIVE_INTERFACE,
163                   &VirtIo->VirtioDevice);
164   if (EFI_ERROR (Status)) {
165     goto UninitVirtio;
166   }
167 
168   return EFI_SUCCESS;
169 
170 UninitVirtio:
171   VirtioMmioUninit (VirtIo);
172 
173 FreeVirtioMem:
174   FreePool (VirtIo);
175   return Status;
176 }
177 
178 EFI_STATUS
VirtioMmioUninstallDevice(IN EFI_HANDLE DeviceHandle)179 VirtioMmioUninstallDevice (
180   IN EFI_HANDLE             DeviceHandle
181   )
182 {
183   VIRTIO_DEVICE_PROTOCOL  *VirtioDevice;
184   VIRTIO_MMIO_DEVICE      *MmioDevice;
185   EFI_STATUS              Status;
186 
187   Status = gBS->OpenProtocol (
188                   DeviceHandle,                  // candidate device
189                   &gVirtioDeviceProtocolGuid,    // retrieve the VirtIo iface
190                   (VOID **)&VirtioDevice,        // target pointer
191                   DeviceHandle,                  // requestor driver identity
192                   DeviceHandle,                  // requesting lookup for dev.
193                   EFI_OPEN_PROTOCOL_GET_PROTOCOL // lookup only, no ref. added
194                   );
195   if (EFI_ERROR (Status)) {
196     return Status;
197   }
198 
199   //
200   // Get the MMIO device from the VirtIo Device instance
201   //
202   MmioDevice = VIRTIO_MMIO_DEVICE_FROM_VIRTIO_DEVICE (VirtioDevice);
203 
204   //
205   // Uninstall the protocol interface
206   //
207   Status = gBS->UninstallProtocolInterface (DeviceHandle,
208       &gVirtioDeviceProtocolGuid, &MmioDevice->VirtioDevice
209       );
210   if (EFI_ERROR (Status)) {
211     return Status;
212   }
213 
214   //
215   // Uninitialize the VirtIo Device
216   //
217   VirtioMmioUninit (MmioDevice);
218   FreePool (MmioDevice);
219 
220   return EFI_SUCCESS;
221 }
222