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_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;
54 static int ventoy_vhd_find_bcd(int *bcdoffset
, int *bcdlen
)
60 grub_snprintf(cmdbuf
, sizeof(cmdbuf
), "loopback vhdiso mem:0x%lx:size:%d", (ulong
)g_vhdboot_isobuf
, g_vhdboot_isolen
);
62 grub_script_execute_sourcecode(cmdbuf
);
64 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "%s", "(vhdiso)/boot/bcd");
67 grub_printf("Failed to open bcd file in the image file\n");
71 grub_file_read(file
, &offset
, 4);
72 offset
= (grub_uint32_t
)grub_iso9660_get_last_read_pos(file
);
74 *bcdoffset
= (int)offset
;
75 *bcdlen
= (int)file
->size
;
77 debug("vhdiso bcd file offset:%d len:%d\n", *bcdoffset
, *bcdlen
);
79 grub_file_close(file
);
81 grub_script_execute_sourcecode("loopback -d vhdiso");
86 static int ventoy_vhd_patch_path(char *vhdpath
, ventoy_patch_vhd
*patch1
, ventoy_patch_vhd
*patch2
)
93 grub_uint16_t
*unicode_path
;
94 const grub_uint8_t winloadexe
[] =
96 0x77, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x61, 0x00, 0x64, 0x00, 0x2E, 0x00,
97 0x65, 0x00, 0x78, 0x00, 0x65, 0x00
100 pathlen
= sizeof(grub_uint16_t
) * (grub_strlen(vhdpath
) + 1);
101 debug("unicode path for <%s> len:%d\n", vhdpath
, (int)pathlen
);
103 unicode_path
= grub_zalloc(pathlen
);
109 plat
= grub_env_get("grub_platform");
111 if (plat
&& (plat
[0] == 'e')) /* UEFI */
113 pos
= g_vhdboot_isobuf
+ g_vhdboot_bcd_offset
;
115 /* winload.exe ==> winload.efi */
116 for (i
= 0; i
+ (int)sizeof(winloadexe
) < g_vhdboot_bcd_len
; i
++)
118 if (*((grub_uint32_t
*)(pos
+ i
)) == 0x00690077 &&
119 grub_memcmp(pos
+ i
, winloadexe
, sizeof(winloadexe
)) == 0)
121 pos
[i
+ sizeof(winloadexe
) - 4] = 0x66;
122 pos
[i
+ sizeof(winloadexe
) - 2] = 0x69;
127 debug("winload patch %d times\n", cnt
);
130 for (pos
= vhdpath
; *pos
; pos
++)
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
);
145 static int ventoy_vhd_patch_disk(ventoy_patch_vhd
*patch1
, ventoy_patch_vhd
*patch2
)
147 char efipart
[16] = {0};
149 grub_memcpy(efipart
, g_ventoy_part_info
->Head
.Signature
, sizeof(g_ventoy_part_info
->Head
.Signature
));
151 debug("part1 type: 0x%x <%s>\n", g_ventoy_part_info
->MBR
.PartTbl
[0].FsFlag
, efipart
);
153 if (grub_strncmp(efipart
, "EFI PART", 8) == 0)
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
);
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);
163 patch1
->part_type
= patch2
->part_type
= 0;
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);
177 grub_err_t
ventoy_cmd_patch_vhdboot(grub_extcmd_context_t ctxt
, int argc
, char **args
)
180 ventoy_patch_vhd
*patch1
;
181 ventoy_patch_vhd
*patch2
;
187 grub_env_unset("vtoy_vhd_buf_addr");
189 debug("patch vhd <%s>\n", args
[0]);
191 if ((!g_vhdboot_enable
) || (!g_vhdboot_totbuf
))
193 debug("vhd boot not ready %d %p\n", g_vhdboot_enable
, g_vhdboot_totbuf
);
197 rc
= ventoy_vhd_find_bcd(&g_vhdboot_bcd_offset
, &g_vhdboot_bcd_len
);
200 debug("failed to get bcd location %d\n", rc
);
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);
207 ventoy_vhd_patch_disk(patch1
, patch2
);
208 ventoy_vhd_patch_path(args
[0], patch1
, patch2
);
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
);
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
);
226 grub_err_t
ventoy_cmd_load_vhdboot(grub_extcmd_context_t ctxt
, int argc
, char **args
)
234 g_vhdboot_enable
= 0;
235 grub_check_free(g_vhdboot_totbuf
);
237 file
= grub_file_open(args
[0], VENTOY_FILE_TYPE
);
243 debug("load vhd boot: <%s> <%lu>\n", args
[0], (ulong
)file
->size
);
245 if (file
->size
< VTOY_SIZE_1KB
* 32)
247 grub_file_close(file
);
251 g_vhdboot_isolen
= (int)file
->size
;
253 buflen
= (int)(file
->size
+ sizeof(ventoy_chain_head
));
255 #ifdef GRUB_MACHINE_EFI
256 g_vhdboot_totbuf
= (char *)grub_efi_allocate_iso_buf(buflen
);
258 g_vhdboot_totbuf
= (char *)grub_malloc(buflen
);
261 if (!g_vhdboot_totbuf
)
263 grub_file_close(file
);
267 g_vhdboot_isobuf
= g_vhdboot_totbuf
+ sizeof(ventoy_chain_head
);
269 grub_file_read(file
, g_vhdboot_isobuf
, file
->size
);
270 grub_file_close(file
);
272 g_vhdboot_enable
= 1;
277 static int ventoy_raw_trim_head(grub_uint64_t offset
)
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;
287 if ((!g_img_chunk_list
.chunk
) || (!offset
))
289 debug("image chunk not ready %p %lu\n", g_img_chunk_list
.chunk
, (ulong
)offset
);
293 debug("image trim head %lu\n", (ulong
)offset
);
295 for (i
= 0; i
< g_img_chunk_list
.cur_chunk
; i
++)
297 cursecs
= g_img_chunk_list
.chunk
[i
].disk_end_sector
+ 1 - g_img_chunk_list
.chunk
[i
].disk_start_sector
;
299 if (sectors
>= offset
)
301 delta
= cursecs
- (sectors
- offset
);
306 if (sectors
< offset
|| i
>= g_img_chunk_list
.cur_chunk
)
308 debug("Invalid size %lu %lu\n", (ulong
)sectors
, (ulong
)offset
);
312 if (sectors
== offset
)
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);
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);
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
;
331 for (i
= 0; i
< g_img_chunk_list
.cur_chunk
; i
++)
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);
342 grub_err_t
ventoy_cmd_get_vtoy_type(grub_extcmd_context_t ctxt
, int argc
, char **args
)
348 grub_uint8_t data
= 0;
349 vhd_footer_t vhdfoot
;
352 ventoy_gpt_info
*gpt
;
356 g_img_trim_head_secnum
= 0;
363 file
= grub_file_open(args
[0], VENTOY_FILE_TYPE
);
366 debug("Failed to open file %s\n", args
[0]);
370 grub_snprintf(type
, sizeof(type
), "unknown");
372 grub_file_seek(file
, file
->size
- 512);
373 grub_file_read(file
, &vhdfoot
, sizeof(vhdfoot
));
375 if (grub_strncmp(vhdfoot
.cookie
, "conectix", 8) == 0)
378 grub_snprintf(type
, sizeof(type
), "vhd%u", grub_swap_bytes32(vhdfoot
.disktype
));
382 grub_file_seek(file
, 0);
383 grub_file_read(file
, &vdihdr
, sizeof(vdihdr
));
384 if (vdihdr
.u32Signature
== VDI_IMAGE_SIGNATURE
&&
385 grub_strncmp(vdihdr
.szFileInfo
, VDI_IMAGE_FILE_INFO
, grub_strlen(VDI_IMAGE_FILE_INFO
)) == 0)
387 offset
= 2 * 1048576;
388 g_img_trim_head_secnum
= offset
/ 512;
389 grub_snprintf(type
, sizeof(type
), "vdi");
394 grub_snprintf(type
, sizeof(type
), "raw");
398 grub_env_set(args
[1], type
);
399 debug("<%s> vtoy type: <%s> ", args
[0], type
);
403 gpt
= grub_zalloc(sizeof(ventoy_gpt_info
));
406 grub_env_set(args
[1], "unknown");
410 grub_file_seek(file
, offset
);
411 grub_file_read(file
, gpt
, sizeof(ventoy_gpt_info
));
413 if (gpt
->MBR
.Byte55
!= 0x55 || gpt
->MBR
.ByteAA
!= 0xAA)
415 grub_env_set(args
[1], "unknown");
416 debug("invalid mbr signature: 0x%x 0x%x\n", gpt
->MBR
.Byte55
, gpt
->MBR
.ByteAA
);
420 if (grub_memcmp(gpt
->Head
.Signature
, "EFI PART", 8) == 0)
422 grub_env_set(args
[2], "gpt");
423 debug("part type: %s\n", "GPT");
425 if (gpt
->MBR
.PartTbl
[0].FsFlag
== 0xEE)
427 for (i
= 0; i
< 128; i
++)
429 if (grub_memcmp(gpt
->PartTbl
[i
].PartType
, "Hah!IdontNeedEFI", 16) == 0)
431 debug("part %d is grub_bios part\n", i
);
433 grub_env_set(args
[3], "1");
436 else if (gpt
->PartTbl
[i
].LastLBA
== 0)
445 if (gpt
->MBR
.BootCode
[92] == 0x22)
447 grub_file_seek(file
, offset
+ 17908);
448 grub_file_read(file
, &data
, 1);
452 grub_env_set(args
[3], "1");
459 grub_env_set(args
[2], "mbr");
460 debug("part type: %s\n", "MBR");
462 for (i
= 0; i
< 4; i
++)
464 if (gpt
->MBR
.PartTbl
[i
].FsFlag
== 0xEF)
466 debug("part %d is esp part in MBR mode\n", i
);
468 grub_env_set(args
[3], "1");
476 debug("part type: %s\n", "xxx");
480 grub_check_free(gpt
);
481 grub_file_close(file
);
482 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
485 grub_err_t
ventoy_cmd_raw_chain_data(grub_extcmd_context_t ctxt
, int argc
, char **args
)
487 grub_uint32_t size
= 0;
488 grub_uint32_t img_chunk_size
= 0;
491 const char *pLastChain
= NULL
;
492 ventoy_chain_head
*chain
;
498 if (NULL
== g_img_chunk_list
.chunk
)
500 grub_printf("ventoy not ready\n");
504 if (g_img_trim_head_secnum
> 0)
506 ventoy_raw_trim_head(g_img_trim_head_secnum
);
509 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "%s", args
[0]);
515 img_chunk_size
= g_img_chunk_list
.cur_chunk
* sizeof(ventoy_img_chunk
);
517 size
= sizeof(ventoy_chain_head
) + img_chunk_size
;
519 pLastChain
= grub_env_get("vtoy_chain_mem_addr");
522 chain
= (ventoy_chain_head
*)grub_strtoul(pLastChain
, NULL
, 16);
525 debug("free last chain memory %p\n", chain
);
530 chain
= grub_malloc(size
);
533 grub_printf("Failed to alloc chain memory size %u\n", size
);
534 grub_file_close(file
);
538 grub_snprintf(envbuf
, sizeof(envbuf
), "0x%lx", (unsigned long)chain
);
539 grub_env_set("vtoy_chain_mem_addr", envbuf
);
540 grub_snprintf(envbuf
, sizeof(envbuf
), "%u", size
);
541 grub_env_set("vtoy_chain_mem_size", envbuf
);
543 grub_env_export("vtoy_chain_mem_addr");
544 grub_env_export("vtoy_chain_mem_size");
546 grub_memset(chain
, 0, sizeof(ventoy_chain_head
));
548 /* part 1: os parameter */
549 g_ventoy_chain_type
= ventoy_chain_linux
;
550 ventoy_fill_os_param(file
, &(chain
->os_param
));
552 /* part 2: chain head */
553 disk
= file
->device
->disk
;
554 chain
->disk_drive
= disk
->id
;
555 chain
->disk_sector_size
= (1 << disk
->log_sector_size
);
557 chain
->real_img_size_in_bytes
= file
->size
;
558 if (g_img_trim_head_secnum
> 0)
560 chain
->real_img_size_in_bytes
-= g_img_trim_head_secnum
* 512;
563 chain
->virt_img_size_in_bytes
= chain
->real_img_size_in_bytes
;
564 chain
->boot_catalog
= 0;
566 /* part 3: image chunk */
567 chain
->img_chunk_offset
= sizeof(ventoy_chain_head
);
568 chain
->img_chunk_num
= g_img_chunk_list
.cur_chunk
;
569 grub_memcpy((char *)chain
+ chain
->img_chunk_offset
, g_img_chunk_list
.chunk
, img_chunk_size
);
571 grub_file_seek(file
, g_img_trim_head_secnum
* 512);
572 grub_file_read(file
, chain
->boot_catalog_sector
, 512);
574 grub_file_close(file
);
576 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);