]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/VirtioGpuDxe/Gop.c
OvmfPkg: Apply uncrustify changes
[mirror_edk2.git] / OvmfPkg / VirtioGpuDxe / Gop.c
1 /** @file
2
3 EFI_GRAPHICS_OUTPUT_PROTOCOL member functions for the VirtIo GPU driver.
4
5 Copyright (C) 2016, Red Hat, Inc.
6
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8
9 **/
10
11 #include <Library/MemoryAllocationLib.h>
12
13 #include "VirtioGpu.h"
14
15 /**
16 Release guest-side and host-side resources that are related to an initialized
17 VGPU_GOP.Gop.
18
19 param[in,out] VgpuGop The VGPU_GOP object to release resources for.
20
21 On input, the caller is responsible for having called
22 VgpuGop->Gop.SetMode() at least once successfully.
23 (This is equivalent to the requirement that
24 VgpuGop->BackingStore be non-NULL. It is also
25 equivalent to the requirement that VgpuGop->ResourceId
26 be nonzero.)
27
28 On output, resources will be released, and
29 VgpuGop->BackingStore and VgpuGop->ResourceId will be
30 nulled.
31
32 param[in] DisableHead Whether this head (scanout) currently references the
33 resource identified by VgpuGop->ResourceId. Only pass
34 FALSE when VgpuGop->Gop.SetMode() calls this function
35 while switching between modes, and set it to TRUE
36 every other time.
37 **/
38 VOID
39 ReleaseGopResources (
40 IN OUT VGPU_GOP *VgpuGop,
41 IN BOOLEAN DisableHead
42 )
43 {
44 EFI_STATUS Status;
45
46 ASSERT (VgpuGop->ResourceId != 0);
47 ASSERT (VgpuGop->BackingStore != NULL);
48
49 //
50 // If any of the following host-side destruction steps fail, we can't get out
51 // of an inconsistent state, so we'll hang. In general errors in object
52 // destruction can hardly be recovered from.
53 //
54 if (DisableHead) {
55 //
56 // Dissociate head (scanout) #0 from the currently used 2D host resource,
57 // by setting ResourceId=0 for it.
58 //
59 Status = VirtioGpuSetScanout (
60 VgpuGop->ParentBus, // VgpuDev
61 0,
62 0,
63 0,
64 0, // X, Y, Width, Height
65 0, // ScanoutId
66 0 // ResourceId
67 );
68 //
69 // HACK BEGINS HERE
70 //
71 // According to the GPU Device section of the VirtIo specification, the
72 // above operation is valid:
73 //
74 // "The driver can use resource_id = 0 to disable a scanout."
75 //
76 // However, in practice QEMU does not allow us to disable head (scanout) #0
77 // -- it rejects the command with response code 0x1202
78 // (VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID). Looking at the QEMU source
79 // code, function virtio_gpu_set_scanout() in "hw/display/virtio-gpu.c",
80 // this appears fully intentional, despite not being documented in the
81 // spec.
82 //
83 // Surprisingly, ignoring the error here, and proceeding to release
84 // host-side resources that presumably underlie head (scanout) #0, work
85 // without any problems -- the driver survives repeated "disconnect" /
86 // "connect -r" commands in the UEFI shell.
87 //
88 // So, for now, let's just suppress the error.
89 //
90 Status = EFI_SUCCESS;
91 //
92 // HACK ENDS HERE
93 //
94
95 ASSERT_EFI_ERROR (Status);
96 if (EFI_ERROR (Status)) {
97 CpuDeadLoop ();
98 }
99 }
100
101 //
102 // Detach backing pages from the currently used 2D host resource.
103 //
104 Status = VirtioGpuResourceDetachBacking (
105 VgpuGop->ParentBus, // VgpuDev
106 VgpuGop->ResourceId // ResourceId
107 );
108 ASSERT_EFI_ERROR (Status);
109 if (EFI_ERROR (Status)) {
110 CpuDeadLoop ();
111 }
112
113 //
114 // Unmap and release backing pages.
115 //
116 VirtioGpuUnmapAndFreeBackingStore (
117 VgpuGop->ParentBus, // VgpuDev
118 VgpuGop->NumberOfPages, // NumberOfPages
119 VgpuGop->BackingStore, // HostAddress
120 VgpuGop->BackingStoreMap // Mapping
121 );
122 VgpuGop->BackingStore = NULL;
123 VgpuGop->NumberOfPages = 0;
124 VgpuGop->BackingStoreMap = NULL;
125
126 //
127 // Destroy the currently used 2D host resource.
128 //
129 Status = VirtioGpuResourceUnref (
130 VgpuGop->ParentBus, // VgpuDev
131 VgpuGop->ResourceId // ResourceId
132 );
133 ASSERT_EFI_ERROR (Status);
134 if (EFI_ERROR (Status)) {
135 CpuDeadLoop ();
136 }
137
138 VgpuGop->ResourceId = 0;
139 }
140
141 //
142 // The resolutions supported by this driver.
143 //
144 typedef struct {
145 UINT32 Width;
146 UINT32 Height;
147 } GOP_RESOLUTION;
148
149 STATIC CONST GOP_RESOLUTION mGopResolutions[] = {
150 { 640, 480 },
151 { 800, 480 },
152 { 800, 600 },
153 { 832, 624 },
154 { 960, 640 },
155 { 1024, 600 },
156 { 1024, 768 },
157 { 1152, 864 },
158 { 1152, 870 },
159 { 1280, 720 },
160 { 1280, 760 },
161 { 1280, 768 },
162 { 1280, 800 },
163 { 1280, 960 },
164 { 1280, 1024 },
165 { 1360, 768 },
166 { 1366, 768 },
167 { 1400, 1050 },
168 { 1440, 900 },
169 { 1600, 900 },
170 { 1600, 1200 },
171 { 1680, 1050 },
172 { 1920, 1080 },
173 { 1920, 1200 },
174 { 1920, 1440 },
175 { 2000, 2000 },
176 { 2048, 1536 },
177 { 2048, 2048 },
178 { 2560, 1440 },
179 { 2560, 1600 },
180 { 2560, 2048 },
181 { 2800, 2100 },
182 { 3200, 2400 },
183 { 3840, 2160 },
184 { 4096, 2160 },
185 { 7680, 4320 },
186 { 8192, 4320 },
187 };
188
189 //
190 // Macro for casting VGPU_GOP.Gop to VGPU_GOP.
191 //
192 #define VGPU_GOP_FROM_GOP(GopPointer) \
193 CR (GopPointer, VGPU_GOP, Gop, VGPU_GOP_SIG)
194
195 //
196 // EFI_GRAPHICS_OUTPUT_PROTOCOL member functions.
197 //
198 STATIC
199 EFI_STATUS
200 EFIAPI
201 GopQueryMode (
202 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
203 IN UINT32 ModeNumber,
204 OUT UINTN *SizeOfInfo,
205 OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info
206 )
207 {
208 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *GopModeInfo;
209
210 if (ModeNumber >= ARRAY_SIZE (mGopResolutions)) {
211 return EFI_INVALID_PARAMETER;
212 }
213
214 GopModeInfo = AllocateZeroPool (sizeof *GopModeInfo);
215 if (GopModeInfo == NULL) {
216 return EFI_OUT_OF_RESOURCES;
217 }
218
219 GopModeInfo->HorizontalResolution = mGopResolutions[ModeNumber].Width;
220 GopModeInfo->VerticalResolution = mGopResolutions[ModeNumber].Height;
221 GopModeInfo->PixelFormat = PixelBltOnly;
222 GopModeInfo->PixelsPerScanLine = mGopResolutions[ModeNumber].Width;
223
224 *SizeOfInfo = sizeof *GopModeInfo;
225 *Info = GopModeInfo;
226 return EFI_SUCCESS;
227 }
228
229 STATIC
230 EFI_STATUS
231 EFIAPI
232 GopSetMode (
233 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
234 IN UINT32 ModeNumber
235 )
236 {
237 VGPU_GOP *VgpuGop;
238 UINT32 NewResourceId;
239 UINTN NewNumberOfBytes;
240 UINTN NewNumberOfPages;
241 VOID *NewBackingStore;
242 EFI_PHYSICAL_ADDRESS NewBackingStoreDeviceAddress;
243 VOID *NewBackingStoreMap;
244
245 EFI_STATUS Status;
246 EFI_STATUS Status2;
247
248 if (ModeNumber >= ARRAY_SIZE (mGopResolutions)) {
249 return EFI_UNSUPPORTED;
250 }
251
252 VgpuGop = VGPU_GOP_FROM_GOP (This);
253
254 //
255 // Distinguish the first (internal) call from the other (protocol consumer)
256 // calls.
257 //
258 if (VgpuGop->ResourceId == 0) {
259 //
260 // Set up the Gop -> GopMode -> GopModeInfo pointer chain, and the other
261 // (nonzero) constant fields.
262 //
263 // No direct framebuffer access is supported, only Blt() is.
264 //
265 VgpuGop->Gop.Mode = &VgpuGop->GopMode;
266
267 VgpuGop->GopMode.MaxMode = (UINT32)(ARRAY_SIZE (mGopResolutions));
268 VgpuGop->GopMode.Info = &VgpuGop->GopModeInfo;
269 VgpuGop->GopMode.SizeOfInfo = sizeof VgpuGop->GopModeInfo;
270
271 VgpuGop->GopModeInfo.PixelFormat = PixelBltOnly;
272
273 //
274 // This is the first time we create a host side resource.
275 //
276 NewResourceId = 1;
277 } else {
278 //
279 // We already have an active host side resource. Create the new one without
280 // interfering with the current one, so that we can cleanly bail out on
281 // error, without disturbing the current graphics mode.
282 //
283 // The formula below will alternate between IDs 1 and 2.
284 //
285 NewResourceId = 3 - VgpuGop->ResourceId;
286 }
287
288 //
289 // Create the 2D host resource.
290 //
291 Status = VirtioGpuResourceCreate2d (
292 VgpuGop->ParentBus, // VgpuDev
293 NewResourceId, // ResourceId
294 VirtioGpuFormatB8G8R8X8Unorm, // Format
295 mGopResolutions[ModeNumber].Width, // Width
296 mGopResolutions[ModeNumber].Height // Height
297 );
298 if (EFI_ERROR (Status)) {
299 return Status;
300 }
301
302 //
303 // Allocate, zero and map guest backing store, for bus master common buffer
304 // operation.
305 //
306 NewNumberOfBytes = mGopResolutions[ModeNumber].Width *
307 mGopResolutions[ModeNumber].Height * sizeof (UINT32);
308 NewNumberOfPages = EFI_SIZE_TO_PAGES (NewNumberOfBytes);
309 Status = VirtioGpuAllocateZeroAndMapBackingStore (
310 VgpuGop->ParentBus, // VgpuDev
311 NewNumberOfPages, // NumberOfPages
312 &NewBackingStore, // HostAddress
313 &NewBackingStoreDeviceAddress, // DeviceAddress
314 &NewBackingStoreMap // Mapping
315 );
316 if (EFI_ERROR (Status)) {
317 goto DestroyHostResource;
318 }
319
320 //
321 // Attach backing store to the host resource.
322 //
323 Status = VirtioGpuResourceAttachBacking (
324 VgpuGop->ParentBus, // VgpuDev
325 NewResourceId, // ResourceId
326 NewBackingStoreDeviceAddress, // BackingStoreDeviceAddress
327 NewNumberOfPages // NumberOfPages
328 );
329 if (EFI_ERROR (Status)) {
330 goto UnmapAndFreeBackingStore;
331 }
332
333 //
334 // Point head (scanout) #0 to the host resource.
335 //
336 Status = VirtioGpuSetScanout (
337 VgpuGop->ParentBus, // VgpuDev
338 0, // X
339 0, // Y
340 mGopResolutions[ModeNumber].Width, // Width
341 mGopResolutions[ModeNumber].Height, // Height
342 0, // ScanoutId
343 NewResourceId // ResourceId
344 );
345 if (EFI_ERROR (Status)) {
346 goto DetachBackingStore;
347 }
348
349 //
350 // If this is not the first (i.e., internal) call, then we have to (a) flush
351 // the new resource to head (scanout) #0, after having flipped the latter to
352 // the former above, plus (b) release the old resources.
353 //
354 if (VgpuGop->ResourceId != 0) {
355 Status = VirtioGpuResourceFlush (
356 VgpuGop->ParentBus, // VgpuDev
357 0, // X
358 0, // Y
359 mGopResolutions[ModeNumber].Width, // Width
360 mGopResolutions[ModeNumber].Height, // Height
361 NewResourceId // ResourceId
362 );
363 if (EFI_ERROR (Status)) {
364 //
365 // Flip head (scanout) #0 back to the current resource. If this fails, we
366 // cannot continue, as this error occurs on the error path and is
367 // therefore non-recoverable.
368 //
369 Status2 = VirtioGpuSetScanout (
370 VgpuGop->ParentBus, // VgpuDev
371 0, // X
372 0, // Y
373 mGopResolutions[This->Mode->Mode].Width, // Width
374 mGopResolutions[This->Mode->Mode].Height, // Height
375 0, // ScanoutId
376 VgpuGop->ResourceId // ResourceId
377 );
378 ASSERT_EFI_ERROR (Status2);
379 if (EFI_ERROR (Status2)) {
380 CpuDeadLoop ();
381 }
382
383 goto DetachBackingStore;
384 }
385
386 //
387 // Flush successful; release the old resources (without disabling head
388 // (scanout) #0).
389 //
390 ReleaseGopResources (VgpuGop, FALSE /* DisableHead */);
391 }
392
393 //
394 // This is either the first (internal) call when we have no old resources
395 // yet, or we've changed the mode successfully and released the old
396 // resources.
397 //
398 ASSERT (VgpuGop->ResourceId == 0);
399 ASSERT (VgpuGop->BackingStore == NULL);
400
401 VgpuGop->ResourceId = NewResourceId;
402 VgpuGop->BackingStore = NewBackingStore;
403 VgpuGop->NumberOfPages = NewNumberOfPages;
404 VgpuGop->BackingStoreMap = NewBackingStoreMap;
405
406 //
407 // Populate Mode and ModeInfo (mutable fields only).
408 //
409 VgpuGop->GopMode.Mode = ModeNumber;
410 VgpuGop->GopModeInfo.HorizontalResolution =
411 mGopResolutions[ModeNumber].Width;
412 VgpuGop->GopModeInfo.VerticalResolution = mGopResolutions[ModeNumber].Height;
413 VgpuGop->GopModeInfo.PixelsPerScanLine = mGopResolutions[ModeNumber].Width;
414 return EFI_SUCCESS;
415
416 DetachBackingStore:
417 Status2 = VirtioGpuResourceDetachBacking (VgpuGop->ParentBus, NewResourceId);
418 ASSERT_EFI_ERROR (Status2);
419 if (EFI_ERROR (Status2)) {
420 CpuDeadLoop ();
421 }
422
423 UnmapAndFreeBackingStore:
424 VirtioGpuUnmapAndFreeBackingStore (
425 VgpuGop->ParentBus, // VgpuDev
426 NewNumberOfPages, // NumberOfPages
427 NewBackingStore, // HostAddress
428 NewBackingStoreMap // Mapping
429 );
430
431 DestroyHostResource:
432 Status2 = VirtioGpuResourceUnref (VgpuGop->ParentBus, NewResourceId);
433 ASSERT_EFI_ERROR (Status2);
434 if (EFI_ERROR (Status2)) {
435 CpuDeadLoop ();
436 }
437
438 return Status;
439 }
440
441 STATIC
442 EFI_STATUS
443 EFIAPI
444 GopBlt (
445 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
446 IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer OPTIONAL,
447 IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
448 IN UINTN SourceX,
449 IN UINTN SourceY,
450 IN UINTN DestinationX,
451 IN UINTN DestinationY,
452 IN UINTN Width,
453 IN UINTN Height,
454 IN UINTN Delta OPTIONAL
455 )
456 {
457 VGPU_GOP *VgpuGop;
458 UINT32 CurrentHorizontal;
459 UINT32 CurrentVertical;
460 UINTN SegmentSize;
461 UINTN Y;
462 UINTN ResourceOffset;
463 EFI_STATUS Status;
464
465 VgpuGop = VGPU_GOP_FROM_GOP (This);
466 CurrentHorizontal = VgpuGop->GopModeInfo.HorizontalResolution;
467 CurrentVertical = VgpuGop->GopModeInfo.VerticalResolution;
468
469 //
470 // We can avoid pixel format conversion in the guest because the internal
471 // representation of EFI_GRAPHICS_OUTPUT_BLT_PIXEL and that of
472 // VirtioGpuFormatB8G8R8X8Unorm are identical.
473 //
474 SegmentSize = Width * sizeof (UINT32);
475
476 //
477 // Delta is relevant for operations that read a rectangle from, or write a
478 // rectangle to, BltBuffer.
479 //
480 // In these cases, Delta is the stride of BltBuffer, in bytes. If Delta is
481 // zero, then Width is the entire width of BltBuffer, and the stride is
482 // supposed to be calculated from Width.
483 //
484 if ((BltOperation == EfiBltVideoToBltBuffer) ||
485 (BltOperation == EfiBltBufferToVideo))
486 {
487 if (Delta == 0) {
488 Delta = SegmentSize;
489 }
490 }
491
492 //
493 // For operations that write to the display, check if the destination fits
494 // onto the display.
495 //
496 if ((BltOperation == EfiBltVideoFill) ||
497 (BltOperation == EfiBltBufferToVideo) ||
498 (BltOperation == EfiBltVideoToVideo))
499 {
500 if ((DestinationX > CurrentHorizontal) ||
501 (Width > CurrentHorizontal - DestinationX) ||
502 (DestinationY > CurrentVertical) ||
503 (Height > CurrentVertical - DestinationY))
504 {
505 return EFI_INVALID_PARAMETER;
506 }
507 }
508
509 //
510 // For operations that read from the display, check if the source fits onto
511 // the display.
512 //
513 if ((BltOperation == EfiBltVideoToBltBuffer) ||
514 (BltOperation == EfiBltVideoToVideo))
515 {
516 if ((SourceX > CurrentHorizontal) ||
517 (Width > CurrentHorizontal - SourceX) ||
518 (SourceY > CurrentVertical) ||
519 (Height > CurrentVertical - SourceY))
520 {
521 return EFI_INVALID_PARAMETER;
522 }
523 }
524
525 //
526 // Render the request. For requests that do not modify the display, there
527 // won't be further steps.
528 //
529 switch (BltOperation) {
530 case EfiBltVideoFill:
531 //
532 // Write data from the BltBuffer pixel (0, 0) directly to every pixel of
533 // the video display rectangle (DestinationX, DestinationY) (DestinationX +
534 // Width, DestinationY + Height). Only one pixel will be used from the
535 // BltBuffer. Delta is NOT used.
536 //
537 for (Y = 0; Y < Height; ++Y) {
538 SetMem32 (
539 VgpuGop->BackingStore +
540 (DestinationY + Y) * CurrentHorizontal + DestinationX,
541 SegmentSize,
542 *(UINT32 *)BltBuffer
543 );
544 }
545
546 break;
547
548 case EfiBltVideoToBltBuffer:
549 //
550 // Read data from the video display rectangle (SourceX, SourceY) (SourceX +
551 // Width, SourceY + Height) and place it in the BltBuffer rectangle
552 // (DestinationX, DestinationY ) (DestinationX + Width, DestinationY +
553 // Height). If DestinationX or DestinationY is not zero then Delta must be
554 // set to the length in bytes of a row in the BltBuffer.
555 //
556 for (Y = 0; Y < Height; ++Y) {
557 CopyMem (
558 (UINT8 *)BltBuffer +
559 (DestinationY + Y) * Delta + DestinationX * sizeof *BltBuffer,
560 VgpuGop->BackingStore +
561 (SourceY + Y) * CurrentHorizontal + SourceX,
562 SegmentSize
563 );
564 }
565
566 return EFI_SUCCESS;
567
568 case EfiBltBufferToVideo:
569 //
570 // Write data from the BltBuffer rectangle (SourceX, SourceY) (SourceX +
571 // Width, SourceY + Height) directly to the video display rectangle
572 // (DestinationX, DestinationY) (DestinationX + Width, DestinationY +
573 // Height). If SourceX or SourceY is not zero then Delta must be set to the
574 // length in bytes of a row in the BltBuffer.
575 //
576 for (Y = 0; Y < Height; ++Y) {
577 CopyMem (
578 VgpuGop->BackingStore +
579 (DestinationY + Y) * CurrentHorizontal + DestinationX,
580 (UINT8 *)BltBuffer +
581 (SourceY + Y) * Delta + SourceX * sizeof *BltBuffer,
582 SegmentSize
583 );
584 }
585
586 break;
587
588 case EfiBltVideoToVideo:
589 //
590 // Copy from the video display rectangle (SourceX, SourceY) (SourceX +
591 // Width, SourceY + Height) to the video display rectangle (DestinationX,
592 // DestinationY) (DestinationX + Width, DestinationY + Height). The
593 // BltBuffer and Delta are not used in this mode.
594 //
595 // A single invocation of CopyMem() handles overlap between source and
596 // destination (that is, within a single line), but for multiple
597 // invocations, we must handle overlaps.
598 //
599 if (SourceY < DestinationY) {
600 Y = Height;
601 while (Y > 0) {
602 --Y;
603 CopyMem (
604 VgpuGop->BackingStore +
605 (DestinationY + Y) * CurrentHorizontal + DestinationX,
606 VgpuGop->BackingStore +
607 (SourceY + Y) * CurrentHorizontal + SourceX,
608 SegmentSize
609 );
610 }
611 } else {
612 for (Y = 0; Y < Height; ++Y) {
613 CopyMem (
614 VgpuGop->BackingStore +
615 (DestinationY + Y) * CurrentHorizontal + DestinationX,
616 VgpuGop->BackingStore +
617 (SourceY + Y) * CurrentHorizontal + SourceX,
618 SegmentSize
619 );
620 }
621 }
622
623 break;
624
625 default:
626 return EFI_INVALID_PARAMETER;
627 }
628
629 //
630 // For operations that wrote to the display, submit the updated area to the
631 // host -- update the host resource from guest memory.
632 //
633 ResourceOffset = sizeof (UINT32) * (DestinationY * CurrentHorizontal +
634 DestinationX);
635 Status = VirtioGpuTransferToHost2d (
636 VgpuGop->ParentBus, // VgpuDev
637 (UINT32)DestinationX, // X
638 (UINT32)DestinationY, // Y
639 (UINT32)Width, // Width
640 (UINT32)Height, // Height
641 ResourceOffset, // Offset
642 VgpuGop->ResourceId // ResourceId
643 );
644 if (EFI_ERROR (Status)) {
645 return Status;
646 }
647
648 //
649 // Flush the updated resource to the display.
650 //
651 Status = VirtioGpuResourceFlush (
652 VgpuGop->ParentBus, // VgpuDev
653 (UINT32)DestinationX, // X
654 (UINT32)DestinationY, // Y
655 (UINT32)Width, // Width
656 (UINT32)Height, // Height
657 VgpuGop->ResourceId // ResourceId
658 );
659 return Status;
660 }
661
662 //
663 // Template for initializing VGPU_GOP.Gop.
664 //
665 CONST EFI_GRAPHICS_OUTPUT_PROTOCOL mGopTemplate = {
666 GopQueryMode,
667 GopSetMode,
668 GopBlt,
669 NULL // Mode, to be overwritten in the actual protocol instance
670 };