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
)
154 grub_file_t file
= NULL
;
155 grub_disk_t disk
= NULL
;
157 file
= grub_file_open(filename
, VENTOY_FILE_TYPE
);
163 disk
= grub_disk_open(file
->device
->disk
->name
);
169 *index
= file
->device
->disk
->partition
->index
;
170 grub_disk_read(disk
, 0, 0, sizeof(ventoy_gpt_info
), gpt
);
175 check_free(file
, grub_file_close
);
176 check_free(disk
, grub_disk_close
);
181 static int ventoy_vhd_patch_disk(const char *vhdpath
, ventoy_patch_vhd
*patch1
, ventoy_patch_vhd
*patch2
)
184 grub_uint64_t offset
= 0;
185 char efipart
[16] = {0};
186 ventoy_gpt_info
*gpt
= NULL
;
188 if (vhdpath
[0] == '/')
190 gpt
= g_ventoy_part_info
;
192 debug("This is Ventoy ISO partIndex %d %s\n", partIndex
, vhdpath
);
196 gpt
= grub_zalloc(sizeof(ventoy_gpt_info
));
197 ventoy_vhd_read_parttbl(vhdpath
, gpt
, &partIndex
);
198 debug("This is HDD partIndex %d %s\n", partIndex
, vhdpath
);
201 grub_memcpy(efipart
, gpt
->Head
.Signature
, sizeof(gpt
->Head
.Signature
));
203 grub_memset(patch1
, 0, OFFSET_OF(ventoy_patch_vhd
, vhd_file_path
));
204 grub_memset(patch2
, 0, OFFSET_OF(ventoy_patch_vhd
, vhd_file_path
));
206 if (grub_strncmp(efipart
, "EFI PART", 8) == 0)
208 ventoy_debug_dump_guid("GPT disk GUID: ", gpt
->Head
.DiskGuid
);
209 ventoy_debug_dump_guid("GPT partIndex GUID: ", gpt
->PartTbl
[partIndex
].PartGuid
);
211 grub_memcpy(patch1
->disk_signature_or_guid
, gpt
->Head
.DiskGuid
, 16);
212 grub_memcpy(patch1
->part_offset_or_guid
, gpt
->PartTbl
[partIndex
].PartGuid
, 16);
213 grub_memcpy(patch2
->disk_signature_or_guid
, gpt
->Head
.DiskGuid
, 16);
214 grub_memcpy(patch2
->part_offset_or_guid
, gpt
->PartTbl
[partIndex
].PartGuid
, 16);
216 patch1
->part_type
= patch2
->part_type
= 0;
220 offset
= gpt
->MBR
.PartTbl
[partIndex
].StartSectorId
;
223 debug("MBR disk signature: %02x%02x%02x%02x Part(%d) offset:%llu\n",
224 gpt
->MBR
.BootCode
[0x1b8 + 0], gpt
->MBR
.BootCode
[0x1b8 + 1],
225 gpt
->MBR
.BootCode
[0x1b8 + 2], gpt
->MBR
.BootCode
[0x1b8 + 3],
226 partIndex
+ 1, offset
);
228 grub_memcpy(patch1
->part_offset_or_guid
, &offset
, 8);
229 grub_memcpy(patch2
->part_offset_or_guid
, &offset
, 8);
231 grub_memcpy(patch1
->disk_signature_or_guid
, gpt
->MBR
.BootCode
+ 0x1b8, 4);
232 grub_memcpy(patch2
->disk_signature_or_guid
, gpt
->MBR
.BootCode
+ 0x1b8, 4);
234 patch1
->part_type
= patch2
->part_type
= 1;
237 if (gpt
!= g_ventoy_part_info
)
245 static int ventoy_find_vhdpatch_offset(int bcdoffset
, int bcdlen
, int *offset
)
249 grub_uint8_t
*buf
= (grub_uint8_t
*)(g_vhdboot_isobuf
+ bcdoffset
);
250 grub_uint8_t magic
[16] = {
251 0x5C, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00
254 for (i
= 0; i
< bcdlen
- 16 && cnt
< 2; i
++)
256 if (*(grub_uint32_t
*)(buf
+ i
) == 0x0058005C)
258 if (grub_memcmp(magic
, buf
+ i
, 16) == 0)
260 *offset
++ = i
- (int)OFFSET_OF(ventoy_patch_vhd
, vhd_file_path
);
269 grub_err_t
ventoy_cmd_patch_vhdboot(grub_extcmd_context_t ctxt
, int argc
, char **args
)
272 int bcdoffset
, bcdlen
;
274 ventoy_patch_vhd
*patch1
;
275 ventoy_patch_vhd
*patch2
;
281 grub_env_unset("vtoy_vhd_buf_addr");
283 debug("patch vhd <%s>\n", args
[0]);
285 if ((!g_vhdboot_enable
) || (!g_vhdboot_totbuf
))
287 debug("vhd boot not ready %d %p\n", g_vhdboot_enable
, g_vhdboot_totbuf
);
291 rc
= ventoy_vhd_find_bcd(&bcdoffset
, &bcdlen
, "/boot/bcd");
294 debug("failed to get bcd location %d\n", rc
);
298 ventoy_find_vhdpatch_offset(bcdoffset
, bcdlen
, patchoffset
);
299 patch1
= (ventoy_patch_vhd
*)(g_vhdboot_isobuf
+ bcdoffset
+ patchoffset
[0]);
300 patch2
= (ventoy_patch_vhd
*)(g_vhdboot_isobuf
+ bcdoffset
+ patchoffset
[1]);
302 debug("Find /boot/bcd (%d %d) now patch it (offset: 0x%x 0x%x) ...\n",
303 bcdoffset
, bcdlen
, patchoffset
[0], patchoffset
[1]);
304 ventoy_vhd_patch_disk(args
[0], patch1
, patch2
);
305 ventoy_vhd_patch_path(args
[0], patch1
, patch2
, bcdoffset
, bcdlen
);
308 rc
= ventoy_vhd_find_bcd(&bcdoffset
, &bcdlen
, "/boot/BCD");
311 debug("No file /boot/BCD \n");
315 ventoy_find_vhdpatch_offset(bcdoffset
, bcdlen
, patchoffset
);
316 patch1
= (ventoy_patch_vhd
*)(g_vhdboot_isobuf
+ bcdoffset
+ patchoffset
[0]);
317 patch2
= (ventoy_patch_vhd
*)(g_vhdboot_isobuf
+ bcdoffset
+ patchoffset
[1]);
319 debug("Find /boot/BCD (%d %d) now patch it (offset: 0x%x 0x%x) ...\n",
320 bcdoffset
, bcdlen
, patchoffset
[0], patchoffset
[1]);
321 ventoy_vhd_patch_disk(args
[0], patch1
, patch2
);
322 ventoy_vhd_patch_path(args
[0], patch1
, patch2
, bcdoffset
, bcdlen
);
325 /* set buffer and size */
326 #ifdef GRUB_MACHINE_EFI
327 grub_snprintf(envbuf
, sizeof(envbuf
), "0x%lx", (ulong
)g_vhdboot_totbuf
);
328 grub_env_set("vtoy_vhd_buf_addr", envbuf
);
329 grub_snprintf(envbuf
, sizeof(envbuf
), "%d", (int)(g_vhdboot_isolen
+ sizeof(ventoy_chain_head
)));
330 grub_env_set("vtoy_vhd_buf_size", envbuf
);
332 grub_snprintf(envbuf
, sizeof(envbuf
), "0x%lx", (ulong
)g_vhdboot_isobuf
);
333 grub_env_set("vtoy_vhd_buf_addr", envbuf
);
334 grub_snprintf(envbuf
, sizeof(envbuf
), "%d", g_vhdboot_isolen
);
335 grub_env_set("vtoy_vhd_buf_size", envbuf
);
338 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
341 grub_err_t
ventoy_cmd_load_vhdboot(grub_extcmd_context_t ctxt
, int argc
, char **args
)
349 g_vhdboot_enable
= 0;
350 grub_check_free(g_vhdboot_totbuf
);
352 file
= grub_file_open(args
[0], VENTOY_FILE_TYPE
);
358 debug("load vhd boot: <%s> <%lu>\n", args
[0], (ulong
)file
->size
);
360 if (file
->size
< VTOY_SIZE_1KB
* 32)
362 grub_file_close(file
);
366 g_vhdboot_isolen
= (int)file
->size
;
368 buflen
= (int)(file
->size
+ sizeof(ventoy_chain_head
));
370 #ifdef GRUB_MACHINE_EFI
371 g_vhdboot_totbuf
= (char *)grub_efi_allocate_iso_buf(buflen
);
373 g_vhdboot_totbuf
= (char *)grub_malloc(buflen
);
376 if (!g_vhdboot_totbuf
)
378 grub_file_close(file
);
382 g_vhdboot_isobuf
= g_vhdboot_totbuf
+ sizeof(ventoy_chain_head
);
384 grub_file_read(file
, g_vhdboot_isobuf
, file
->size
);
385 grub_file_close(file
);
387 g_vhdboot_enable
= 1;
392 static int ventoy_raw_trim_head(grub_uint64_t offset
)
395 grub_uint32_t memsize
;
396 grub_uint32_t imgstart
= 0;
397 grub_uint32_t imgsecs
= 0;
398 grub_uint64_t sectors
= 0;
399 grub_uint64_t cursecs
= 0;
400 grub_uint64_t delta
= 0;
402 if ((!g_img_chunk_list
.chunk
) || (!offset
))
404 debug("image chunk not ready %p %lu\n", g_img_chunk_list
.chunk
, (ulong
)offset
);
408 debug("image trim head %lu\n", (ulong
)offset
);
410 for (i
= 0; i
< g_img_chunk_list
.cur_chunk
; i
++)
412 cursecs
= g_img_chunk_list
.chunk
[i
].disk_end_sector
+ 1 - g_img_chunk_list
.chunk
[i
].disk_start_sector
;
414 if (sectors
>= offset
)
416 delta
= cursecs
- (sectors
- offset
);
421 if (sectors
< offset
|| i
>= g_img_chunk_list
.cur_chunk
)
423 debug("Invalid size %lu %lu\n", (ulong
)sectors
, (ulong
)offset
);
427 if (sectors
== offset
)
429 memsize
= (g_img_chunk_list
.cur_chunk
- (i
+ 1)) * sizeof(ventoy_img_chunk
);
430 grub_memmove(g_img_chunk_list
.chunk
, g_img_chunk_list
.chunk
+ i
+ 1, memsize
);
431 g_img_chunk_list
.cur_chunk
-= (i
+ 1);
435 g_img_chunk_list
.chunk
[i
].disk_start_sector
+= delta
;
436 g_img_chunk_list
.chunk
[i
].img_start_sector
+= (grub_uint32_t
)(delta
/ 4);
440 memsize
= (g_img_chunk_list
.cur_chunk
- i
) * sizeof(ventoy_img_chunk
);
441 grub_memmove(g_img_chunk_list
.chunk
, g_img_chunk_list
.chunk
+ i
, memsize
);
442 g_img_chunk_list
.cur_chunk
-= i
;
446 for (i
= 0; i
< g_img_chunk_list
.cur_chunk
; i
++)
448 imgsecs
= g_img_chunk_list
.chunk
[i
].img_end_sector
+ 1 - g_img_chunk_list
.chunk
[i
].img_start_sector
;
449 g_img_chunk_list
.chunk
[i
].img_start_sector
= imgstart
;
450 g_img_chunk_list
.chunk
[i
].img_end_sector
= imgstart
+ (imgsecs
- 1);
457 grub_err_t
ventoy_cmd_get_vtoy_type(grub_extcmd_context_t ctxt
, int argc
, char **args
)
463 grub_uint8_t data
= 0;
464 vhd_footer_t vhdfoot
;
467 ventoy_gpt_info
*gpt
;
471 g_img_trim_head_secnum
= 0;
478 file
= grub_file_open(args
[0], VENTOY_FILE_TYPE
);
481 debug("Failed to open file %s\n", args
[0]);
485 grub_snprintf(type
, sizeof(type
), "unknown");
487 grub_file_seek(file
, file
->size
- 512);
488 grub_file_read(file
, &vhdfoot
, sizeof(vhdfoot
));
490 if (grub_strncmp(vhdfoot
.cookie
, "conectix", 8) == 0)
493 grub_snprintf(type
, sizeof(type
), "vhd%u", grub_swap_bytes32(vhdfoot
.disktype
));
497 grub_file_seek(file
, 0);
498 grub_file_read(file
, &vdihdr
, sizeof(vdihdr
));
499 if (vdihdr
.u32Signature
== VDI_IMAGE_SIGNATURE
&&
500 grub_strncmp(vdihdr
.szFileInfo
, VDI_IMAGE_FILE_INFO
, grub_strlen(VDI_IMAGE_FILE_INFO
)) == 0)
502 offset
= 2 * 1048576;
503 g_img_trim_head_secnum
= offset
/ 512;
504 grub_snprintf(type
, sizeof(type
), "vdi");
509 grub_snprintf(type
, sizeof(type
), "raw");
513 grub_env_set(args
[1], type
);
514 debug("<%s> vtoy type: <%s> ", args
[0], type
);
518 gpt
= grub_zalloc(sizeof(ventoy_gpt_info
));
521 grub_env_set(args
[1], "unknown");
525 grub_file_seek(file
, offset
);
526 grub_file_read(file
, gpt
, sizeof(ventoy_gpt_info
));
528 if (gpt
->MBR
.Byte55
!= 0x55 || gpt
->MBR
.ByteAA
!= 0xAA)
530 grub_env_set(args
[1], "unknown");
531 debug("invalid mbr signature: 0x%x 0x%x\n", gpt
->MBR
.Byte55
, gpt
->MBR
.ByteAA
);
535 if (grub_memcmp(gpt
->Head
.Signature
, "EFI PART", 8) == 0)
537 grub_env_set(args
[2], "gpt");
538 debug("part type: %s\n", "GPT");
540 if (gpt
->MBR
.PartTbl
[0].FsFlag
== 0xEE)
542 for (i
= 0; i
< 128; i
++)
544 if (grub_memcmp(gpt
->PartTbl
[i
].PartType
, "Hah!IdontNeedEFI", 16) == 0)
546 debug("part %d is grub_bios part\n", i
);
548 grub_env_set(args
[3], "1");
551 else if (gpt
->PartTbl
[i
].LastLBA
== 0)
560 if (gpt
->MBR
.BootCode
[92] == 0x22)
562 grub_file_seek(file
, offset
+ 17908);
563 grub_file_read(file
, &data
, 1);
567 grub_env_set(args
[3], "1");
574 grub_env_set(args
[2], "mbr");
575 debug("part type: %s\n", "MBR");
577 for (i
= 0; i
< 4; i
++)
579 if (gpt
->MBR
.PartTbl
[i
].FsFlag
== 0xEF)
581 debug("part %d is esp part in MBR mode\n", i
);
583 grub_env_set(args
[3], "1");
591 debug("part type: %s\n", "xxx");
595 grub_check_free(gpt
);
596 grub_file_close(file
);
597 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
600 grub_err_t
ventoy_cmd_raw_chain_data(grub_extcmd_context_t ctxt
, int argc
, char **args
)
602 grub_uint32_t size
= 0;
603 grub_uint32_t img_chunk_size
= 0;
606 const char *pLastChain
= NULL
;
607 ventoy_chain_head
*chain
;
613 if (NULL
== g_img_chunk_list
.chunk
)
615 grub_printf("ventoy not ready\n");
619 if (g_img_trim_head_secnum
> 0)
621 ventoy_raw_trim_head(g_img_trim_head_secnum
);
624 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "%s", args
[0]);
630 img_chunk_size
= g_img_chunk_list
.cur_chunk
* sizeof(ventoy_img_chunk
);
632 size
= sizeof(ventoy_chain_head
) + img_chunk_size
;
634 pLastChain
= grub_env_get("vtoy_chain_mem_addr");
637 chain
= (ventoy_chain_head
*)grub_strtoul(pLastChain
, NULL
, 16);
640 debug("free last chain memory %p\n", chain
);
645 chain
= grub_malloc(size
);
648 grub_printf("Failed to alloc chain memory size %u\n", size
);
649 grub_file_close(file
);
653 grub_snprintf(envbuf
, sizeof(envbuf
), "0x%lx", (unsigned long)chain
);
654 grub_env_set("vtoy_chain_mem_addr", envbuf
);
655 grub_snprintf(envbuf
, sizeof(envbuf
), "%u", size
);
656 grub_env_set("vtoy_chain_mem_size", envbuf
);
658 grub_env_export("vtoy_chain_mem_addr");
659 grub_env_export("vtoy_chain_mem_size");
661 grub_memset(chain
, 0, sizeof(ventoy_chain_head
));
663 /* part 1: os parameter */
664 g_ventoy_chain_type
= ventoy_chain_linux
;
665 ventoy_fill_os_param(file
, &(chain
->os_param
));
667 /* part 2: chain head */
668 disk
= file
->device
->disk
;
669 chain
->disk_drive
= disk
->id
;
670 chain
->disk_sector_size
= (1 << disk
->log_sector_size
);
672 chain
->real_img_size_in_bytes
= file
->size
;
673 if (g_img_trim_head_secnum
> 0)
675 chain
->real_img_size_in_bytes
-= g_img_trim_head_secnum
* 512;
678 chain
->virt_img_size_in_bytes
= chain
->real_img_size_in_bytes
;
679 chain
->boot_catalog
= 0;
681 /* part 3: image chunk */
682 chain
->img_chunk_offset
= sizeof(ventoy_chain_head
);
683 chain
->img_chunk_num
= g_img_chunk_list
.cur_chunk
;
684 grub_memcpy((char *)chain
+ chain
->img_chunk_offset
, g_img_chunk_list
.chunk
, img_chunk_size
);
686 grub_file_seek(file
, g_img_trim_head_secnum
* 512);
687 grub_file_read(file
, chain
->boot_catalog_sector
, 512);
689 grub_file_close(file
);
691 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);