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
;
53 static int ventoy_vhd_find_bcd(int *bcdoffset
, int *bcdlen
)
59 grub_snprintf(cmdbuf
, sizeof(cmdbuf
), "loopback vhdiso mem:0x%lx:size:%d", (ulong
)g_vhdboot_isobuf
, g_vhdboot_isolen
);
61 grub_script_execute_sourcecode(cmdbuf
);
63 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "%s", "(vhdiso)/boot/bcd");
66 grub_printf("Failed to open bcd file in the image file\n");
70 grub_file_read(file
, &offset
, 4);
71 offset
= (grub_uint32_t
)grub_iso9660_get_last_read_pos(file
);
73 *bcdoffset
= (int)offset
;
74 *bcdlen
= (int)file
->size
;
76 debug("vhdiso bcd file offset:%d len:%d\n", *bcdoffset
, *bcdlen
);
78 grub_file_close(file
);
80 grub_script_execute_sourcecode("loopback -d vhdiso");
85 static int ventoy_vhd_patch_path(char *vhdpath
, ventoy_patch_vhd
*patch1
, ventoy_patch_vhd
*patch2
)
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 pathlen
= sizeof(grub_uint16_t
) * (grub_strlen(vhdpath
) + 1);
100 debug("unicode path for <%s> len:%d\n", vhdpath
, (int)pathlen
);
102 unicode_path
= grub_zalloc(pathlen
);
108 plat
= grub_env_get("grub_platform");
110 if (plat
&& (plat
[0] == 'e')) /* UEFI */
112 pos
= g_vhdboot_isobuf
+ g_vhdboot_bcd_offset
;
114 /* winload.exe ==> winload.efi */
115 for (i
= 0; i
+ (int)sizeof(winloadexe
) < g_vhdboot_bcd_len
; i
++)
117 if (*((grub_uint32_t
*)(pos
+ i
)) == 0x00690077 &&
118 grub_memcmp(pos
+ i
, winloadexe
, sizeof(winloadexe
)) == 0)
120 pos
[i
+ sizeof(winloadexe
) - 4] = 0x66;
121 pos
[i
+ sizeof(winloadexe
) - 2] = 0x69;
126 debug("winload patch %d times\n", cnt
);
129 for (pos
= vhdpath
; *pos
; pos
++)
137 grub_utf8_to_utf16(unicode_path
, pathlen
, (grub_uint8_t
*)vhdpath
, -1, NULL
);
138 grub_memcpy(patch1
->vhd_file_path
, unicode_path
, pathlen
);
139 grub_memcpy(patch2
->vhd_file_path
, unicode_path
, pathlen
);
144 static int ventoy_vhd_patch_disk(ventoy_patch_vhd
*patch1
, ventoy_patch_vhd
*patch2
)
146 char efipart
[16] = {0};
148 grub_memcpy(efipart
, g_ventoy_part_info
->Head
.Signature
, sizeof(g_ventoy_part_info
->Head
.Signature
));
150 debug("part1 type: 0x%x <%s>\n", g_ventoy_part_info
->MBR
.PartTbl
[0].FsFlag
, efipart
);
152 if (grub_strncmp(efipart
, "EFI PART", 8) == 0)
154 ventoy_debug_dump_guid("GPT disk GUID: ", g_ventoy_part_info
->Head
.DiskGuid
);
155 ventoy_debug_dump_guid("GPT part GUID: ", g_ventoy_part_info
->PartTbl
[0].PartGuid
);
157 grub_memcpy(patch1
->disk_signature_or_guid
, g_ventoy_part_info
->Head
.DiskGuid
, 16);
158 grub_memcpy(patch1
->part_offset_or_guid
, g_ventoy_part_info
->PartTbl
[0].PartGuid
, 16);
159 grub_memcpy(patch2
->disk_signature_or_guid
, g_ventoy_part_info
->Head
.DiskGuid
, 16);
160 grub_memcpy(patch2
->part_offset_or_guid
, g_ventoy_part_info
->PartTbl
[0].PartGuid
, 16);
162 patch1
->part_type
= patch2
->part_type
= 0;
166 debug("MBR disk signature: %02x%02x%02x%02x\n",
167 g_ventoy_part_info
->MBR
.BootCode
[0x1b8 + 0], g_ventoy_part_info
->MBR
.BootCode
[0x1b8 + 1],
168 g_ventoy_part_info
->MBR
.BootCode
[0x1b8 + 2], g_ventoy_part_info
->MBR
.BootCode
[0x1b8 + 3]);
169 grub_memcpy(patch1
->disk_signature_or_guid
, g_ventoy_part_info
->MBR
.BootCode
+ 0x1b8, 4);
170 grub_memcpy(patch2
->disk_signature_or_guid
, g_ventoy_part_info
->MBR
.BootCode
+ 0x1b8, 4);
176 grub_err_t
ventoy_cmd_patch_vhdboot(grub_extcmd_context_t ctxt
, int argc
, char **args
)
179 ventoy_patch_vhd
*patch1
;
180 ventoy_patch_vhd
*patch2
;
186 grub_env_unset("vtoy_vhd_buf_addr");
188 debug("patch vhd <%s>\n", args
[0]);
190 if ((!g_vhdboot_enable
) || (!g_vhdboot_totbuf
))
192 debug("vhd boot not ready %d %p\n", g_vhdboot_enable
, g_vhdboot_totbuf
);
196 rc
= ventoy_vhd_find_bcd(&g_vhdboot_bcd_offset
, &g_vhdboot_bcd_len
);
199 debug("failed to get bcd location %d\n", rc
);
203 patch1
= (ventoy_patch_vhd
*)(g_vhdboot_isobuf
+ g_vhdboot_bcd_offset
+ 0x495a);
204 patch2
= (ventoy_patch_vhd
*)(g_vhdboot_isobuf
+ g_vhdboot_bcd_offset
+ 0x50aa);
206 ventoy_vhd_patch_disk(patch1
, patch2
);
207 ventoy_vhd_patch_path(args
[0], patch1
, patch2
);
209 /* set buffer and size */
210 #ifdef GRUB_MACHINE_EFI
211 grub_snprintf(envbuf
, sizeof(envbuf
), "0x%lx", (ulong
)g_vhdboot_totbuf
);
212 grub_env_set("vtoy_vhd_buf_addr", envbuf
);
213 grub_snprintf(envbuf
, sizeof(envbuf
), "%d", (int)(g_vhdboot_isolen
+ sizeof(ventoy_chain_head
)));
214 grub_env_set("vtoy_vhd_buf_size", envbuf
);
216 grub_snprintf(envbuf
, sizeof(envbuf
), "0x%lx", (ulong
)g_vhdboot_isobuf
);
217 grub_env_set("vtoy_vhd_buf_addr", envbuf
);
218 grub_snprintf(envbuf
, sizeof(envbuf
), "%d", g_vhdboot_isolen
);
219 grub_env_set("vtoy_vhd_buf_size", envbuf
);
225 grub_err_t
ventoy_cmd_load_vhdboot(grub_extcmd_context_t ctxt
, int argc
, char **args
)
233 g_vhdboot_enable
= 0;
234 grub_check_free(g_vhdboot_totbuf
);
236 file
= grub_file_open(args
[0], VENTOY_FILE_TYPE
);
242 debug("load vhd boot: <%s> <%lu>\n", args
[0], (ulong
)file
->size
);
244 if (file
->size
< VTOY_SIZE_1KB
* 32)
246 grub_file_close(file
);
250 g_vhdboot_isolen
= (int)file
->size
;
252 buflen
= (int)(file
->size
+ sizeof(ventoy_chain_head
));
254 #ifdef GRUB_MACHINE_EFI
255 g_vhdboot_totbuf
= (char *)grub_efi_allocate_iso_buf(buflen
);
257 g_vhdboot_totbuf
= (char *)grub_malloc(buflen
);
260 if (!g_vhdboot_totbuf
)
262 grub_file_close(file
);
266 g_vhdboot_isobuf
= g_vhdboot_totbuf
+ sizeof(ventoy_chain_head
);
268 grub_file_read(file
, g_vhdboot_isobuf
, file
->size
);
269 grub_file_close(file
);
271 g_vhdboot_enable
= 1;
276 grub_err_t
ventoy_cmd_get_vtoy_type(grub_extcmd_context_t ctxt
, int argc
, char **args
)
281 vhd_footer_t vhdfoot
;
285 ventoy_gpt_info
*gpt
;
294 file
= grub_file_open(args
[0], VENTOY_FILE_TYPE
);
297 debug("Failed to open file %s\n", args
[0]);
301 grub_snprintf(type
, sizeof(type
), "unknown");
303 grub_file_seek(file
, file
->size
- 512);
304 grub_file_read(file
, &vhdfoot
, sizeof(vhdfoot
));
306 if (grub_strncmp(vhdfoot
.cookie
, "conectix", 8) == 0)
309 grub_snprintf(type
, sizeof(type
), "vhd%u", grub_swap_bytes32(vhdfoot
.disktype
));
313 grub_file_seek(file
, 0);
314 grub_file_read(file
, &vdihdr
, sizeof(vdihdr
));
315 if (vdihdr
.u32Signature
== VDI_IMAGE_SIGNATURE
&&
316 grub_strncmp(vdihdr
.szFileInfo
, VDI_IMAGE_FILE_INFO
, grub_strlen(VDI_IMAGE_FILE_INFO
)) == 0)
318 offset
= 2 * 1048576;
319 grub_snprintf(type
, sizeof(type
), "vdi");
324 grub_snprintf(type
, sizeof(type
), "raw");
328 grub_env_set(args
[1], type
);
329 debug("<%s> vtoy type: <%s> ", args
[0], type
);
333 grub_file_seek(file
, offset
);
334 grub_file_read(file
, &mbr
, sizeof(mbr
));
336 if (mbr
.Byte55
!= 0x55 || mbr
.ByteAA
!= 0xAA)
338 grub_env_set(args
[1], "unknown");
339 debug("invalid mbr signature: 0x%x 0x%x\n", mbr
.Byte55
, mbr
.ByteAA
);
343 if (mbr
.PartTbl
[0].FsFlag
== 0xEE)
345 grub_env_set(args
[2], "gpt");
346 debug("part type: %s\n", "GPT");
348 gpt
= grub_zalloc(sizeof(ventoy_gpt_info
));
351 grub_file_seek(file
, offset
);
352 grub_file_read(file
, gpt
, sizeof(ventoy_gpt_info
));
354 for (i
= 0; i
< 128; i
++)
356 if (grub_memcmp(gpt
->PartTbl
[i
].PartType
, "Hah!IdontNeedEFI", 16) == 0)
358 debug("part %d is grub_bios part\n", i
);
359 grub_env_set(args
[3], "1");
362 else if (gpt
->PartTbl
[i
].LastLBA
== 0)
373 grub_env_set(args
[2], "mbr");
374 debug("part type: %s\n", "MBR");
379 debug("part type: %s\n", "xxx");
383 grub_file_close(file
);
384 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
387 grub_err_t
ventoy_cmd_raw_chain_data(grub_extcmd_context_t ctxt
, int argc
, char **args
)
389 grub_uint32_t size
= 0;
390 grub_uint32_t img_chunk_size
= 0;
393 const char *pLastChain
= NULL
;
394 ventoy_chain_head
*chain
;
400 if (NULL
== g_img_chunk_list
.chunk
)
402 grub_printf("ventoy not ready\n");
406 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "%s", args
[0]);
412 img_chunk_size
= g_img_chunk_list
.cur_chunk
* sizeof(ventoy_img_chunk
);
414 size
= sizeof(ventoy_chain_head
) + img_chunk_size
;
416 pLastChain
= grub_env_get("vtoy_chain_mem_addr");
419 chain
= (ventoy_chain_head
*)grub_strtoul(pLastChain
, NULL
, 16);
422 debug("free last chain memory %p\n", chain
);
427 chain
= grub_malloc(size
);
430 grub_printf("Failed to alloc chain memory size %u\n", size
);
431 grub_file_close(file
);
435 grub_snprintf(envbuf
, sizeof(envbuf
), "0x%lx", (unsigned long)chain
);
436 grub_env_set("vtoy_chain_mem_addr", envbuf
);
437 grub_snprintf(envbuf
, sizeof(envbuf
), "%u", size
);
438 grub_env_set("vtoy_chain_mem_size", envbuf
);
440 grub_env_export("vtoy_chain_mem_addr");
441 grub_env_export("vtoy_chain_mem_size");
443 grub_memset(chain
, 0, sizeof(ventoy_chain_head
));
445 /* part 1: os parameter */
446 g_ventoy_chain_type
= ventoy_chain_linux
;
447 ventoy_fill_os_param(file
, &(chain
->os_param
));
449 /* part 2: chain head */
450 disk
= file
->device
->disk
;
451 chain
->disk_drive
= disk
->id
;
452 chain
->disk_sector_size
= (1 << disk
->log_sector_size
);
453 chain
->real_img_size_in_bytes
= file
->size
;
454 chain
->virt_img_size_in_bytes
= (file
->size
+ 2047) / 2048 * 2048;
455 chain
->boot_catalog
= 0;
457 /* part 3: image chunk */
458 chain
->img_chunk_offset
= sizeof(ventoy_chain_head
);
459 chain
->img_chunk_num
= g_img_chunk_list
.cur_chunk
;
460 grub_memcpy((char *)chain
+ chain
->img_chunk_offset
, g_img_chunk_list
.chunk
, img_chunk_size
);
462 grub_file_seek(file
, 0);
463 grub_file_read(file
, chain
->boot_catalog_sector
, 512);
465 grub_file_close(file
);
467 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);