]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/ventoy/ventoy_vhd.c
5d64b5b2fd0096816599f1c2b6aeda6a8764ba56
[Ventoy.git] / GRUB2 / MOD_SRC / grub-2.04 / grub-core / ventoy / ventoy_vhd.c
1 /******************************************************************************
2 * ventoy_vhd.c
3 *
4 * Copyright (c) 2020, 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 <grub/types.h>
22 #include <grub/misc.h>
23 #include <grub/mm.h>
24 #include <grub/err.h>
25 #include <grub/dl.h>
26 #include <grub/disk.h>
27 #include <grub/device.h>
28 #include <grub/term.h>
29 #include <grub/partition.h>
30 #include <grub/file.h>
31 #include <grub/normal.h>
32 #include <grub/extcmd.h>
33 #include <grub/datetime.h>
34 #include <grub/i18n.h>
35 #include <grub/net.h>
36 #include <grub/time.h>
37 #include <grub/crypto.h>
38 #include <grub/charset.h>
39 #ifdef GRUB_MACHINE_EFI
40 #include <grub/efi/efi.h>
41 #endif
42 #include <grub/ventoy.h>
43 #include "ventoy_def.h"
44
45 GRUB_MOD_LICENSE ("GPLv3+");
46
47 static int g_vhdboot_bcd_offset = 0;
48 static int g_vhdboot_bcd_len = 0;
49 static int g_vhdboot_isolen = 0;
50 static char *g_vhdboot_totbuf = NULL;
51 static char *g_vhdboot_isobuf = NULL;
52 static grub_uint64_t g_img_trim_head_secnum = 0;
53
54 static int ventoy_vhd_find_bcd(int *bcdoffset, int *bcdlen)
55 {
56 grub_uint32_t offset;
57 grub_file_t file;
58 char cmdbuf[128];
59
60 grub_snprintf(cmdbuf, sizeof(cmdbuf), "loopback vhdiso mem:0x%lx:size:%d", (ulong)g_vhdboot_isobuf, g_vhdboot_isolen);
61
62 grub_script_execute_sourcecode(cmdbuf);
63
64 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", "(vhdiso)/boot/bcd");
65 if (!file)
66 {
67 grub_printf("Failed to open bcd file in the image file\n");
68 return 1;
69 }
70
71 grub_file_read(file, &offset, 4);
72 offset = (grub_uint32_t)grub_iso9660_get_last_read_pos(file);
73
74 *bcdoffset = (int)offset;
75 *bcdlen = (int)file->size;
76
77 debug("vhdiso bcd file offset:%d len:%d\n", *bcdoffset, *bcdlen);
78
79 grub_file_close(file);
80
81 grub_script_execute_sourcecode("loopback -d vhdiso");
82
83 return 0;
84 }
85
86 static int ventoy_vhd_patch_path(char *vhdpath, ventoy_patch_vhd *patch1, ventoy_patch_vhd *patch2)
87 {
88 int i;
89 int cnt = 0;
90 char *pos;
91 grub_size_t pathlen;
92 const char *plat;
93 grub_uint16_t *unicode_path;
94 const grub_uint8_t winloadexe[] =
95 {
96 0x77, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x61, 0x00, 0x64, 0x00, 0x2E, 0x00,
97 0x65, 0x00, 0x78, 0x00, 0x65, 0x00
98 };
99
100 pathlen = sizeof(grub_uint16_t) * (grub_strlen(vhdpath) + 1);
101 debug("unicode path for <%s> len:%d\n", vhdpath, (int)pathlen);
102
103 unicode_path = grub_zalloc(pathlen);
104 if (!unicode_path)
105 {
106 return 0;
107 }
108
109 plat = grub_env_get("grub_platform");
110
111 if (plat && (plat[0] == 'e')) /* UEFI */
112 {
113 pos = g_vhdboot_isobuf + g_vhdboot_bcd_offset;
114
115 /* winload.exe ==> winload.efi */
116 for (i = 0; i + (int)sizeof(winloadexe) < g_vhdboot_bcd_len; i++)
117 {
118 if (*((grub_uint32_t *)(pos + i)) == 0x00690077 &&
119 grub_memcmp(pos + i, winloadexe, sizeof(winloadexe)) == 0)
120 {
121 pos[i + sizeof(winloadexe) - 4] = 0x66;
122 pos[i + sizeof(winloadexe) - 2] = 0x69;
123 cnt++;
124 }
125 }
126
127 debug("winload patch %d times\n", cnt);
128 }
129
130 for (pos = vhdpath; *pos; pos++)
131 {
132 if (*pos == '/')
133 {
134 *pos = '\\';
135 }
136 }
137
138 grub_utf8_to_utf16(unicode_path, pathlen, (grub_uint8_t *)vhdpath, -1, NULL);
139 grub_memcpy(patch1->vhd_file_path, unicode_path, pathlen);
140 grub_memcpy(patch2->vhd_file_path, unicode_path, pathlen);
141
142 return 0;
143 }
144
145 static int ventoy_vhd_patch_disk(ventoy_patch_vhd *patch1, ventoy_patch_vhd *patch2)
146 {
147 char efipart[16] = {0};
148
149 grub_memcpy(efipart, g_ventoy_part_info->Head.Signature, sizeof(g_ventoy_part_info->Head.Signature));
150
151 debug("part1 type: 0x%x <%s>\n", g_ventoy_part_info->MBR.PartTbl[0].FsFlag, efipart);
152
153 if (grub_strncmp(efipart, "EFI PART", 8) == 0)
154 {
155 ventoy_debug_dump_guid("GPT disk GUID: ", g_ventoy_part_info->Head.DiskGuid);
156 ventoy_debug_dump_guid("GPT part GUID: ", g_ventoy_part_info->PartTbl[0].PartGuid);
157
158 grub_memcpy(patch1->disk_signature_or_guid, g_ventoy_part_info->Head.DiskGuid, 16);
159 grub_memcpy(patch1->part_offset_or_guid, g_ventoy_part_info->PartTbl[0].PartGuid, 16);
160 grub_memcpy(patch2->disk_signature_or_guid, g_ventoy_part_info->Head.DiskGuid, 16);
161 grub_memcpy(patch2->part_offset_or_guid, g_ventoy_part_info->PartTbl[0].PartGuid, 16);
162
163 patch1->part_type = patch2->part_type = 0;
164 }
165 else
166 {
167 debug("MBR disk signature: %02x%02x%02x%02x\n",
168 g_ventoy_part_info->MBR.BootCode[0x1b8 + 0], g_ventoy_part_info->MBR.BootCode[0x1b8 + 1],
169 g_ventoy_part_info->MBR.BootCode[0x1b8 + 2], g_ventoy_part_info->MBR.BootCode[0x1b8 + 3]);
170 grub_memcpy(patch1->disk_signature_or_guid, g_ventoy_part_info->MBR.BootCode + 0x1b8, 4);
171 grub_memcpy(patch2->disk_signature_or_guid, g_ventoy_part_info->MBR.BootCode + 0x1b8, 4);
172 }
173
174 return 0;
175 }
176
177 grub_err_t ventoy_cmd_patch_vhdboot(grub_extcmd_context_t ctxt, int argc, char **args)
178 {
179 int rc;
180 ventoy_patch_vhd *patch1;
181 ventoy_patch_vhd *patch2;
182 char envbuf[64];
183
184 (void)ctxt;
185 (void)argc;
186
187 grub_env_unset("vtoy_vhd_buf_addr");
188
189 debug("patch vhd <%s>\n", args[0]);
190
191 if ((!g_vhdboot_enable) || (!g_vhdboot_totbuf))
192 {
193 debug("vhd boot not ready %d %p\n", g_vhdboot_enable, g_vhdboot_totbuf);
194 return 0;
195 }
196
197 rc = ventoy_vhd_find_bcd(&g_vhdboot_bcd_offset, &g_vhdboot_bcd_len);
198 if (rc)
199 {
200 debug("failed to get bcd location %d\n", rc);
201 return 0;
202 }
203
204 patch1 = (ventoy_patch_vhd *)(g_vhdboot_isobuf + g_vhdboot_bcd_offset + 0x495a);
205 patch2 = (ventoy_patch_vhd *)(g_vhdboot_isobuf + g_vhdboot_bcd_offset + 0x50aa);
206
207 ventoy_vhd_patch_disk(patch1, patch2);
208 ventoy_vhd_patch_path(args[0], patch1, patch2);
209
210 /* set buffer and size */
211 #ifdef GRUB_MACHINE_EFI
212 grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (ulong)g_vhdboot_totbuf);
213 grub_env_set("vtoy_vhd_buf_addr", envbuf);
214 grub_snprintf(envbuf, sizeof(envbuf), "%d", (int)(g_vhdboot_isolen + sizeof(ventoy_chain_head)));
215 grub_env_set("vtoy_vhd_buf_size", envbuf);
216 #else
217 grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (ulong)g_vhdboot_isobuf);
218 grub_env_set("vtoy_vhd_buf_addr", envbuf);
219 grub_snprintf(envbuf, sizeof(envbuf), "%d", g_vhdboot_isolen);
220 grub_env_set("vtoy_vhd_buf_size", envbuf);
221 #endif
222
223 return 0;
224 }
225
226 grub_err_t ventoy_cmd_load_vhdboot(grub_extcmd_context_t ctxt, int argc, char **args)
227 {
228 int buflen;
229 grub_file_t file;
230
231 (void)ctxt;
232 (void)argc;
233
234 g_vhdboot_enable = 0;
235 grub_check_free(g_vhdboot_totbuf);
236
237 file = grub_file_open(args[0], VENTOY_FILE_TYPE);
238 if (!file)
239 {
240 return 0;
241 }
242
243 debug("load vhd boot: <%s> <%lu>\n", args[0], (ulong)file->size);
244
245 if (file->size < VTOY_SIZE_1KB * 32)
246 {
247 grub_file_close(file);
248 return 0;
249 }
250
251 g_vhdboot_isolen = (int)file->size;
252
253 buflen = (int)(file->size + sizeof(ventoy_chain_head));
254
255 #ifdef GRUB_MACHINE_EFI
256 g_vhdboot_totbuf = (char *)grub_efi_allocate_iso_buf(buflen);
257 #else
258 g_vhdboot_totbuf = (char *)grub_malloc(buflen);
259 #endif
260
261 if (!g_vhdboot_totbuf)
262 {
263 grub_file_close(file);
264 return 0;
265 }
266
267 g_vhdboot_isobuf = g_vhdboot_totbuf + sizeof(ventoy_chain_head);
268
269 grub_file_read(file, g_vhdboot_isobuf, file->size);
270 grub_file_close(file);
271
272 g_vhdboot_enable = 1;
273
274 return 0;
275 }
276
277 static int ventoy_raw_trim_head(grub_uint64_t offset)
278 {
279 grub_uint32_t i;
280 grub_uint32_t memsize;
281 grub_uint32_t imgstart = 0;
282 grub_uint32_t imgsecs = 0;
283 grub_uint64_t sectors = 0;
284 grub_uint64_t cursecs = 0;
285 grub_uint64_t delta = 0;
286
287 if ((!g_img_chunk_list.chunk) || (!offset))
288 {
289 debug("image chunk not ready %p %lu\n", g_img_chunk_list.chunk, (ulong)offset);
290 return 0;
291 }
292
293 debug("image trim head %lu\n", (ulong)offset);
294
295 for (i = 0; i < g_img_chunk_list.cur_chunk; i++)
296 {
297 cursecs = g_img_chunk_list.chunk[i].disk_end_sector + 1 - g_img_chunk_list.chunk[i].disk_start_sector;
298 sectors += cursecs;
299 if (sectors >= offset)
300 {
301 delta = cursecs - (sectors - offset);
302 break;
303 }
304 }
305
306 if (sectors < offset || i >= g_img_chunk_list.cur_chunk)
307 {
308 debug("Invalid size %lu %lu\n", (ulong)sectors, (ulong)offset);
309 return 0;
310 }
311
312 if (sectors == offset)
313 {
314 memsize = (g_img_chunk_list.cur_chunk - (i + 1)) * sizeof(ventoy_img_chunk);
315 grub_memmove(g_img_chunk_list.chunk, g_img_chunk_list.chunk + i + 1, memsize);
316 g_img_chunk_list.cur_chunk -= (i + 1);
317 }
318 else
319 {
320 g_img_chunk_list.chunk[i].disk_start_sector += delta;
321 g_img_chunk_list.chunk[i].img_start_sector += (grub_uint32_t)(delta / 4);
322
323 if (i > 0)
324 {
325 memsize = (g_img_chunk_list.cur_chunk - i) * sizeof(ventoy_img_chunk);
326 grub_memmove(g_img_chunk_list.chunk, g_img_chunk_list.chunk + i, memsize);
327 g_img_chunk_list.cur_chunk -= i;
328 }
329 }
330
331 for (i = 0; i < g_img_chunk_list.cur_chunk; i++)
332 {
333 imgsecs = g_img_chunk_list.chunk[i].img_end_sector + 1 - g_img_chunk_list.chunk[i].img_start_sector;
334 g_img_chunk_list.chunk[i].img_start_sector = imgstart;
335 g_img_chunk_list.chunk[i].img_end_sector = imgstart + (imgsecs - 1);
336 imgstart += imgsecs;
337 }
338
339 return 0;
340 }
341
342 grub_err_t ventoy_cmd_get_vtoy_type(grub_extcmd_context_t ctxt, int argc, char **args)
343 {
344 int i;
345 int offset = -1;
346 grub_file_t file;
347 vhd_footer_t vhdfoot;
348 VDIPREHEADER vdihdr;
349 char type[16] = {0};
350 ventoy_gpt_info *gpt;
351
352 (void)ctxt;
353
354 g_img_trim_head_secnum = 0;
355
356 if (argc != 4)
357 {
358 return 0;
359 }
360
361 file = grub_file_open(args[0], VENTOY_FILE_TYPE);
362 if (!file)
363 {
364 debug("Failed to open file %s\n", args[0]);
365 return 0;
366 }
367
368 grub_snprintf(type, sizeof(type), "unknown");
369
370 grub_file_seek(file, file->size - 512);
371 grub_file_read(file, &vhdfoot, sizeof(vhdfoot));
372
373 if (grub_strncmp(vhdfoot.cookie, "conectix", 8) == 0)
374 {
375 offset = 0;
376 grub_snprintf(type, sizeof(type), "vhd%u", grub_swap_bytes32(vhdfoot.disktype));
377 }
378 else
379 {
380 grub_file_seek(file, 0);
381 grub_file_read(file, &vdihdr, sizeof(vdihdr));
382 if (vdihdr.u32Signature == VDI_IMAGE_SIGNATURE &&
383 grub_strncmp(vdihdr.szFileInfo, VDI_IMAGE_FILE_INFO, grub_strlen(VDI_IMAGE_FILE_INFO)) == 0)
384 {
385 offset = 2 * 1048576;
386 g_img_trim_head_secnum = offset / 512;
387 grub_snprintf(type, sizeof(type), "vdi");
388 }
389 else
390 {
391 offset = 0;
392 grub_snprintf(type, sizeof(type), "raw");
393 }
394 }
395
396 grub_env_set(args[1], type);
397 debug("<%s> vtoy type: <%s> ", args[0], type);
398
399 if (offset >= 0)
400 {
401 gpt = grub_zalloc(sizeof(ventoy_gpt_info));
402 if (!gpt)
403 {
404 grub_env_set(args[1], "unknown");
405 goto end;
406 }
407
408 grub_file_seek(file, offset);
409 grub_file_read(file, gpt, sizeof(ventoy_gpt_info));
410
411 if (gpt->MBR.Byte55 != 0x55 || gpt->MBR.ByteAA != 0xAA)
412 {
413 grub_env_set(args[1], "unknown");
414 debug("invalid mbr signature: 0x%x 0x%x\n", gpt->MBR.Byte55, gpt->MBR.ByteAA);
415 goto end;
416 }
417
418 if (grub_memcmp(gpt->Head.Signature, "EFI PART", 8) == 0)
419 {
420 grub_env_set(args[2], "gpt");
421 debug("part type: %s\n", "GPT");
422
423 if (gpt->MBR.PartTbl[0].FsFlag == 0xEE)
424 {
425 for (i = 0; i < 128; i++)
426 {
427 if (grub_memcmp(gpt->PartTbl[i].PartType, "Hah!IdontNeedEFI", 16) == 0)
428 {
429 debug("part %d is grub_bios part\n", i);
430 grub_env_set(args[3], "1");
431 break;
432 }
433 else if (gpt->PartTbl[i].LastLBA == 0)
434 {
435 break;
436 }
437 }
438 }
439 }
440 else
441 {
442 grub_env_set(args[2], "mbr");
443 debug("part type: %s\n", "MBR");
444
445 for (i = 0; i < 4; i++)
446 {
447 if (gpt->MBR.PartTbl[i].FsFlag == 0xEF)
448 {
449 debug("part %d is esp part in MBR mode\n", i);
450 grub_env_set(args[3], "1");
451 break;
452 }
453 }
454 }
455 }
456 else
457 {
458 debug("part type: %s\n", "xxx");
459 }
460
461 end:
462 grub_check_free(gpt);
463 grub_file_close(file);
464 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
465 }
466
467 grub_err_t ventoy_cmd_raw_chain_data(grub_extcmd_context_t ctxt, int argc, char **args)
468 {
469 grub_uint32_t size = 0;
470 grub_uint32_t img_chunk_size = 0;
471 grub_file_t file;
472 grub_disk_t disk;
473 const char *pLastChain = NULL;
474 ventoy_chain_head *chain;
475 char envbuf[64];
476
477 (void)ctxt;
478 (void)argc;
479
480 if (NULL == g_img_chunk_list.chunk)
481 {
482 grub_printf("ventoy not ready\n");
483 return 1;
484 }
485
486 if (g_img_trim_head_secnum > 0)
487 {
488 ventoy_raw_trim_head(g_img_trim_head_secnum);
489 }
490
491 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
492 if (!file)
493 {
494 return 1;
495 }
496
497 img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
498
499 size = sizeof(ventoy_chain_head) + img_chunk_size;
500
501 pLastChain = grub_env_get("vtoy_chain_mem_addr");
502 if (pLastChain)
503 {
504 chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16);
505 if (chain)
506 {
507 debug("free last chain memory %p\n", chain);
508 grub_free(chain);
509 }
510 }
511
512 chain = grub_malloc(size);
513 if (!chain)
514 {
515 grub_printf("Failed to alloc chain memory size %u\n", size);
516 grub_file_close(file);
517 return 1;
518 }
519
520 grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)chain);
521 grub_env_set("vtoy_chain_mem_addr", envbuf);
522 grub_snprintf(envbuf, sizeof(envbuf), "%u", size);
523 grub_env_set("vtoy_chain_mem_size", envbuf);
524
525 grub_env_export("vtoy_chain_mem_addr");
526 grub_env_export("vtoy_chain_mem_size");
527
528 grub_memset(chain, 0, sizeof(ventoy_chain_head));
529
530 /* part 1: os parameter */
531 g_ventoy_chain_type = ventoy_chain_linux;
532 ventoy_fill_os_param(file, &(chain->os_param));
533
534 /* part 2: chain head */
535 disk = file->device->disk;
536 chain->disk_drive = disk->id;
537 chain->disk_sector_size = (1 << disk->log_sector_size);
538
539 chain->real_img_size_in_bytes = file->size;
540 if (g_img_trim_head_secnum > 0)
541 {
542 chain->real_img_size_in_bytes -= g_img_trim_head_secnum * 512;
543 }
544
545 chain->virt_img_size_in_bytes = chain->real_img_size_in_bytes;
546 chain->boot_catalog = 0;
547
548 /* part 3: image chunk */
549 chain->img_chunk_offset = sizeof(ventoy_chain_head);
550 chain->img_chunk_num = g_img_chunk_list.cur_chunk;
551 grub_memcpy((char *)chain + chain->img_chunk_offset, g_img_chunk_list.chunk, img_chunk_size);
552
553 grub_file_seek(file, g_img_trim_head_secnum * 512);
554 grub_file_read(file, chain->boot_catalog_sector, 512);
555
556 grub_file_close(file);
557
558 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
559 }