c2e4d72feb679cd64b3d607268d2e548c714ef19
[mirror_edk2.git] / OvmfPkg / VirtioGpuDxe / Commands.c
1 /** @file
2
3 VirtIo GPU initialization, and commands (primitives) for the GPU device.
4
5 Copyright (C) 2016, Red Hat, Inc.
6 Copyright (c) 2017, AMD Inc, All rights reserved.<BR>
7
8 This program and the accompanying materials are licensed and made available
9 under the terms and conditions of the BSD License which accompanies this
10 distribution. The full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
12
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
14 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15
16 **/
17
18 #include <Library/VirtioLib.h>
19
20 #include "VirtioGpu.h"
21
22 /**
23 Configure the VirtIo GPU device that underlies VgpuDev.
24
25 @param[in,out] VgpuDev The VGPU_DEV object to set up VirtIo messaging for.
26 On input, the caller is responsible for having
27 initialized VgpuDev->VirtIo. On output, VgpuDev->Ring
28 has been initialized, and synchronous VirtIo GPU
29 commands (primitives) can be submitted to the device.
30
31 @retval EFI_SUCCESS VirtIo GPU configuration successful.
32
33 @retval EFI_UNSUPPORTED The host-side configuration of the VirtIo GPU is not
34 supported by this driver.
35
36 @retval Error codes from underlying functions.
37 **/
38 EFI_STATUS
39 VirtioGpuInit (
40 IN OUT VGPU_DEV *VgpuDev
41 )
42 {
43 UINT8 NextDevStat;
44 EFI_STATUS Status;
45 UINT64 Features;
46 UINT16 QueueSize;
47
48 //
49 // Execute virtio-v1.0-cs04, 3.1.1 Driver Requirements: Device
50 // Initialization.
51 //
52 // 1. Reset the device.
53 //
54 NextDevStat = 0;
55 Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
56 if (EFI_ERROR (Status)) {
57 goto Failed;
58 }
59
60 //
61 // 2. Set the ACKNOWLEDGE status bit [...]
62 //
63 NextDevStat |= VSTAT_ACK;
64 Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
65 if (EFI_ERROR (Status)) {
66 goto Failed;
67 }
68
69 //
70 // 3. Set the DRIVER status bit [...]
71 //
72 NextDevStat |= VSTAT_DRIVER;
73 Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
74 if (EFI_ERROR (Status)) {
75 goto Failed;
76 }
77
78 //
79 // 4. Read device feature bits...
80 //
81 Status = VgpuDev->VirtIo->GetDeviceFeatures (VgpuDev->VirtIo, &Features);
82 if (EFI_ERROR (Status)) {
83 goto Failed;
84 }
85 if ((Features & VIRTIO_F_VERSION_1) == 0) {
86 Status = EFI_UNSUPPORTED;
87 goto Failed;
88 }
89 //
90 // We only want the most basic 2D features.
91 //
92 Features &= VIRTIO_F_VERSION_1;
93
94 //
95 // ... and write the subset of feature bits understood by the [...] driver to
96 // the device. [...]
97 // 5. Set the FEATURES_OK status bit.
98 // 6. Re-read device status to ensure the FEATURES_OK bit is still set [...]
99 //
100 Status = Virtio10WriteFeatures (VgpuDev->VirtIo, Features, &NextDevStat);
101 if (EFI_ERROR (Status)) {
102 goto Failed;
103 }
104
105 //
106 // 7. Perform device-specific setup, including discovery of virtqueues for
107 // the device [...]
108 //
109 Status = VgpuDev->VirtIo->SetQueueSel (VgpuDev->VirtIo,
110 VIRTIO_GPU_CONTROL_QUEUE);
111 if (EFI_ERROR (Status)) {
112 goto Failed;
113 }
114 Status = VgpuDev->VirtIo->GetQueueNumMax (VgpuDev->VirtIo, &QueueSize);
115 if (EFI_ERROR (Status)) {
116 goto Failed;
117 }
118
119 //
120 // We implement each VirtIo GPU command that we use with two descriptors:
121 // request, response.
122 //
123 if (QueueSize < 2) {
124 Status = EFI_UNSUPPORTED;
125 goto Failed;
126 }
127
128 //
129 // [...] population of virtqueues [...]
130 //
131 Status = VirtioRingInit (VgpuDev->VirtIo, QueueSize, &VgpuDev->Ring);
132 if (EFI_ERROR (Status)) {
133 goto Failed;
134 }
135 Status = VgpuDev->VirtIo->SetQueueAddress (VgpuDev->VirtIo, &VgpuDev->Ring);
136 if (EFI_ERROR (Status)) {
137 goto ReleaseQueue;
138 }
139
140 //
141 // 8. Set the DRIVER_OK status bit.
142 //
143 NextDevStat |= VSTAT_DRIVER_OK;
144 Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
145 if (EFI_ERROR (Status)) {
146 goto ReleaseQueue;
147 }
148
149 return EFI_SUCCESS;
150
151 ReleaseQueue:
152 VirtioRingUninit (VgpuDev->VirtIo, &VgpuDev->Ring);
153
154 Failed:
155 //
156 // If any of these steps go irrecoverably wrong, the driver SHOULD set the
157 // FAILED status bit to indicate that it has given up on the device (it can
158 // reset the device later to restart if desired). [...]
159 //
160 // VirtIo access failure here should not mask the original error.
161 //
162 NextDevStat |= VSTAT_FAILED;
163 VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
164
165 return Status;
166 }
167
168 /**
169 De-configure the VirtIo GPU device that underlies VgpuDev.
170
171 @param[in,out] VgpuDev The VGPU_DEV object to tear down VirtIo messaging
172 for. On input, the caller is responsible for having
173 called VirtioGpuInit(). On output, VgpuDev->Ring has
174 been uninitialized; VirtIo GPU commands (primitives)
175 can no longer be submitted to the device.
176 **/
177 VOID
178 VirtioGpuUninit (
179 IN OUT VGPU_DEV *VgpuDev
180 )
181 {
182 //
183 // Resetting the VirtIo device makes it release its resources and forget its
184 // configuration.
185 //
186 VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, 0);
187 VirtioRingUninit (VgpuDev->VirtIo, &VgpuDev->Ring);
188 }
189
190 /**
191 EFI_EVENT_NOTIFY function for the VGPU_DEV.ExitBoot event. It resets the
192 VirtIo device, causing it to release its resources and to forget its
193 configuration.
194
195 This function may only be called (that is, VGPU_DEV.ExitBoot may only be
196 signaled) after VirtioGpuInit() returns and before VirtioGpuUninit() is
197 called.
198
199 @param[in] Event Event whose notification function is being invoked.
200
201 @param[in] Context Pointer to the associated VGPU_DEV object.
202 **/
203 VOID
204 EFIAPI
205 VirtioGpuExitBoot (
206 IN EFI_EVENT Event,
207 IN VOID *Context
208 )
209 {
210 VGPU_DEV *VgpuDev;
211
212 VgpuDev = Context;
213 VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, 0);
214 }
215
216 /**
217 Internal utility function that sends a request to the VirtIo GPU device
218 model, awaits the answer from the host, and returns a status.
219
220 @param[in,out] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
221 device. The caller is responsible to have
222 successfully invoked VirtioGpuInit() on VgpuDev
223 previously, while VirtioGpuUninit() must not have
224 been called on VgpuDev.
225
226 @param[in] RequestType The type of the request. The caller is responsible
227 for providing a VirtioGpuCmd* RequestType which, on
228 success, elicits a VirtioGpuRespOkNodata response
229 from the host.
230
231 @param[in] Fence Whether to enable fencing for this request. Fencing
232 forces the host to complete the command before
233 producing a response. If Fence is TRUE, then
234 VgpuDev->FenceId is consumed, and incremented.
235
236 @param[in,out] Header Pointer to the caller-allocated request object. The
237 request must start with VIRTIO_GPU_CONTROL_HEADER.
238 This function overwrites all fields of Header before
239 submitting the request to the host:
240
241 - it sets Type from RequestType,
242
243 - it sets Flags and FenceId based on Fence,
244
245 - it zeroes CtxId and Padding.
246
247 @param[in] RequestSize Size of the entire caller-allocated request object,
248 including the leading VIRTIO_GPU_CONTROL_HEADER.
249
250 @retval EFI_SUCCESS Operation successful.
251
252 @retval EFI_DEVICE_ERROR The host rejected the request. The host error
253 code has been logged on the EFI_D_ERROR level.
254
255 @return Codes for unexpected errors in VirtIo
256 messaging.
257 **/
258 STATIC
259 EFI_STATUS
260 VirtioGpuSendCommand (
261 IN OUT VGPU_DEV *VgpuDev,
262 IN VIRTIO_GPU_CONTROL_TYPE RequestType,
263 IN BOOLEAN Fence,
264 IN OUT volatile VIRTIO_GPU_CONTROL_HEADER *Header,
265 IN UINTN RequestSize
266 )
267 {
268 DESC_INDICES Indices;
269 volatile VIRTIO_GPU_CONTROL_HEADER Response;
270 EFI_STATUS Status;
271 UINT32 ResponseSize;
272
273 //
274 // Initialize Header.
275 //
276 Header->Type = RequestType;
277 if (Fence) {
278 Header->Flags = VIRTIO_GPU_FLAG_FENCE;
279 Header->FenceId = VgpuDev->FenceId++;
280 } else {
281 Header->Flags = 0;
282 Header->FenceId = 0;
283 }
284 Header->CtxId = 0;
285 Header->Padding = 0;
286
287 ASSERT (RequestSize >= sizeof *Header);
288 ASSERT (RequestSize <= MAX_UINT32);
289
290 //
291 // Compose the descriptor chain.
292 //
293 VirtioPrepare (&VgpuDev->Ring, &Indices);
294 VirtioAppendDesc (&VgpuDev->Ring, (UINTN)Header, (UINT32)RequestSize,
295 VRING_DESC_F_NEXT, &Indices);
296 VirtioAppendDesc (&VgpuDev->Ring, (UINTN)&Response, sizeof Response,
297 VRING_DESC_F_WRITE, &Indices);
298
299 //
300 // Send the command.
301 //
302 Status = VirtioFlush (VgpuDev->VirtIo, VIRTIO_GPU_CONTROL_QUEUE,
303 &VgpuDev->Ring, &Indices, &ResponseSize);
304 if (EFI_ERROR (Status)) {
305 return Status;
306 }
307
308 //
309 // Parse the response.
310 //
311 if (ResponseSize != sizeof Response) {
312 DEBUG ((EFI_D_ERROR, "%a: malformed response to Request=0x%x\n",
313 __FUNCTION__, (UINT32)RequestType));
314 return EFI_PROTOCOL_ERROR;
315 }
316
317 if (Response.Type == VirtioGpuRespOkNodata) {
318 return EFI_SUCCESS;
319 }
320
321 DEBUG ((EFI_D_ERROR, "%a: Request=0x%x Response=0x%x\n", __FUNCTION__,
322 (UINT32)RequestType, Response.Type));
323 return EFI_DEVICE_ERROR;
324 }
325
326 /**
327 The following functions send requests to the VirtIo GPU device model, await
328 the answer from the host, and return a status. They share the following
329 interface details:
330
331 @param[in,out] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
332 device. The caller is responsible to have
333 successfully invoked VirtioGpuInit() on VgpuDev
334 previously, while VirtioGpuUninit() must not have
335 been called on VgpuDev.
336
337 @retval EFI_INVALID_PARAMETER Invalid command-specific parameters were
338 detected by this driver.
339
340 @retval EFI_SUCCESS Operation successful.
341
342 @retval EFI_DEVICE_ERROR The host rejected the request. The host error
343 code has been logged on the EFI_D_ERROR level.
344
345 @return Codes for unexpected errors in VirtIo
346 messaging.
347
348 For the command-specific parameters, please consult the GPU Device section of
349 the VirtIo 1.0 specification (see references in
350 "OvmfPkg/Include/IndustryStandard/VirtioGpu.h").
351 **/
352 EFI_STATUS
353 VirtioGpuResourceCreate2d (
354 IN OUT VGPU_DEV *VgpuDev,
355 IN UINT32 ResourceId,
356 IN VIRTIO_GPU_FORMATS Format,
357 IN UINT32 Width,
358 IN UINT32 Height
359 )
360 {
361 volatile VIRTIO_GPU_RESOURCE_CREATE_2D Request;
362
363 if (ResourceId == 0) {
364 return EFI_INVALID_PARAMETER;
365 }
366
367 Request.ResourceId = ResourceId;
368 Request.Format = (UINT32)Format;
369 Request.Width = Width;
370 Request.Height = Height;
371
372 return VirtioGpuSendCommand (
373 VgpuDev,
374 VirtioGpuCmdResourceCreate2d,
375 FALSE, // Fence
376 &Request.Header,
377 sizeof Request
378 );
379 }
380
381 EFI_STATUS
382 VirtioGpuResourceUnref (
383 IN OUT VGPU_DEV *VgpuDev,
384 IN UINT32 ResourceId
385 )
386 {
387 volatile VIRTIO_GPU_RESOURCE_UNREF Request;
388
389 if (ResourceId == 0) {
390 return EFI_INVALID_PARAMETER;
391 }
392
393 Request.ResourceId = ResourceId;
394 Request.Padding = 0;
395
396 return VirtioGpuSendCommand (
397 VgpuDev,
398 VirtioGpuCmdResourceUnref,
399 FALSE, // Fence
400 &Request.Header,
401 sizeof Request
402 );
403 }
404
405 EFI_STATUS
406 VirtioGpuResourceAttachBacking (
407 IN OUT VGPU_DEV *VgpuDev,
408 IN UINT32 ResourceId,
409 IN VOID *FirstBackingPage,
410 IN UINTN NumberOfPages
411 )
412 {
413 volatile VIRTIO_GPU_RESOURCE_ATTACH_BACKING Request;
414
415 if (ResourceId == 0) {
416 return EFI_INVALID_PARAMETER;
417 }
418
419 Request.ResourceId = ResourceId;
420 Request.NrEntries = 1;
421 Request.Entry.Addr = (UINTN)FirstBackingPage;
422 Request.Entry.Length = (UINT32)EFI_PAGES_TO_SIZE (NumberOfPages);
423 Request.Entry.Padding = 0;
424
425 return VirtioGpuSendCommand (
426 VgpuDev,
427 VirtioGpuCmdResourceAttachBacking,
428 FALSE, // Fence
429 &Request.Header,
430 sizeof Request
431 );
432 }
433
434 EFI_STATUS
435 VirtioGpuResourceDetachBacking (
436 IN OUT VGPU_DEV *VgpuDev,
437 IN UINT32 ResourceId
438 )
439 {
440 volatile VIRTIO_GPU_RESOURCE_DETACH_BACKING Request;
441
442 if (ResourceId == 0) {
443 return EFI_INVALID_PARAMETER;
444 }
445
446 Request.ResourceId = ResourceId;
447 Request.Padding = 0;
448
449 //
450 // In this case, we set Fence to TRUE, because after this function returns,
451 // the caller might reasonably want to repurpose the backing pages
452 // immediately. Thus we should ensure that the host releases all references
453 // to the backing pages before we return.
454 //
455 return VirtioGpuSendCommand (
456 VgpuDev,
457 VirtioGpuCmdResourceDetachBacking,
458 TRUE, // Fence
459 &Request.Header,
460 sizeof Request
461 );
462 }
463
464 EFI_STATUS
465 VirtioGpuSetScanout (
466 IN OUT VGPU_DEV *VgpuDev,
467 IN UINT32 X,
468 IN UINT32 Y,
469 IN UINT32 Width,
470 IN UINT32 Height,
471 IN UINT32 ScanoutId,
472 IN UINT32 ResourceId
473 )
474 {
475 volatile VIRTIO_GPU_SET_SCANOUT Request;
476
477 //
478 // Unlike for most other commands, ResourceId=0 is valid; it
479 // is used to disable a scanout.
480 //
481 Request.Rectangle.X = X;
482 Request.Rectangle.Y = Y;
483 Request.Rectangle.Width = Width;
484 Request.Rectangle.Height = Height;
485 Request.ScanoutId = ScanoutId;
486 Request.ResourceId = ResourceId;
487
488 return VirtioGpuSendCommand (
489 VgpuDev,
490 VirtioGpuCmdSetScanout,
491 FALSE, // Fence
492 &Request.Header,
493 sizeof Request
494 );
495 }
496
497 EFI_STATUS
498 VirtioGpuTransferToHost2d (
499 IN OUT VGPU_DEV *VgpuDev,
500 IN UINT32 X,
501 IN UINT32 Y,
502 IN UINT32 Width,
503 IN UINT32 Height,
504 IN UINT64 Offset,
505 IN UINT32 ResourceId
506 )
507 {
508 volatile VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D Request;
509
510 if (ResourceId == 0) {
511 return EFI_INVALID_PARAMETER;
512 }
513
514 Request.Rectangle.X = X;
515 Request.Rectangle.Y = Y;
516 Request.Rectangle.Width = Width;
517 Request.Rectangle.Height = Height;
518 Request.Offset = Offset;
519 Request.ResourceId = ResourceId;
520 Request.Padding = 0;
521
522 return VirtioGpuSendCommand (
523 VgpuDev,
524 VirtioGpuCmdTransferToHost2d,
525 FALSE, // Fence
526 &Request.Header,
527 sizeof Request
528 );
529 }
530
531 EFI_STATUS
532 VirtioGpuResourceFlush (
533 IN OUT VGPU_DEV *VgpuDev,
534 IN UINT32 X,
535 IN UINT32 Y,
536 IN UINT32 Width,
537 IN UINT32 Height,
538 IN UINT32 ResourceId
539 )
540 {
541 volatile VIRTIO_GPU_RESOURCE_FLUSH Request;
542
543 if (ResourceId == 0) {
544 return EFI_INVALID_PARAMETER;
545 }
546
547 Request.Rectangle.X = X;
548 Request.Rectangle.Y = Y;
549 Request.Rectangle.Width = Width;
550 Request.Rectangle.Height = Height;
551 Request.ResourceId = ResourceId;
552 Request.Padding = 0;
553
554 return VirtioGpuSendCommand (
555 VgpuDev,
556 VirtioGpuCmdResourceFlush,
557 FALSE, // Fence
558 &Request.Header,
559 sizeof Request
560 );
561 }