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