]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/VirtioGpuDxe/Commands.c
c1951a807e98d9c32ceae7e41f436c77f28f4f07
[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 UINT64 RingBaseShift;
48
49 //
50 // Execute virtio-v1.0-cs04, 3.1.1 Driver Requirements: Device
51 // Initialization.
52 //
53 // 1. Reset the device.
54 //
55 NextDevStat = 0;
56 Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
57 if (EFI_ERROR (Status)) {
58 goto Failed;
59 }
60
61 //
62 // 2. Set the ACKNOWLEDGE status bit [...]
63 //
64 NextDevStat |= VSTAT_ACK;
65 Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
66 if (EFI_ERROR (Status)) {
67 goto Failed;
68 }
69
70 //
71 // 3. Set the DRIVER status bit [...]
72 //
73 NextDevStat |= VSTAT_DRIVER;
74 Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
75 if (EFI_ERROR (Status)) {
76 goto Failed;
77 }
78
79 //
80 // 4. Read device feature bits...
81 //
82 Status = VgpuDev->VirtIo->GetDeviceFeatures (VgpuDev->VirtIo, &Features);
83 if (EFI_ERROR (Status)) {
84 goto Failed;
85 }
86 if ((Features & VIRTIO_F_VERSION_1) == 0) {
87 Status = EFI_UNSUPPORTED;
88 goto Failed;
89 }
90 //
91 // We only want the most basic 2D features.
92 //
93 Features &= VIRTIO_F_VERSION_1;
94
95 //
96 // ... and write the subset of feature bits understood by the [...] driver to
97 // the device. [...]
98 // 5. Set the FEATURES_OK status bit.
99 // 6. Re-read device status to ensure the FEATURES_OK bit is still set [...]
100 //
101 Status = Virtio10WriteFeatures (VgpuDev->VirtIo, Features, &NextDevStat);
102 if (EFI_ERROR (Status)) {
103 goto Failed;
104 }
105
106 //
107 // 7. Perform device-specific setup, including discovery of virtqueues for
108 // the device [...]
109 //
110 Status = VgpuDev->VirtIo->SetQueueSel (VgpuDev->VirtIo,
111 VIRTIO_GPU_CONTROL_QUEUE);
112 if (EFI_ERROR (Status)) {
113 goto Failed;
114 }
115 Status = VgpuDev->VirtIo->GetQueueNumMax (VgpuDev->VirtIo, &QueueSize);
116 if (EFI_ERROR (Status)) {
117 goto Failed;
118 }
119
120 //
121 // We implement each VirtIo GPU command that we use with two descriptors:
122 // request, response.
123 //
124 if (QueueSize < 2) {
125 Status = EFI_UNSUPPORTED;
126 goto Failed;
127 }
128
129 //
130 // [...] population of virtqueues [...]
131 //
132 Status = VirtioRingInit (VgpuDev->VirtIo, QueueSize, &VgpuDev->Ring);
133 if (EFI_ERROR (Status)) {
134 goto Failed;
135 }
136 //
137 // If anything fails from here on, we have to release the ring.
138 //
139 Status = VirtioRingMap (
140 VgpuDev->VirtIo,
141 &VgpuDev->Ring,
142 &RingBaseShift,
143 &VgpuDev->RingMap
144 );
145 if (EFI_ERROR (Status)) {
146 goto ReleaseQueue;
147 }
148 //
149 // If anything fails from here on, we have to unmap the ring.
150 //
151 Status = VgpuDev->VirtIo->SetQueueAddress (
152 VgpuDev->VirtIo,
153 &VgpuDev->Ring,
154 RingBaseShift
155 );
156 if (EFI_ERROR (Status)) {
157 goto UnmapQueue;
158 }
159
160 //
161 // 8. Set the DRIVER_OK status bit.
162 //
163 NextDevStat |= VSTAT_DRIVER_OK;
164 Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
165 if (EFI_ERROR (Status)) {
166 goto UnmapQueue;
167 }
168
169 return EFI_SUCCESS;
170
171 UnmapQueue:
172 VgpuDev->VirtIo->UnmapSharedBuffer (VgpuDev->VirtIo, VgpuDev->RingMap);
173
174 ReleaseQueue:
175 VirtioRingUninit (VgpuDev->VirtIo, &VgpuDev->Ring);
176
177 Failed:
178 //
179 // If any of these steps go irrecoverably wrong, the driver SHOULD set the
180 // FAILED status bit to indicate that it has given up on the device (it can
181 // reset the device later to restart if desired). [...]
182 //
183 // VirtIo access failure here should not mask the original error.
184 //
185 NextDevStat |= VSTAT_FAILED;
186 VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);
187
188 return Status;
189 }
190
191 /**
192 De-configure the VirtIo GPU device that underlies VgpuDev.
193
194 @param[in,out] VgpuDev The VGPU_DEV object to tear down VirtIo messaging
195 for. On input, the caller is responsible for having
196 called VirtioGpuInit(). On output, VgpuDev->Ring has
197 been uninitialized; VirtIo GPU commands (primitives)
198 can no longer be submitted to the device.
199 **/
200 VOID
201 VirtioGpuUninit (
202 IN OUT VGPU_DEV *VgpuDev
203 )
204 {
205 //
206 // Resetting the VirtIo device makes it release its resources and forget its
207 // configuration.
208 //
209 VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, 0);
210 VgpuDev->VirtIo->UnmapSharedBuffer (VgpuDev->VirtIo, VgpuDev->RingMap);
211 VirtioRingUninit (VgpuDev->VirtIo, &VgpuDev->Ring);
212 }
213
214 /**
215 EFI_EVENT_NOTIFY function for the VGPU_DEV.ExitBoot event. It resets the
216 VirtIo device, causing it to release its resources and to forget its
217 configuration.
218
219 This function may only be called (that is, VGPU_DEV.ExitBoot may only be
220 signaled) after VirtioGpuInit() returns and before VirtioGpuUninit() is
221 called.
222
223 @param[in] Event Event whose notification function is being invoked.
224
225 @param[in] Context Pointer to the associated VGPU_DEV object.
226 **/
227 VOID
228 EFIAPI
229 VirtioGpuExitBoot (
230 IN EFI_EVENT Event,
231 IN VOID *Context
232 )
233 {
234 VGPU_DEV *VgpuDev;
235
236 VgpuDev = Context;
237 VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, 0);
238 VgpuDev->VirtIo->UnmapSharedBuffer (VgpuDev->VirtIo, VgpuDev->RingMap);
239 }
240
241 /**
242 Internal utility function that sends a request to the VirtIo GPU device
243 model, awaits the answer from the host, and returns a status.
244
245 @param[in,out] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
246 device. The caller is responsible to have
247 successfully invoked VirtioGpuInit() on VgpuDev
248 previously, while VirtioGpuUninit() must not have
249 been called on VgpuDev.
250
251 @param[in] RequestType The type of the request. The caller is responsible
252 for providing a VirtioGpuCmd* RequestType which, on
253 success, elicits a VirtioGpuRespOkNodata response
254 from the host.
255
256 @param[in] Fence Whether to enable fencing for this request. Fencing
257 forces the host to complete the command before
258 producing a response. If Fence is TRUE, then
259 VgpuDev->FenceId is consumed, and incremented.
260
261 @param[in,out] Header Pointer to the caller-allocated request object. The
262 request must start with VIRTIO_GPU_CONTROL_HEADER.
263 This function overwrites all fields of Header before
264 submitting the request to the host:
265
266 - it sets Type from RequestType,
267
268 - it sets Flags and FenceId based on Fence,
269
270 - it zeroes CtxId and Padding.
271
272 @param[in] RequestSize Size of the entire caller-allocated request object,
273 including the leading VIRTIO_GPU_CONTROL_HEADER.
274
275 @retval EFI_SUCCESS Operation successful.
276
277 @retval EFI_DEVICE_ERROR The host rejected the request. The host error
278 code has been logged on the EFI_D_ERROR level.
279
280 @return Codes for unexpected errors in VirtIo
281 messaging, or request/response
282 mapping/unmapping.
283 **/
284 STATIC
285 EFI_STATUS
286 VirtioGpuSendCommand (
287 IN OUT VGPU_DEV *VgpuDev,
288 IN VIRTIO_GPU_CONTROL_TYPE RequestType,
289 IN BOOLEAN Fence,
290 IN OUT volatile VIRTIO_GPU_CONTROL_HEADER *Header,
291 IN UINTN RequestSize
292 )
293 {
294 DESC_INDICES Indices;
295 volatile VIRTIO_GPU_CONTROL_HEADER Response;
296 EFI_STATUS Status;
297 UINT32 ResponseSize;
298 EFI_PHYSICAL_ADDRESS RequestDeviceAddress;
299 VOID *RequestMap;
300 EFI_PHYSICAL_ADDRESS ResponseDeviceAddress;
301 VOID *ResponseMap;
302
303 //
304 // Initialize Header.
305 //
306 Header->Type = RequestType;
307 if (Fence) {
308 Header->Flags = VIRTIO_GPU_FLAG_FENCE;
309 Header->FenceId = VgpuDev->FenceId++;
310 } else {
311 Header->Flags = 0;
312 Header->FenceId = 0;
313 }
314 Header->CtxId = 0;
315 Header->Padding = 0;
316
317 ASSERT (RequestSize >= sizeof *Header);
318 ASSERT (RequestSize <= MAX_UINT32);
319
320 //
321 // Map request and response to bus master device addresses.
322 //
323 Status = VirtioMapAllBytesInSharedBuffer (
324 VgpuDev->VirtIo,
325 VirtioOperationBusMasterRead,
326 (VOID *)Header,
327 RequestSize,
328 &RequestDeviceAddress,
329 &RequestMap
330 );
331 if (EFI_ERROR (Status)) {
332 return Status;
333 }
334 Status = VirtioMapAllBytesInSharedBuffer (
335 VgpuDev->VirtIo,
336 VirtioOperationBusMasterWrite,
337 (VOID *)&Response,
338 sizeof Response,
339 &ResponseDeviceAddress,
340 &ResponseMap
341 );
342 if (EFI_ERROR (Status)) {
343 goto UnmapRequest;
344 }
345
346 //
347 // Compose the descriptor chain.
348 //
349 VirtioPrepare (&VgpuDev->Ring, &Indices);
350 VirtioAppendDesc (
351 &VgpuDev->Ring,
352 RequestDeviceAddress,
353 (UINT32)RequestSize,
354 VRING_DESC_F_NEXT,
355 &Indices
356 );
357 VirtioAppendDesc (
358 &VgpuDev->Ring,
359 ResponseDeviceAddress,
360 (UINT32)sizeof Response,
361 VRING_DESC_F_WRITE,
362 &Indices
363 );
364
365 //
366 // Send the command.
367 //
368 Status = VirtioFlush (VgpuDev->VirtIo, VIRTIO_GPU_CONTROL_QUEUE,
369 &VgpuDev->Ring, &Indices, &ResponseSize);
370 if (EFI_ERROR (Status)) {
371 goto UnmapResponse;
372 }
373
374 //
375 // Verify response size.
376 //
377 if (ResponseSize != sizeof Response) {
378 DEBUG ((EFI_D_ERROR, "%a: malformed response to Request=0x%x\n",
379 __FUNCTION__, (UINT32)RequestType));
380 Status = EFI_PROTOCOL_ERROR;
381 goto UnmapResponse;
382 }
383
384 //
385 // Unmap response and request, in reverse order of mapping. On error, the
386 // respective mapping is invalidated anyway, only the data may not have been
387 // committed to system memory (in case of VirtioOperationBusMasterWrite).
388 //
389 Status = VgpuDev->VirtIo->UnmapSharedBuffer (VgpuDev->VirtIo, ResponseMap);
390 if (EFI_ERROR (Status)) {
391 goto UnmapRequest;
392 }
393 Status = VgpuDev->VirtIo->UnmapSharedBuffer (VgpuDev->VirtIo, RequestMap);
394 if (EFI_ERROR (Status)) {
395 return Status;
396 }
397
398 //
399 // Parse the response.
400 //
401 if (Response.Type == VirtioGpuRespOkNodata) {
402 return EFI_SUCCESS;
403 }
404
405 DEBUG ((EFI_D_ERROR, "%a: Request=0x%x Response=0x%x\n", __FUNCTION__,
406 (UINT32)RequestType, Response.Type));
407 return EFI_DEVICE_ERROR;
408
409 UnmapResponse:
410 VgpuDev->VirtIo->UnmapSharedBuffer (VgpuDev->VirtIo, ResponseMap);
411
412 UnmapRequest:
413 VgpuDev->VirtIo->UnmapSharedBuffer (VgpuDev->VirtIo, RequestMap);
414
415 return Status;
416 }
417
418 /**
419 The following functions send requests to the VirtIo GPU device model, await
420 the answer from the host, and return a status. They share the following
421 interface details:
422
423 @param[in,out] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
424 device. The caller is responsible to have
425 successfully invoked VirtioGpuInit() on VgpuDev
426 previously, while VirtioGpuUninit() must not have
427 been called on VgpuDev.
428
429 @retval EFI_INVALID_PARAMETER Invalid command-specific parameters were
430 detected by this driver.
431
432 @retval EFI_SUCCESS Operation successful.
433
434 @retval EFI_DEVICE_ERROR The host rejected the request. The host error
435 code has been logged on the EFI_D_ERROR level.
436
437 @return Codes for unexpected errors in VirtIo
438 messaging.
439
440 For the command-specific parameters, please consult the GPU Device section of
441 the VirtIo 1.0 specification (see references in
442 "OvmfPkg/Include/IndustryStandard/VirtioGpu.h").
443 **/
444 EFI_STATUS
445 VirtioGpuResourceCreate2d (
446 IN OUT VGPU_DEV *VgpuDev,
447 IN UINT32 ResourceId,
448 IN VIRTIO_GPU_FORMATS Format,
449 IN UINT32 Width,
450 IN UINT32 Height
451 )
452 {
453 volatile VIRTIO_GPU_RESOURCE_CREATE_2D Request;
454
455 if (ResourceId == 0) {
456 return EFI_INVALID_PARAMETER;
457 }
458
459 Request.ResourceId = ResourceId;
460 Request.Format = (UINT32)Format;
461 Request.Width = Width;
462 Request.Height = Height;
463
464 return VirtioGpuSendCommand (
465 VgpuDev,
466 VirtioGpuCmdResourceCreate2d,
467 FALSE, // Fence
468 &Request.Header,
469 sizeof Request
470 );
471 }
472
473 EFI_STATUS
474 VirtioGpuResourceUnref (
475 IN OUT VGPU_DEV *VgpuDev,
476 IN UINT32 ResourceId
477 )
478 {
479 volatile VIRTIO_GPU_RESOURCE_UNREF Request;
480
481 if (ResourceId == 0) {
482 return EFI_INVALID_PARAMETER;
483 }
484
485 Request.ResourceId = ResourceId;
486 Request.Padding = 0;
487
488 return VirtioGpuSendCommand (
489 VgpuDev,
490 VirtioGpuCmdResourceUnref,
491 FALSE, // Fence
492 &Request.Header,
493 sizeof Request
494 );
495 }
496
497 EFI_STATUS
498 VirtioGpuResourceAttachBacking (
499 IN OUT VGPU_DEV *VgpuDev,
500 IN UINT32 ResourceId,
501 IN EFI_PHYSICAL_ADDRESS BackingStoreDeviceAddress,
502 IN UINTN NumberOfPages
503 )
504 {
505 volatile VIRTIO_GPU_RESOURCE_ATTACH_BACKING Request;
506
507 if (ResourceId == 0) {
508 return EFI_INVALID_PARAMETER;
509 }
510
511 Request.ResourceId = ResourceId;
512 Request.NrEntries = 1;
513 Request.Entry.Addr = BackingStoreDeviceAddress;
514 Request.Entry.Length = (UINT32)EFI_PAGES_TO_SIZE (NumberOfPages);
515 Request.Entry.Padding = 0;
516
517 return VirtioGpuSendCommand (
518 VgpuDev,
519 VirtioGpuCmdResourceAttachBacking,
520 FALSE, // Fence
521 &Request.Header,
522 sizeof Request
523 );
524 }
525
526 EFI_STATUS
527 VirtioGpuResourceDetachBacking (
528 IN OUT VGPU_DEV *VgpuDev,
529 IN UINT32 ResourceId
530 )
531 {
532 volatile VIRTIO_GPU_RESOURCE_DETACH_BACKING Request;
533
534 if (ResourceId == 0) {
535 return EFI_INVALID_PARAMETER;
536 }
537
538 Request.ResourceId = ResourceId;
539 Request.Padding = 0;
540
541 //
542 // In this case, we set Fence to TRUE, because after this function returns,
543 // the caller might reasonably want to repurpose the backing pages
544 // immediately. Thus we should ensure that the host releases all references
545 // to the backing pages before we return.
546 //
547 return VirtioGpuSendCommand (
548 VgpuDev,
549 VirtioGpuCmdResourceDetachBacking,
550 TRUE, // Fence
551 &Request.Header,
552 sizeof Request
553 );
554 }
555
556 EFI_STATUS
557 VirtioGpuSetScanout (
558 IN OUT VGPU_DEV *VgpuDev,
559 IN UINT32 X,
560 IN UINT32 Y,
561 IN UINT32 Width,
562 IN UINT32 Height,
563 IN UINT32 ScanoutId,
564 IN UINT32 ResourceId
565 )
566 {
567 volatile VIRTIO_GPU_SET_SCANOUT Request;
568
569 //
570 // Unlike for most other commands, ResourceId=0 is valid; it
571 // is used to disable a scanout.
572 //
573 Request.Rectangle.X = X;
574 Request.Rectangle.Y = Y;
575 Request.Rectangle.Width = Width;
576 Request.Rectangle.Height = Height;
577 Request.ScanoutId = ScanoutId;
578 Request.ResourceId = ResourceId;
579
580 return VirtioGpuSendCommand (
581 VgpuDev,
582 VirtioGpuCmdSetScanout,
583 FALSE, // Fence
584 &Request.Header,
585 sizeof Request
586 );
587 }
588
589 EFI_STATUS
590 VirtioGpuTransferToHost2d (
591 IN OUT VGPU_DEV *VgpuDev,
592 IN UINT32 X,
593 IN UINT32 Y,
594 IN UINT32 Width,
595 IN UINT32 Height,
596 IN UINT64 Offset,
597 IN UINT32 ResourceId
598 )
599 {
600 volatile VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D Request;
601
602 if (ResourceId == 0) {
603 return EFI_INVALID_PARAMETER;
604 }
605
606 Request.Rectangle.X = X;
607 Request.Rectangle.Y = Y;
608 Request.Rectangle.Width = Width;
609 Request.Rectangle.Height = Height;
610 Request.Offset = Offset;
611 Request.ResourceId = ResourceId;
612 Request.Padding = 0;
613
614 return VirtioGpuSendCommand (
615 VgpuDev,
616 VirtioGpuCmdTransferToHost2d,
617 FALSE, // Fence
618 &Request.Header,
619 sizeof Request
620 );
621 }
622
623 EFI_STATUS
624 VirtioGpuResourceFlush (
625 IN OUT VGPU_DEV *VgpuDev,
626 IN UINT32 X,
627 IN UINT32 Y,
628 IN UINT32 Width,
629 IN UINT32 Height,
630 IN UINT32 ResourceId
631 )
632 {
633 volatile VIRTIO_GPU_RESOURCE_FLUSH Request;
634
635 if (ResourceId == 0) {
636 return EFI_INVALID_PARAMETER;
637 }
638
639 Request.Rectangle.X = X;
640 Request.Rectangle.Y = Y;
641 Request.Rectangle.Width = Width;
642 Request.Rectangle.Height = Height;
643 Request.ResourceId = ResourceId;
644 Request.Padding = 0;
645
646 return VirtioGpuSendCommand (
647 VgpuDev,
648 VirtioGpuCmdResourceFlush,
649 FALSE, // Fence
650 &Request.Header,
651 sizeof Request
652 );
653 }