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 grub_err_t
ventoy_cmd_unix_reset(grub_extcmd_context_t ctxt
, int argc
, char **args
)
246 g_mod_override_offset
= 0;
247 g_conf_override_offset
= 0;
249 check_free(g_mod_new_data
, grub_free
);
250 check_free(g_conf_new_data
, grub_free
);
252 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
255 grub_err_t
ventoy_cmd_parse_freenas_ver(grub_extcmd_context_t ctxt
, int argc
, char **args
)
258 const char *ver
= NULL
;
260 VTOY_JSON
*json
= NULL
;
265 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "%s", args
[0]);
268 debug("Failed to open file %s\n", args
[0]);
272 buf
= grub_malloc(file
->size
+ 2);
275 grub_file_close(file
);
278 grub_file_read(file
, buf
, file
->size
);
281 json
= vtoy_json_create();
287 if (vtoy_json_parse(json
, buf
))
292 ver
= vtoy_json_get_string_ex(json
->pstChild
, "Version");
295 debug("freenas version:<%s>\n", ver
);
296 ventoy_set_env(args
[1], ver
);
300 debug("freenas version:<%s>\n", "NOT FOUND");
301 grub_env_unset(args
[1]);
305 grub_check_free(buf
);
306 check_free(json
, vtoy_json_destroy
);
307 grub_file_close(file
);
309 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
312 grub_err_t
ventoy_cmd_unix_freebsd_ver(grub_extcmd_context_t ctxt
, int argc
, char **args
)
317 char *nextline
= NULL
;
323 file
= ventoy_grub_file_open(GRUB_FILE_TYPE_LINUX_INITRD
, "%s", args
[0]);
326 debug("Failed to open file %s\n", args
[0]);
330 buf
= grub_zalloc(file
->size
+ 2);
333 grub_file_close(file
);
336 grub_file_read(file
, buf
, file
->size
);
338 for (start
= buf
; start
; start
= nextline
)
340 if (grub_strncmp(start
, "USERLAND_VERSION", 16) == 0)
343 while (*nextline
&& *nextline
!= '\r' && *nextline
!= '\n')
351 nextline
= ventoy_get_line(start
);
356 debug("freebsd version:<%s>\n", start
);
357 ventoy_set_env(args
[1], start
);
361 debug("freebsd version:<%s>\n", "NOT FOUND");
362 grub_env_unset(args
[1]);
366 grub_file_close(file
);
368 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
371 grub_err_t
ventoy_cmd_unix_replace_conf(grub_extcmd_context_t ctxt
, int argc
, char **args
)
375 grub_uint64_t offset
;
377 const char *confile
= NULL
;
378 const char * loader_conf
[] =
381 "/boot/defaults/loader.conf",
388 debug("Replace conf invalid argc %d\n", argc
);
392 for (i
= 0; i
< sizeof(loader_conf
) / sizeof(loader_conf
[0]); i
++)
394 if (ventoy_get_file_override(loader_conf
[i
], &offset
) == 0)
396 confile
= loader_conf
[i
];
397 g_conf_override_offset
= offset
;
404 debug("Can't find loader.conf file from %u locations\n", i
);
408 file
= ventoy_grub_file_open(GRUB_FILE_TYPE_LINUX_INITRD
, "(loop)/%s", confile
);
411 debug("Failed to open %s \n", confile
);
415 debug("old conf file size:%d\n", (int)file
->size
);
417 data
= grub_malloc(VTOY_MAX_SCRIPT_BUF
);
420 grub_file_close(file
);
424 grub_file_read(file
, data
, file
->size
);
425 grub_file_close(file
);
427 g_conf_new_data
= data
;
428 g_conf_new_len
= (int)file
->size
;
430 if (grub_strcmp(args
[0], "FreeBSD") == 0)
432 g_conf_new_len
+= ventoy_freebsd_append_conf(data
+ file
->size
, args
[1]);
435 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
438 grub_err_t
ventoy_cmd_unix_replace_ko(grub_extcmd_context_t ctxt
, int argc
, char **args
)
441 grub_uint64_t offset
;
448 debug("Replace ko invalid argc %d\n", argc
);
452 debug("replace ko %s\n", args
[0]);
454 if (ventoy_get_file_override(args
[0], &offset
) == 0)
456 grub_snprintf(g_ko_mod_path
, sizeof(g_ko_mod_path
), "%s", args
[0]);
457 g_mod_override_offset
= offset
;
461 debug("Can't find replace ko file from %s\n", args
[0]);
465 file
= ventoy_grub_file_open(GRUB_FILE_TYPE_LINUX_INITRD
, "%s", args
[1]);
468 debug("Failed to open %s \n", args
[1]);
472 debug("new ko file size:%d\n", (int)file
->size
);
474 data
= grub_malloc(file
->size
);
477 grub_file_close(file
);
481 grub_file_read(file
, data
, file
->size
);
482 grub_file_close(file
);
484 g_mod_new_data
= data
;
485 g_mod_new_len
= (int)file
->size
;
487 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
490 grub_err_t
ventoy_cmd_unix_chain_data(grub_extcmd_context_t ctxt
, int argc
, char **args
)
492 int ventoy_compatible
= 0;
493 grub_uint32_t size
= 0;
494 grub_uint64_t isosize
= 0;
495 grub_uint32_t boot_catlog
= 0;
496 grub_uint32_t img_chunk_size
= 0;
497 grub_uint32_t override_count
= 0;
498 grub_uint32_t override_size
= 0;
499 grub_uint32_t virt_chunk_size
= 0;
502 const char *pLastChain
= NULL
;
503 const char *compatible
;
504 ventoy_chain_head
*chain
;
510 compatible
= grub_env_get("ventoy_compatible");
511 if (compatible
&& compatible
[0] == 'Y')
513 ventoy_compatible
= 1;
516 if (NULL
== g_img_chunk_list
.chunk
)
518 grub_printf("ventoy not ready\n");
522 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "%s", args
[0]);
528 isosize
= file
->size
;
530 boot_catlog
= ventoy_get_iso_boot_catlog(file
);
533 if (ventoy_is_efi_os() && (!ventoy_has_efi_eltorito(file
, boot_catlog
)))
535 grub_env_set("LoadIsoEfiDriver", "on");
540 if (ventoy_is_efi_os())
542 grub_env_set("LoadIsoEfiDriver", "on");
546 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "File %s is not bootable", args
[0]);
550 img_chunk_size
= g_img_chunk_list
.cur_chunk
* sizeof(ventoy_img_chunk
);
552 if (ventoy_compatible
)
554 size
= sizeof(ventoy_chain_head
) + img_chunk_size
;
558 override_count
= ventoy_unix_get_override_chunk_count();
559 override_size
= override_count
* sizeof(ventoy_override_chunk
);
561 virt_chunk_size
= ventoy_unix_get_virt_chunk_size();
562 size
= sizeof(ventoy_chain_head
) + img_chunk_size
+ override_size
+ virt_chunk_size
;
565 pLastChain
= grub_env_get("vtoy_chain_mem_addr");
568 chain
= (ventoy_chain_head
*)grub_strtoul(pLastChain
, NULL
, 16);
571 debug("free last chain memory %p\n", chain
);
576 chain
= grub_malloc(size
);
579 grub_printf("Failed to alloc chain memory size %u\n", size
);
580 grub_file_close(file
);
584 grub_snprintf(envbuf
, sizeof(envbuf
), "0x%lx", (unsigned long)chain
);
585 grub_env_set("vtoy_chain_mem_addr", envbuf
);
586 grub_snprintf(envbuf
, sizeof(envbuf
), "%u", size
);
587 grub_env_set("vtoy_chain_mem_size", envbuf
);
589 grub_memset(chain
, 0, sizeof(ventoy_chain_head
));
591 /* part 1: os parameter */
592 g_ventoy_chain_type
= ventoy_chain_linux
;
593 ventoy_fill_os_param(file
, &(chain
->os_param
));
595 /* part 2: chain head */
596 disk
= file
->device
->disk
;
597 chain
->disk_drive
= disk
->id
;
598 chain
->disk_sector_size
= (1 << disk
->log_sector_size
);
599 chain
->real_img_size_in_bytes
= file
->size
;
600 chain
->virt_img_size_in_bytes
= (file
->size
+ 2047) / 2048 * 2048;
601 chain
->boot_catalog
= boot_catlog
;
603 if (!ventoy_is_efi_os())
605 grub_file_seek(file
, boot_catlog
* 2048);
606 grub_file_read(file
, chain
->boot_catalog_sector
, sizeof(chain
->boot_catalog_sector
));
609 /* part 3: image chunk */
610 chain
->img_chunk_offset
= sizeof(ventoy_chain_head
);
611 chain
->img_chunk_num
= g_img_chunk_list
.cur_chunk
;
612 grub_memcpy((char *)chain
+ chain
->img_chunk_offset
, g_img_chunk_list
.chunk
, img_chunk_size
);
614 if (ventoy_compatible
)
619 /* part 4: override chunk */
620 chain
->override_chunk_offset
= chain
->img_chunk_offset
+ img_chunk_size
;
621 chain
->override_chunk_num
= override_count
;
622 ventoy_unix_fill_override_data(isosize
, (char *)chain
+ chain
->override_chunk_offset
);
624 /* part 5: virt chunk */
625 chain
->virt_chunk_offset
= chain
->override_chunk_offset
+ override_size
;
626 chain
->virt_chunk_num
= ventoy_unix_get_virt_chunk_count();
627 ventoy_unix_fill_virt_data(isosize
, chain
);
629 grub_file_close(file
);
631 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);