1 /******************************************************************************
4 * Copyright (c) 2020, 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/>.
21 #include <grub/types.h>
22 #include <grub/misc.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>
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>
42 #include <grub/ventoy.h>
43 #include "ventoy_def.h"
45 GRUB_MOD_LICENSE ("GPLv3+");
47 static int g_vhdboot_isolen
= 0;
48 static char *g_vhdboot_totbuf
= NULL
;
49 static char *g_vhdboot_isobuf
= NULL
;
50 static grub_uint64_t g_img_trim_head_secnum
= 0;
52 static int ventoy_vhd_find_bcd(int *bcdoffset
, int *bcdlen
, const char *path
)
58 grub_snprintf(cmdbuf
, sizeof(cmdbuf
), "loopback vhdiso mem:0x%lx:size:%d", (ulong
)g_vhdboot_isobuf
, g_vhdboot_isolen
);
60 grub_script_execute_sourcecode(cmdbuf
);
62 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "(vhdiso)%s", path
);
68 grub_file_read(file
, &offset
, 4);
69 offset
= (grub_uint32_t
)grub_iso9660_get_last_read_pos(file
);
71 *bcdoffset
= (int)offset
;
72 *bcdlen
= (int)file
->size
;
74 debug("vhdiso bcd file offset:%d len:%d\n", *bcdoffset
, *bcdlen
);
76 grub_file_close(file
);
78 grub_script_execute_sourcecode("loopback -d vhdiso");
83 static int ventoy_vhd_patch_path(char *vhdpath
, ventoy_patch_vhd
*patch1
, ventoy_patch_vhd
*patch2
,
84 int bcdoffset
, int bcdlen
)
92 grub_uint16_t
*unicode_path
;
93 const grub_uint8_t winloadexe
[] =
95 0x77, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x61, 0x00, 0x64, 0x00, 0x2E, 0x00,
96 0x65, 0x00, 0x78, 0x00, 0x65, 0x00
99 while ((*vhdpath
) != '/')
104 pathlen
= sizeof(grub_uint16_t
) * (grub_strlen(vhdpath
) + 1);
105 debug("unicode path for <%s> len:%d\n", vhdpath
, (int)pathlen
);
107 unicode_path
= grub_zalloc(pathlen
);
113 plat
= grub_env_get("grub_platform");
115 if (plat
&& (plat
[0] == 'e')) /* UEFI */
117 pos
= g_vhdboot_isobuf
+ bcdoffset
;
119 /* winload.exe ==> winload.efi */
120 for (i
= 0; i
+ (int)sizeof(winloadexe
) < bcdlen
; i
++)
122 if (*((grub_uint32_t
*)(pos
+ i
)) == 0x00690077 &&
123 grub_memcmp(pos
+ i
, winloadexe
, sizeof(winloadexe
)) == 0)
125 pos
[i
+ sizeof(winloadexe
) - 4] = 0x66;
126 pos
[i
+ sizeof(winloadexe
) - 2] = 0x69;
131 debug("winload patch %d times\n", cnt
);
134 newpath
= grub_strdup(vhdpath
);
135 for (pos
= newpath
; *pos
; pos
++)
143 grub_utf8_to_utf16(unicode_path
, pathlen
, (grub_uint8_t
*)newpath
, -1, NULL
);
144 grub_memcpy(patch1
->vhd_file_path
, unicode_path
, pathlen
);
145 grub_memcpy(patch2
->vhd_file_path
, unicode_path
, pathlen
);
151 static int ventoy_vhd_read_parttbl(const char *filename
, ventoy_gpt_info
*gpt
, int *index
, grub_uint64_t
*poffset
)
157 grub_file_t file
= NULL
;
158 grub_disk_t disk
= NULL
;
159 grub_uint8_t zeroguid
[16] = {0};
161 file
= grub_file_open(filename
, VENTOY_FILE_TYPE
);
167 disk
= grub_disk_open(file
->device
->disk
->name
);
173 grub_disk_read(disk
, 0, 0, sizeof(ventoy_gpt_info
), gpt
);
175 start
= file
->device
->disk
->partition
->start
;
177 if (grub_memcmp(gpt
->Head
.Signature
, "EFI PART", 8) == 0)
179 debug("GPT part start: %llu\n", (ulonglong
)start
);
180 for (i
= 0; i
< 128; i
++)
182 if (grub_memcmp(gpt
->PartTbl
[i
].PartGuid
, zeroguid
, 16))
184 if (start
== gpt
->PartTbl
[i
].StartLBA
)
195 debug("MBR part start: %llu\n", (ulonglong
)start
);
196 for (i
= 0; i
< 4; i
++)
198 if ((grub_uint32_t
)start
== gpt
->MBR
.PartTbl
[i
].StartSectorId
)
207 if (find
== 0) // MBR Logical partition
209 if (file
->device
->disk
->partition
->number
> 0)
211 *index
= file
->device
->disk
->partition
->number
;
212 debug("Fall back part number: %d\n", *index
);
220 check_free(file
, grub_file_close
);
221 check_free(disk
, grub_disk_close
);
226 static int ventoy_vhd_patch_disk(const char *vhdpath
, ventoy_patch_vhd
*patch1
, ventoy_patch_vhd
*patch2
)
229 grub_uint64_t offset
= 0;
230 char efipart
[16] = {0};
231 ventoy_gpt_info
*gpt
= NULL
;
233 if (vhdpath
[0] == '/')
235 gpt
= g_ventoy_part_info
;
237 debug("This is Ventoy ISO partIndex %d %s\n", partIndex
, vhdpath
);
241 gpt
= grub_zalloc(sizeof(ventoy_gpt_info
));
242 ventoy_vhd_read_parttbl(vhdpath
, gpt
, &partIndex
, &offset
);
243 debug("This is HDD partIndex %d %s\n", partIndex
, vhdpath
);
246 grub_memcpy(efipart
, gpt
->Head
.Signature
, sizeof(gpt
->Head
.Signature
));
248 grub_memset(patch1
, 0, OFFSET_OF(ventoy_patch_vhd
, vhd_file_path
));
249 grub_memset(patch2
, 0, OFFSET_OF(ventoy_patch_vhd
, vhd_file_path
));
251 if (grub_strncmp(efipart
, "EFI PART", 8) == 0)
253 ventoy_debug_dump_guid("GPT disk GUID: ", gpt
->Head
.DiskGuid
);
254 ventoy_debug_dump_guid("GPT partIndex GUID: ", gpt
->PartTbl
[partIndex
].PartGuid
);
256 grub_memcpy(patch1
->disk_signature_or_guid
, gpt
->Head
.DiskGuid
, 16);
257 grub_memcpy(patch1
->part_offset_or_guid
, gpt
->PartTbl
[partIndex
].PartGuid
, 16);
258 grub_memcpy(patch2
->disk_signature_or_guid
, gpt
->Head
.DiskGuid
, 16);
259 grub_memcpy(patch2
->part_offset_or_guid
, gpt
->PartTbl
[partIndex
].PartGuid
, 16);
261 patch1
->part_type
= patch2
->part_type
= 0;
267 offset
= gpt
->MBR
.PartTbl
[partIndex
].StartSectorId
;
270 debug("MBR disk signature: %02x%02x%02x%02x Part(%d) offset:%llu\n",
271 gpt
->MBR
.BootCode
[0x1b8 + 0], gpt
->MBR
.BootCode
[0x1b8 + 1],
272 gpt
->MBR
.BootCode
[0x1b8 + 2], gpt
->MBR
.BootCode
[0x1b8 + 3],
273 partIndex
+ 1, offset
);
275 grub_memcpy(patch1
->part_offset_or_guid
, &offset
, 8);
276 grub_memcpy(patch2
->part_offset_or_guid
, &offset
, 8);
278 grub_memcpy(patch1
->disk_signature_or_guid
, gpt
->MBR
.BootCode
+ 0x1b8, 4);
279 grub_memcpy(patch2
->disk_signature_or_guid
, gpt
->MBR
.BootCode
+ 0x1b8, 4);
281 patch1
->part_type
= patch2
->part_type
= 1;
284 if (gpt
!= g_ventoy_part_info
)
292 static int ventoy_find_vhdpatch_offset(int bcdoffset
, int bcdlen
, int *offset
)
296 grub_uint8_t
*buf
= (grub_uint8_t
*)(g_vhdboot_isobuf
+ bcdoffset
);
297 grub_uint8_t magic
[16] = {
298 0x5C, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00
301 for (i
= 0; i
< bcdlen
- 16 && cnt
< 2; i
++)
303 if (*(grub_uint32_t
*)(buf
+ i
) == 0x0058005C)
305 if (grub_memcmp(magic
, buf
+ i
, 16) == 0)
307 *offset
++ = i
- (int)OFFSET_OF(ventoy_patch_vhd
, vhd_file_path
);
316 grub_err_t
ventoy_cmd_patch_vhdboot(grub_extcmd_context_t ctxt
, int argc
, char **args
)
319 int bcdoffset
, bcdlen
;
321 ventoy_patch_vhd
*patch1
;
322 ventoy_patch_vhd
*patch2
;
327 grub_env_unset("vtoy_vhd_buf_addr");
329 debug("patch vhd <%s>\n", args
[0]);
331 if ((!g_vhdboot_enable
) || (!g_vhdboot_totbuf
))
333 debug("vhd boot not ready %d %p\n", g_vhdboot_enable
, g_vhdboot_totbuf
);
337 rc
= ventoy_vhd_find_bcd(&bcdoffset
, &bcdlen
, "/boot/bcd");
340 debug("failed to get bcd location %d\n", rc
);
344 ventoy_find_vhdpatch_offset(bcdoffset
, bcdlen
, patchoffset
);
345 patch1
= (ventoy_patch_vhd
*)(g_vhdboot_isobuf
+ bcdoffset
+ patchoffset
[0]);
346 patch2
= (ventoy_patch_vhd
*)(g_vhdboot_isobuf
+ bcdoffset
+ patchoffset
[1]);
348 debug("Find /boot/bcd (%d %d) now patch it (offset: 0x%x 0x%x) ...\n",
349 bcdoffset
, bcdlen
, patchoffset
[0], patchoffset
[1]);
350 ventoy_vhd_patch_disk(args
[0], patch1
, patch2
);
351 ventoy_vhd_patch_path(args
[0], patch1
, patch2
, bcdoffset
, bcdlen
);
354 rc
= ventoy_vhd_find_bcd(&bcdoffset
, &bcdlen
, "/boot/BCD");
357 debug("No file /boot/BCD \n");
361 ventoy_find_vhdpatch_offset(bcdoffset
, bcdlen
, patchoffset
);
362 patch1
= (ventoy_patch_vhd
*)(g_vhdboot_isobuf
+ bcdoffset
+ patchoffset
[0]);
363 patch2
= (ventoy_patch_vhd
*)(g_vhdboot_isobuf
+ bcdoffset
+ patchoffset
[1]);
365 debug("Find /boot/BCD (%d %d) now patch it (offset: 0x%x 0x%x) ...\n",
366 bcdoffset
, bcdlen
, patchoffset
[0], patchoffset
[1]);
367 ventoy_vhd_patch_disk(args
[0], patch1
, patch2
);
368 ventoy_vhd_patch_path(args
[0], patch1
, patch2
, bcdoffset
, bcdlen
);
371 /* set buffer and size */
372 #ifdef GRUB_MACHINE_EFI
373 ventoy_memfile_env_set("vtoy_vhd_buf", g_vhdboot_totbuf
, (ulonglong
)(g_vhdboot_isolen
+ sizeof(ventoy_chain_head
)));
375 ventoy_memfile_env_set("vtoy_vhd_buf", g_vhdboot_isobuf
, (ulonglong
)g_vhdboot_isolen
);
378 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
381 grub_err_t
ventoy_cmd_load_vhdboot(grub_extcmd_context_t ctxt
, int argc
, char **args
)
389 g_vhdboot_enable
= 0;
390 grub_check_free(g_vhdboot_totbuf
);
392 file
= grub_file_open(args
[0], VENTOY_FILE_TYPE
);
398 debug("load vhd boot: <%s> <%lu>\n", args
[0], (ulong
)file
->size
);
400 if (file
->size
< VTOY_SIZE_1KB
* 32)
402 grub_file_close(file
);
406 g_vhdboot_isolen
= (int)file
->size
;
408 buflen
= (int)(file
->size
+ sizeof(ventoy_chain_head
));
410 #ifdef GRUB_MACHINE_EFI
411 g_vhdboot_totbuf
= (char *)grub_efi_allocate_iso_buf(buflen
);
413 g_vhdboot_totbuf
= (char *)grub_malloc(buflen
);
416 if (!g_vhdboot_totbuf
)
418 grub_file_close(file
);
422 g_vhdboot_isobuf
= g_vhdboot_totbuf
+ sizeof(ventoy_chain_head
);
424 grub_file_read(file
, g_vhdboot_isobuf
, file
->size
);
425 grub_file_close(file
);
427 g_vhdboot_enable
= 1;
432 static int ventoy_raw_trim_head(grub_uint64_t offset
)
435 grub_uint32_t memsize
;
436 grub_uint32_t imgstart
= 0;
437 grub_uint32_t imgsecs
= 0;
438 grub_uint64_t sectors
= 0;
439 grub_uint64_t cursecs
= 0;
440 grub_uint64_t delta
= 0;
442 if ((!g_img_chunk_list
.chunk
) || (!offset
))
444 debug("image chunk not ready %p %lu\n", g_img_chunk_list
.chunk
, (ulong
)offset
);
448 debug("image trim head %lu\n", (ulong
)offset
);
450 for (i
= 0; i
< g_img_chunk_list
.cur_chunk
; i
++)
452 cursecs
= g_img_chunk_list
.chunk
[i
].disk_end_sector
+ 1 - g_img_chunk_list
.chunk
[i
].disk_start_sector
;
454 if (sectors
>= offset
)
456 delta
= cursecs
- (sectors
- offset
);
461 if (sectors
< offset
|| i
>= g_img_chunk_list
.cur_chunk
)
463 debug("Invalid size %lu %lu\n", (ulong
)sectors
, (ulong
)offset
);
467 if (sectors
== offset
)
469 memsize
= (g_img_chunk_list
.cur_chunk
- (i
+ 1)) * sizeof(ventoy_img_chunk
);
470 grub_memmove(g_img_chunk_list
.chunk
, g_img_chunk_list
.chunk
+ i
+ 1, memsize
);
471 g_img_chunk_list
.cur_chunk
-= (i
+ 1);
475 g_img_chunk_list
.chunk
[i
].disk_start_sector
+= delta
;
476 g_img_chunk_list
.chunk
[i
].img_start_sector
+= (grub_uint32_t
)(delta
/ 4);
480 memsize
= (g_img_chunk_list
.cur_chunk
- i
) * sizeof(ventoy_img_chunk
);
481 grub_memmove(g_img_chunk_list
.chunk
, g_img_chunk_list
.chunk
+ i
, memsize
);
482 g_img_chunk_list
.cur_chunk
-= i
;
486 for (i
= 0; i
< g_img_chunk_list
.cur_chunk
; i
++)
488 imgsecs
= g_img_chunk_list
.chunk
[i
].img_end_sector
+ 1 - g_img_chunk_list
.chunk
[i
].img_start_sector
;
489 g_img_chunk_list
.chunk
[i
].img_start_sector
= imgstart
;
490 g_img_chunk_list
.chunk
[i
].img_end_sector
= imgstart
+ (imgsecs
- 1);
497 grub_err_t
ventoy_cmd_get_vtoy_type(grub_extcmd_context_t ctxt
, int argc
, char **args
)
503 grub_uint8_t data
= 0;
504 vhd_footer_t vhdfoot
;
507 ventoy_gpt_info
*gpt
= NULL
;
511 g_img_trim_head_secnum
= 0;
518 file
= grub_file_open(args
[0], VENTOY_FILE_TYPE
);
521 debug("Failed to open file %s\n", args
[0]);
525 grub_snprintf(type
, sizeof(type
), "unknown");
527 grub_file_seek(file
, file
->size
- 512);
528 grub_file_read(file
, &vhdfoot
, sizeof(vhdfoot
));
530 if (grub_strncmp(vhdfoot
.cookie
, "conectix", 8) == 0)
533 grub_snprintf(type
, sizeof(type
), "vhd%u", grub_swap_bytes32(vhdfoot
.disktype
));
537 grub_file_seek(file
, 0);
538 grub_file_read(file
, &vdihdr
, sizeof(vdihdr
));
539 if (vdihdr
.u32Signature
== VDI_IMAGE_SIGNATURE
&&
540 grub_strncmp(vdihdr
.szFileInfo
, VDI_IMAGE_FILE_INFO
, grub_strlen(VDI_IMAGE_FILE_INFO
)) == 0)
542 offset
= 2 * 1048576;
543 g_img_trim_head_secnum
= offset
/ 512;
544 grub_snprintf(type
, sizeof(type
), "vdi");
549 grub_snprintf(type
, sizeof(type
), "raw");
553 grub_env_set(args
[1], type
);
554 debug("<%s> vtoy type: <%s> offset:%d\n", args
[0], type
, offset
);
558 gpt
= grub_zalloc(sizeof(ventoy_gpt_info
));
561 grub_env_set(args
[1], "unknown");
565 grub_file_seek(file
, offset
);
566 grub_file_read(file
, gpt
, sizeof(ventoy_gpt_info
));
568 if (gpt
->MBR
.Byte55
!= 0x55 || gpt
->MBR
.ByteAA
!= 0xAA)
570 grub_env_set(args
[1], "unknown");
571 debug("invalid mbr signature: 0x%x 0x%x\n", gpt
->MBR
.Byte55
, gpt
->MBR
.ByteAA
);
575 if (grub_memcmp(gpt
->Head
.Signature
, "EFI PART", 8) == 0)
577 grub_env_set(args
[2], "gpt");
578 debug("part type: %s\n", "GPT");
580 if (gpt
->MBR
.PartTbl
[0].FsFlag
== 0xEE)
582 for (i
= 0; i
< 128; i
++)
584 if (grub_memcmp(gpt
->PartTbl
[i
].PartType
, "Hah!IdontNeedEFI", 16) == 0)
586 debug("part %d is grub_bios part\n", i
);
588 grub_env_set(args
[3], "1");
591 else if (gpt
->PartTbl
[i
].LastLBA
== 0)
600 if (gpt
->MBR
.BootCode
[92] == 0x22)
602 grub_file_seek(file
, offset
+ 17908);
603 grub_file_read(file
, &data
, 1);
607 grub_env_set(args
[3], "1");
611 debug("offset data=0x%x\n", data
);
616 debug("BootCode: 0x%x\n", gpt
->MBR
.BootCode
[92]);
622 grub_env_set(args
[2], "mbr");
623 debug("part type: %s\n", "MBR");
625 for (i
= 0; i
< 4; i
++)
627 if (gpt
->MBR
.PartTbl
[i
].FsFlag
== 0xEF)
629 debug("part %d is esp part in MBR mode\n", i
);
631 grub_env_set(args
[3], "1");
639 debug("part type: %s\n", "xxx");
643 grub_check_free(gpt
);
644 grub_file_close(file
);
645 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
648 grub_err_t
ventoy_cmd_raw_chain_data(grub_extcmd_context_t ctxt
, int argc
, char **args
)
650 grub_uint32_t size
= 0;
651 grub_uint32_t img_chunk_size
= 0;
654 const char *pLastChain
= NULL
;
655 ventoy_chain_head
*chain
;
660 if (NULL
== g_img_chunk_list
.chunk
)
662 grub_printf("ventoy not ready\n");
666 if (g_img_trim_head_secnum
> 0)
668 ventoy_raw_trim_head(g_img_trim_head_secnum
);
671 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "%s", args
[0]);
677 if (grub_strncmp(args
[0], g_iso_path
, grub_strlen(g_iso_path
)))
682 img_chunk_size
= g_img_chunk_list
.cur_chunk
* sizeof(ventoy_img_chunk
);
684 size
= sizeof(ventoy_chain_head
) + img_chunk_size
;
686 pLastChain
= grub_env_get("vtoy_chain_mem_addr");
689 chain
= (ventoy_chain_head
*)grub_strtoul(pLastChain
, NULL
, 16);
692 debug("free last chain memory %p\n", chain
);
697 chain
= ventoy_alloc_chain(size
);
700 grub_printf("Failed to alloc chain raw memory size %u\n", size
);
701 grub_file_close(file
);
705 ventoy_memfile_env_set("vtoy_chain_mem", chain
, (ulonglong
)size
);
707 grub_env_export("vtoy_chain_mem_addr");
708 grub_env_export("vtoy_chain_mem_size");
710 grub_memset(chain
, 0, sizeof(ventoy_chain_head
));
712 /* part 1: os parameter */
713 g_ventoy_chain_type
= ventoy_chain_linux
;
714 ventoy_fill_os_param(file
, &(chain
->os_param
));
716 /* part 2: chain head */
717 disk
= file
->device
->disk
;
718 chain
->disk_drive
= disk
->id
;
719 chain
->disk_sector_size
= (1 << disk
->log_sector_size
);
721 chain
->real_img_size_in_bytes
= file
->size
;
722 if (g_img_trim_head_secnum
> 0)
724 chain
->real_img_size_in_bytes
-= g_img_trim_head_secnum
* 512;
727 chain
->virt_img_size_in_bytes
= chain
->real_img_size_in_bytes
;
728 chain
->boot_catalog
= 0;
730 /* part 3: image chunk */
731 chain
->img_chunk_offset
= sizeof(ventoy_chain_head
);
732 chain
->img_chunk_num
= g_img_chunk_list
.cur_chunk
;
733 grub_memcpy((char *)chain
+ chain
->img_chunk_offset
, g_img_chunk_list
.chunk
, img_chunk_size
);
735 grub_file_seek(file
, g_img_trim_head_secnum
* 512);
736 grub_file_read(file
, chain
->boot_catalog_sector
, 512);
738 grub_file_close(file
);
740 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);