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
)
156 grub_file_t file
= NULL
;
157 grub_disk_t disk
= NULL
;
158 grub_uint8_t zeroguid
[16] = {0};
160 file
= grub_file_open(filename
, VENTOY_FILE_TYPE
);
166 disk
= grub_disk_open(file
->device
->disk
->name
);
172 grub_disk_read(disk
, 0, 0, sizeof(ventoy_gpt_info
), gpt
);
174 start
= file
->device
->disk
->partition
->start
;
176 if (grub_memcmp(gpt
->Head
.Signature
, "EFI PART", 8) == 0)
178 debug("GPT part start: %llu\n", (ulonglong
)start
);
179 for (i
= 0; i
< 128; i
++)
181 if (grub_memcmp(gpt
->PartTbl
[i
].PartGuid
, zeroguid
, 16))
183 if (start
== gpt
->PartTbl
[i
].StartLBA
)
193 debug("MBR part start: %llu\n", (ulonglong
)start
);
194 for (i
= 0; i
< 4; i
++)
196 if ((grub_uint32_t
)start
== gpt
->MBR
.PartTbl
[i
].StartSectorId
)
207 check_free(file
, grub_file_close
);
208 check_free(disk
, grub_disk_close
);
213 static int ventoy_vhd_patch_disk(const char *vhdpath
, ventoy_patch_vhd
*patch1
, ventoy_patch_vhd
*patch2
)
216 grub_uint64_t offset
= 0;
217 char efipart
[16] = {0};
218 ventoy_gpt_info
*gpt
= NULL
;
220 if (vhdpath
[0] == '/')
222 gpt
= g_ventoy_part_info
;
224 debug("This is Ventoy ISO partIndex %d %s\n", partIndex
, vhdpath
);
228 gpt
= grub_zalloc(sizeof(ventoy_gpt_info
));
229 ventoy_vhd_read_parttbl(vhdpath
, gpt
, &partIndex
);
230 debug("This is HDD partIndex %d %s\n", partIndex
, vhdpath
);
233 grub_memcpy(efipart
, gpt
->Head
.Signature
, sizeof(gpt
->Head
.Signature
));
235 grub_memset(patch1
, 0, OFFSET_OF(ventoy_patch_vhd
, vhd_file_path
));
236 grub_memset(patch2
, 0, OFFSET_OF(ventoy_patch_vhd
, vhd_file_path
));
238 if (grub_strncmp(efipart
, "EFI PART", 8) == 0)
240 ventoy_debug_dump_guid("GPT disk GUID: ", gpt
->Head
.DiskGuid
);
241 ventoy_debug_dump_guid("GPT partIndex GUID: ", gpt
->PartTbl
[partIndex
].PartGuid
);
243 grub_memcpy(patch1
->disk_signature_or_guid
, gpt
->Head
.DiskGuid
, 16);
244 grub_memcpy(patch1
->part_offset_or_guid
, gpt
->PartTbl
[partIndex
].PartGuid
, 16);
245 grub_memcpy(patch2
->disk_signature_or_guid
, gpt
->Head
.DiskGuid
, 16);
246 grub_memcpy(patch2
->part_offset_or_guid
, gpt
->PartTbl
[partIndex
].PartGuid
, 16);
248 patch1
->part_type
= patch2
->part_type
= 0;
252 offset
= gpt
->MBR
.PartTbl
[partIndex
].StartSectorId
;
255 debug("MBR disk signature: %02x%02x%02x%02x Part(%d) offset:%llu\n",
256 gpt
->MBR
.BootCode
[0x1b8 + 0], gpt
->MBR
.BootCode
[0x1b8 + 1],
257 gpt
->MBR
.BootCode
[0x1b8 + 2], gpt
->MBR
.BootCode
[0x1b8 + 3],
258 partIndex
+ 1, offset
);
260 grub_memcpy(patch1
->part_offset_or_guid
, &offset
, 8);
261 grub_memcpy(patch2
->part_offset_or_guid
, &offset
, 8);
263 grub_memcpy(patch1
->disk_signature_or_guid
, gpt
->MBR
.BootCode
+ 0x1b8, 4);
264 grub_memcpy(patch2
->disk_signature_or_guid
, gpt
->MBR
.BootCode
+ 0x1b8, 4);
266 patch1
->part_type
= patch2
->part_type
= 1;
269 if (gpt
!= g_ventoy_part_info
)
277 static int ventoy_find_vhdpatch_offset(int bcdoffset
, int bcdlen
, int *offset
)
281 grub_uint8_t
*buf
= (grub_uint8_t
*)(g_vhdboot_isobuf
+ bcdoffset
);
282 grub_uint8_t magic
[16] = {
283 0x5C, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00
286 for (i
= 0; i
< bcdlen
- 16 && cnt
< 2; i
++)
288 if (*(grub_uint32_t
*)(buf
+ i
) == 0x0058005C)
290 if (grub_memcmp(magic
, buf
+ i
, 16) == 0)
292 *offset
++ = i
- (int)OFFSET_OF(ventoy_patch_vhd
, vhd_file_path
);
301 grub_err_t
ventoy_cmd_patch_vhdboot(grub_extcmd_context_t ctxt
, int argc
, char **args
)
304 int bcdoffset
, bcdlen
;
306 ventoy_patch_vhd
*patch1
;
307 ventoy_patch_vhd
*patch2
;
313 grub_env_unset("vtoy_vhd_buf_addr");
315 debug("patch vhd <%s>\n", args
[0]);
317 if ((!g_vhdboot_enable
) || (!g_vhdboot_totbuf
))
319 debug("vhd boot not ready %d %p\n", g_vhdboot_enable
, g_vhdboot_totbuf
);
323 rc
= ventoy_vhd_find_bcd(&bcdoffset
, &bcdlen
, "/boot/bcd");
326 debug("failed to get bcd location %d\n", rc
);
330 ventoy_find_vhdpatch_offset(bcdoffset
, bcdlen
, patchoffset
);
331 patch1
= (ventoy_patch_vhd
*)(g_vhdboot_isobuf
+ bcdoffset
+ patchoffset
[0]);
332 patch2
= (ventoy_patch_vhd
*)(g_vhdboot_isobuf
+ bcdoffset
+ patchoffset
[1]);
334 debug("Find /boot/bcd (%d %d) now patch it (offset: 0x%x 0x%x) ...\n",
335 bcdoffset
, bcdlen
, patchoffset
[0], patchoffset
[1]);
336 ventoy_vhd_patch_disk(args
[0], patch1
, patch2
);
337 ventoy_vhd_patch_path(args
[0], patch1
, patch2
, bcdoffset
, bcdlen
);
340 rc
= ventoy_vhd_find_bcd(&bcdoffset
, &bcdlen
, "/boot/BCD");
343 debug("No file /boot/BCD \n");
347 ventoy_find_vhdpatch_offset(bcdoffset
, bcdlen
, patchoffset
);
348 patch1
= (ventoy_patch_vhd
*)(g_vhdboot_isobuf
+ bcdoffset
+ patchoffset
[0]);
349 patch2
= (ventoy_patch_vhd
*)(g_vhdboot_isobuf
+ bcdoffset
+ patchoffset
[1]);
351 debug("Find /boot/BCD (%d %d) now patch it (offset: 0x%x 0x%x) ...\n",
352 bcdoffset
, bcdlen
, patchoffset
[0], patchoffset
[1]);
353 ventoy_vhd_patch_disk(args
[0], patch1
, patch2
);
354 ventoy_vhd_patch_path(args
[0], patch1
, patch2
, bcdoffset
, bcdlen
);
357 /* set buffer and size */
358 #ifdef GRUB_MACHINE_EFI
359 grub_snprintf(envbuf
, sizeof(envbuf
), "0x%lx", (ulong
)g_vhdboot_totbuf
);
360 grub_env_set("vtoy_vhd_buf_addr", envbuf
);
361 grub_snprintf(envbuf
, sizeof(envbuf
), "%d", (int)(g_vhdboot_isolen
+ sizeof(ventoy_chain_head
)));
362 grub_env_set("vtoy_vhd_buf_size", envbuf
);
364 grub_snprintf(envbuf
, sizeof(envbuf
), "0x%lx", (ulong
)g_vhdboot_isobuf
);
365 grub_env_set("vtoy_vhd_buf_addr", envbuf
);
366 grub_snprintf(envbuf
, sizeof(envbuf
), "%d", g_vhdboot_isolen
);
367 grub_env_set("vtoy_vhd_buf_size", envbuf
);
370 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
373 grub_err_t
ventoy_cmd_load_vhdboot(grub_extcmd_context_t ctxt
, int argc
, char **args
)
381 g_vhdboot_enable
= 0;
382 grub_check_free(g_vhdboot_totbuf
);
384 file
= grub_file_open(args
[0], VENTOY_FILE_TYPE
);
390 debug("load vhd boot: <%s> <%lu>\n", args
[0], (ulong
)file
->size
);
392 if (file
->size
< VTOY_SIZE_1KB
* 32)
394 grub_file_close(file
);
398 g_vhdboot_isolen
= (int)file
->size
;
400 buflen
= (int)(file
->size
+ sizeof(ventoy_chain_head
));
402 #ifdef GRUB_MACHINE_EFI
403 g_vhdboot_totbuf
= (char *)grub_efi_allocate_iso_buf(buflen
);
405 g_vhdboot_totbuf
= (char *)grub_malloc(buflen
);
408 if (!g_vhdboot_totbuf
)
410 grub_file_close(file
);
414 g_vhdboot_isobuf
= g_vhdboot_totbuf
+ sizeof(ventoy_chain_head
);
416 grub_file_read(file
, g_vhdboot_isobuf
, file
->size
);
417 grub_file_close(file
);
419 g_vhdboot_enable
= 1;
424 static int ventoy_raw_trim_head(grub_uint64_t offset
)
427 grub_uint32_t memsize
;
428 grub_uint32_t imgstart
= 0;
429 grub_uint32_t imgsecs
= 0;
430 grub_uint64_t sectors
= 0;
431 grub_uint64_t cursecs
= 0;
432 grub_uint64_t delta
= 0;
434 if ((!g_img_chunk_list
.chunk
) || (!offset
))
436 debug("image chunk not ready %p %lu\n", g_img_chunk_list
.chunk
, (ulong
)offset
);
440 debug("image trim head %lu\n", (ulong
)offset
);
442 for (i
= 0; i
< g_img_chunk_list
.cur_chunk
; i
++)
444 cursecs
= g_img_chunk_list
.chunk
[i
].disk_end_sector
+ 1 - g_img_chunk_list
.chunk
[i
].disk_start_sector
;
446 if (sectors
>= offset
)
448 delta
= cursecs
- (sectors
- offset
);
453 if (sectors
< offset
|| i
>= g_img_chunk_list
.cur_chunk
)
455 debug("Invalid size %lu %lu\n", (ulong
)sectors
, (ulong
)offset
);
459 if (sectors
== offset
)
461 memsize
= (g_img_chunk_list
.cur_chunk
- (i
+ 1)) * sizeof(ventoy_img_chunk
);
462 grub_memmove(g_img_chunk_list
.chunk
, g_img_chunk_list
.chunk
+ i
+ 1, memsize
);
463 g_img_chunk_list
.cur_chunk
-= (i
+ 1);
467 g_img_chunk_list
.chunk
[i
].disk_start_sector
+= delta
;
468 g_img_chunk_list
.chunk
[i
].img_start_sector
+= (grub_uint32_t
)(delta
/ 4);
472 memsize
= (g_img_chunk_list
.cur_chunk
- i
) * sizeof(ventoy_img_chunk
);
473 grub_memmove(g_img_chunk_list
.chunk
, g_img_chunk_list
.chunk
+ i
, memsize
);
474 g_img_chunk_list
.cur_chunk
-= i
;
478 for (i
= 0; i
< g_img_chunk_list
.cur_chunk
; i
++)
480 imgsecs
= g_img_chunk_list
.chunk
[i
].img_end_sector
+ 1 - g_img_chunk_list
.chunk
[i
].img_start_sector
;
481 g_img_chunk_list
.chunk
[i
].img_start_sector
= imgstart
;
482 g_img_chunk_list
.chunk
[i
].img_end_sector
= imgstart
+ (imgsecs
- 1);
489 grub_err_t
ventoy_cmd_get_vtoy_type(grub_extcmd_context_t ctxt
, int argc
, char **args
)
495 grub_uint8_t data
= 0;
496 vhd_footer_t vhdfoot
;
499 ventoy_gpt_info
*gpt
;
503 g_img_trim_head_secnum
= 0;
510 file
= grub_file_open(args
[0], VENTOY_FILE_TYPE
);
513 debug("Failed to open file %s\n", args
[0]);
517 grub_snprintf(type
, sizeof(type
), "unknown");
519 grub_file_seek(file
, file
->size
- 512);
520 grub_file_read(file
, &vhdfoot
, sizeof(vhdfoot
));
522 if (grub_strncmp(vhdfoot
.cookie
, "conectix", 8) == 0)
525 grub_snprintf(type
, sizeof(type
), "vhd%u", grub_swap_bytes32(vhdfoot
.disktype
));
529 grub_file_seek(file
, 0);
530 grub_file_read(file
, &vdihdr
, sizeof(vdihdr
));
531 if (vdihdr
.u32Signature
== VDI_IMAGE_SIGNATURE
&&
532 grub_strncmp(vdihdr
.szFileInfo
, VDI_IMAGE_FILE_INFO
, grub_strlen(VDI_IMAGE_FILE_INFO
)) == 0)
534 offset
= 2 * 1048576;
535 g_img_trim_head_secnum
= offset
/ 512;
536 grub_snprintf(type
, sizeof(type
), "vdi");
541 grub_snprintf(type
, sizeof(type
), "raw");
545 grub_env_set(args
[1], type
);
546 debug("<%s> vtoy type: <%s> ", args
[0], type
);
550 gpt
= grub_zalloc(sizeof(ventoy_gpt_info
));
553 grub_env_set(args
[1], "unknown");
557 grub_file_seek(file
, offset
);
558 grub_file_read(file
, gpt
, sizeof(ventoy_gpt_info
));
560 if (gpt
->MBR
.Byte55
!= 0x55 || gpt
->MBR
.ByteAA
!= 0xAA)
562 grub_env_set(args
[1], "unknown");
563 debug("invalid mbr signature: 0x%x 0x%x\n", gpt
->MBR
.Byte55
, gpt
->MBR
.ByteAA
);
567 if (grub_memcmp(gpt
->Head
.Signature
, "EFI PART", 8) == 0)
569 grub_env_set(args
[2], "gpt");
570 debug("part type: %s\n", "GPT");
572 if (gpt
->MBR
.PartTbl
[0].FsFlag
== 0xEE)
574 for (i
= 0; i
< 128; i
++)
576 if (grub_memcmp(gpt
->PartTbl
[i
].PartType
, "Hah!IdontNeedEFI", 16) == 0)
578 debug("part %d is grub_bios part\n", i
);
580 grub_env_set(args
[3], "1");
583 else if (gpt
->PartTbl
[i
].LastLBA
== 0)
592 if (gpt
->MBR
.BootCode
[92] == 0x22)
594 grub_file_seek(file
, offset
+ 17908);
595 grub_file_read(file
, &data
, 1);
599 grub_env_set(args
[3], "1");
606 grub_env_set(args
[2], "mbr");
607 debug("part type: %s\n", "MBR");
609 for (i
= 0; i
< 4; i
++)
611 if (gpt
->MBR
.PartTbl
[i
].FsFlag
== 0xEF)
613 debug("part %d is esp part in MBR mode\n", i
);
615 grub_env_set(args
[3], "1");
623 debug("part type: %s\n", "xxx");
627 grub_check_free(gpt
);
628 grub_file_close(file
);
629 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
632 grub_err_t
ventoy_cmd_raw_chain_data(grub_extcmd_context_t ctxt
, int argc
, char **args
)
634 grub_uint32_t size
= 0;
635 grub_uint32_t img_chunk_size
= 0;
638 const char *pLastChain
= NULL
;
639 ventoy_chain_head
*chain
;
645 if (NULL
== g_img_chunk_list
.chunk
)
647 grub_printf("ventoy not ready\n");
651 if (g_img_trim_head_secnum
> 0)
653 ventoy_raw_trim_head(g_img_trim_head_secnum
);
656 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "%s", args
[0]);
662 img_chunk_size
= g_img_chunk_list
.cur_chunk
* sizeof(ventoy_img_chunk
);
664 size
= sizeof(ventoy_chain_head
) + img_chunk_size
;
666 pLastChain
= grub_env_get("vtoy_chain_mem_addr");
669 chain
= (ventoy_chain_head
*)grub_strtoul(pLastChain
, NULL
, 16);
672 debug("free last chain memory %p\n", chain
);
677 chain
= grub_malloc(size
);
680 grub_printf("Failed to alloc chain memory size %u\n", size
);
681 grub_file_close(file
);
685 grub_snprintf(envbuf
, sizeof(envbuf
), "0x%lx", (unsigned long)chain
);
686 grub_env_set("vtoy_chain_mem_addr", envbuf
);
687 grub_snprintf(envbuf
, sizeof(envbuf
), "%u", size
);
688 grub_env_set("vtoy_chain_mem_size", envbuf
);
690 grub_env_export("vtoy_chain_mem_addr");
691 grub_env_export("vtoy_chain_mem_size");
693 grub_memset(chain
, 0, sizeof(ventoy_chain_head
));
695 /* part 1: os parameter */
696 g_ventoy_chain_type
= ventoy_chain_linux
;
697 ventoy_fill_os_param(file
, &(chain
->os_param
));
699 /* part 2: chain head */
700 disk
= file
->device
->disk
;
701 chain
->disk_drive
= disk
->id
;
702 chain
->disk_sector_size
= (1 << disk
->log_sector_size
);
704 chain
->real_img_size_in_bytes
= file
->size
;
705 if (g_img_trim_head_secnum
> 0)
707 chain
->real_img_size_in_bytes
-= g_img_trim_head_secnum
* 512;
710 chain
->virt_img_size_in_bytes
= chain
->real_img_size_in_bytes
;
711 chain
->boot_catalog
= 0;
713 /* part 3: image chunk */
714 chain
->img_chunk_offset
= sizeof(ventoy_chain_head
);
715 chain
->img_chunk_num
= g_img_chunk_list
.cur_chunk
;
716 grub_memcpy((char *)chain
+ chain
->img_chunk_offset
, g_img_chunk_list
.chunk
, img_chunk_size
);
718 grub_file_seek(file
, g_img_trim_head_secnum
* 512);
719 grub_file_read(file
, chain
->boot_catalog_sector
, 512);
721 grub_file_close(file
);
723 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);