]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/VirtioFsDxe/Helpers.c
OvmfPkg/VirtioFsDxe: implement virtio device (un)initialization
[mirror_edk2.git] / OvmfPkg / VirtioFsDxe / Helpers.c
1 /** @file
2 Initialization and helper routines for the Virtio Filesystem device.
3
4 Copyright (C) 2020, Red Hat, Inc.
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7 **/
8
9 #include <Library/VirtioLib.h> // Virtio10WriteFeatures()
10
11 #include "VirtioFsDxe.h"
12
13 /**
14 Read the Virtio Filesystem device configuration structure in full.
15
16 @param[in] Virtio The Virtio protocol underlying the VIRTIO_FS object.
17
18 @param[out] Config The fully populated VIRTIO_FS_CONFIG structure.
19
20 @retval EFI_SUCCESS Config has been filled in.
21
22 @return Error codes propagated from Virtio->ReadDevice(). The
23 contents of Config are indeterminate.
24 **/
25 STATIC
26 EFI_STATUS
27 VirtioFsReadConfig (
28 IN VIRTIO_DEVICE_PROTOCOL *Virtio,
29 OUT VIRTIO_FS_CONFIG *Config
30 )
31 {
32 UINTN Idx;
33 EFI_STATUS Status;
34
35 for (Idx = 0; Idx < VIRTIO_FS_TAG_BYTES; Idx++) {
36 Status = Virtio->ReadDevice (
37 Virtio, // This
38 OFFSET_OF (VIRTIO_FS_CONFIG, Tag[Idx]), // FieldOffset
39 sizeof Config->Tag[Idx], // FieldSize
40 sizeof Config->Tag[Idx], // BufferSize
41 &Config->Tag[Idx] // Buffer
42 );
43 if (EFI_ERROR (Status)) {
44 return Status;
45 }
46 }
47
48 Status = Virtio->ReadDevice (
49 Virtio, // This
50 OFFSET_OF (VIRTIO_FS_CONFIG, NumReqQueues), // FieldOffset
51 sizeof Config->NumReqQueues, // FieldSize
52 sizeof Config->NumReqQueues, // BufferSize
53 &Config->NumReqQueues // Buffer
54 );
55 return Status;
56 }
57
58 /**
59 Configure the Virtio Filesystem device underlying VirtioFs.
60
61 @param[in,out] VirtioFs The VIRTIO_FS object for which Virtio communication
62 should be set up. On input, the caller is
63 responsible for VirtioFs->Virtio having been
64 initialized. On output, synchronous Virtio
65 Filesystem commands (primitives) may be submitted to
66 the device.
67
68 @retval EFI_SUCCESS Virtio machinery has been set up.
69
70 @retval EFI_UNSUPPORTED The host-side configuration of the Virtio Filesystem
71 is not supported by this driver.
72
73 @return Error codes from underlying functions.
74 **/
75 EFI_STATUS
76 VirtioFsInit (
77 IN OUT VIRTIO_FS *VirtioFs
78 )
79 {
80 UINT8 NextDevStat;
81 EFI_STATUS Status;
82 UINT64 Features;
83 VIRTIO_FS_CONFIG Config;
84 UINTN Idx;
85 UINT64 RingBaseShift;
86
87 //
88 // Execute virtio-v1.1-cs01-87fa6b5d8155, 3.1.1 Driver Requirements: Device
89 // Initialization.
90 //
91 // 1. Reset the device.
92 //
93 NextDevStat = 0;
94 Status = VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
95 if (EFI_ERROR (Status)) {
96 goto Failed;
97 }
98
99 //
100 // 2. Set the ACKNOWLEDGE status bit [...]
101 //
102 NextDevStat |= VSTAT_ACK;
103 Status = VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
104 if (EFI_ERROR (Status)) {
105 goto Failed;
106 }
107
108 //
109 // 3. Set the DRIVER status bit [...]
110 //
111 NextDevStat |= VSTAT_DRIVER;
112 Status = VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
113 if (EFI_ERROR (Status)) {
114 goto Failed;
115 }
116
117 //
118 // 4. Read device feature bits...
119 //
120 Status = VirtioFs->Virtio->GetDeviceFeatures (VirtioFs->Virtio, &Features);
121 if (EFI_ERROR (Status)) {
122 goto Failed;
123 }
124 if ((Features & VIRTIO_F_VERSION_1) == 0) {
125 Status = EFI_UNSUPPORTED;
126 goto Failed;
127 }
128 //
129 // No device-specific feature bits have been defined in file "virtio-fs.tex"
130 // of the virtio spec at <https://github.com/oasis-tcs/virtio-spec.git>, as
131 // of commit 87fa6b5d8155.
132 //
133 Features &= VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM;
134
135 //
136 // ... and write the subset of feature bits understood by the [...] driver to
137 // the device. [...]
138 // 5. Set the FEATURES_OK status bit.
139 // 6. Re-read device status to ensure the FEATURES_OK bit is still set [...]
140 //
141 Status = Virtio10WriteFeatures (VirtioFs->Virtio, Features, &NextDevStat);
142 if (EFI_ERROR (Status)) {
143 goto Failed;
144 }
145
146 //
147 // 7. Perform device-specific setup, including discovery of virtqueues for
148 // the device, [...] reading [...] the device's virtio configuration space
149 //
150 Status = VirtioFsReadConfig (VirtioFs->Virtio, &Config);
151 if (EFI_ERROR (Status)) {
152 goto Failed;
153 }
154
155 //
156 // 7.a. Convert the filesystem label from UTF-8 to UCS-2. Only labels with
157 // printable ASCII code points (U+0020 through U+007E) are supported.
158 // NUL-terminate at either the terminator we find, or right after the
159 // original label.
160 //
161 for (Idx = 0; Idx < VIRTIO_FS_TAG_BYTES && Config.Tag[Idx] != '\0'; Idx++) {
162 if (Config.Tag[Idx] < 0x20 || Config.Tag[Idx] > 0x7E) {
163 Status = EFI_UNSUPPORTED;
164 goto Failed;
165 }
166 VirtioFs->Label[Idx] = Config.Tag[Idx];
167 }
168 VirtioFs->Label[Idx] = L'\0';
169
170 //
171 // 7.b. We need one queue for sending normal priority requests.
172 //
173 if (Config.NumReqQueues < 1) {
174 Status = EFI_UNSUPPORTED;
175 goto Failed;
176 }
177
178 //
179 // 7.c. Fetch and remember the number of descriptors we can place on the
180 // queue at once. We'll need two descriptors per request, as a minimum --
181 // request header, response header.
182 //
183 Status = VirtioFs->Virtio->SetQueueSel (VirtioFs->Virtio,
184 VIRTIO_FS_REQUEST_QUEUE);
185 if (EFI_ERROR (Status)) {
186 goto Failed;
187 }
188 Status = VirtioFs->Virtio->GetQueueNumMax (VirtioFs->Virtio,
189 &VirtioFs->QueueSize);
190 if (EFI_ERROR (Status)) {
191 goto Failed;
192 }
193 if (VirtioFs->QueueSize < 2) {
194 Status = EFI_UNSUPPORTED;
195 goto Failed;
196 }
197
198 //
199 // 7.d. [...] population of virtqueues [...]
200 //
201 Status = VirtioRingInit (VirtioFs->Virtio, VirtioFs->QueueSize,
202 &VirtioFs->Ring);
203 if (EFI_ERROR (Status)) {
204 goto Failed;
205 }
206
207 Status = VirtioRingMap (VirtioFs->Virtio, &VirtioFs->Ring, &RingBaseShift,
208 &VirtioFs->RingMap);
209 if (EFI_ERROR (Status)) {
210 goto ReleaseQueue;
211 }
212
213 Status = VirtioFs->Virtio->SetQueueAddress (VirtioFs->Virtio,
214 &VirtioFs->Ring, RingBaseShift);
215 if (EFI_ERROR (Status)) {
216 goto UnmapQueue;
217 }
218
219 //
220 // 8. Set the DRIVER_OK status bit.
221 //
222 NextDevStat |= VSTAT_DRIVER_OK;
223 Status = VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
224 if (EFI_ERROR (Status)) {
225 goto UnmapQueue;
226 }
227
228 return EFI_SUCCESS;
229
230 UnmapQueue:
231 VirtioFs->Virtio->UnmapSharedBuffer (VirtioFs->Virtio, VirtioFs->RingMap);
232
233 ReleaseQueue:
234 VirtioRingUninit (VirtioFs->Virtio, &VirtioFs->Ring);
235
236 Failed:
237 //
238 // If any of these steps go irrecoverably wrong, the driver SHOULD set the
239 // FAILED status bit to indicate that it has given up on the device (it can
240 // reset the device later to restart if desired). [...]
241 //
242 // Virtio access failure here should not mask the original error.
243 //
244 NextDevStat |= VSTAT_FAILED;
245 VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
246
247 return Status;
248 }
249
250 /**
251 De-configure the Virtio Filesystem device underlying VirtioFs.
252
253 @param[in] VirtioFs The VIRTIO_FS object for which Virtio communication
254 should be torn down. On input, the caller is responsible
255 for having called VirtioFsInit(). On output, Virtio
256 Filesystem commands (primitives) must no longer be
257 submitted to the device.
258 **/
259 VOID
260 VirtioFsUninit (
261 IN OUT VIRTIO_FS *VirtioFs
262 )
263 {
264 //
265 // Resetting the Virtio device makes it release its resources and forget its
266 // configuration.
267 //
268 VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, 0);
269 VirtioFs->Virtio->UnmapSharedBuffer (VirtioFs->Virtio, VirtioFs->RingMap);
270 VirtioRingUninit (VirtioFs->Virtio, &VirtioFs->Ring);
271 }
272
273 /**
274 ExitBootServices event notification function for a Virtio Filesystem object.
275
276 This function resets the VIRTIO_FS.Virtio device, causing it to release all
277 references to guest-side resources. The function may only be called after
278 VirtioFsInit() returns successfully and before VirtioFsUninit() is called.
279
280 @param[in] ExitBootEvent The VIRTIO_FS.ExitBoot event that has been
281 signaled.
282
283 @param[in] VirtioFsAsVoid Pointer to the VIRTIO_FS object, passed in as
284 (VOID*).
285 **/
286 VOID
287 EFIAPI
288 VirtioFsExitBoot (
289 IN EFI_EVENT ExitBootEvent,
290 IN VOID *VirtioFsAsVoid
291 )
292 {
293 VIRTIO_FS *VirtioFs;
294
295 VirtioFs = VirtioFsAsVoid;
296 DEBUG ((DEBUG_VERBOSE, "%a: VirtioFs=0x%p Label=\"%s\"\n", __FUNCTION__,
297 VirtioFsAsVoid, VirtioFs->Label));
298 VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, 0);
299 }