]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/grub-2.04/grub-core/ventoy/ventoy_linux.c
Merge branch 'master' of https://github.com/ventoy/Ventoy
[Ventoy.git] / GRUB2 / grub-2.04 / grub-core / ventoy / ventoy_linux.c
1 /******************************************************************************
2 * ventoy_linux.c
3 *
4 * Copyright (c) 2020, longpanda <admin@ventoy.net>
5 *
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.
10 *
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.
15 *
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/>.
18 *
19 */
20 #include <grub/types.h>
21 #include <grub/misc.h>
22 #include <grub/mm.h>
23 #include <grub/err.h>
24 #include <grub/dl.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>
34 #include <grub/net.h>
35 #include <grub/time.h>
36 #include <grub/ventoy.h>
37 #include "ventoy_def.h"
38
39 GRUB_MOD_LICENSE ("GPLv3+");
40
41
42 static char * ventoy_get_line(char *start)
43 {
44 if (start == NULL)
45 {
46 return NULL;
47 }
48
49 while (*start && *start != '\n')
50 {
51 start++;
52 }
53
54 if (*start == 0)
55 {
56 return NULL;
57 }
58 else
59 {
60 *start = 0;
61 return start + 1;
62 }
63 }
64
65 static initrd_info * ventoy_find_initrd_by_name(initrd_info *list, const char *name)
66 {
67 initrd_info *node = list;
68
69 while (node)
70 {
71 if (grub_strcmp(node->name, name) == 0)
72 {
73 return node;
74 }
75 node = node->next;
76 }
77
78 return NULL;
79 }
80
81 grub_err_t ventoy_cmd_clear_initrd_list(grub_extcmd_context_t ctxt, int argc, char **args)
82 {
83 initrd_info *node = g_initrd_img_list;
84 initrd_info *next;
85
86 (void)ctxt;
87 (void)argc;
88 (void)args;
89
90 while (node)
91 {
92 next = node->next;
93 grub_free(node);
94 node = next;
95 }
96
97 g_initrd_img_list = NULL;
98 g_initrd_img_tail = NULL;
99 g_initrd_img_count = 0;
100 g_valid_initrd_count = 0;
101
102 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
103 }
104
105 grub_err_t ventoy_cmd_dump_initrd_list(grub_extcmd_context_t ctxt, int argc, char **args)
106 {
107 int i = 0;
108 initrd_info *node = g_initrd_img_list;
109
110 (void)ctxt;
111 (void)argc;
112 (void)args;
113
114 grub_printf("###################\n");
115 grub_printf("initrd info list: valid count:%d\n", g_valid_initrd_count);
116
117 while (node)
118 {
119 grub_printf("%s ", node->size > 0 ? "*" : " ");
120 grub_printf("%02u %s offset:%llu size:%llu \n", i++, node->name, (unsigned long long)node->offset,
121 (unsigned long long)node->size);
122 node = node->next;
123 }
124
125 grub_printf("###################\n");
126
127 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
128 }
129
130 static void ventoy_parse_directory(char *path, char *dir, int buflen)
131 {
132 int end;
133 char *pos;
134
135 pos = grub_strstr(path, ")");
136 if (!pos)
137 {
138 pos = path;
139 }
140
141 end = grub_snprintf(dir, buflen, "%s", pos + 1);
142 while (end > 0)
143 {
144 if (dir[end] == '/')
145 {
146 dir[end + 1] = 0;
147 break;
148 }
149 end--;
150 }
151 }
152
153 static grub_err_t ventoy_isolinux_initrd_collect(grub_file_t file, const char *prefix)
154 {
155 int i = 0;
156 int offset;
157 int prefixlen = 0;
158 char *buf = NULL;
159 char *pos = NULL;
160 char *start = NULL;
161 char *nextline = NULL;
162 initrd_info *img = NULL;
163
164 prefixlen = grub_strlen(prefix);
165
166 buf = grub_zalloc(file->size + 2);
167 if (!buf)
168 {
169 return 0;
170 }
171
172 grub_file_read(file, buf, file->size);
173
174 for (start = buf; start; start = nextline)
175 {
176 nextline = ventoy_get_line(start);
177
178 while (ventoy_isspace(*start))
179 {
180 start++;
181 }
182
183 offset = 7; // strlen("initrd=") or "INITRD " or "initrd "
184 pos = grub_strstr(start, "initrd=");
185 if (pos == NULL)
186 {
187 pos = start;
188
189 if (grub_strncmp(start, "INITRD", 6) != 0 && grub_strncmp(start, "initrd", 6) != 0)
190 {
191 if (grub_strstr(start, "xen") &&
192 ((pos = grub_strstr(start, "--- /install.img")) != NULL ||
193 (pos = grub_strstr(start, "--- initrd.img")) != NULL
194 ))
195 {
196 offset = 4; // "--- "
197 }
198 else
199 {
200 continue;
201 }
202 }
203 }
204
205 pos += offset;
206
207 while (1)
208 {
209 i = 0;
210 img = grub_zalloc(sizeof(initrd_info));
211 if (!img)
212 {
213 break;
214 }
215
216 if (*pos != '/')
217 {
218 grub_strcpy(img->name, prefix);
219 i = prefixlen;
220 }
221
222 while (i < 255 && (0 == ventoy_is_word_end(*pos)))
223 {
224 img->name[i++] = *pos++;
225 }
226
227 if (ventoy_find_initrd_by_name(g_initrd_img_list, img->name))
228 {
229 grub_free(img);
230 }
231 else
232 {
233 if (g_initrd_img_list)
234 {
235 img->prev = g_initrd_img_tail;
236 g_initrd_img_tail->next = img;
237 }
238 else
239 {
240 g_initrd_img_list = img;
241 }
242
243 g_initrd_img_tail = img;
244 g_initrd_img_count++;
245 }
246
247 if (*pos == ',')
248 {
249 pos++;
250 }
251 else
252 {
253 break;
254 }
255 }
256 }
257
258 grub_free(buf);
259 return GRUB_ERR_NONE;
260 }
261
262 static int ventoy_isolinux_initrd_hook(const char *filename, const struct grub_dirhook_info *info, void *data)
263 {
264 grub_file_t file = NULL;
265 ventoy_initrd_ctx *ctx = (ventoy_initrd_ctx *)data;
266
267 (void)info;
268
269 if (NULL == grub_strstr(filename, ".cfg") && NULL == grub_strstr(filename, ".CFG"))
270 {
271 return 0;
272 }
273
274 debug("init hook dir <%s%s>\n", ctx->path_prefix, filename);
275
276 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s%s", ctx->path_prefix, filename);
277 if (!file)
278 {
279 return 0;
280 }
281
282 ventoy_isolinux_initrd_collect(file, ctx->dir_prefix);
283 grub_file_close(file);
284
285 return 0;
286 }
287
288 grub_err_t ventoy_cmd_isolinux_initrd_collect(grub_extcmd_context_t ctxt, int argc, char **args)
289 {
290 grub_fs_t fs;
291 grub_device_t dev = NULL;
292 char *device_name = NULL;
293 ventoy_initrd_ctx ctx;
294 char directory[256];
295
296 (void)ctxt;
297 (void)argc;
298
299 device_name = grub_file_get_device_name(args[0]);
300 if (!device_name)
301 {
302 goto end;
303 }
304
305 dev = grub_device_open(device_name);
306 if (!dev)
307 {
308 goto end;
309 }
310
311 fs = grub_fs_probe(dev);
312 if (!fs)
313 {
314 goto end;
315 }
316
317 debug("isolinux initrd collect %s\n", args[0]);
318
319 ventoy_parse_directory(args[0], directory, sizeof(directory) - 1);
320 ctx.path_prefix = args[0];
321 ctx.dir_prefix = (argc > 1) ? args[1] : directory;
322
323 debug("path_prefix=<%s> dir_prefix=<%s>\n", ctx.path_prefix, ctx.dir_prefix);
324
325 fs->fs_dir(dev, directory, ventoy_isolinux_initrd_hook, &ctx);
326
327 end:
328 check_free(device_name, grub_free);
329 check_free(dev, grub_device_close);
330
331 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
332 }
333
334 static grub_err_t ventoy_grub_cfg_initrd_collect(const char *fileName)
335 {
336 int i = 0;
337 grub_file_t file = NULL;
338 char *buf = NULL;
339 char *start = NULL;
340 char *nextline = NULL;
341 initrd_info *img = NULL;
342
343 debug("grub initrd collect %s\n", fileName);
344
345 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", fileName);
346 if (!file)
347 {
348 return 0;
349 }
350
351 buf = grub_zalloc(file->size + 2);
352 if (!buf)
353 {
354 grub_file_close(file);
355 return 0;
356 }
357
358 grub_file_read(file, buf, file->size);
359
360 for (start = buf; start; start = nextline)
361 {
362 nextline = ventoy_get_line(start);
363
364 while (ventoy_isspace(*start))
365 {
366 start++;
367 }
368
369 if (grub_strncmp(start, "initrd", 6) != 0)
370 {
371 continue;
372 }
373
374 start += 6;
375 while (*start && (!ventoy_isspace(*start)))
376 {
377 start++;
378 }
379
380 while (ventoy_isspace(*start))
381 {
382 start++;
383 }
384
385 while (*start)
386 {
387 img = grub_zalloc(sizeof(initrd_info));
388 if (!img)
389 {
390 break;
391 }
392
393 for (i = 0; i < 255 && (0 == ventoy_is_word_end(*start)); i++)
394 {
395 img->name[i] = *start++;
396 }
397
398 if (ventoy_find_initrd_by_name(g_initrd_img_list, img->name))
399 {
400 grub_free(img);
401 }
402 else
403 {
404 if (g_initrd_img_list)
405 {
406 img->prev = g_initrd_img_tail;
407 g_initrd_img_tail->next = img;
408 }
409 else
410 {
411 g_initrd_img_list = img;
412 }
413
414 g_initrd_img_tail = img;
415 g_initrd_img_count++;
416 }
417
418 if (*start == ' ' || *start == '\t')
419 {
420 while (ventoy_isspace(*start))
421 {
422 start++;
423 }
424 }
425 else
426 {
427 break;
428 }
429 }
430 }
431
432 grub_free(buf);
433 grub_file_close(file);
434
435 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
436 }
437
438 static int ventoy_grub_initrd_hook(const char *filename, const struct grub_dirhook_info *info, void *data)
439 {
440 char filePath[256];
441 ventoy_initrd_ctx *ctx = (ventoy_initrd_ctx *)data;
442
443 (void)info;
444
445 debug("ventoy_grub_initrd_hook %s\n", filename);
446
447 if (NULL == grub_strstr(filename, ".cfg") &&
448 NULL == grub_strstr(filename, ".CFG") &&
449 NULL == grub_strstr(filename, ".conf"))
450 {
451 return 0;
452 }
453
454 debug("init hook dir <%s%s>\n", ctx->path_prefix, filename);
455
456 grub_snprintf(filePath, sizeof(filePath) - 1, "%s%s", ctx->dir_prefix, filename);
457 ventoy_grub_cfg_initrd_collect(filePath);
458
459 return 0;
460 }
461
462 grub_err_t ventoy_cmd_grub_initrd_collect(grub_extcmd_context_t ctxt, int argc, char **args)
463 {
464 grub_fs_t fs;
465 grub_device_t dev = NULL;
466 char *device_name = NULL;
467 ventoy_initrd_ctx ctx;
468
469 (void)ctxt;
470 (void)argc;
471
472 if (argc != 2)
473 {
474 return 0;
475 }
476
477 debug("grub initrd collect %s %s\n", args[0], args[1]);
478
479 if (grub_strcmp(args[0], "file") == 0)
480 {
481 return ventoy_grub_cfg_initrd_collect(args[1]);
482 }
483
484 device_name = grub_file_get_device_name(args[1]);
485 if (!device_name)
486 {
487 debug("failed to get device name %s\n", args[1]);
488 goto end;
489 }
490
491 dev = grub_device_open(device_name);
492 if (!dev)
493 {
494 debug("failed to open device %s\n", device_name);
495 goto end;
496 }
497
498 fs = grub_fs_probe(dev);
499 if (!fs)
500 {
501 debug("failed to probe fs %d\n", grub_errno);
502 goto end;
503 }
504
505 ctx.dir_prefix = args[1];
506 ctx.path_prefix = grub_strstr(args[1], device_name);
507 if (ctx.path_prefix)
508 {
509 ctx.path_prefix += grub_strlen(device_name) + 1;
510 }
511 else
512 {
513 ctx.path_prefix = args[1];
514 }
515
516 debug("ctx.path_prefix:<%s>\n", ctx.path_prefix);
517
518 fs->fs_dir(dev, ctx.path_prefix, ventoy_grub_initrd_hook, &ctx);
519
520 end:
521 check_free(device_name, grub_free);
522 check_free(dev, grub_device_close);
523
524
525 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
526 }
527
528 grub_err_t ventoy_cmd_specify_initrd_file(grub_extcmd_context_t ctxt, int argc, char **args)
529 {
530 initrd_info *img = NULL;
531
532 (void)ctxt;
533 (void)argc;
534
535 debug("ventoy_cmd_specify_initrd_file %s\n", args[0]);
536
537 img = grub_zalloc(sizeof(initrd_info));
538 if (!img)
539 {
540 return 1;
541 }
542
543 grub_strncpy(img->name, args[0], sizeof(img->name));
544 if (ventoy_find_initrd_by_name(g_initrd_img_list, img->name))
545 {
546 grub_free(img);
547 }
548 else
549 {
550 if (g_initrd_img_list)
551 {
552 img->prev = g_initrd_img_tail;
553 g_initrd_img_tail->next = img;
554 }
555 else
556 {
557 g_initrd_img_list = img;
558 }
559
560 g_initrd_img_tail = img;
561 g_initrd_img_count++;
562 }
563
564 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
565 }
566
567 static void ventoy_cpio_newc_fill_int(grub_uint32_t value, char *buf, int buflen)
568 {
569 int i;
570 int len;
571 char intbuf[32];
572
573 len = grub_snprintf(intbuf, sizeof(intbuf), "%x", value);
574
575 for (i = 0; i < buflen; i++)
576 {
577 buf[i] = '0';
578 }
579
580 if (len > buflen)
581 {
582 grub_printf("int buf len overflow %d %d\n", len, buflen);
583 }
584 else
585 {
586 grub_memcpy(buf + buflen - len, intbuf, len);
587 }
588 }
589
590 int ventoy_cpio_newc_fill_head(void *buf, int filesize, void *filedata, const char *name)
591 {
592 int namelen = 0;
593 int headlen = 0;
594 static grub_uint32_t cpio_ino = 0xFFFFFFF0;
595 cpio_newc_header *cpio = (cpio_newc_header *)buf;
596
597 namelen = grub_strlen(name) + 1;
598 headlen = sizeof(cpio_newc_header) + namelen;
599 headlen = ventoy_align(headlen, 4);
600
601 grub_memset(cpio, '0', sizeof(cpio_newc_header));
602 grub_memset(cpio + 1, 0, headlen - sizeof(cpio_newc_header));
603
604 grub_memcpy(cpio->c_magic, "070701", 6);
605 ventoy_cpio_newc_fill_int(cpio_ino--, cpio->c_ino, 8);
606 ventoy_cpio_newc_fill_int(0100777, cpio->c_mode, 8);
607 ventoy_cpio_newc_fill_int(1, cpio->c_nlink, 8);
608 ventoy_cpio_newc_fill_int(filesize, cpio->c_filesize, 8);
609 ventoy_cpio_newc_fill_int(namelen, cpio->c_namesize, 8);
610 grub_memcpy(cpio + 1, name, namelen);
611
612 if (filedata)
613 {
614 grub_memcpy((char *)cpio + headlen, filedata, filesize);
615 }
616
617 return headlen;
618 }
619
620 static grub_uint32_t ventoy_linux_get_virt_chunk_size(void)
621 {
622 return (sizeof(ventoy_virt_chunk) + g_ventoy_cpio_size) * g_valid_initrd_count;
623 }
624
625 static void ventoy_linux_fill_virt_data( grub_uint64_t isosize, ventoy_chain_head *chain)
626 {
627 int id = 0;
628 initrd_info *node;
629 grub_uint64_t sector;
630 grub_uint32_t offset;
631 grub_uint32_t cpio_secs;
632 grub_uint32_t initrd_secs;
633 char *override;
634 ventoy_virt_chunk *cur;
635 char name[32];
636
637 override = (char *)chain + chain->virt_chunk_offset;
638 sector = (isosize + 2047) / 2048;
639 cpio_secs = g_ventoy_cpio_size / 2048;
640
641 offset = g_valid_initrd_count * sizeof(ventoy_virt_chunk);
642 cur = (ventoy_virt_chunk *)override;
643
644 for (node = g_initrd_img_list; node; node = node->next)
645 {
646 if (node->size == 0)
647 {
648 continue;
649 }
650
651 initrd_secs = (grub_uint32_t)((node->size + 2047) / 2048);
652
653 cur->mem_sector_start = sector;
654 cur->mem_sector_end = cur->mem_sector_start + cpio_secs;
655 cur->mem_sector_offset = offset;
656 cur->remap_sector_start = cur->mem_sector_end;
657 cur->remap_sector_end = cur->remap_sector_start + initrd_secs;
658 cur->org_sector_start = (grub_uint32_t)(node->offset / 2048);
659
660 grub_memcpy(g_ventoy_runtime_buf, &chain->os_param, sizeof(ventoy_os_param));
661
662 grub_memset(name, 0, 16);
663 grub_snprintf(name, sizeof(name), "initrd%03d", ++id);
664
665 grub_memcpy(g_ventoy_initrd_head + 1, name, 16);
666 ventoy_cpio_newc_fill_int((grub_uint32_t)node->size, g_ventoy_initrd_head->c_filesize, 8);
667
668 grub_memcpy(override + offset, g_ventoy_cpio_buf, g_ventoy_cpio_size);
669
670 chain->virt_img_size_in_bytes += g_ventoy_cpio_size + initrd_secs * 2048;
671
672 offset += g_ventoy_cpio_size;
673 sector += cpio_secs + initrd_secs;
674 cur++;
675 }
676
677 return;
678 }
679
680 static grub_uint32_t ventoy_linux_get_override_chunk_size(void)
681 {
682 return sizeof(ventoy_override_chunk) * g_valid_initrd_count;
683 }
684
685 static void ventoy_linux_fill_override_data( grub_uint64_t isosize, void *override)
686 {
687 initrd_info *node;
688 grub_uint64_t sector;
689 ventoy_override_chunk *cur;
690
691 sector = (isosize + 2047) / 2048;
692
693 cur = (ventoy_override_chunk *)override;
694 for (node = g_initrd_img_list; node; node = node->next)
695 {
696 if (node->size == 0)
697 {
698 continue;
699 }
700
701 if (node->iso_type == 0)
702 {
703 ventoy_iso9660_override *dirent = (ventoy_iso9660_override *)node->override_data;
704
705 node->override_length = sizeof(ventoy_iso9660_override);
706 dirent->first_sector = (grub_uint32_t)sector;
707 dirent->size = (grub_uint32_t)(node->size + g_ventoy_cpio_size);
708 dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector);
709 dirent->size_be = grub_swap_bytes32(dirent->size);
710
711 sector += (dirent->size + 2047) / 2048;
712 }
713 else
714 {
715 ventoy_udf_override *udf = (ventoy_udf_override *)node->override_data;
716
717 node->override_length = sizeof(ventoy_udf_override);
718 udf->length = (grub_uint32_t)(node->size + g_ventoy_cpio_size);
719 udf->position = (grub_uint32_t)sector - node->udf_start_block;
720
721 sector += (udf->length + 2047) / 2048;
722 }
723
724 cur->img_offset = node->override_offset;
725 cur->override_size = node->override_length;
726 grub_memcpy(cur->override_data, node->override_data, cur->override_size);
727 cur++;
728 }
729
730 return;
731 }
732
733 grub_err_t ventoy_cmd_initrd_count(grub_extcmd_context_t ctxt, int argc, char **args)
734 {
735 char buf[32] = {0};
736
737 (void)ctxt;
738 (void)argc;
739 (void)args;
740
741 if (argc == 1)
742 {
743 grub_snprintf(buf, sizeof(buf), "%d", g_valid_initrd_count);
744 grub_env_set(args[0], buf);
745 }
746
747 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
748 }
749
750 static grub_err_t ventoy_linux_locate_initrd(int filt, int *filtcnt)
751 {
752 int data;
753 int sizefilt = 0;
754 grub_file_t file;
755 initrd_info *node;
756
757 debug("ventoy_linux_locate_initrd %d\n", filt);
758
759 g_valid_initrd_count = 0;
760
761 for (node = g_initrd_img_list; node; node = node->next)
762 {
763 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "(loop)%s", node->name);
764 if (!file)
765 {
766 continue;
767 }
768
769 debug("file <%s> size:%d\n", node->name, (int)file->size);
770
771 /* initrd file too small */
772 if (filt > 0 && file->size <= g_ventoy_cpio_size + 2048)
773 {
774 debug("file size too small %d\n", (int)g_ventoy_cpio_size);
775 grub_file_close(file);
776 sizefilt++;
777 continue;
778 }
779
780 if (grub_strcmp(file->fs->name, "iso9660") == 0)
781 {
782 node->iso_type = 0;
783 node->override_offset = grub_iso9660_get_last_file_dirent_pos(file) + 2;
784
785 grub_file_read(file, &data, 1); // just read for hook trigger
786 node->offset = grub_iso9660_get_last_read_pos(file);
787 }
788 else
789 {
790 /* TBD */
791 }
792
793 node->size = file->size;
794 g_valid_initrd_count++;
795
796 grub_file_close(file);
797 }
798
799 *filtcnt = sizefilt;
800
801 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
802 }
803
804
805 grub_err_t ventoy_cmd_linux_locate_initrd(grub_extcmd_context_t ctxt, int argc, char **args)
806 {
807 int sizefilt = 0;
808
809 (void)ctxt;
810 (void)argc;
811 (void)args;
812
813 ventoy_linux_locate_initrd(1, &sizefilt);
814
815 if (g_valid_initrd_count == 0 && sizefilt > 0)
816 {
817 ventoy_linux_locate_initrd(0, &sizefilt);
818 }
819
820 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
821 }
822
823 grub_err_t ventoy_cmd_load_cpio(grub_extcmd_context_t ctxt, int argc, char **args)
824 {
825 grub_uint8_t *buf;
826 grub_uint32_t mod;
827 grub_uint32_t headlen;
828 grub_uint32_t initrd_head_len;
829 grub_uint32_t padlen;
830 grub_uint32_t img_chunk_size;
831 grub_file_t file;
832
833 (void)ctxt;
834 (void)argc;
835
836 if (argc != 1)
837 {
838 return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s cpiofile\n", cmd_raw_name);
839 }
840
841 if (g_img_chunk_list.chunk == NULL || g_img_chunk_list.cur_chunk == 0)
842 {
843 return grub_error(GRUB_ERR_BAD_ARGUMENT, "image chunk is null\n");
844 }
845
846 img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
847
848 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
849 if (!file)
850 {
851 return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't open file %s\n", args[0]);
852 }
853
854 if (g_ventoy_cpio_buf)
855 {
856 grub_free(g_ventoy_cpio_buf);
857 g_ventoy_cpio_buf = NULL;
858 g_ventoy_cpio_size = 0;
859 }
860
861 g_ventoy_cpio_buf = grub_malloc(file->size + 4096 + img_chunk_size);
862 if (NULL == g_ventoy_cpio_buf)
863 {
864 grub_file_close(file);
865 return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't alloc memory %llu\n", file->size + 4096 + img_chunk_size);
866 }
867
868 grub_file_read(file, g_ventoy_cpio_buf, file->size);
869
870 buf = (grub_uint8_t *)(g_ventoy_cpio_buf + file->size - 4);
871 while (*((grub_uint32_t *)buf) != 0x37303730)
872 {
873 buf -= 4;
874 }
875
876 /* get initrd head len */
877 initrd_head_len = ventoy_cpio_newc_fill_head(buf, 0, NULL, "initrd000.xx");
878
879 /* step1: insert image chunk data to cpio */
880 headlen = ventoy_cpio_newc_fill_head(buf, img_chunk_size, g_img_chunk_list.chunk, "ventoy/ventoy_image_map");
881 buf += headlen + ventoy_align(img_chunk_size, 4);
882
883 /* step2: insert os param to cpio */
884 headlen = ventoy_cpio_newc_fill_head(buf, 0, NULL, "ventoy/ventoy_os_param");
885 padlen = sizeof(ventoy_os_param);
886 g_ventoy_cpio_size = (grub_uint32_t)(buf - g_ventoy_cpio_buf) + headlen + padlen + initrd_head_len;
887 mod = g_ventoy_cpio_size % 2048;
888 if (mod)
889 {
890 g_ventoy_cpio_size += 2048 - mod;
891 padlen += 2048 - mod;
892 }
893
894 /* update os param data size, the data will be updated before chain boot */
895 ventoy_cpio_newc_fill_int(padlen, ((cpio_newc_header *)buf)->c_filesize, 8);
896 g_ventoy_runtime_buf = (grub_uint8_t *)buf + headlen;
897
898 /* step3: fill initrd cpio head, the file size will be updated before chain boot */
899 g_ventoy_initrd_head = (cpio_newc_header *)(g_ventoy_runtime_buf + padlen);
900 ventoy_cpio_newc_fill_head(g_ventoy_initrd_head, 0, NULL, "initrd000.xx");
901
902 grub_file_close(file);
903
904 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
905 }
906
907
908 grub_err_t ventoy_cmd_linux_chain_data(grub_extcmd_context_t ctxt, int argc, char **args)
909 {
910 int ventoy_compatible = 0;
911 grub_uint32_t size = 0;
912 grub_uint64_t isosize = 0;
913 grub_uint32_t boot_catlog = 0;
914 grub_uint32_t img_chunk_size = 0;
915 grub_uint32_t override_size = 0;
916 grub_uint32_t virt_chunk_size = 0;
917 grub_file_t file;
918 grub_disk_t disk;
919 const char *pLastChain = NULL;
920 const char *compatible;
921 ventoy_chain_head *chain;
922 char envbuf[64];
923
924 (void)ctxt;
925 (void)argc;
926
927 compatible = grub_env_get("ventoy_compatible");
928 if (compatible && compatible[0] == 'Y')
929 {
930 ventoy_compatible = 1;
931 }
932
933 if ((NULL == g_img_chunk_list.chunk) || (0 == ventoy_compatible && g_ventoy_cpio_buf == NULL))
934 {
935 grub_printf("ventoy not ready\n");
936 return 1;
937 }
938
939 file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
940 if (!file)
941 {
942 return 1;
943 }
944
945 isosize = file->size;
946
947 boot_catlog = ventoy_get_iso_boot_catlog(file);
948 if (boot_catlog)
949 {
950 if (ventoy_is_efi_os() && (!ventoy_has_efi_eltorito(file, boot_catlog)))
951 {
952 grub_env_set("LoadIsoEfiDriver", "on");
953 }
954 }
955 else
956 {
957 if (ventoy_is_efi_os())
958 {
959 grub_env_set("LoadIsoEfiDriver", "on");
960 }
961 else
962 {
963 return grub_error(GRUB_ERR_BAD_ARGUMENT, "File %s is not bootable", args[0]);
964 }
965 }
966
967 img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
968
969 if (ventoy_compatible)
970 {
971 size = sizeof(ventoy_chain_head) + img_chunk_size;
972 }
973 else
974 {
975 override_size = ventoy_linux_get_override_chunk_size();
976 virt_chunk_size = ventoy_linux_get_virt_chunk_size();
977 size = sizeof(ventoy_chain_head) + img_chunk_size + override_size + virt_chunk_size;
978 }
979
980 pLastChain = grub_env_get("vtoy_chain_mem_addr");
981 if (pLastChain)
982 {
983 chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16);
984 if (chain)
985 {
986 debug("free last chain memory %p\n", chain);
987 grub_free(chain);
988 }
989 }
990
991 chain = grub_malloc(size);
992 if (!chain)
993 {
994 grub_printf("Failed to alloc chain memory size %u\n", size);
995 grub_file_close(file);
996 return 1;
997 }
998
999 grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)chain);
1000 grub_env_set("vtoy_chain_mem_addr", envbuf);
1001 grub_snprintf(envbuf, sizeof(envbuf), "%u", size);
1002 grub_env_set("vtoy_chain_mem_size", envbuf);
1003
1004 grub_memset(chain, 0, sizeof(ventoy_chain_head));
1005
1006 /* part 1: os parameter */
1007 ventoy_fill_os_param(file, &(chain->os_param));
1008
1009 /* part 2: chain head */
1010 disk = file->device->disk;
1011 chain->disk_drive = disk->id;
1012 chain->disk_sector_size = (1 << disk->log_sector_size);
1013 chain->real_img_size_in_bytes = file->size;
1014 chain->virt_img_size_in_bytes = (file->size + 2047) / 2048 * 2048;
1015 chain->boot_catalog = boot_catlog;
1016
1017 if (!ventoy_is_efi_os())
1018 {
1019 grub_file_seek(file, boot_catlog * 2048);
1020 grub_file_read(file, chain->boot_catalog_sector, sizeof(chain->boot_catalog_sector));
1021 }
1022
1023 /* part 3: image chunk */
1024 chain->img_chunk_offset = sizeof(ventoy_chain_head);
1025 chain->img_chunk_num = g_img_chunk_list.cur_chunk;
1026 grub_memcpy((char *)chain + chain->img_chunk_offset, g_img_chunk_list.chunk, img_chunk_size);
1027
1028 if (ventoy_compatible)
1029 {
1030 return 0;
1031 }
1032
1033 if (g_valid_initrd_count == 0)
1034 {
1035 return 0;
1036 }
1037
1038 /* part 4: override chunk */
1039 chain->override_chunk_offset = chain->img_chunk_offset + img_chunk_size;
1040 chain->override_chunk_num = g_valid_initrd_count;
1041 ventoy_linux_fill_override_data(isosize, (char *)chain + chain->override_chunk_offset);
1042
1043 /* part 5: virt chunk */
1044 chain->virt_chunk_offset = chain->override_chunk_offset + override_size;
1045 chain->virt_chunk_num = g_valid_initrd_count;
1046 ventoy_linux_fill_virt_data(isosize, chain);
1047
1048 VENTOY_CMD_RETURN(GRUB_ERR_NONE);
1049 }
1050