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_guid
[16];
199 debug("ventoy_freebsd_append_conf %s\n", isopath
);
201 isofile
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "%s", isopath
);
207 vtoy_ssprintf(buf
, pos
, "ventoy_load=\"%s\"\n", "YES");
208 vtoy_ssprintf(buf
, pos
, "ventoy_name=\"%s\"\n", g_ko_mod_path
);
210 disk
= isofile
->device
->disk
;
212 ventoy_get_disk_guid(isofile
->name
, disk_guid
);
214 for (i
= 0; i
< 16; i
++)
216 grub_snprintf(uuid
+ i
* 2, sizeof(uuid
), "%02x", disk_guid
[i
]);
219 vtoy_ssprintf(buf
, pos
, "hint.ventoy.0.disksize=%llu\n", (ulonglong
)(disk
->total_sectors
* (1 << disk
->log_sector_size
)));
220 vtoy_ssprintf(buf
, pos
, "hint.ventoy.0.diskuuid=\"%s\"\n", uuid
);
221 vtoy_ssprintf(buf
, pos
, "hint.ventoy.0.segnum=%u\n", g_img_chunk_list
.cur_chunk
);
223 for (i
= 0; i
< g_img_chunk_list
.cur_chunk
; i
++)
225 chunk
= g_img_chunk_list
.chunk
+ i
;
226 vtoy_ssprintf(buf
, pos
, "hint.ventoy.%u.seg=\"0x%llx@0x%llx\"\n",
227 i
, (ulonglong
)(chunk
->disk_start_sector
* 512),
228 (ulonglong
)((chunk
->disk_end_sector
+ 1) * 512));
231 grub_file_close(isofile
);
236 grub_err_t
ventoy_cmd_unix_reset(grub_extcmd_context_t ctxt
, int argc
, char **args
)
244 g_mod_override_offset
= 0;
245 g_conf_override_offset
= 0;
247 check_free(g_mod_new_data
, grub_free
);
248 check_free(g_conf_new_data
, grub_free
);
250 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
253 grub_err_t
ventoy_cmd_parse_freenas_ver(grub_extcmd_context_t ctxt
, int argc
, char **args
)
256 const char *ver
= NULL
;
258 VTOY_JSON
*json
= NULL
;
263 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "%s", args
[0]);
266 debug("Failed to open file %s\n", args
[0]);
270 buf
= grub_malloc(file
->size
+ 2);
273 grub_file_close(file
);
276 grub_file_read(file
, buf
, file
->size
);
279 json
= vtoy_json_create();
285 if (vtoy_json_parse(json
, buf
))
290 ver
= vtoy_json_get_string_ex(json
->pstChild
, "Version");
293 debug("freenas version:<%s>\n", ver
);
294 ventoy_set_env(args
[1], ver
);
298 debug("freenas version:<%s>\n", "NOT FOUND");
299 grub_env_unset(args
[1]);
303 grub_check_free(buf
);
304 check_free(json
, vtoy_json_destroy
);
305 grub_file_close(file
);
307 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
310 grub_err_t
ventoy_cmd_unix_freebsd_ver(grub_extcmd_context_t ctxt
, int argc
, char **args
)
315 char *nextline
= NULL
;
321 file
= ventoy_grub_file_open(GRUB_FILE_TYPE_LINUX_INITRD
, "%s", args
[0]);
324 debug("Failed to open file %s\n", args
[0]);
328 buf
= grub_zalloc(file
->size
+ 2);
331 grub_file_close(file
);
334 grub_file_read(file
, buf
, file
->size
);
336 for (start
= buf
; start
; start
= nextline
)
338 if (grub_strncmp(start
, "USERLAND_VERSION", 16) == 0)
341 while (*nextline
&& *nextline
!= '\r' && *nextline
!= '\n')
349 nextline
= ventoy_get_line(start
);
354 debug("freebsd version:<%s>\n", start
);
355 ventoy_set_env(args
[1], start
);
359 debug("freebsd version:<%s>\n", "NOT FOUND");
360 grub_env_unset(args
[1]);
364 grub_file_close(file
);
366 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
369 grub_err_t
ventoy_cmd_unix_replace_conf(grub_extcmd_context_t ctxt
, int argc
, char **args
)
373 grub_uint64_t offset
;
375 const char *confile
= NULL
;
376 const char * loader_conf
[] =
379 "/boot/defaults/loader.conf",
386 debug("Replace conf invalid argc %d\n", argc
);
390 for (i
= 0; i
< sizeof(loader_conf
) / sizeof(loader_conf
[0]); i
++)
392 if (ventoy_get_file_override(loader_conf
[i
], &offset
) == 0)
394 confile
= loader_conf
[i
];
395 g_conf_override_offset
= offset
;
402 debug("Can't find loader.conf file from %u locations\n", i
);
406 file
= ventoy_grub_file_open(GRUB_FILE_TYPE_LINUX_INITRD
, "(loop)/%s", confile
);
409 debug("Failed to open %s \n", confile
);
413 debug("old conf file size:%d\n", (int)file
->size
);
415 data
= grub_malloc(VTOY_MAX_SCRIPT_BUF
);
418 grub_file_close(file
);
422 grub_file_read(file
, data
, file
->size
);
423 grub_file_close(file
);
425 g_conf_new_data
= data
;
426 g_conf_new_len
= (int)file
->size
;
428 if (grub_strcmp(args
[0], "FreeBSD") == 0)
430 g_conf_new_len
+= ventoy_freebsd_append_conf(data
+ file
->size
, args
[1]);
433 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
436 grub_err_t
ventoy_cmd_unix_replace_ko(grub_extcmd_context_t ctxt
, int argc
, char **args
)
439 grub_uint64_t offset
;
446 debug("Replace ko invalid argc %d\n", argc
);
450 debug("replace ko %s\n", args
[0]);
452 if (ventoy_get_file_override(args
[0], &offset
) == 0)
454 grub_snprintf(g_ko_mod_path
, sizeof(g_ko_mod_path
), "%s", args
[0]);
455 g_mod_override_offset
= offset
;
459 debug("Can't find replace ko file from %s\n", args
[0]);
463 file
= ventoy_grub_file_open(GRUB_FILE_TYPE_LINUX_INITRD
, "%s", args
[1]);
466 debug("Failed to open %s \n", args
[1]);
470 debug("new ko file size:%d\n", (int)file
->size
);
472 data
= grub_malloc(file
->size
);
475 grub_file_close(file
);
479 grub_file_read(file
, data
, file
->size
);
480 grub_file_close(file
);
482 g_mod_new_data
= data
;
483 g_mod_new_len
= (int)file
->size
;
485 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);
488 grub_err_t
ventoy_cmd_unix_chain_data(grub_extcmd_context_t ctxt
, int argc
, char **args
)
490 int ventoy_compatible
= 0;
491 grub_uint32_t size
= 0;
492 grub_uint64_t isosize
= 0;
493 grub_uint32_t boot_catlog
= 0;
494 grub_uint32_t img_chunk_size
= 0;
495 grub_uint32_t override_count
= 0;
496 grub_uint32_t override_size
= 0;
497 grub_uint32_t virt_chunk_size
= 0;
500 const char *pLastChain
= NULL
;
501 const char *compatible
;
502 ventoy_chain_head
*chain
;
508 compatible
= grub_env_get("ventoy_compatible");
509 if (compatible
&& compatible
[0] == 'Y')
511 ventoy_compatible
= 1;
514 if (NULL
== g_img_chunk_list
.chunk
)
516 grub_printf("ventoy not ready\n");
520 file
= ventoy_grub_file_open(VENTOY_FILE_TYPE
, "%s", args
[0]);
526 isosize
= file
->size
;
528 boot_catlog
= ventoy_get_iso_boot_catlog(file
);
531 if (ventoy_is_efi_os() && (!ventoy_has_efi_eltorito(file
, boot_catlog
)))
533 grub_env_set("LoadIsoEfiDriver", "on");
538 if (ventoy_is_efi_os())
540 grub_env_set("LoadIsoEfiDriver", "on");
544 return grub_error(GRUB_ERR_BAD_ARGUMENT
, "File %s is not bootable", args
[0]);
548 img_chunk_size
= g_img_chunk_list
.cur_chunk
* sizeof(ventoy_img_chunk
);
550 if (ventoy_compatible
)
552 size
= sizeof(ventoy_chain_head
) + img_chunk_size
;
556 override_count
= ventoy_unix_get_override_chunk_count();
557 override_size
= override_count
* sizeof(ventoy_override_chunk
);
559 virt_chunk_size
= ventoy_unix_get_virt_chunk_size();
560 size
= sizeof(ventoy_chain_head
) + img_chunk_size
+ override_size
+ virt_chunk_size
;
563 pLastChain
= grub_env_get("vtoy_chain_mem_addr");
566 chain
= (ventoy_chain_head
*)grub_strtoul(pLastChain
, NULL
, 16);
569 debug("free last chain memory %p\n", chain
);
574 chain
= grub_malloc(size
);
577 grub_printf("Failed to alloc chain memory size %u\n", size
);
578 grub_file_close(file
);
582 grub_snprintf(envbuf
, sizeof(envbuf
), "0x%lx", (unsigned long)chain
);
583 grub_env_set("vtoy_chain_mem_addr", envbuf
);
584 grub_snprintf(envbuf
, sizeof(envbuf
), "%u", size
);
585 grub_env_set("vtoy_chain_mem_size", envbuf
);
587 grub_memset(chain
, 0, sizeof(ventoy_chain_head
));
589 /* part 1: os parameter */
590 g_ventoy_chain_type
= ventoy_chain_linux
;
591 ventoy_fill_os_param(file
, &(chain
->os_param
));
593 /* part 2: chain head */
594 disk
= file
->device
->disk
;
595 chain
->disk_drive
= disk
->id
;
596 chain
->disk_sector_size
= (1 << disk
->log_sector_size
);
597 chain
->real_img_size_in_bytes
= file
->size
;
598 chain
->virt_img_size_in_bytes
= (file
->size
+ 2047) / 2048 * 2048;
599 chain
->boot_catalog
= boot_catlog
;
601 if (!ventoy_is_efi_os())
603 grub_file_seek(file
, boot_catlog
* 2048);
604 grub_file_read(file
, chain
->boot_catalog_sector
, sizeof(chain
->boot_catalog_sector
));
607 /* part 3: image chunk */
608 chain
->img_chunk_offset
= sizeof(ventoy_chain_head
);
609 chain
->img_chunk_num
= g_img_chunk_list
.cur_chunk
;
610 grub_memcpy((char *)chain
+ chain
->img_chunk_offset
, g_img_chunk_list
.chunk
, img_chunk_size
);
612 if (ventoy_compatible
)
617 /* part 4: override chunk */
618 chain
->override_chunk_offset
= chain
->img_chunk_offset
+ img_chunk_size
;
619 chain
->override_chunk_num
= override_count
;
620 ventoy_unix_fill_override_data(isosize
, (char *)chain
+ chain
->override_chunk_offset
);
622 /* part 5: virt chunk */
623 chain
->virt_chunk_offset
= chain
->override_chunk_offset
+ override_size
;
624 chain
->virt_chunk_num
= ventoy_unix_get_virt_chunk_count();
625 ventoy_unix_fill_virt_data(isosize
, chain
);
627 grub_file_close(file
);
629 VENTOY_CMD_RETURN(GRUB_ERR_NONE
);