]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - EDK2/edk2_mod/edk2-edk2-stable201911/MdeModulePkg/Application/VDiskChain/VDiskChain.c
update for vdiskchain
[Ventoy.git] / EDK2 / edk2_mod / edk2-edk2-stable201911 / MdeModulePkg / Application / VDiskChain / VDiskChain.c
1 /******************************************************************************
2 * VDiskChain.c
3 *
4 * Copyright (c) 2021, longpanda <admin@ventoy.net>
5 *
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.
10 *
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.
15 *
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/>.
18 *
19 */
20
21 #include <Uefi.h>
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>
39
40 BOOLEAN gVDiskDebugPrint = FALSE;
41 vdisk_block_data gVDiskBlockData;
42
43 /* Boot filename */
44 CONST CHAR16 *gEfiBootFileName[] =
45 {
46 L"@",
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",
68 #endif
69
70 };
71
72 UINT8 *g_disk_buf_addr = NULL;
73 UINT64 g_disk_buf_size = 0;
74
75 STATIC EFI_GET_VARIABLE g_org_get_variable = NULL;
76 STATIC EFI_EXIT_BOOT_SERVICES g_org_exit_boot_service = NULL;
77
78 VOID EFIAPI VDiskDebug(IN CONST CHAR8 *Format, ...)
79 {
80 VA_LIST Marker;
81 CHAR16 Buffer[512];
82
83 VA_START (Marker, Format);
84 UnicodeVSPrintAsciiFormat(Buffer, sizeof(Buffer), Format, Marker);
85 VA_END (Marker);
86
87 gST->ConOut->OutputString(gST->ConOut, Buffer);
88 }
89
90 VOID EFIAPI vdisk_clear_input(VOID)
91 {
92 EFI_INPUT_KEY Key;
93
94 gST->ConIn->Reset(gST->ConIn, FALSE);
95 while (EFI_SUCCESS == gST->ConIn->ReadKeyStroke(gST->ConIn, &Key))
96 {
97 ;
98 }
99 gST->ConIn->Reset(gST->ConIn, FALSE);
100 }
101
102 STATIC EFI_STATUS EFIAPI vdisk_load_image
103 (
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
109 )
110 {
111 EFI_STATUS Status = EFI_SUCCESS;
112 CHAR16 TmpBuf[256] = {0};
113 FILEPATH_DEVICE_PATH *pFilePath = NULL;
114 EFI_DEVICE_PATH_PROTOCOL *pImgPath = NULL;
115
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);
122
123 pImgPath = AppendDevicePathNode(pDevicePath, (EFI_DEVICE_PATH_PROTOCOL *)pFilePath);
124 if (!pImgPath)
125 {
126 return EFI_NOT_FOUND;
127 }
128
129 Status = gBS->LoadImage(FALSE, ImageHandle, pImgPath, NULL, 0, Image);
130
131 debug("Load Image File %r DP: <%s>", Status, ConvertDevicePathToText(pImgPath, FALSE, FALSE));
132
133 FreePool(pImgPath);
134
135 return Status;
136 }
137
138 STATIC EFI_STATUS EFIAPI vdisk_decompress_vdisk(IN EFI_LOADED_IMAGE_PROTOCOL *pImageInfo)
139 {
140 UINT32 Size;
141 UINT32 DestinationSize;
142 UINT32 ScratchSize;
143 UINT8 *buf;
144 VOID *ScratchBuf;
145 EFI_STATUS Status = EFI_SUCCESS;
146
147 (VOID)pImageInfo;
148
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);
152
153 g_disk_buf_size = DestinationSize;
154 g_disk_buf_addr = AllocatePool(DestinationSize);
155 ScratchBuf = AllocatePool(ScratchSize);
156
157 Status = UefiDecompress(buf + VDISK_MAGIC_LEN, g_disk_buf_addr, ScratchBuf);
158 FreePool(ScratchBuf);
159
160 debug("Status:%r %p %u", Status, g_disk_buf_addr, (UINT32)g_disk_buf_size);
161
162 return EFI_SUCCESS;
163 }
164
165 STATIC EFI_STATUS vdisk_patch_vdisk_path(CHAR16 *pos)
166 {
167 UINTN i;
168 UINTN j;
169 CHAR16 *end;
170 CHAR8 *buf = (char *)g_disk_buf_addr;
171
172 if (*pos == L'\"')
173 {
174 pos++;
175 }
176
177 end = StrStr(pos, L".vtoy");
178 end += 5;//string length
179
180 for (i = 0; i < g_disk_buf_size; i++)
181 {
182 if (*(UINT32 *)(buf + i) == 0x59595959)
183 {
184 for (j = 0; j < 300; j++)
185 {
186 if (buf[i + j] != 'Y')
187 {
188 break;
189 }
190 }
191
192 if (j >= 300)
193 {
194 break;
195 }
196 }
197 }
198
199 if (i >= g_disk_buf_size)
200 {
201 debug("No need to fill vdisk path");
202 return 0;
203 }
204
205 debug("Fill vdisk path at %d", i);
206
207 while (pos != end)
208 {
209 buf[i++] = (CHAR8)(*pos++);
210 }
211
212 buf[i++] = '\"';
213
214 while (buf[i] == 'Y' || buf[i] == '\"')
215 {
216 buf[i] = ' ';
217 i++;
218 }
219
220 return 0;
221 }
222
223 EFI_STATUS EFIAPI vdisk_get_variable_wrapper
224 (
225 IN CHAR16 *VariableName,
226 IN EFI_GUID *VendorGuid,
227 OUT UINT32 *Attributes, OPTIONAL
228 IN OUT UINTN *DataSize,
229 OUT VOID *Data OPTIONAL
230 )
231 {
232 EFI_STATUS Status = EFI_SUCCESS;
233
234 Status = g_org_get_variable(VariableName, VendorGuid, Attributes, DataSize, Data);
235 if (StrCmp(VariableName, L"SecureBoot") == 0)
236 {
237 if ((*DataSize == 1) && Data)
238 {
239 *(UINT8 *)Data = 0;
240 }
241 }
242
243 return Status;
244 }
245
246 EFI_STATUS EFIAPI vdisk_exit_boot_service_wrapper
247 (
248 IN EFI_HANDLE ImageHandle,
249 IN UINTN MapKey
250 )
251 {
252 return g_org_exit_boot_service(ImageHandle, MapKey);
253 }
254
255 STATIC EFI_STATUS EFIAPI vdisk_disable_secure_boot(IN EFI_HANDLE ImageHandle)
256 {
257 /* step1: wrapper security protocol. */
258 /* Do we still need it since we have been loaded ? */
259
260
261 /* step2: fake SecureBoot variable */
262 g_org_exit_boot_service = gBS->ExitBootServices;
263 gBS->ExitBootServices = vdisk_exit_boot_service_wrapper;
264
265 g_org_get_variable = gRT->GetVariable;
266 gRT->GetVariable = vdisk_get_variable_wrapper;
267
268 return EFI_SUCCESS;
269 }
270
271 STATIC EFI_STATUS EFIAPI vdisk_parse_cmdline(IN EFI_HANDLE ImageHandle)
272 {
273 CHAR16 *Pos = NULL;
274 CHAR16 *pCmdLine = NULL;
275 EFI_STATUS Status = EFI_SUCCESS;
276 EFI_LOADED_IMAGE_PROTOCOL *pImageInfo = NULL;
277
278 Status = gBS->HandleProtocol(ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&pImageInfo);
279 if (EFI_ERROR(Status))
280 {
281 VDiskDebug("Failed to handle load image protocol %r\n", Status);
282 return Status;
283 }
284
285 pCmdLine = (CHAR16 *)AllocatePool(pImageInfo->LoadOptionsSize + 4);
286 SetMem(pCmdLine, pImageInfo->LoadOptionsSize + 4, 0);
287 CopyMem(pCmdLine, pImageInfo->LoadOptions, pImageInfo->LoadOptionsSize);
288
289 if (StrStr(pCmdLine, L"debug"))
290 {
291 gVDiskDebugPrint = TRUE;
292 }
293
294 debug("cmdline:<%s>", pCmdLine);
295 vdisk_debug_pause();
296
297 Pos = StrStr(pCmdLine, L"vdisk=");
298 if (NULL == Pos || NULL == StrStr(pCmdLine, L".vtoy"))
299 {
300 VDiskDebug("vdisk parameter not found!\n");
301 return EFI_NOT_FOUND;
302 }
303
304 vdisk_decompress_vdisk(pImageInfo);
305
306 vdisk_patch_vdisk_path(Pos + 6);
307
308 if (StrStr(pCmdLine, L"secureboot=off"))
309 {
310 vdisk_disable_secure_boot(ImageHandle);
311 }
312
313 FreePool(pCmdLine);
314 return EFI_SUCCESS;
315 }
316
317 EFI_STATUS EFIAPI vdisk_boot(IN EFI_HANDLE ImageHandle)
318 {
319 UINTN t = 0;
320 UINTN i = 0;
321 UINTN j = 0;
322 UINTN Find = 0;
323 UINTN Count = 0;
324 EFI_HANDLE Image = NULL;
325 EFI_HANDLE *Handles = NULL;
326 EFI_STATUS Status = EFI_SUCCESS;
327 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *pFile = NULL;
328 EFI_DEVICE_PATH_PROTOCOL *pDevPath = NULL;
329
330 for (t = 0; t < 3; t++)
331 {
332 Count = 0;
333 Handles = NULL;
334
335 Status = gBS->LocateHandleBuffer(ByProtocol, &gEfiSimpleFileSystemProtocolGuid,
336 NULL, &Count, &Handles);
337 if (EFI_ERROR(Status))
338 {
339 return Status;
340 }
341
342 debug("vdisk_boot fs count:%u", Count);
343
344 for (i = 0; i < Count; i++)
345 {
346 Status = gBS->HandleProtocol(Handles[i], &gEfiSimpleFileSystemProtocolGuid, (VOID **)&pFile);
347 if (EFI_ERROR(Status))
348 {
349 continue;
350 }
351
352 debug("FS:%u Protocol:%p OpenVolume:%p", i, pFile, pFile->OpenVolume);
353
354 Status = gBS->OpenProtocol(Handles[i], &gEfiDevicePathProtocolGuid,
355 (VOID **)&pDevPath,
356 ImageHandle,
357 Handles[i],
358 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
359 if (EFI_ERROR(Status))
360 {
361 debug("Failed to open device path protocol %r", Status);
362 continue;
363 }
364
365 debug("Handle:%p FS DP: <%s>", Handles[i], ConvertDevicePathToText(pDevPath, FALSE, FALSE));
366 if (CompareMem(gVDiskBlockData.Path, pDevPath, gVDiskBlockData.DevicePathCompareLen))
367 {
368 debug("Not ventoy disk file system");
369 continue;
370 }
371
372 for (j = 1; j < ARRAY_SIZE(gEfiBootFileName); j++)
373 {
374 Status = vdisk_load_image(ImageHandle, pDevPath, gEfiBootFileName[j],
375 StrSize(gEfiBootFileName[j]), &Image);
376 if (EFI_SUCCESS == Status)
377 {
378 break;
379 }
380 debug("Failed to load image %r <%s>", Status, gEfiBootFileName[j]);
381 }
382
383 if (j >= ARRAY_SIZE(gEfiBootFileName))
384 {
385 continue;
386 }
387
388 Find++;
389 debug("Find boot file, now try to boot .....");
390 vdisk_debug_pause();
391
392 if (gVDiskDebugPrint)
393 {
394 gST->ConIn->Reset(gST->ConIn, FALSE);
395 }
396
397 /* can't add debug print here */
398 //ventoy_wrapper_system();
399 Status = gBS->StartImage(Image, NULL, NULL);
400 if (EFI_ERROR(Status))
401 {
402 debug("Failed to start image %r", Status);
403 sleep(3);
404 gBS->UnloadImage(Image);
405 break;
406 }
407 }
408
409 FreePool(Handles);
410
411 if (Find == 0)
412 {
413 debug("Fs not found, now wait and retry...");
414 sleep(2);
415 }
416 }
417
418 if (Find == 0)
419 {
420 return EFI_NOT_FOUND;
421 }
422
423 return EFI_SUCCESS;
424 }
425
426 EFI_STATUS EFIAPI VDiskChainEfiMain
427 (
428 IN EFI_HANDLE ImageHandle,
429 IN EFI_SYSTEM_TABLE *SystemTable
430 )
431 {
432 EFI_STATUS Status = EFI_SUCCESS;
433
434 gST->ConOut->ClearScreen(gST->ConOut);
435 vdisk_clear_input();
436
437 Status = vdisk_parse_cmdline(ImageHandle);
438 if (EFI_ERROR(Status))
439 {
440 return Status;
441 }
442
443 vdisk_install_blockio(ImageHandle, g_disk_buf_size);
444 vdisk_debug_pause();
445
446 Status = vdisk_boot(ImageHandle);
447
448 gBS->DisconnectController(gVDiskBlockData.Handle, NULL, NULL);
449 gBS->UninstallMultipleProtocolInterfaces(gVDiskBlockData.Handle,
450 &gEfiBlockIoProtocolGuid, &gVDiskBlockData.BlockIo,
451 &gEfiDevicePathProtocolGuid, gVDiskBlockData.Path,
452 NULL);
453
454 if (EFI_NOT_FOUND == Status)
455 {
456 gST->ConOut->OutputString(gST->ConOut, L"No bootfile found for UEFI!\r\n");
457 gST->ConOut->OutputString(gST->ConOut, L"Maybe the image does not support " VENTOY_UEFI_DESC L"!\r\n");
458 sleep(30);
459 }
460
461 vdisk_clear_input();
462 gST->ConOut->ClearScreen(gST->ConOut);
463
464 return EFI_SUCCESS;
465 }
466