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