1 /** @file 2 Try to load an EFI-stubbed ARM Linux kernel from QEMU's fw_cfg. 3 4 This implementation differs from OvmfPkg/Library/LoadLinuxLib. An EFI 5 stub in the subject kernel is a hard requirement here. 6 7 Copyright (C) 2014, Red Hat, Inc. 8 9 This program and the accompanying materials are licensed and made available 10 under the terms and conditions of the BSD License which accompanies this 11 distribution. The full text of the license may be found at 12 http://opensource.org/licenses/bsd-license.php 13 14 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT 15 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 16 **/ 17 18 #include <Guid/FileInfo.h> 19 #include <Guid/FileSystemInfo.h> 20 #include <Guid/FileSystemVolumeLabelInfo.h> 21 #include <Library/PrintLib.h> 22 #include <Library/QemuFwCfgLib.h> 23 #include <Library/UefiLib.h> 24 #include <Protocol/DevicePath.h> 25 #include <Protocol/LoadedImage.h> 26 #include <Protocol/SimpleFileSystem.h> 27 28 #include "IntelBdsPlatform.h" 29 30 // 31 // Static data that hosts the fw_cfg blobs and serves file requests. 32 // 33 typedef enum { 34 KernelBlobTypeKernel, 35 KernelBlobTypeInitrd, 36 KernelBlobTypeCommandLine, 37 KernelBlobTypeMax 38 } KERNEL_BLOB_TYPE; 39 40 typedef struct { 41 FIRMWARE_CONFIG_ITEM CONST SizeKey; 42 FIRMWARE_CONFIG_ITEM CONST DataKey; 43 CONST CHAR16 * CONST Name; 44 UINT32 Size; 45 UINT8 *Data; 46 } KERNEL_BLOB; 47 48 STATIC KERNEL_BLOB mKernelBlob[KernelBlobTypeMax] = { 49 { QemuFwCfgItemKernelSize, QemuFwCfgItemKernelData, L"kernel" }, 50 { QemuFwCfgItemInitrdSize, QemuFwCfgItemInitrdData, L"initrd" }, 51 { QemuFwCfgItemCommandLineSize, QemuFwCfgItemCommandLineData, L"cmdline" } 52 }; 53 54 STATIC UINT64 mTotalBlobBytes; 55 56 // 57 // Device path for the handle that incorporates our "EFI stub filesystem". The 58 // GUID is arbitrary and need not be standardized or advertized. 59 // 60 #pragma pack(1) 61 typedef struct { 62 VENDOR_DEVICE_PATH VenHwNode; 63 EFI_DEVICE_PATH_PROTOCOL EndNode; 64 } SINGLE_VENHW_NODE_DEVPATH; 65 #pragma pack() 66 67 STATIC CONST SINGLE_VENHW_NODE_DEVPATH mFileSystemDevicePath = { 68 { 69 { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH) } }, 70 { 71 0xb0fae7e7, 0x6b07, 0x49d0, 72 { 0x9e, 0x5b, 0x3b, 0xde, 0xc8, 0x3b, 0x03, 0x9d } 73 } 74 }, 75 76 { 77 END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, 78 { sizeof (EFI_DEVICE_PATH_PROTOCOL) } 79 } 80 }; 81 82 // 83 // The "file in the EFI stub filesystem" abstraction. 84 // 85 STATIC EFI_TIME mInitTime; 86 87 #define STUB_FILE_SIG SIGNATURE_64 ('S', 'T', 'U', 'B', 'F', 'I', 'L', 'E') 88 89 typedef struct { 90 UINT64 Signature; // Carries STUB_FILE_SIG. 91 92 KERNEL_BLOB_TYPE BlobType; // Index into mKernelBlob. KernelBlobTypeMax 93 // denotes the root directory of the filesystem. 94 95 UINT64 Position; // Byte position for regular files; 96 // next directory entry to return for the root 97 // directory. 98 99 EFI_FILE_PROTOCOL File; // Standard protocol interface. 100 } STUB_FILE; 101 102 #define STUB_FILE_FROM_FILE(FilePointer) \ 103 CR (FilePointer, STUB_FILE, File, STUB_FILE_SIG) 104 105 // 106 // Tentative definition of the file protocol template. The initializer 107 // (external definition) will be provided later. 108 // 109 STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate; 110 111 112 // 113 // Protocol member functions for File. 114 // 115 116 /** 117 Opens a new file relative to the source file's location. 118 119 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is 120 the file handle to the source location. This would 121 typically be an open handle to a directory. 122 123 @param[out] NewHandle A pointer to the location to return the opened handle 124 for the new file. 125 126 @param[in] FileName The Null-terminated string of the name of the file to 127 be opened. The file name may contain the following 128 path modifiers: "\", ".", and "..". 129 130 @param[in] OpenMode The mode to open the file. The only valid 131 combinations that the file may be opened with are: 132 Read, Read/Write, or Create/Read/Write. 133 134 @param[in] Attributes Only valid for EFI_FILE_MODE_CREATE, in which case 135 these are the attribute bits for the newly created 136 file. 137 138 @retval EFI_SUCCESS The file was opened. 139 @retval EFI_NOT_FOUND The specified file could not be found on the 140 device. 141 @retval EFI_NO_MEDIA The device has no medium. 142 @retval EFI_MEDIA_CHANGED The device has a different medium in it or the 143 medium is no longer supported. 144 @retval EFI_DEVICE_ERROR The device reported an error. 145 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. 146 @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a 147 file for write when the media is 148 write-protected. 149 @retval EFI_ACCESS_DENIED The service denied access to the file. 150 @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the 151 file. 152 @retval EFI_VOLUME_FULL The volume is full. 153 **/ 154 STATIC 155 EFI_STATUS 156 EFIAPI 157 StubFileOpen ( 158 IN EFI_FILE_PROTOCOL *This, 159 OUT EFI_FILE_PROTOCOL **NewHandle, 160 IN CHAR16 *FileName, 161 IN UINT64 OpenMode, 162 IN UINT64 Attributes 163 ) 164 { 165 CONST STUB_FILE *StubFile; 166 UINTN BlobType; 167 STUB_FILE *NewStubFile; 168 169 // 170 // We're read-only. 171 // 172 switch (OpenMode) { 173 case EFI_FILE_MODE_READ: 174 break; 175 176 case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE: 177 case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE: 178 return EFI_WRITE_PROTECTED; 179 180 default: 181 return EFI_INVALID_PARAMETER; 182 } 183 184 // 185 // Only the root directory supports opening files in it. 186 // 187 StubFile = STUB_FILE_FROM_FILE (This); 188 if (StubFile->BlobType != KernelBlobTypeMax) { 189 return EFI_UNSUPPORTED; 190 } 191 192 // 193 // Locate the file. 194 // 195 for (BlobType = 0; BlobType < KernelBlobTypeMax; ++BlobType) { 196 if (StrCmp (FileName, mKernelBlob[BlobType].Name) == 0) { 197 break; 198 } 199 } 200 if (BlobType == KernelBlobTypeMax) { 201 return EFI_NOT_FOUND; 202 } 203 204 // 205 // Found it. 206 // 207 NewStubFile = AllocatePool (sizeof *NewStubFile); 208 if (NewStubFile == NULL) { 209 return EFI_OUT_OF_RESOURCES; 210 } 211 212 NewStubFile->Signature = STUB_FILE_SIG; 213 NewStubFile->BlobType = (KERNEL_BLOB_TYPE)BlobType; 214 NewStubFile->Position = 0; 215 CopyMem (&NewStubFile->File, &mEfiFileProtocolTemplate, 216 sizeof mEfiFileProtocolTemplate); 217 *NewHandle = &NewStubFile->File; 218 219 return EFI_SUCCESS; 220 } 221 222 223 /** 224 Closes a specified file handle. 225 226 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file 227 handle to close. 228 229 @retval EFI_SUCCESS The file was closed. 230 **/ 231 STATIC 232 EFI_STATUS 233 EFIAPI 234 StubFileClose ( 235 IN EFI_FILE_PROTOCOL *This 236 ) 237 { 238 FreePool (STUB_FILE_FROM_FILE (This)); 239 return EFI_SUCCESS; 240 } 241 242 243 /** 244 Close and delete the file handle. 245 246 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the 247 handle to the file to delete. 248 249 @retval EFI_SUCCESS The file was closed and deleted, and the 250 handle was closed. 251 @retval EFI_WARN_DELETE_FAILURE The handle was closed, but the file was not 252 deleted. 253 254 **/ 255 STATIC 256 EFI_STATUS 257 EFIAPI 258 StubFileDelete ( 259 IN EFI_FILE_PROTOCOL *This 260 ) 261 { 262 FreePool (STUB_FILE_FROM_FILE (This)); 263 return EFI_WARN_DELETE_FAILURE; 264 } 265 266 267 /** 268 Helper function that formats an EFI_FILE_INFO structure into the 269 user-allocated buffer, for any valid KERNEL_BLOB_TYPE value (including 270 KernelBlobTypeMax, which stands for the root directory). 271 272 The interface follows the EFI_FILE_GET_INFO -- and for directories, the 273 EFI_FILE_READ -- interfaces. 274 275 @param[in] BlobType The KERNEL_BLOB_TYPE value identifying the fw_cfg 276 blob backing the STUB_FILE that information is 277 being requested about. If BlobType equals 278 KernelBlobTypeMax, then information will be 279 provided about the root directory of the 280 filesystem. 281 282 @param[in,out] BufferSize On input, the size of Buffer. On output, the 283 amount of data returned in Buffer. In both cases, 284 the size is measured in bytes. 285 286 @param[out] Buffer A pointer to the data buffer to return. The 287 buffer's type is EFI_FILE_INFO. 288 289 @retval EFI_SUCCESS The information was returned. 290 @retval EFI_BUFFER_TOO_SMALL BufferSize is too small to store the 291 EFI_FILE_INFO structure. BufferSize has been 292 updated with the size needed to complete the 293 request. 294 **/ 295 STATIC 296 EFI_STATUS 297 ConvertKernelBlobTypeToFileInfo ( 298 IN KERNEL_BLOB_TYPE BlobType, 299 IN OUT UINTN *BufferSize, 300 OUT VOID *Buffer 301 ) 302 { 303 CONST CHAR16 *Name; 304 UINT64 FileSize; 305 UINT64 Attribute; 306 307 UINTN NameSize; 308 UINTN FileInfoSize; 309 EFI_FILE_INFO *FileInfo; 310 UINTN OriginalBufferSize; 311 312 if (BlobType == KernelBlobTypeMax) { 313 // 314 // getting file info about the root directory 315 // 316 Name = L"\\"; 317 FileSize = KernelBlobTypeMax; 318 Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY; 319 } else { 320 CONST KERNEL_BLOB *Blob; 321 322 Blob = &mKernelBlob[BlobType]; 323 Name = Blob->Name; 324 FileSize = Blob->Size; 325 Attribute = EFI_FILE_READ_ONLY; 326 } 327 328 NameSize = (StrLen(Name) + 1) * 2; 329 FileInfoSize = OFFSET_OF (EFI_FILE_INFO, FileName) + NameSize; 330 ASSERT (FileInfoSize >= sizeof *FileInfo); 331 332 OriginalBufferSize = *BufferSize; 333 *BufferSize = FileInfoSize; 334 if (OriginalBufferSize < *BufferSize) { 335 return EFI_BUFFER_TOO_SMALL; 336 } 337 338 FileInfo = (EFI_FILE_INFO *)Buffer; 339 FileInfo->Size = FileInfoSize; 340 FileInfo->FileSize = FileSize; 341 FileInfo->PhysicalSize = FileSize; 342 FileInfo->Attribute = Attribute; 343 344 CopyMem (&FileInfo->CreateTime, &mInitTime, sizeof mInitTime); 345 CopyMem (&FileInfo->LastAccessTime, &mInitTime, sizeof mInitTime); 346 CopyMem (&FileInfo->ModificationTime, &mInitTime, sizeof mInitTime); 347 CopyMem (FileInfo->FileName, Name, NameSize); 348 349 return EFI_SUCCESS; 350 } 351 352 353 /** 354 Reads data from a file, or continues scanning a directory. 355 356 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that 357 is the file handle to read data from. 358 359 @param[in,out] BufferSize On input, the size of the Buffer. On output, the 360 amount of data returned in Buffer. In both cases, 361 the size is measured in bytes. If the read goes 362 beyond the end of the file, the read length is 363 truncated to the end of the file. 364 365 If This is a directory, the function reads the 366 directory entry at the current position and 367 returns the entry (as EFI_FILE_INFO) in Buffer. If 368 there are no more directory entries, the 369 BufferSize is set to zero on output. 370 371 @param[out] Buffer The buffer into which the data is read. 372 373 @retval EFI_SUCCESS Data was read. 374 @retval EFI_NO_MEDIA The device has no medium. 375 @retval EFI_DEVICE_ERROR The device reported an error. 376 @retval EFI_DEVICE_ERROR An attempt was made to read from a deleted 377 file. 378 @retval EFI_DEVICE_ERROR On entry, the current file position is beyond 379 the end of the file. 380 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. 381 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to store the 382 current directory entry as a EFI_FILE_INFO 383 structure. BufferSize has been updated with the 384 size needed to complete the request, and the 385 directory position has not been advanced. 386 **/ 387 STATIC 388 EFI_STATUS 389 EFIAPI 390 StubFileRead ( 391 IN EFI_FILE_PROTOCOL *This, 392 IN OUT UINTN *BufferSize, 393 OUT VOID *Buffer 394 ) 395 { 396 STUB_FILE *StubFile; 397 CONST KERNEL_BLOB *Blob; 398 UINT64 Left; 399 400 StubFile = STUB_FILE_FROM_FILE (This); 401 402 // 403 // Scanning the root directory? 404 // 405 if (StubFile->BlobType == KernelBlobTypeMax) { 406 EFI_STATUS Status; 407 408 if (StubFile->Position == KernelBlobTypeMax) { 409 // 410 // Scanning complete. 411 // 412 *BufferSize = 0; 413 return EFI_SUCCESS; 414 } 415 416 Status = ConvertKernelBlobTypeToFileInfo ( 417 (KERNEL_BLOB_TYPE)StubFile->Position, 418 BufferSize, 419 Buffer); 420 if (EFI_ERROR (Status)) { 421 return Status; 422 } 423 424 ++StubFile->Position; 425 return EFI_SUCCESS; 426 } 427 428 // 429 // Reading a file. 430 // 431 Blob = &mKernelBlob[StubFile->BlobType]; 432 if (StubFile->Position > Blob->Size) { 433 return EFI_DEVICE_ERROR; 434 } 435 436 Left = Blob->Size - StubFile->Position; 437 if (*BufferSize > Left) { 438 *BufferSize = (UINTN)Left; 439 } 440 if (Blob->Data != NULL) { 441 CopyMem (Buffer, Blob->Data + StubFile->Position, *BufferSize); 442 } 443 StubFile->Position += *BufferSize; 444 return EFI_SUCCESS; 445 } 446 447 448 /** 449 Writes data to a file. 450 451 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that 452 is the file handle to write data to. 453 454 @param[in,out] BufferSize On input, the size of the Buffer. On output, the 455 amount of data actually written. In both cases, 456 the size is measured in bytes. 457 458 @param[in] Buffer The buffer of data to write. 459 460 @retval EFI_SUCCESS Data was written. 461 @retval EFI_UNSUPPORTED Writes to open directory files are not 462 supported. 463 @retval EFI_NO_MEDIA The device has no medium. 464 @retval EFI_DEVICE_ERROR The device reported an error. 465 @retval EFI_DEVICE_ERROR An attempt was made to write to a deleted file. 466 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. 467 @retval EFI_WRITE_PROTECTED The file or medium is write-protected. 468 @retval EFI_ACCESS_DENIED The file was opened read only. 469 @retval EFI_VOLUME_FULL The volume is full. 470 **/ 471 STATIC 472 EFI_STATUS 473 EFIAPI 474 StubFileWrite ( 475 IN EFI_FILE_PROTOCOL *This, 476 IN OUT UINTN *BufferSize, 477 IN VOID *Buffer 478 ) 479 { 480 STUB_FILE *StubFile; 481 482 StubFile = STUB_FILE_FROM_FILE (This); 483 return (StubFile->BlobType == KernelBlobTypeMax) ? 484 EFI_UNSUPPORTED : 485 EFI_WRITE_PROTECTED; 486 } 487 488 489 /** 490 Returns a file's current position. 491 492 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the 493 file handle to get the current position on. 494 495 @param[out] Position The address to return the file's current position 496 value. 497 498 @retval EFI_SUCCESS The position was returned. 499 @retval EFI_UNSUPPORTED The request is not valid on open directories. 500 @retval EFI_DEVICE_ERROR An attempt was made to get the position from a 501 deleted file. 502 **/ 503 STATIC 504 EFI_STATUS 505 EFIAPI 506 StubFileGetPosition ( 507 IN EFI_FILE_PROTOCOL *This, 508 OUT UINT64 *Position 509 ) 510 { 511 STUB_FILE *StubFile; 512 513 StubFile = STUB_FILE_FROM_FILE (This); 514 if (StubFile->BlobType == KernelBlobTypeMax) { 515 return EFI_UNSUPPORTED; 516 } 517 518 *Position = StubFile->Position; 519 return EFI_SUCCESS; 520 } 521 522 523 /** 524 Sets a file's current position. 525 526 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the 527 file handle to set the requested position on. 528 529 @param[in] Position The byte position from the start of the file to set. For 530 regular files, MAX_UINT64 means "seek to end". For 531 directories, zero means "rewind directory scan". 532 533 @retval EFI_SUCCESS The position was set. 534 @retval EFI_UNSUPPORTED The seek request for nonzero is not valid on open 535 directories. 536 @retval EFI_DEVICE_ERROR An attempt was made to set the position of a 537 deleted file. 538 **/ 539 STATIC 540 EFI_STATUS 541 EFIAPI 542 StubFileSetPosition ( 543 IN EFI_FILE_PROTOCOL *This, 544 IN UINT64 Position 545 ) 546 { 547 STUB_FILE *StubFile; 548 KERNEL_BLOB *Blob; 549 550 StubFile = STUB_FILE_FROM_FILE (This); 551 552 if (StubFile->BlobType == KernelBlobTypeMax) { 553 if (Position == 0) { 554 // 555 // rewinding a directory scan is allowed 556 // 557 StubFile->Position = 0; 558 return EFI_SUCCESS; 559 } 560 return EFI_UNSUPPORTED; 561 } 562 563 // 564 // regular file seek 565 // 566 Blob = &mKernelBlob[StubFile->BlobType]; 567 if (Position == MAX_UINT64) { 568 // 569 // seek to end 570 // 571 StubFile->Position = Blob->Size; 572 } else { 573 // 574 // absolute seek from beginning -- seeking past the end is allowed 575 // 576 StubFile->Position = Position; 577 } 578 return EFI_SUCCESS; 579 } 580 581 582 /** 583 Returns information about a file. 584 585 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance 586 that is the file handle the requested 587 information is for. 588 589 @param[in] InformationType The type identifier GUID for the information 590 being requested. The following information 591 types are supported, storing the 592 corresponding structures in Buffer: 593 594 - gEfiFileInfoGuid: EFI_FILE_INFO 595 596 - gEfiFileSystemInfoGuid: 597 EFI_FILE_SYSTEM_INFO 598 599 - gEfiFileSystemVolumeLabelInfoIdGuid: 600 EFI_FILE_SYSTEM_VOLUME_LABEL 601 602 @param[in,out] BufferSize On input, the size of Buffer. On output, the 603 amount of data returned in Buffer. In both 604 cases, the size is measured in bytes. 605 606 @param[out] Buffer A pointer to the data buffer to return. The 607 buffer's type is indicated by 608 InformationType. 609 610 @retval EFI_SUCCESS The information was returned. 611 @retval EFI_UNSUPPORTED The InformationType is not known. 612 @retval EFI_NO_MEDIA The device has no medium. 613 @retval EFI_DEVICE_ERROR The device reported an error. 614 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. 615 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to store the 616 information structure requested by 617 InformationType. BufferSize has been updated 618 with the size needed to complete the request. 619 **/ 620 STATIC 621 EFI_STATUS 622 EFIAPI 623 StubFileGetInfo ( 624 IN EFI_FILE_PROTOCOL *This, 625 IN EFI_GUID *InformationType, 626 IN OUT UINTN *BufferSize, 627 OUT VOID *Buffer 628 ) 629 { 630 CONST STUB_FILE *StubFile; 631 UINTN OriginalBufferSize; 632 633 StubFile = STUB_FILE_FROM_FILE (This); 634 635 if (CompareGuid (InformationType, &gEfiFileInfoGuid)) { 636 return ConvertKernelBlobTypeToFileInfo (StubFile->BlobType, BufferSize, 637 Buffer); 638 } 639 640 OriginalBufferSize = *BufferSize; 641 642 if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) { 643 EFI_FILE_SYSTEM_INFO *FileSystemInfo; 644 645 *BufferSize = sizeof *FileSystemInfo; 646 if (OriginalBufferSize < *BufferSize) { 647 return EFI_BUFFER_TOO_SMALL; 648 } 649 650 FileSystemInfo = (EFI_FILE_SYSTEM_INFO *)Buffer; 651 FileSystemInfo->Size = sizeof *FileSystemInfo; 652 FileSystemInfo->ReadOnly = TRUE; 653 FileSystemInfo->VolumeSize = mTotalBlobBytes; 654 FileSystemInfo->FreeSpace = 0; 655 FileSystemInfo->BlockSize = 1; 656 FileSystemInfo->VolumeLabel[0] = L'\0'; 657 658 return EFI_SUCCESS; 659 } 660 661 if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { 662 EFI_FILE_SYSTEM_VOLUME_LABEL *FileSystemVolumeLabel; 663 664 *BufferSize = sizeof *FileSystemVolumeLabel; 665 if (OriginalBufferSize < *BufferSize) { 666 return EFI_BUFFER_TOO_SMALL; 667 } 668 669 FileSystemVolumeLabel = (EFI_FILE_SYSTEM_VOLUME_LABEL *)Buffer; 670 FileSystemVolumeLabel->VolumeLabel[0] = L'\0'; 671 672 return EFI_SUCCESS; 673 } 674 675 return EFI_UNSUPPORTED; 676 } 677 678 679 /** 680 Sets information about a file. 681 682 @param[in] File A pointer to the EFI_FILE_PROTOCOL instance that 683 is the file handle the information is for. 684 685 @param[in] InformationType The type identifier for the information being 686 set. 687 688 @param[in] BufferSize The size, in bytes, of Buffer. 689 690 @param[in] Buffer A pointer to the data buffer to write. The 691 buffer's type is indicated by InformationType. 692 693 @retval EFI_SUCCESS The information was set. 694 @retval EFI_UNSUPPORTED The InformationType is not known. 695 @retval EFI_NO_MEDIA The device has no medium. 696 @retval EFI_DEVICE_ERROR The device reported an error. 697 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. 698 @retval EFI_WRITE_PROTECTED InformationType is EFI_FILE_INFO_ID and the 699 media is read-only. 700 @retval EFI_WRITE_PROTECTED InformationType is 701 EFI_FILE_PROTOCOL_SYSTEM_INFO_ID and the media 702 is read only. 703 @retval EFI_WRITE_PROTECTED InformationType is 704 EFI_FILE_SYSTEM_VOLUME_LABEL_ID and the media 705 is read-only. 706 @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file 707 to a file that is already present. 708 @retval EFI_ACCESS_DENIED An attempt is being made to change the 709 EFI_FILE_DIRECTORY Attribute. 710 @retval EFI_ACCESS_DENIED An attempt is being made to change the size of 711 a directory. 712 @retval EFI_ACCESS_DENIED InformationType is EFI_FILE_INFO_ID and the 713 file was opened read-only and an attempt is 714 being made to modify a field other than 715 Attribute. 716 @retval EFI_VOLUME_FULL The volume is full. 717 @retval EFI_BAD_BUFFER_SIZE BufferSize is smaller than the size of the type 718 indicated by InformationType. 719 **/ 720 STATIC 721 EFI_STATUS 722 EFIAPI 723 StubFileSetInfo ( 724 IN EFI_FILE_PROTOCOL *This, 725 IN EFI_GUID *InformationType, 726 IN UINTN BufferSize, 727 IN VOID *Buffer 728 ) 729 { 730 return EFI_WRITE_PROTECTED; 731 } 732 733 734 /** 735 Flushes all modified data associated with a file to a device. 736 737 @param [in] This A pointer to the EFI_FILE_PROTOCOL instance that is the 738 file handle to flush. 739 740 @retval EFI_SUCCESS The data was flushed. 741 @retval EFI_NO_MEDIA The device has no medium. 742 @retval EFI_DEVICE_ERROR The device reported an error. 743 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. 744 @retval EFI_WRITE_PROTECTED The file or medium is write-protected. 745 @retval EFI_ACCESS_DENIED The file was opened read-only. 746 @retval EFI_VOLUME_FULL The volume is full. 747 **/ 748 STATIC 749 EFI_STATUS 750 EFIAPI 751 StubFileFlush ( 752 IN EFI_FILE_PROTOCOL *This 753 ) 754 { 755 return EFI_WRITE_PROTECTED; 756 } 757 758 // 759 // External definition of the file protocol template. 760 // 761 STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate = { 762 EFI_FILE_PROTOCOL_REVISION, // revision 1 763 StubFileOpen, 764 StubFileClose, 765 StubFileDelete, 766 StubFileRead, 767 StubFileWrite, 768 StubFileGetPosition, 769 StubFileSetPosition, 770 StubFileGetInfo, 771 StubFileSetInfo, 772 StubFileFlush, 773 NULL, // OpenEx, revision 2 774 NULL, // ReadEx, revision 2 775 NULL, // WriteEx, revision 2 776 NULL // FlushEx, revision 2 777 }; 778 779 780 // 781 // Protocol member functions for SimpleFileSystem. 782 // 783 784 /** 785 Open the root directory on a volume. 786 787 @param[in] This A pointer to the volume to open the root directory on. 788 789 @param[out] Root A pointer to the location to return the opened file handle 790 for the root directory in. 791 792 @retval EFI_SUCCESS The device was opened. 793 @retval EFI_UNSUPPORTED This volume does not support the requested file 794 system type. 795 @retval EFI_NO_MEDIA The device has no medium. 796 @retval EFI_DEVICE_ERROR The device reported an error. 797 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. 798 @retval EFI_ACCESS_DENIED The service denied access to the file. 799 @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of 800 resources. 801 @retval EFI_MEDIA_CHANGED The device has a different medium in it or the 802 medium is no longer supported. Any existing 803 file handles for this volume are no longer 804 valid. To access the files on the new medium, 805 the volume must be reopened with OpenVolume(). 806 **/ 807 STATIC 808 EFI_STATUS 809 EFIAPI 810 StubFileSystemOpenVolume ( 811 IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, 812 OUT EFI_FILE_PROTOCOL **Root 813 ) 814 { 815 STUB_FILE *StubFile; 816 817 StubFile = AllocatePool (sizeof *StubFile); 818 if (StubFile == NULL) { 819 return EFI_OUT_OF_RESOURCES; 820 } 821 822 StubFile->Signature = STUB_FILE_SIG; 823 StubFile->BlobType = KernelBlobTypeMax; 824 StubFile->Position = 0; 825 CopyMem (&StubFile->File, &mEfiFileProtocolTemplate, 826 sizeof mEfiFileProtocolTemplate); 827 *Root = &StubFile->File; 828 829 return EFI_SUCCESS; 830 } 831 832 STATIC CONST EFI_SIMPLE_FILE_SYSTEM_PROTOCOL mFileSystem = { 833 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION, 834 StubFileSystemOpenVolume 835 }; 836 837 838 // 839 // Utility functions. 840 // 841 842 /** 843 Populate a blob in mKernelBlob. 844 845 param[in,out] Blob Pointer to the KERNEL_BLOB element in mKernelBlob that is 846 to be filled from fw_cfg. 847 848 @retval EFI_SUCCESS Blob has been populated. If fw_cfg reported a 849 size of zero for the blob, then Blob->Data has 850 been left unchanged. 851 852 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for Blob->Data. 853 **/ 854 STATIC 855 EFI_STATUS 856 FetchBlob ( 857 IN OUT KERNEL_BLOB *Blob 858 ) 859 { 860 UINT32 Left; 861 862 // 863 // Read blob size. 864 // 865 QemuFwCfgSelectItem (Blob->SizeKey); 866 Blob->Size = QemuFwCfgRead32 (); 867 if (Blob->Size == 0) { 868 return EFI_SUCCESS; 869 } 870 871 // 872 // Read blob. 873 // 874 Blob->Data = AllocatePool (Blob->Size); 875 if (Blob->Data == NULL) { 876 DEBUG ((EFI_D_ERROR, "%a: failed to allocate %Ld bytes for \"%s\"\n", 877 __FUNCTION__, (INT64)Blob->Size, Blob->Name)); 878 return EFI_OUT_OF_RESOURCES; 879 } 880 881 DEBUG ((EFI_D_INFO, "%a: loading %Ld bytes for \"%s\"\n", __FUNCTION__, 882 (INT64)Blob->Size, Blob->Name)); 883 QemuFwCfgSelectItem (Blob->DataKey); 884 885 Left = Blob->Size; 886 do { 887 UINT32 Chunk; 888 889 Chunk = (Left < SIZE_1MB) ? Left : SIZE_1MB; 890 QemuFwCfgReadBytes (Chunk, Blob->Data + (Blob->Size - Left)); 891 Left -= Chunk; 892 DEBUG ((EFI_D_VERBOSE, "%a: %Ld bytes remaining for \"%s\"\n", 893 __FUNCTION__, (INT64)Left, Blob->Name)); 894 } while (Left > 0); 895 return EFI_SUCCESS; 896 } 897 898 899 // 900 // The entry point of the feature. 901 // 902 903 /** 904 Download the kernel, the initial ramdisk, and the kernel command line from 905 QEMU's fw_cfg. Construct a minimal SimpleFileSystem that contains the two 906 image files, and load and start the kernel from it. 907 908 The kernel will be instructed via its command line to load the initrd from 909 the same Simple FileSystem. 910 911 @retval EFI_NOT_FOUND Kernel image was not found. 912 @retval EFI_OUT_OF_RESOURCES Memory allocation failed. 913 @retval EFI_PROTOCOL_ERROR Unterminated kernel command line. 914 915 @return Error codes from any of the underlying 916 functions. On success, the function doesn't 917 return. 918 **/ 919 EFI_STATUS 920 EFIAPI 921 TryRunningQemuKernel ( 922 VOID 923 ) 924 { 925 UINTN BlobType; 926 KERNEL_BLOB *CurrentBlob; 927 KERNEL_BLOB *KernelBlob, *InitrdBlob, *CommandLineBlob; 928 EFI_STATUS Status; 929 EFI_HANDLE FileSystemHandle; 930 EFI_DEVICE_PATH_PROTOCOL *KernelDevicePath; 931 EFI_HANDLE KernelImageHandle; 932 EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage; 933 934 Status = gRT->GetTime (&mInitTime, NULL /* Capabilities */); 935 if (EFI_ERROR (Status)) { 936 DEBUG ((EFI_D_ERROR, "%a: GetTime(): %r\n", __FUNCTION__, Status)); 937 return Status; 938 } 939 940 // 941 // Fetch all blobs. 942 // 943 for (BlobType = 0; BlobType < KernelBlobTypeMax; ++BlobType) { 944 CurrentBlob = &mKernelBlob[BlobType]; 945 Status = FetchBlob (CurrentBlob); 946 if (EFI_ERROR (Status)) { 947 goto FreeBlobs; 948 } 949 mTotalBlobBytes += CurrentBlob->Size; 950 } 951 KernelBlob = &mKernelBlob[KernelBlobTypeKernel]; 952 InitrdBlob = &mKernelBlob[KernelBlobTypeInitrd]; 953 CommandLineBlob = &mKernelBlob[KernelBlobTypeCommandLine]; 954 955 if (KernelBlob->Data == NULL) { 956 Status = EFI_NOT_FOUND; 957 goto FreeBlobs; 958 } 959 960 // 961 // Create a new handle with a single VenHw() node device path protocol on it, 962 // plus a custom SimpleFileSystem protocol on it. 963 // 964 FileSystemHandle = NULL; 965 Status = gBS->InstallMultipleProtocolInterfaces (&FileSystemHandle, 966 &gEfiDevicePathProtocolGuid, &mFileSystemDevicePath, 967 &gEfiSimpleFileSystemProtocolGuid, &mFileSystem, 968 NULL); 969 if (EFI_ERROR (Status)) { 970 DEBUG ((EFI_D_ERROR, "%a: InstallMultipleProtocolInterfaces(): %r\n", 971 __FUNCTION__, Status)); 972 goto FreeBlobs; 973 } 974 975 // 976 // Create a device path for the kernel image to be loaded from that will call 977 // back into our file system. 978 // 979 KernelDevicePath = FileDevicePath (FileSystemHandle, KernelBlob->Name); 980 if (KernelDevicePath == NULL) { 981 DEBUG ((EFI_D_ERROR, "%a: failed to allocate kernel device path\n", 982 __FUNCTION__)); 983 Status = EFI_OUT_OF_RESOURCES; 984 goto UninstallProtocols; 985 } 986 987 // 988 // Load the image. This should call back into our file system. 989 // 990 Status = gBS->LoadImage ( 991 FALSE, // BootPolicy: exact match required 992 gImageHandle, // ParentImageHandle 993 KernelDevicePath, 994 NULL, // SourceBuffer 995 0, // SourceSize 996 &KernelImageHandle 997 ); 998 if (EFI_ERROR (Status)) { 999 DEBUG ((EFI_D_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status)); 1000 goto FreeKernelDevicePath; 1001 } 1002 1003 // 1004 // Construct the kernel command line. 1005 // 1006 Status = gBS->OpenProtocol ( 1007 KernelImageHandle, 1008 &gEfiLoadedImageProtocolGuid, 1009 (VOID **)&KernelLoadedImage, 1010 gImageHandle, // AgentHandle 1011 NULL, // ControllerHandle 1012 EFI_OPEN_PROTOCOL_GET_PROTOCOL 1013 ); 1014 ASSERT_EFI_ERROR (Status); 1015 1016 if (CommandLineBlob->Data == NULL) { 1017 KernelLoadedImage->LoadOptionsSize = 0; 1018 } else { 1019 // 1020 // Verify NUL-termination of the command line. 1021 // 1022 if (CommandLineBlob->Data[CommandLineBlob->Size - 1] != '\0') { 1023 DEBUG ((EFI_D_ERROR, "%a: kernel command line is not NUL-terminated\n", 1024 __FUNCTION__)); 1025 Status = EFI_PROTOCOL_ERROR; 1026 goto UnloadKernelImage; 1027 } 1028 1029 // 1030 // Drop the terminating NUL, convert to UTF-16. 1031 // 1032 KernelLoadedImage->LoadOptionsSize = (CommandLineBlob->Size - 1) * 2; 1033 } 1034 1035 if (InitrdBlob->Data != NULL) { 1036 // 1037 // Append ' initrd=<name>' in UTF-16. 1038 // 1039 KernelLoadedImage->LoadOptionsSize += 1040 (8 + StrLen(InitrdBlob->Name)) * 2; 1041 } 1042 1043 if (KernelLoadedImage->LoadOptionsSize == 0) { 1044 KernelLoadedImage->LoadOptions = NULL; 1045 } else { 1046 // 1047 // NUL-terminate in UTF-16. 1048 // 1049 KernelLoadedImage->LoadOptionsSize += 2; 1050 1051 KernelLoadedImage->LoadOptions = AllocatePool ( 1052 KernelLoadedImage->LoadOptionsSize); 1053 if (KernelLoadedImage->LoadOptions == NULL) { 1054 KernelLoadedImage->LoadOptionsSize = 0; 1055 Status = EFI_OUT_OF_RESOURCES; 1056 goto UnloadKernelImage; 1057 } 1058 1059 UnicodeSPrintAsciiFormat ( 1060 KernelLoadedImage->LoadOptions, 1061 KernelLoadedImage->LoadOptionsSize, 1062 "%a%a%s", 1063 (CommandLineBlob->Data == NULL) ? "" : (CHAR8 *)CommandLineBlob->Data, 1064 (InitrdBlob->Data == NULL) ? "" : " initrd=", 1065 (InitrdBlob->Data == NULL) ? L"" : InitrdBlob->Name 1066 ); 1067 DEBUG ((EFI_D_INFO, "%a: command line: \"%s\"\n", __FUNCTION__, 1068 (CHAR16 *)KernelLoadedImage->LoadOptions)); 1069 } 1070 1071 // 1072 // Signal the EFI_EVENT_GROUP_READY_TO_BOOT event. 1073 // 1074 EfiSignalEventReadyToBoot(); 1075 1076 // 1077 // Start the image. 1078 // 1079 Status = gBS->StartImage ( 1080 KernelImageHandle, 1081 NULL, // ExitDataSize 1082 NULL // ExitData 1083 ); 1084 if (EFI_ERROR (Status)) { 1085 DEBUG ((EFI_D_ERROR, "%a: StartImage(): %r\n", __FUNCTION__, Status)); 1086 } 1087 1088 if (KernelLoadedImage->LoadOptions != NULL) { 1089 FreePool (KernelLoadedImage->LoadOptions); 1090 } 1091 KernelLoadedImage->LoadOptionsSize = 0; 1092 1093 UnloadKernelImage: 1094 gBS->UnloadImage (KernelImageHandle); 1095 1096 FreeKernelDevicePath: 1097 FreePool (KernelDevicePath); 1098 1099 UninstallProtocols: 1100 gBS->UninstallMultipleProtocolInterfaces (FileSystemHandle, 1101 &gEfiSimpleFileSystemProtocolGuid, &mFileSystem, 1102 &gEfiDevicePathProtocolGuid, &mFileSystemDevicePath, 1103 NULL); 1104 1105 FreeBlobs: 1106 while (BlobType > 0) { 1107 CurrentBlob = &mKernelBlob[--BlobType]; 1108 if (CurrentBlob->Data != NULL) { 1109 FreePool (CurrentBlob->Data); 1110 CurrentBlob->Size = 0; 1111 CurrentBlob->Data = NULL; 1112 } 1113 } 1114 1115 return Status; 1116 } 1117