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/>.
20 #include <grub/types.h>
21 #include <grub/misc.h>
25 #include <grub/disk.h>
26 #include <grub/device.h>
27 #include <grub/term.h>
28 #include <grub/partition.h>
29 #include <grub/file.h>
30 #include <grub/normal.h>
31 #include <grub/extcmd.h>
32 #include <grub/datetime.h>
33 #include <grub/i18n.h>
35 #include <grub/time.h>
36 #include <grub/ventoy.h>
37 #include "ventoy_def.h"
39 GRUB_MOD_LICENSE ("GPLv3+");
41 char g_ko_mod_path
[256];
42 int g_conf_new_len
= 0;
43 char *g_conf_new_data
= NULL
;
45 int g_mod_new_len
= 0;
46 char *g_mod_new_data
= NULL
;
48 grub_uint64_t g_mod_override_offset
= 0;
49 grub_uint64_t g_conf_override_offset
= 0;
51 static int ventoy_get_file_override(const char *filename
, grub_uint64_t
*offset
)
57 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "(loop)%s", filename
);
63 *offset
= grub_iso9660_get_last_file_dirent_pos(file
) + 2;
65 grub_file_close(file
);
70 static grub_uint32_t
ventoy_unix_get_override_chunk_count(void)
72 grub_uint32_t count
= 0;
74 if (g_conf_new_len
> 0)
79 if (g_mod_new_len
> 0)
87 static grub_uint32_t
ventoy_unix_get_virt_chunk_count(void)
89 grub_uint32_t count
= 0;
91 if (g_conf_new_len
> 0)
96 if (g_mod_new_len
> 0)
103 static grub_uint32_t
ventoy_unix_get_virt_chunk_size(void)
107 size
= sizeof(ventoy_virt_chunk
) * ventoy_unix_get_virt_chunk_count();
109 if (g_conf_new_len
> 0)
111 size
+= ventoy_align_2k(g_conf_new_len
);
114 if (g_mod_new_len
> 0)
116 size
+= ventoy_align_2k(g_mod_new_len
);
122 static void ventoy_unix_fill_override_data( grub_uint64_t isosize
, void *override
)
124 grub_uint64_t sector
;
125 ventoy_override_chunk
*cur
;
126 ventoy_iso9660_override
*dirent
;
128 sector
= (isosize
+ 2047) / 2048;
130 cur
= (ventoy_override_chunk
*)override
;
132 if (g_conf_new_len
> 0)
135 cur
->img_offset
= g_conf_override_offset
;
136 cur
->override_size
= sizeof(ventoy_iso9660_override
);
137 dirent
= (ventoy_iso9660_override
*)cur
->override_data
;
138 dirent
->first_sector
= (grub_uint32_t
)sector
;
139 dirent
->size
= (grub_uint32_t
)g_conf_new_len
;
140 dirent
->first_sector_be
= grub_swap_bytes32(dirent
->first_sector
);
141 dirent
->size_be
= grub_swap_bytes32(dirent
->size
);
142 sector
+= (dirent
->size
+ 2047) / 2048;
145 if (g_mod_new_len
> 0)
149 cur
->img_offset
= g_mod_override_offset
;
150 cur
->override_size
= sizeof(ventoy_iso9660_override
);
151 dirent
= (ventoy_iso9660_override
*)cur
->override_data
;
152 dirent
->first_sector
= (grub_uint32_t
)sector
;
153 dirent
->size
= (grub_uint32_t
)g_mod_new_len
;
154 dirent
->first_sector_be
= grub_swap_bytes32(dirent
->first_sector
);
155 dirent
->size_be
= grub_swap_bytes32(dirent
->size
);
156 sector
+= (dirent
->size
+ 2047) / 2048;
162 static void ventoy_unix_fill_virt_data( grub_uint64_t isosize
, ventoy_chain_head
*chain
)
164 grub_uint64_t sector
;
165 grub_uint32_t offset
;
166 grub_uint32_t data_secs
;
168 ventoy_virt_chunk
*cur
;
170 override
= (char *)chain
+ chain
->virt_chunk_offset
;
171 cur
= (ventoy_virt_chunk
*)override
;
173 sector
= (isosize
+ 2047) / 2048;
174 offset
= 2 * sizeof(ventoy_virt_chunk
);
176 if (g_conf_new_len
> 0)
178 ventoy_unix_fill_virt(g_conf_new_data
, g_conf_new_len
);
181 if (g_mod_new_len
> 0)
183 ventoy_unix_fill_virt(g_mod_new_data
, g_mod_new_len
);
189 static int ventoy_freebsd_append_conf(char *buf
, const char *isopath
)
196 ventoy_img_chunk
*chunk
;
197 grub_uint8_t disk_sig
[4];
198 grub_uint8_t disk_guid
[16];
200 debug("ventoy_freebsd_append_conf %s\n", isopath
);
202 isofile
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "%s", isopath
);
208 vtoy_ssprintf(buf
, pos
, "ventoy_load=\"%s\"\n", "YES");
209 vtoy_ssprintf(buf
, pos
, "ventoy_name=\"%s\"\n", g_ko_mod_path
);
211 disk
= isofile
->device
->disk
;
213 ventoy_get_disk_guid(isofile
->name
, disk_guid
, disk_sig
);
215 for (i
= 0; i
< 16; i
++)
217 grub_snprintf(uuid
+ i
* 2, sizeof(uuid
), "%02x", disk_guid
[i
]);
220 vtoy_ssprintf(buf
, pos
, "hint.ventoy.0.disksize=%llu\n", (ulonglong
)(disk
->total_sectors
* (1 << disk
->log_sector_size
)));
221 vtoy_ssprintf(buf
, pos
, "hint.ventoy.0.diskuuid=\"%s\"\n", uuid
);
222 vtoy_ssprintf(buf
, pos
, "hint.ventoy.0.disksignature=%02x%02x%02x%02x\n", disk_sig
[0], disk_sig
[1], disk_sig
[2], disk_sig
[3]);
223 vtoy_ssprintf(buf
, pos
, "hint.ventoy.0.segnum=%u\n", g_img_chunk_list
.cur_chunk
);
225 for (i
= 0; i
< g_img_chunk_list
.cur_chunk
; i
++)
227 chunk
= g_img_chunk_list
.chunk
+ i
;
228 vtoy_ssprintf(buf
, pos
, "hint.ventoy.%u.seg=\"0x%llx@0x%llx\"\n",
229 i
, (ulonglong
)(chunk
->disk_start_sector
* 512),
230 (ulonglong
)((chunk
->disk_end_sector
+ 1) * 512));
233 grub_file_close(isofile
);
238 static int ventoy_dragonfly_append_conf(char *buf
, const char *isopath
)
242 debug("ventoy_dragonfly_append_conf %s\n", isopath
);
244 vtoy_ssprintf(buf
, pos
, "tmpfs_load=\"%s\"\n", "YES");
245 vtoy_ssprintf(buf
, pos
, "dm_target_linear_load=\"%s\"\n", "YES");
246 vtoy_ssprintf(buf
, pos
, "initrd.img_load=\"%s\"\n", "YES");
247 vtoy_ssprintf(buf
, pos
, "initrd.img_type=\"%s\"\n", "md_image");
248 vtoy_ssprintf(buf
, pos
, "vfs.root.mountfrom=\"%s\"\n", "ufs:md0s0");
253 grub_err_t
ventoy_cmd_unix_reset(grub_extcmd_context_t ctxt
, int argc
, char **args
)
261 g_mod_override_offset
= 0;
262 g_conf_override_offset
= 0;
264 check_free(g_mod_new_data
, grub_free
);
265 check_free(g_conf_new_data
, grub_free
);
267 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
270 grub_err_t
ventoy_cmd_parse_freenas_ver(grub_extcmd_context_t ctxt
, int argc
, char **args
)
273 const char *ver
= NULL
;
275 VTOY_JSON
*json
= NULL
;
280 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "%s", args
[0]);
283 debug("Failed to open file %s\n", args
[0]);
287 buf
= grub_malloc(file
->size
+ 2);
290 grub_file_close(file
);
293 grub_file_read(file
, buf
, file
->size
);
296 json
= vtoy_json_create();
302 if (vtoy_json_parse(json
, buf
))
307 ver
= vtoy_json_get_string_ex(json
->pstChild
, "Version");
310 debug("freenas version:<%s>\n", ver
);
311 ventoy_set_env(args
[1], ver
);
315 debug("freenas version:<%s>\n", "NOT FOUND");
316 grub_env_unset(args
[1]);
320 grub_check_free(buf
);
321 check_free(json
, vtoy_json_destroy
);
322 grub_file_close(file
);
324 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
327 grub_err_t
ventoy_cmd_unix_freebsd_ver(grub_extcmd_context_t ctxt
, int argc
, char **args
)
332 char *nextline
= NULL
;
338 file
= ventoy_grub_file_open(GRUB_FILE_TYPE_LINUX_INITRD
, "%s", args
[0]);
341 debug("Failed to open file %s\n", args
[0]);
345 buf
= grub_zalloc(file
->size
+ 2);
348 grub_file_close(file
);
351 grub_file_read(file
, buf
, file
->size
);
353 for (start
= buf
; start
; start
= nextline
)
355 if (grub_strncmp(start
, "USERLAND_VERSION", 16) == 0)
358 while (*nextline
&& *nextline
!= '\r' && *nextline
!= '\n')
366 nextline
= ventoy_get_line(start
);
371 debug("freebsd version:<%s>\n", start
);
372 ventoy_set_env(args
[1], start
);
376 debug("freebsd version:<%s>\n", "NOT FOUND");
377 grub_env_unset(args
[1]);
381 grub_file_close(file
);
383 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
386 grub_err_t
ventoy_cmd_unix_replace_conf(grub_extcmd_context_t ctxt
, int argc
, char **args
)
390 grub_uint64_t offset
;
392 const char *confile
= NULL
;
393 const char * loader_conf
[] =
396 "/boot/defaults/loader.conf",
403 debug("Replace conf invalid argc %d\n", argc
);
407 for (i
= 0; i
< sizeof(loader_conf
) / sizeof(loader_conf
[0]); i
++)
409 if (ventoy_get_file_override(loader_conf
[i
], &offset
) == 0)
411 confile
= loader_conf
[i
];
412 g_conf_override_offset
= offset
;
419 debug("Can't find loader.conf file from %u locations\n", i
);
423 file
= ventoy_grub_file_open(GRUB_FILE_TYPE_LINUX_INITRD
, "(loop)/%s", confile
);
426 debug("Failed to open %s \n", confile
);
430 debug("old conf file size:%d\n", (int)file
->size
);
432 data
= grub_malloc(VTOY_MAX_SCRIPT_BUF
);
435 grub_file_close(file
);
439 grub_file_read(file
, data
, file
->size
);
440 grub_file_close(file
);
442 g_conf_new_data
= data
;
443 g_conf_new_len
= (int)file
->size
;
445 if (grub_strcmp(args
[0], "FreeBSD") == 0)
447 g_conf_new_len
+= ventoy_freebsd_append_conf(data
+ file
->size
, args
[1]);
449 else if (grub_strcmp(args
[0], "DragonFly") == 0)
451 g_conf_new_len
+= ventoy_dragonfly_append_conf(data
+ file
->size
, args
[1]);
454 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
457 grub_err_t
ventoy_cmd_unix_replace_ko(grub_extcmd_context_t ctxt
, int argc
, char **args
)
460 grub_uint64_t offset
;
467 debug("Replace ko invalid argc %d\n", argc
);
471 debug("replace ko %s\n", args
[0]);
473 if (ventoy_get_file_override(args
[0], &offset
) == 0)
475 grub_snprintf(g_ko_mod_path
, sizeof(g_ko_mod_path
), "%s", args
[0]);
476 g_mod_override_offset
= offset
;
480 debug("Can't find replace ko file from %s\n", args
[0]);
484 file
= ventoy_grub_file_open(GRUB_FILE_TYPE_LINUX_INITRD
, "%s", args
[1]);
487 debug("Failed to open %s \n", args
[1]);
491 debug("new ko file size:%d\n", (int)file
->size
);
493 data
= grub_malloc(file
->size
);
496 debug("Failed to alloc memory for new ko %d\n", (int)file
->size
);
497 grub_file_close(file
);
501 grub_file_read(file
, data
, file
->size
);
502 grub_file_close(file
);
504 g_mod_new_data
= data
;
505 g_mod_new_len
= (int)file
->size
;
507 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
510 grub_err_t
ventoy_cmd_unix_fill_image_desc(grub_extcmd_context_t ctxt
, int argc
, char **args
)
514 grub_uint32_t memsize
;
515 ventoy_image_desc
*desc
;
516 grub_uint8_t flag
[32] = {
517 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00,
518 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
525 debug("ventoy_cmd_unix_fill_image_desc %p\n", g_mod_new_data
);
532 byte
= (grub_uint8_t
*)g_mod_new_data
;
533 for (i
= 0; i
< g_mod_new_len
- 32; i
+= 16)
535 if (byte
[i
] == 0xFF && byte
[i
+ 1] == 0xEE)
537 if (grub_memcmp(flag
, byte
+ i
, 32) == 0)
539 debug("Find position flag at %d(0x%x)\n", i
, i
);
545 if (i
>= g_mod_new_len
- 32)
547 debug("Failed to find position flag %d\n", i
);
551 desc
= (ventoy_image_desc
*)(byte
+ i
);
552 desc
->disk_size
= g_ventoy_disk_size
;
553 desc
->part1_size
= ventoy_get_part1_size(g_ventoy_part_info
);
554 grub_memcpy(desc
->disk_uuid
, g_ventoy_part_info
->MBR
.BootCode
+ 0x180, 16);
555 grub_memcpy(desc
->disk_signature
, g_ventoy_part_info
->MBR
.BootCode
+ 0x1B8, 4);
557 desc
->img_chunk_count
= g_img_chunk_list
.cur_chunk
;
558 memsize
= g_img_chunk_list
.cur_chunk
* sizeof(ventoy_img_chunk
);
560 debug("image chunk count:%u memsize:%u\n", desc
->img_chunk_count
, memsize
);
562 if (memsize
>= VTOY_SIZE_1MB
* 8)
564 grub_printf("image chunk count:%u memsize:%u too big\n", desc
->img_chunk_count
, memsize
);
568 grub_memcpy(desc
+ 1, g_img_chunk_list
.chunk
, memsize
);
571 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
574 grub_err_t
ventoy_cmd_unix_gzip_newko(grub_extcmd_context_t ctxt
, int argc
, char **args
)
583 debug("ventoy_cmd_unix_gzip_newko %p\n", g_mod_new_data
);
590 buf
= grub_malloc(g_mod_new_len
);
596 newlen
= ventoy_gzip_compress(g_mod_new_data
, g_mod_new_len
, buf
, g_mod_new_len
);
598 grub_free(g_mod_new_data
);
600 debug("gzip org len:%d newlen:%d\n", g_mod_new_len
, newlen
);
602 g_mod_new_data
= (char *)buf
;
603 g_mod_new_len
= newlen
;
606 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
609 grub_err_t
ventoy_cmd_unix_chain_data(grub_extcmd_context_t ctxt
, int argc
, char **args
)
611 int ventoy_compatible
= 0;
612 grub_uint32_t size
= 0;
613 grub_uint64_t isosize
= 0;
614 grub_uint32_t boot_catlog
= 0;
615 grub_uint32_t img_chunk_size
= 0;
616 grub_uint32_t override_count
= 0;
617 grub_uint32_t override_size
= 0;
618 grub_uint32_t virt_chunk_size
= 0;
621 const char *pLastChain
= NULL
;
622 const char *compatible
;
623 ventoy_chain_head
*chain
;
629 compatible
= grub_env_get("ventoy_compatible");
630 if (compatible
&& compatible
[0] == 'Y')
632 ventoy_compatible
= 1;
635 if (NULL
== g_img_chunk_list
.chunk
)
637 grub_printf("ventoy not ready\n");
641 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "%s", args
[0]);
647 isosize
= file
->size
;
649 boot_catlog
= ventoy_get_iso_boot_catlog(file
);
652 if (ventoy_is_efi_os() && (!ventoy_has_efi_eltorito(file
, boot_catlog
)))
654 grub_env_set("LoadIsoEfiDriver", "on");
659 if (ventoy_is_efi_os())
661 grub_env_set("LoadIsoEfiDriver", "on");
665 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "File %s is not bootable", args
[0]);
669 img_chunk_size
= g_img_chunk_list
.cur_chunk
* sizeof(ventoy_img_chunk
);
671 if (ventoy_compatible
)
673 size
= sizeof(ventoy_chain_head
) + img_chunk_size
;
677 override_count
= ventoy_unix_get_override_chunk_count();
678 override_size
= override_count
* sizeof(ventoy_override_chunk
);
680 virt_chunk_size
= ventoy_unix_get_virt_chunk_size();
681 size
= sizeof(ventoy_chain_head
) + img_chunk_size
+ override_size
+ virt_chunk_size
;
684 pLastChain
= grub_env_get("vtoy_chain_mem_addr");
687 chain
= (ventoy_chain_head
*)grub_strtoul(pLastChain
, NULL
, 16);
690 debug("free last chain memory %p\n", chain
);
695 chain
= grub_malloc(size
);
698 grub_printf("Failed to alloc chain memory size %u\n", size
);
699 grub_file_close(file
);
703 grub_snprintf(envbuf
, sizeof(envbuf
), "0x%lx", (unsigned long)chain
);
704 grub_env_set("vtoy_chain_mem_addr", envbuf
);
705 grub_snprintf(envbuf
, sizeof(envbuf
), "%u", size
);
706 grub_env_set("vtoy_chain_mem_size", envbuf
);
708 grub_memset(chain
, 0, sizeof(ventoy_chain_head
));
710 /* part 1: os parameter */
711 g_ventoy_chain_type
= ventoy_chain_linux
;
712 ventoy_fill_os_param(file
, &(chain
->os_param
));
714 /* part 2: chain head */
715 disk
= file
->device
->disk
;
716 chain
->disk_drive
= disk
->id
;
717 chain
->disk_sector_size
= (1 << disk
->log_sector_size
);
718 chain
->real_img_size_in_bytes
= file
->size
;
719 chain
->virt_img_size_in_bytes
= (file
->size
+ 2047) / 2048 * 2048;
720 chain
->boot_catalog
= boot_catlog
;
722 if (!ventoy_is_efi_os())
724 grub_file_seek(file
, boot_catlog
* 2048);
725 grub_file_read(file
, chain
->boot_catalog_sector
, sizeof(chain
->boot_catalog_sector
));
728 /* part 3: image chunk */
729 chain
->img_chunk_offset
= sizeof(ventoy_chain_head
);
730 chain
->img_chunk_num
= g_img_chunk_list
.cur_chunk
;
731 grub_memcpy((char *)chain
+ chain
->img_chunk_offset
, g_img_chunk_list
.chunk
, img_chunk_size
);
733 if (ventoy_compatible
)
738 /* part 4: override chunk */
739 chain
->override_chunk_offset
= chain
->img_chunk_offset
+ img_chunk_size
;
740 chain
->override_chunk_num
= override_count
;
741 ventoy_unix_fill_override_data(isosize
, (char *)chain
+ chain
->override_chunk_offset
);
743 /* part 5: virt chunk */
744 chain
->virt_chunk_offset
= chain
->override_chunk_offset
+ override_size
;
745 chain
->virt_chunk_num
= ventoy_unix_get_virt_chunk_count();
746 ventoy_unix_fill_virt_data(isosize
, chain
);
748 grub_file_close(file
);
750 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);