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