]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/VirtioGpuDxe/Commands.c
OvmfPkg/VirtioGpuDxe: map VRING for bus master common buffer operation
[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.
282 **/
283 STATIC
284 EFI_STATUS
285 VirtioGpuSendCommand (
286 IN OUT VGPU_DEV *VgpuDev,
287 IN VIRTIO_GPU_CONTROL_TYPE RequestType,
288 IN BOOLEAN Fence,
289 IN OUT volatile VIRTIO_GPU_CONTROL_HEADER *Header,
290 IN UINTN RequestSize
291 )
292 {
293 DESC_INDICES Indices;
294 volatile VIRTIO_GPU_CONTROL_HEADER Response;
295 EFI_STATUS Status;
296 UINT32 ResponseSize;
297
298 //
299 // Initialize Header.
300 //
301 Header->Type = RequestType;
302 if (Fence) {
303 Header->Flags = VIRTIO_GPU_FLAG_FENCE;
304 Header->FenceId = VgpuDev->FenceId++;
305 } else {
306 Header->Flags = 0;
307 Header->FenceId = 0;
308 }
309 Header->CtxId = 0;
310 Header->Padding = 0;
311
312 ASSERT (RequestSize >= sizeof *Header);
313 ASSERT (RequestSize <= MAX_UINT32);
314
315 //
316 // Compose the descriptor chain.
317 //
318 VirtioPrepare (&VgpuDev->Ring, &Indices);
319 VirtioAppendDesc (&VgpuDev->Ring, (UINTN)Header, (UINT32)RequestSize,
320 VRING_DESC_F_NEXT, &Indices);
321 VirtioAppendDesc (&VgpuDev->Ring, (UINTN)&Response, sizeof Response,
322 VRING_DESC_F_WRITE, &Indices);
323
324 //
325 // Send the command.
326 //
327 Status = VirtioFlush (VgpuDev->VirtIo, VIRTIO_GPU_CONTROL_QUEUE,
328 &VgpuDev->Ring, &Indices, &ResponseSize);
329 if (EFI_ERROR (Status)) {
330 return Status;
331 }
332
333 //
334 // Parse the response.
335 //
336 if (ResponseSize != sizeof Response) {
337 DEBUG ((EFI_D_ERROR, "%a: malformed response to Request=0x%x\n",
338 __FUNCTION__, (UINT32)RequestType));
339 return EFI_PROTOCOL_ERROR;
340 }
341
342 if (Response.Type == VirtioGpuRespOkNodata) {
343 return EFI_SUCCESS;
344 }
345
346 DEBUG ((EFI_D_ERROR, "%a: Request=0x%x Response=0x%x\n", __FUNCTION__,
347 (UINT32)RequestType, Response.Type));
348 return EFI_DEVICE_ERROR;
349 }
350
351 /**
352 The following functions send requests to the VirtIo GPU device model, await
353 the answer from the host, and return a status. They share the following
354 interface details:
355
356 @param[in,out] VgpuDev The VGPU_DEV object that represents the VirtIo GPU
357 device. The caller is responsible to have
358 successfully invoked VirtioGpuInit() on VgpuDev
359 previously, while VirtioGpuUninit() must not have
360 been called on VgpuDev.
361
362 @retval EFI_INVALID_PARAMETER Invalid command-specific parameters were
363 detected by this driver.
364
365 @retval EFI_SUCCESS Operation successful.
366
367 @retval EFI_DEVICE_ERROR The host rejected the request. The host error
368 code has been logged on the EFI_D_ERROR level.
369
370 @return Codes for unexpected errors in VirtIo
371 messaging.
372
373 For the command-specific parameters, please consult the GPU Device section of
374 the VirtIo 1.0 specification (see references in
375 "OvmfPkg/Include/IndustryStandard/VirtioGpu.h").
376 **/
377 EFI_STATUS
378 VirtioGpuResourceCreate2d (
379 IN OUT VGPU_DEV *VgpuDev,
380 IN UINT32 ResourceId,
381 IN VIRTIO_GPU_FORMATS Format,
382 IN UINT32 Width,
383 IN UINT32 Height
384 )
385 {
386 volatile VIRTIO_GPU_RESOURCE_CREATE_2D Request;
387
388 if (ResourceId == 0) {
389 return EFI_INVALID_PARAMETER;
390 }
391
392 Request.ResourceId = ResourceId;
393 Request.Format = (UINT32)Format;
394 Request.Width = Width;
395 Request.Height = Height;
396
397 return VirtioGpuSendCommand (
398 VgpuDev,
399 VirtioGpuCmdResourceCreate2d,
400 FALSE, // Fence
401 &Request.Header,
402 sizeof Request
403 );
404 }
405
406 EFI_STATUS
407 VirtioGpuResourceUnref (
408 IN OUT VGPU_DEV *VgpuDev,
409 IN UINT32 ResourceId
410 )
411 {
412 volatile VIRTIO_GPU_RESOURCE_UNREF Request;
413
414 if (ResourceId == 0) {
415 return EFI_INVALID_PARAMETER;
416 }
417
418 Request.ResourceId = ResourceId;
419 Request.Padding = 0;
420
421 return VirtioGpuSendCommand (
422 VgpuDev,
423 VirtioGpuCmdResourceUnref,
424 FALSE, // Fence
425 &Request.Header,
426 sizeof Request
427 );
428 }
429
430 EFI_STATUS
431 VirtioGpuResourceAttachBacking (
432 IN OUT VGPU_DEV *VgpuDev,
433 IN UINT32 ResourceId,
434 IN VOID *FirstBackingPage,
435 IN UINTN NumberOfPages
436 )
437 {
438 volatile VIRTIO_GPU_RESOURCE_ATTACH_BACKING Request;
439
440 if (ResourceId == 0) {
441 return EFI_INVALID_PARAMETER;
442 }
443
444 Request.ResourceId = ResourceId;
445 Request.NrEntries = 1;
446 Request.Entry.Addr = (UINTN)FirstBackingPage;
447 Request.Entry.Length = (UINT32)EFI_PAGES_TO_SIZE (NumberOfPages);
448 Request.Entry.Padding = 0;
449
450 return VirtioGpuSendCommand (
451 VgpuDev,
452 VirtioGpuCmdResourceAttachBacking,
453 FALSE, // Fence
454 &Request.Header,
455 sizeof Request
456 );
457 }
458
459 EFI_STATUS
460 VirtioGpuResourceDetachBacking (
461 IN OUT VGPU_DEV *VgpuDev,
462 IN UINT32 ResourceId
463 )
464 {
465 volatile VIRTIO_GPU_RESOURCE_DETACH_BACKING Request;
466
467 if (ResourceId == 0) {
468 return EFI_INVALID_PARAMETER;
469 }
470
471 Request.ResourceId = ResourceId;
472 Request.Padding = 0;
473
474 //
475 // In this case, we set Fence to TRUE, because after this function returns,
476 // the caller might reasonably want to repurpose the backing pages
477 // immediately. Thus we should ensure that the host releases all references
478 // to the backing pages before we return.
479 //
480 return VirtioGpuSendCommand (
481 VgpuDev,
482 VirtioGpuCmdResourceDetachBacking,
483 TRUE, // Fence
484 &Request.Header,
485 sizeof Request
486 );
487 }
488
489 EFI_STATUS
490 VirtioGpuSetScanout (
491 IN OUT VGPU_DEV *VgpuDev,
492 IN UINT32 X,
493 IN UINT32 Y,
494 IN UINT32 Width,
495 IN UINT32 Height,
496 IN UINT32 ScanoutId,
497 IN UINT32 ResourceId
498 )
499 {
500 volatile VIRTIO_GPU_SET_SCANOUT Request;
501
502 //
503 // Unlike for most other commands, ResourceId=0 is valid; it
504 // is used to disable a scanout.
505 //
506 Request.Rectangle.X = X;
507 Request.Rectangle.Y = Y;
508 Request.Rectangle.Width = Width;
509 Request.Rectangle.Height = Height;
510 Request.ScanoutId = ScanoutId;
511 Request.ResourceId = ResourceId;
512
513 return VirtioGpuSendCommand (
514 VgpuDev,
515 VirtioGpuCmdSetScanout,
516 FALSE, // Fence
517 &Request.Header,
518 sizeof Request
519 );
520 }
521
522 EFI_STATUS
523 VirtioGpuTransferToHost2d (
524 IN OUT VGPU_DEV *VgpuDev,
525 IN UINT32 X,
526 IN UINT32 Y,
527 IN UINT32 Width,
528 IN UINT32 Height,
529 IN UINT64 Offset,
530 IN UINT32 ResourceId
531 )
532 {
533 volatile VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D Request;
534
535 if (ResourceId == 0) {
536 return EFI_INVALID_PARAMETER;
537 }
538
539 Request.Rectangle.X = X;
540 Request.Rectangle.Y = Y;
541 Request.Rectangle.Width = Width;
542 Request.Rectangle.Height = Height;
543 Request.Offset = Offset;
544 Request.ResourceId = ResourceId;
545 Request.Padding = 0;
546
547 return VirtioGpuSendCommand (
548 VgpuDev,
549 VirtioGpuCmdTransferToHost2d,
550 FALSE, // Fence
551 &Request.Header,
552 sizeof Request
553 );
554 }
555
556 EFI_STATUS
557 VirtioGpuResourceFlush (
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 ResourceId
564 )
565 {
566 volatile VIRTIO_GPU_RESOURCE_FLUSH Request;
567
568 if (ResourceId == 0) {
569 return EFI_INVALID_PARAMETER;
570 }
571
572 Request.Rectangle.X = X;
573 Request.Rectangle.Y = Y;
574 Request.Rectangle.Width = Width;
575 Request.Rectangle.Height = Height;
576 Request.ResourceId = ResourceId;
577 Request.Padding = 0;
578
579 return VirtioGpuSendCommand (
580 VgpuDev,
581 VirtioGpuCmdResourceFlush,
582 FALSE, // Fence
583 &Request.Header,
584 sizeof Request
585 );
586 }