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