1 /******************************************************************************
4 * Copyright (c) 2021, longpanda <admin@ventoy.net>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 3 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
22 #include <Library/DebugLib.h>
23 #include <Library/PrintLib.h>
24 #include <Library/UefiLib.h>
25 #include <Library/BaseMemoryLib.h>
26 #include <Library/DevicePathLib.h>
27 #include <Library/MemoryAllocationLib.h>
28 #include <Library/UefiBootServicesTableLib.h>
29 #include <Library/UefiRuntimeServicesTableLib.h>
30 #include <Library/UefiApplicationEntryPoint.h>
31 #include <Library/UefiDecompressLib.h>
32 #include <Protocol/LoadedImage.h>
33 #include <Guid/FileInfo.h>
34 #include <Guid/FileSystemInfo.h>
35 #include <Protocol/BlockIo.h>
36 #include <Protocol/RamDisk.h>
37 #include <Protocol/SimpleFileSystem.h>
38 #include <VDiskChain.h>
40 BOOLEAN gVDiskDebugPrint
= FALSE
;
41 vdisk_block_data gVDiskBlockData
;
44 CONST CHAR16
*gEfiBootFileName
[] =
47 EFI_REMOVABLE_MEDIA_FILE_NAME
,
48 #if defined (MDE_CPU_IA32)
49 L
"\\EFI\\BOOT\\GRUBIA32.EFI",
50 L
"\\EFI\\BOOT\\BOOTia32.EFI",
51 L
"\\EFI\\BOOT\\bootia32.efi",
52 L
"\\efi\\boot\\bootia32.efi",
53 #elif defined (MDE_CPU_X64)
54 L
"\\EFI\\BOOT\\GRUBX64.EFI",
55 L
"\\EFI\\BOOT\\BOOTx64.EFI",
56 L
"\\EFI\\BOOT\\bootx64.efi",
57 L
"\\efi\\boot\\bootx64.efi",
58 #elif defined (MDE_CPU_ARM)
59 L
"\\EFI\\BOOT\\GRUBARM.EFI",
60 L
"\\EFI\\BOOT\\BOOTarm.EFI",
61 L
"\\EFI\\BOOT\\bootarm.efi",
62 L
"\\efi\\boot\\bootarm.efi",
63 #elif defined (MDE_CPU_AARCH64)
64 L
"\\EFI\\BOOT\\GRUBAA64.EFI",
65 L
"\\EFI\\BOOT\\BOOTaa64.EFI",
66 L
"\\EFI\\BOOT\\bootaa64.efi",
67 L
"\\efi\\boot\\bootaa64.efi",
72 UINT8
*g_disk_buf_addr
= NULL
;
73 UINT64 g_disk_buf_size
= 0;
75 STATIC EFI_GET_VARIABLE g_org_get_variable
= NULL
;
76 STATIC EFI_EXIT_BOOT_SERVICES g_org_exit_boot_service
= NULL
;
78 VOID EFIAPI
VDiskDebug(IN CONST CHAR8
*Format
, ...)
83 VA_START (Marker
, Format
);
84 UnicodeVSPrintAsciiFormat(Buffer
, sizeof(Buffer
), Format
, Marker
);
87 gST
->ConOut
->OutputString(gST
->ConOut
, Buffer
);
90 VOID EFIAPI
vdisk_clear_input(VOID
)
94 gST
->ConIn
->Reset(gST
->ConIn
, FALSE
);
95 while (EFI_SUCCESS
== gST
->ConIn
->ReadKeyStroke(gST
->ConIn
, &Key
))
99 gST
->ConIn
->Reset(gST
->ConIn
, FALSE
);
102 STATIC EFI_STATUS EFIAPI vdisk_load_image
104 IN EFI_HANDLE ImageHandle
,
105 IN EFI_DEVICE_PATH_PROTOCOL
*pDevicePath
,
106 IN CONST CHAR16
*FileName
,
107 IN UINTN FileNameLen
,
108 OUT EFI_HANDLE
*Image
111 EFI_STATUS Status
= EFI_SUCCESS
;
112 CHAR16 TmpBuf
[256] = {0};
113 FILEPATH_DEVICE_PATH
*pFilePath
= NULL
;
114 EFI_DEVICE_PATH_PROTOCOL
*pImgPath
= NULL
;
116 pFilePath
= (FILEPATH_DEVICE_PATH
*)TmpBuf
;
117 pFilePath
->Header
.Type
= MEDIA_DEVICE_PATH
;
118 pFilePath
->Header
.SubType
= MEDIA_FILEPATH_DP
;
119 pFilePath
->Header
.Length
[0] = FileNameLen
+ sizeof(EFI_DEVICE_PATH_PROTOCOL
);
120 pFilePath
->Header
.Length
[1] = 0;
121 CopyMem(pFilePath
->PathName
, FileName
, FileNameLen
);
123 pImgPath
= AppendDevicePathNode(pDevicePath
, (EFI_DEVICE_PATH_PROTOCOL
*)pFilePath
);
126 return EFI_NOT_FOUND
;
129 Status
= gBS
->LoadImage(FALSE
, ImageHandle
, pImgPath
, NULL
, 0, Image
);
131 debug("Load Image File %r DP: <%s>", Status
, ConvertDevicePathToText(pImgPath
, FALSE
, FALSE
));
138 STATIC EFI_STATUS EFIAPI
vdisk_decompress_vdisk(IN EFI_LOADED_IMAGE_PROTOCOL
*pImageInfo
)
141 UINT32 DestinationSize
;
145 EFI_STATUS Status
= EFI_SUCCESS
;
149 vdisk_get_vdisk_raw(&buf
, &Size
);
150 UefiDecompressGetInfo(buf
+ VDISK_MAGIC_LEN
, Size
- VDISK_MAGIC_LEN
, &DestinationSize
, &ScratchSize
);
151 debug("vdisk: size:%u realsize:%u", Size
, DestinationSize
);
153 g_disk_buf_size
= DestinationSize
;
154 g_disk_buf_addr
= AllocatePool(DestinationSize
);
155 ScratchBuf
= AllocatePool(ScratchSize
);
157 Status
= UefiDecompress(buf
+ VDISK_MAGIC_LEN
, g_disk_buf_addr
, ScratchBuf
);
158 FreePool(ScratchBuf
);
160 debug("Status:%r %p %u", Status
, g_disk_buf_addr
, (UINT32
)g_disk_buf_size
);
165 STATIC EFI_STATUS
vdisk_patch_vdisk_path(CHAR16
*pos
)
170 CHAR8
*buf
= (char *)g_disk_buf_addr
;
177 end
= StrStr(pos
, L
".vtoy");
178 end
+= 5;//string length
180 for (i
= 0; i
< g_disk_buf_size
; i
++)
182 if (*(UINT32
*)(buf
+ i
) == 0x59595959)
184 for (j
= 0; j
< 300; j
++)
186 if (buf
[i
+ j
] != 'Y')
199 if (i
>= g_disk_buf_size
)
201 debug("No need to fill vdisk path");
205 debug("Fill vdisk path at %d", i
);
209 buf
[i
++] = (CHAR8
)(*pos
++);
214 while (buf
[i
] == 'Y' || buf
[i
] == '\"')
223 EFI_STATUS EFIAPI vdisk_get_variable_wrapper
225 IN CHAR16
*VariableName
,
226 IN EFI_GUID
*VendorGuid
,
227 OUT UINT32
*Attributes
, OPTIONAL
228 IN OUT UINTN
*DataSize
,
229 OUT VOID
*Data OPTIONAL
232 EFI_STATUS Status
= EFI_SUCCESS
;
234 Status
= g_org_get_variable(VariableName
, VendorGuid
, Attributes
, DataSize
, Data
);
235 if (StrCmp(VariableName
, L
"SecureBoot") == 0)
237 if ((*DataSize
== 1) && Data
)
246 EFI_STATUS EFIAPI vdisk_exit_boot_service_wrapper
248 IN EFI_HANDLE ImageHandle
,
252 if (g_org_get_variable
)
254 gRT
->GetVariable
= g_org_get_variable
;
255 g_org_get_variable
= NULL
;
258 return g_org_exit_boot_service(ImageHandle
, MapKey
);
261 STATIC EFI_STATUS EFIAPI
vdisk_disable_secure_boot(IN EFI_HANDLE ImageHandle
)
263 /* step1: wrapper security protocol. */
264 /* Do we still need it since we have been loaded ? */
267 /* step2: fake SecureBoot variable */
268 g_org_exit_boot_service
= gBS
->ExitBootServices
;
269 gBS
->ExitBootServices
= vdisk_exit_boot_service_wrapper
;
271 g_org_get_variable
= gRT
->GetVariable
;
272 gRT
->GetVariable
= vdisk_get_variable_wrapper
;
277 STATIC EFI_STATUS EFIAPI
vdisk_parse_cmdline(IN EFI_HANDLE ImageHandle
)
280 CHAR16
*pCmdLine
= NULL
;
281 EFI_STATUS Status
= EFI_SUCCESS
;
282 EFI_LOADED_IMAGE_PROTOCOL
*pImageInfo
= NULL
;
284 Status
= gBS
->HandleProtocol(ImageHandle
, &gEfiLoadedImageProtocolGuid
, (VOID
**)&pImageInfo
);
285 if (EFI_ERROR(Status
))
287 VDiskDebug("Failed to handle load image protocol %r\n", Status
);
291 pCmdLine
= (CHAR16
*)AllocatePool(pImageInfo
->LoadOptionsSize
+ 4);
292 SetMem(pCmdLine
, pImageInfo
->LoadOptionsSize
+ 4, 0);
293 CopyMem(pCmdLine
, pImageInfo
->LoadOptions
, pImageInfo
->LoadOptionsSize
);
295 if (StrStr(pCmdLine
, L
"debug"))
297 gVDiskDebugPrint
= TRUE
;
300 debug("cmdline:<%s>", pCmdLine
);
303 Pos
= StrStr(pCmdLine
, L
"vdisk=");
304 if (NULL
== Pos
|| NULL
== StrStr(pCmdLine
, L
".vtoy"))
306 VDiskDebug("vdisk parameter not found!\n");
307 return EFI_NOT_FOUND
;
310 vdisk_decompress_vdisk(pImageInfo
);
312 vdisk_patch_vdisk_path(Pos
+ 6);
314 if (StrStr(pCmdLine
, L
"secureboot=off"))
316 vdisk_disable_secure_boot(ImageHandle
);
323 EFI_STATUS EFIAPI
vdisk_boot(IN EFI_HANDLE ImageHandle
)
330 EFI_HANDLE Image
= NULL
;
331 EFI_HANDLE
*Handles
= NULL
;
332 EFI_STATUS Status
= EFI_SUCCESS
;
333 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*pFile
= NULL
;
334 EFI_DEVICE_PATH_PROTOCOL
*pDevPath
= NULL
;
336 for (t
= 0; t
< 3; t
++)
341 Status
= gBS
->LocateHandleBuffer(ByProtocol
, &gEfiSimpleFileSystemProtocolGuid
,
342 NULL
, &Count
, &Handles
);
343 if (EFI_ERROR(Status
))
348 debug("vdisk_boot fs count:%u", Count
);
350 for (i
= 0; i
< Count
; i
++)
352 Status
= gBS
->HandleProtocol(Handles
[i
], &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&pFile
);
353 if (EFI_ERROR(Status
))
358 debug("FS:%u Protocol:%p OpenVolume:%p", i
, pFile
, pFile
->OpenVolume
);
360 Status
= gBS
->OpenProtocol(Handles
[i
], &gEfiDevicePathProtocolGuid
,
364 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
365 if (EFI_ERROR(Status
))
367 debug("Failed to open device path protocol %r", Status
);
371 debug("Handle:%p FS DP: <%s>", Handles
[i
], ConvertDevicePathToText(pDevPath
, FALSE
, FALSE
));
372 if (CompareMem(gVDiskBlockData
.Path
, pDevPath
, gVDiskBlockData
.DevicePathCompareLen
))
374 debug("Not ventoy disk file system");
378 for (j
= 1; j
< ARRAY_SIZE(gEfiBootFileName
); j
++)
380 Status
= vdisk_load_image(ImageHandle
, pDevPath
, gEfiBootFileName
[j
],
381 StrSize(gEfiBootFileName
[j
]), &Image
);
382 if (EFI_SUCCESS
== Status
)
386 debug("Failed to load image %r <%s>", Status
, gEfiBootFileName
[j
]);
389 if (j
>= ARRAY_SIZE(gEfiBootFileName
))
395 debug("Find boot file, now try to boot .....");
398 if (gVDiskDebugPrint
)
400 gST
->ConIn
->Reset(gST
->ConIn
, FALSE
);
403 /* can't add debug print here */
404 //ventoy_wrapper_system();
405 Status
= gBS
->StartImage(Image
, NULL
, NULL
);
406 if (EFI_ERROR(Status
))
408 debug("Failed to start image %r", Status
);
410 gBS
->UnloadImage(Image
);
419 debug("Fs not found, now wait and retry...");
426 return EFI_NOT_FOUND
;
432 EFI_STATUS EFIAPI VDiskChainEfiMain
434 IN EFI_HANDLE ImageHandle
,
435 IN EFI_SYSTEM_TABLE
*SystemTable
438 EFI_STATUS Status
= EFI_SUCCESS
;
440 gST
->ConOut
->ClearScreen(gST
->ConOut
);
443 Status
= vdisk_parse_cmdline(ImageHandle
);
444 if (EFI_ERROR(Status
))
449 vdisk_install_blockio(ImageHandle
, g_disk_buf_size
);
452 Status
= vdisk_boot(ImageHandle
);
454 gBS
->DisconnectController(gVDiskBlockData
.Handle
, NULL
, NULL
);
455 gBS
->UninstallMultipleProtocolInterfaces(gVDiskBlockData
.Handle
,
456 &gEfiBlockIoProtocolGuid
, &gVDiskBlockData
.BlockIo
,
457 &gEfiDevicePathProtocolGuid
, gVDiskBlockData
.Path
,
460 if (EFI_NOT_FOUND
== Status
)
462 gST
->ConOut
->OutputString(gST
->ConOut
, L
"No bootfile found for UEFI!\r\n");
463 gST
->ConOut
->OutputString(gST
->ConOut
, L
"Maybe the image does not support " VENTOY_UEFI_DESC L
"!\r\n");
468 gST
->ConOut
->ClearScreen(gST
->ConOut
);