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(char *isopart
, ventoy_patch_vhd
*patch1
, ventoy_patch_vhd
*patch2
)
148 ventoy_gpt_info
*gptinfo
;
149 char efipart
[16] = {0};
151 pos
= grub_strchr(isopart
, ',');
163 debug("vhd disk <%s>\n", pos
);
165 disk
= grub_disk_open(pos
);
168 debug("Failed to open disk %s\n", pos
);
172 gptinfo
= (ventoy_gpt_info
*)(g_vhdboot_isobuf
+ g_vhdboot_isolen
);
173 grub_disk_read(disk
, 0, 0, sizeof(ventoy_gpt_info
), gptinfo
);
175 grub_memcpy(efipart
, gptinfo
->Head
.Signature
, sizeof(gptinfo
->Head
.Signature
));
177 debug("part1 type: 0x%x <%s>\n", gptinfo
->MBR
.PartTbl
[0].FsFlag
, efipart
);
179 if (grub_strncmp(efipart
, "EFI PART", 8) == 0)
181 ventoy_debug_dump_guid("GPT disk GUID: ", gptinfo
->Head
.DiskGuid
);
182 ventoy_debug_dump_guid("GPT part GUID: ", gptinfo
->PartTbl
[0].PartGuid
);
184 grub_memcpy(patch1
->disk_signature_or_guid
, gptinfo
->Head
.DiskGuid
, 16);
185 grub_memcpy(patch1
->part_offset_or_guid
, gptinfo
->PartTbl
[0].PartGuid
, 16);
186 grub_memcpy(patch2
->disk_signature_or_guid
, gptinfo
->Head
.DiskGuid
, 16);
187 grub_memcpy(patch2
->part_offset_or_guid
, gptinfo
->PartTbl
[0].PartGuid
, 16);
189 patch1
->part_type
= patch2
->part_type
= 0;
193 debug("MBR disk signature: %02x%02x%02x%02x\n",
194 gptinfo
->MBR
.BootCode
[0x1b8 + 0], gptinfo
->MBR
.BootCode
[0x1b8 + 1],
195 gptinfo
->MBR
.BootCode
[0x1b8 + 2], gptinfo
->MBR
.BootCode
[0x1b8 + 3]);
196 grub_memcpy(patch1
->disk_signature_or_guid
, gptinfo
->MBR
.BootCode
+ 0x1b8, 4);
197 grub_memcpy(patch2
->disk_signature_or_guid
, gptinfo
->MBR
.BootCode
+ 0x1b8, 4);
200 grub_disk_close(disk
);
205 grub_err_t
ventoy_cmd_patch_vhdboot(grub_extcmd_context_t ctxt
, int argc
, char **args
)
208 ventoy_patch_vhd
*patch1
;
209 ventoy_patch_vhd
*patch2
;
215 grub_env_unset("vtoy_vhd_buf_addr");
217 debug("patch vhd <%s> <%s>\n", args
[0], args
[1]);
219 if ((!g_vhdboot_enable
) || (!g_vhdboot_totbuf
))
221 debug("vhd boot not ready %d %p\n", g_vhdboot_enable
, g_vhdboot_totbuf
);
225 rc
= ventoy_vhd_find_bcd(&g_vhdboot_bcd_offset
, &g_vhdboot_bcd_len
);
228 debug("failed to get bcd location %d\n", rc
);
232 patch1
= (ventoy_patch_vhd
*)(g_vhdboot_isobuf
+ g_vhdboot_bcd_offset
+ 0x495a);
233 patch2
= (ventoy_patch_vhd
*)(g_vhdboot_isobuf
+ g_vhdboot_bcd_offset
+ 0x50aa);
235 ventoy_vhd_patch_disk(args
[0], patch1
, patch2
);
236 ventoy_vhd_patch_path(args
[1], patch1
, patch2
);
238 /* set buffer and size */
239 #ifdef GRUB_MACHINE_EFI
240 grub_snprintf(envbuf
, sizeof(envbuf
), "0x%lx", (ulong
)g_vhdboot_totbuf
);
241 grub_env_set("vtoy_vhd_buf_addr", envbuf
);
242 grub_snprintf(envbuf
, sizeof(envbuf
), "%d", (int)(g_vhdboot_isolen
+ sizeof(ventoy_chain_head
)));
243 grub_env_set("vtoy_vhd_buf_size", envbuf
);
245 grub_snprintf(envbuf
, sizeof(envbuf
), "0x%lx", (ulong
)g_vhdboot_isobuf
);
246 grub_env_set("vtoy_vhd_buf_addr", envbuf
);
247 grub_snprintf(envbuf
, sizeof(envbuf
), "%d", g_vhdboot_isolen
);
248 grub_env_set("vtoy_vhd_buf_size", envbuf
);
254 grub_err_t
ventoy_cmd_load_vhdboot(grub_extcmd_context_t ctxt
, int argc
, char **args
)
262 g_vhdboot_enable
= 0;
263 grub_check_free(g_vhdboot_totbuf
);
265 file
= grub_file_open(args
[0], VENTOY_FILE_TYPE
);
271 debug("load vhd boot: <%s> <%lu>\n", args
[0], (ulong
)file
->size
);
273 if (file
->size
< VTOY_SIZE_1KB
* 32)
275 grub_file_close(file
);
279 g_vhdboot_isolen
= (int)file
->size
;
281 buflen
= (int)(file
->size
+ sizeof(ventoy_gpt_info
) + sizeof(ventoy_chain_head
));
283 #ifdef GRUB_MACHINE_EFI
284 g_vhdboot_totbuf
= (char *)grub_efi_allocate_iso_buf(buflen
);
286 g_vhdboot_totbuf
= (char *)grub_malloc(buflen
);
289 if (!g_vhdboot_totbuf
)
291 grub_file_close(file
);
295 g_vhdboot_isobuf
= g_vhdboot_totbuf
+ sizeof(ventoy_chain_head
);
297 grub_file_read(file
, g_vhdboot_isobuf
, file
->size
);
298 grub_file_close(file
);
300 g_vhdboot_enable
= 1;