Adding debian version 4.03+dfsg-7.
[syslinux-debian/hramrach.git] / com32 / gfxboot / gfxboot.c
blob2323f8ed44518161f0a9ff677a15173fea26626d
1 /*
3 * gfxboot.c
5 * A com32 module to load gfxboot graphics.
7 * Copyright (c) 2009 Steffen Winterfeldt.
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, Inc., 53 Temple Place Ste 330, Boston MA
12 * 02111-1307, USA; either version 2 of the License, or (at your option) any
13 * later version; incorporated herein by reference.
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <string.h>
21 #include <fcntl.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <minmax.h>
26 #include <syslinux/loadfile.h>
27 #include <syslinux/config.h>
28 #include <syslinux/linux.h>
29 #include <syslinux/boot.h>
30 #include <console.h>
31 #include <com32.h>
34 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
35 #define MAX_CONFIG_LINE_LEN 2048
36 #define MAX_CMDLINE_LEN 2048
38 // buffer for realmode callback
39 // must be at least block size; can in theory be larger than 4k, but there's
40 // not enough space left
41 #define REALMODE_BUF_SIZE 4096
43 // gfxboot working memory in MB
44 #define GFX_MEMORY_SIZE 7
46 // read chunk size for progress bar
47 #define CHUNK_SIZE (64 << 10)
49 // callback function numbers
50 #define GFX_CB_INIT 0
51 #define GFX_CB_DONE 1
52 #define GFX_CB_INPUT 2
53 #define GFX_CB_MENU_INIT 3
54 #define GFX_CB_INFOBOX_INIT 4
55 #define GFX_CB_INFOBOX_DONE 5
56 #define GFX_CB_PROGRESS_INIT 6
57 #define GFX_CB_PROGRESS_DONE 7
58 #define GFX_CB_PROGRESS_UPDATE 8
59 #define GFX_CB_PROGRESS_LIMIT 9 // unused
60 #define GFX_CB_PASSWORD_INIT 10
61 #define GFX_CB_PASSWORD_DONE 11
63 // real mode code chunk, will be placed into bounce buffer
64 extern void realmode_callback_start, realmode_callback_end;
66 // gets in the way
67 #undef linux
70 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
71 // gfxboot config data (64 bytes)
72 typedef struct __attribute__ ((packed)) {
73 uint8_t bootloader; // 0: boot loader type (0: lilo, 1: syslinux, 2: grub)
74 uint8_t sector_shift; // 1: sector shift
75 uint8_t media_type; // 2: media type (0: disk, 1: floppy, 2: cdrom)
76 uint8_t failsafe; // 3: turn on failsafe mode (bitmask)
77 // 0: SHIFT pressed
78 // 1: skip gfxboot
79 // 2: skip monitor detection
80 uint8_t sysconfig_size; // 4: size of sysconfig data
81 uint8_t boot_drive; // 5: BIOS boot drive
82 uint16_t callback; // 6: offset to callback handler
83 uint16_t bootloader_seg; // 8: code/data segment used by bootloader; must follow gfx_callback
84 uint16_t serial_port; // 10: syslinux initialized serial port from 'serial' option
85 uint32_t user_info_0; // 12: data for info box
86 uint32_t user_info_1; // 16: data for info box
87 uint32_t bios_mem_size; // 20: BIOS memory size (in bytes)
88 uint16_t xmem_0; // 24: extended mem area 0 (start:size in MB; 12:4 bits) - obsolete
89 uint16_t xmem_1; // 26: extended mem area 1 - obsolete
90 uint16_t xmem_2; // 28: extended mem area 2 - obsolete
91 uint16_t xmem_3; // 30: extended mem area 3 - obsolete
92 uint32_t file; // 32: start of gfx file
93 uint32_t archive_start; // 36: start of cpio archive
94 uint32_t archive_end; // 40: end of cpio archive
95 uint32_t mem0_start; // 44: low free memory start
96 uint32_t mem0_end; // 48: low free memory end
97 uint32_t xmem_start; // 52: extended mem start
98 uint32_t xmem_end; // 56: extended mem end
99 uint16_t features; // 60: feature flags returned by GFX_CB_INIT
100 // 0: GFX_CB_MENU_INIT accepts 32 bit addresses
101 // 1: knows about xmem_start, xmem_end
102 uint16_t reserved_1; // 62:
103 } gfx_config_t;
106 // gfxboot menu description (18 bytes)
107 typedef struct __attribute__ ((packed)) {
108 uint16_t entries;
109 char *default_entry;
110 char *label_list;
111 uint16_t label_size;
112 char *arg_list;
113 uint16_t arg_size;
114 } gfx_menu_t;
117 // menu description
118 typedef struct menu_s {
119 struct menu_s *next;
120 char *label; // config entry name
121 char *menu_label; // text to show in boot menu
122 char *kernel; // name of program to load
123 char *alt_kernel; // alternative name in case user has replaced it
124 char *linux; // de facto an alias for 'kernel'
125 char *localboot; // boot from local disk
126 char *initrd; // initrd as separate line (instead of as part of 'append')
127 char *append; // kernel args
128 char *ipappend; // append special pxelinux args (see doc)
129 } menu_t;
132 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
133 gfx_config_t gfx_config;
134 gfx_menu_t gfx_menu;
136 menu_t *menu;
137 menu_t *menu_default;
138 static menu_t *menu_ptr, **menu_next;
140 struct {
141 uint32_t jmp_table[12];
142 uint16_t code_seg;
143 char fname_buf[64];
144 } gfx;
146 void *lowmem_buf;
147 unsigned lowmem_buf_size;
149 int timeout;
151 char cmdline[MAX_CMDLINE_LEN];
153 void *save_buf;
154 unsigned save_buf_size;
156 // progress bar is visible
157 unsigned progress_active;
160 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
161 void show_message(char *file);
162 char *get_config_file_name(void);
163 char *skip_spaces(char *s);
164 char *skip_nonspaces(char *s);
165 void chop_line(char *s);
166 int read_config_file(const char *filename);
167 unsigned magic_ok(unsigned char *buf, unsigned *code_size);
168 unsigned find_file(unsigned char *buf, unsigned len, unsigned *gfx_file_start, unsigned *file_len, unsigned *code_size);
169 int gfx_init(char *file);
170 int gfx_menu_init(void);
171 void gfx_done(void);
172 int gfx_input(void);
173 void gfx_infobox(int type, char *str1, char *str2);
174 void gfx_progress_init(ssize_t kernel_size, char *label);
175 void gfx_progress_update(ssize_t size);
176 void gfx_progress_done(void);
177 ssize_t save_read(int fd, void *buf, size_t size);
178 void *load_one(char *file, ssize_t *file_size);
179 void boot(int index);
180 void boot_entry(menu_t *menu_ptr, char *arg);
183 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
184 int main(int argc, char **argv)
186 int menu_index;
187 const union syslinux_derivative_info *sdi;
189 openconsole(&dev_stdcon_r, &dev_stdcon_w);
191 lowmem_buf = __com32.cs_bounce;
192 lowmem_buf_size = __com32.cs_bounce_size;
194 sdi = syslinux_derivative_info();
196 gfx_config.sector_shift = sdi->disk.sector_shift;
197 gfx_config.boot_drive = sdi->disk.drive_number;
199 if(sdi->c.filesystem == SYSLINUX_FS_PXELINUX) {
200 gfx_config.sector_shift = 11;
201 gfx_config.boot_drive = 0;
204 gfx_config.media_type = gfx_config.boot_drive < 0x80 ? 1 : 0;
206 if(sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
207 gfx_config.media_type = sdi->iso.cd_mode ? 0 : 2;
210 gfx_config.bootloader = 1;
211 gfx_config.sysconfig_size = sizeof gfx_config;
212 gfx_config.bootloader_seg = 0; // apparently not needed
214 save_buf_size = lowmem_buf_size;
215 save_buf = malloc(save_buf_size);
217 if(argc < 2) {
218 printf("Usage: gfxboot.c32 bootlogo_file [message_file]\n");
219 if(argc > 2) show_message(argv[2]);
221 return 0;
224 if(read_config_file("~")) {
225 printf("Error reading config file\n");
226 if(argc > 2) show_message(argv[2]);
228 return 0;
231 if(gfx_init(argv[1])) {
232 printf("Error setting up gfxboot\n");
233 if(argc > 2) show_message(argv[2]);
235 return 0;
238 gfx_menu_init();
240 for(;;) {
241 menu_index = gfx_input();
243 // abort gfx, return to text mode prompt
244 if(menu_index == -1) {
245 gfx_done();
246 break;
249 // does not return if it succeeds
250 boot(menu_index);
253 if(argc > 2) show_message(argv[2]);
255 return 0;
259 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
260 void show_message(char *file)
262 int c;
263 FILE *f;
265 if(!(f = fopen(file, "r"))) return;
267 while((c = getc(f)) != EOF) {
268 if(c < ' ' && c != '\n' && c != '\t') continue;
269 printf("%c", c);
272 fclose(f);
276 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
277 char *skip_spaces(char *s)
279 while(*s && (*s == ' ' || *s == '\t')) s++;
281 return s;
285 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
286 char *skip_nonspaces(char *s)
288 while(*s && *s != ' ' && *s != '\t') s++;
290 return s;
294 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
295 void chop_line(char *s)
297 int i = strlen(s);
299 if(!i) return;
301 while(--i >= 0) {
302 if(s[i] == ' ' || s[i] == '\t' || s[i] == '\n') {
303 s[i] = 0;
305 else {
306 break;
312 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
313 // Read and parse syslinux config file.
315 // return:
316 // 0: ok, 1: error
318 int read_config_file(const char *filename)
320 FILE *f;
321 char *s, *t, buf[MAX_CONFIG_LINE_LEN];
322 unsigned u, top_level = 0;
324 if(!strcmp(filename, "~")) {
325 top_level = 1;
326 filename = syslinux_config_file();
327 gfx_menu.entries = 0;
328 gfx_menu.label_size = 0;
329 gfx_menu.arg_size = 0;
330 menu_ptr = NULL;
331 menu_next = &menu;
332 menu_default = calloc(1, sizeof *menu_default);
335 if(!(f = fopen(filename, "r"))) return 1;
337 while((s = fgets(buf, sizeof buf, f))) {
338 chop_line(s);
339 s = skip_spaces(s);
340 if(!*s || *s == '#') continue;
341 t = skip_nonspaces(s);
342 if(*t) *t++ = 0;
343 t = skip_spaces(t);
345 if(!strcasecmp(s, "timeout")) {
346 timeout = atoi(t);
347 continue;
350 if(!strcasecmp(s, "default")) {
351 menu_default->label = strdup(t);
352 u = strlen(t);
353 if(u > gfx_menu.label_size) gfx_menu.label_size = u;
354 continue;
357 if(!strcasecmp(s, "label")) {
358 menu_ptr = *menu_next = calloc(1, sizeof **menu_next);
359 menu_next = &menu_ptr->next;
360 gfx_menu.entries++;
361 menu_ptr->label = menu_ptr->menu_label = strdup(t);
362 u = strlen(t);
363 if(u > gfx_menu.label_size) gfx_menu.label_size = u;
364 continue;
367 if(!strcasecmp(s, "kernel") && menu_ptr) {
368 menu_ptr->kernel = strdup(t);
369 continue;
372 if(!strcasecmp(s, "linux") && menu_ptr) {
373 menu_ptr->linux = strdup(t);
374 continue;
377 if(!strcasecmp(s, "localboot") && menu_ptr) {
378 menu_ptr->localboot = strdup(t);
379 continue;
382 if(!strcasecmp(s, "initrd") && menu_ptr) {
383 menu_ptr->initrd = strdup(t);
384 continue;
387 if(!strcasecmp(s, "append")) {
388 (menu_ptr ?: menu_default)->append = strdup(t);
389 u = strlen(t);
390 if(u > gfx_menu.arg_size) gfx_menu.arg_size = u;
391 continue;
394 if(!strcasecmp(s, "ipappend")) {
395 (menu_ptr ?: menu_default)->ipappend = strdup(t);
396 continue;
399 if(!strcasecmp(s, "menu") && menu_ptr) {
400 s = skip_spaces(t);
401 t = skip_nonspaces(s);
402 if(*t) *t++ = 0;
403 t = skip_spaces(t);
405 if(!strcasecmp(s, "label")) {
406 menu_ptr->menu_label = strdup(t);
407 u = strlen(t);
408 if(u > gfx_menu.label_size) gfx_menu.label_size = u;
409 continue;
412 if(!strcasecmp(s, "include")) {
413 goto do_include;
417 if (!strcasecmp(s, "include")) {
418 do_include:
419 s = t;
420 t = skip_nonspaces(s);
421 if (*t) *t = 0;
422 read_config_file(s);
426 fclose(f);
428 if (!top_level)
429 return 0;
431 // final '\0'
432 gfx_menu.label_size++;
433 gfx_menu.arg_size++;
435 // ensure we have a default entry
436 if(!menu_default->label) menu_default->label = menu->label;
438 if(menu_default->label) {
439 for(menu_ptr = menu; menu_ptr; menu_ptr = menu_ptr->next) {
440 if(!strcmp(menu_default->label, menu_ptr->label)) {
441 menu_default->menu_label = menu_ptr->menu_label;
442 break;
447 gfx_menu.default_entry = menu_default->menu_label;
448 gfx_menu.label_list = calloc(gfx_menu.entries, gfx_menu.label_size);
449 gfx_menu.arg_list = calloc(gfx_menu.entries, gfx_menu.arg_size);
451 for(u = 0, menu_ptr = menu; menu_ptr; menu_ptr = menu_ptr->next, u++) {
452 if(!menu_ptr->append) menu_ptr->append = menu_default->append;
453 if(!menu_ptr->ipappend) menu_ptr->ipappend = menu_default->ipappend;
455 if(menu_ptr->menu_label) strcpy(gfx_menu.label_list + u * gfx_menu.label_size, menu_ptr->menu_label);
456 if(menu_ptr->append) strcpy(gfx_menu.arg_list + u * gfx_menu.arg_size, menu_ptr->append);
459 return 0;
463 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
464 // Check header and return code start offset.
466 unsigned magic_ok(unsigned char *buf, unsigned *code_size)
469 *(unsigned *) buf == 0x0b2d97f00 && // magic id
470 (buf[4] == 8) // version 8
472 *code_size = *(unsigned *) (buf + 12);
473 return *(unsigned *) (buf + 8);
476 return 0;
480 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
481 // Search (cpio archive) for gfx file.
483 unsigned find_file(unsigned char *buf, unsigned len, unsigned *gfx_file_start, unsigned *file_len, unsigned *code_size)
485 unsigned i, fname_len, code_start = 0;
487 *gfx_file_start = 0;
488 *code_size = 0;
490 if((code_start = magic_ok(buf, code_size))) return code_start;
492 for(i = 0; i < len;) {
493 if((len - i) >= 0x1a && (buf[i] + (buf[i + 1] << 8)) == 0x71c7) {
494 fname_len = *(unsigned short *) (buf + i + 20);
495 *file_len = *(unsigned short *) (buf + i + 24) + (*(unsigned short *) (buf + i + 22) << 16);
496 i += 26 + fname_len;
497 i = ((i + 1) & ~1);
498 if((code_start = magic_ok(buf + i, code_size))) {
499 *gfx_file_start = i;
500 return code_start;
502 i += *file_len;
503 i = ((i + 1) & ~1);
505 else {
506 break;
510 return code_start;
514 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
515 // Initialize gfxboot code.
517 // return:
518 // 0: ok, 1: error
520 int gfx_init(char *file)
522 size_t archive_size = 0;
523 void *archive;
524 unsigned code_start, code_size, file_start, file_len, u;
525 com32sys_t r;
526 void *lowmem = lowmem_buf;
527 unsigned lowmem_size = lowmem_buf_size;
529 progress_active = 0;
531 printf("Loading %s...\n", file);
532 if(loadfile(file, &archive, &archive_size)) return 1;
534 if(!archive_size) return 1;
536 // printf("%s: %d\n", file, archive_size);
538 gfx_config.archive_start = (uint32_t) archive;
539 gfx_config.archive_end = gfx_config.archive_start + archive_size;
541 // locate file inside cpio archive
542 if(!(code_start = find_file(archive, archive_size, &file_start, &file_len, &code_size))) {
543 printf("%s: invalid file format\n", file);
544 return 1;
547 #if 0
548 printf(
549 "code_start = 0x%x, code_size = 0x%x\n"
550 "archive_start = 0x%x, archive size = 0x%x\n"
551 "file_start = 0x%x, file_len = 0x%x\n",
552 code_start, code_size,
553 gfx_config.archive_start, archive_size,
554 file_start, file_len
556 #endif
558 gfx_config.file = gfx_config.archive_start + file_start;
560 u = &realmode_callback_end - &realmode_callback_start;
561 u = (u + REALMODE_BUF_SIZE + 0xf) & ~0xf;
563 if(u + code_size > lowmem_size) {
564 printf("bounce buffer too small: size %u, needed %u\n", lowmem_size, u + code_size);
565 return 1;
568 memcpy(lowmem + REALMODE_BUF_SIZE, &realmode_callback_start, &realmode_callback_end - &realmode_callback_start);
570 // fill in buffer size and location
571 *(uint16_t *) (lowmem + REALMODE_BUF_SIZE) = REALMODE_BUF_SIZE;
572 *(uint16_t *) (lowmem + REALMODE_BUF_SIZE + 2) = (uint32_t) lowmem >> 4;
574 gfx_config.bootloader_seg = ((uint32_t) lowmem + REALMODE_BUF_SIZE) >> 4;
575 gfx_config.callback = 4; // start address
577 lowmem += u;
578 lowmem_size -= u;
580 memcpy(lowmem, archive + file_start + code_start, code_size);
582 gfx_config.mem0_start = (uint32_t) lowmem + code_size;
583 gfx_config.mem0_end = (uint32_t) lowmem + lowmem_size;
584 // align a bit
585 gfx_config.mem0_start = (gfx_config.mem0_start + 0xf) & ~0xf;
587 gfx_config.xmem_start = (uint32_t) malloc(GFX_MEMORY_SIZE << 20);
588 if(gfx_config.xmem_start) {
589 gfx_config.xmem_end = gfx_config.xmem_start + (GFX_MEMORY_SIZE << 20);
592 // fake; not used anyway
593 gfx_config.bios_mem_size = 256 << 20;
595 gfx.code_seg = (uint32_t) lowmem >> 4;
597 for(u = 0; u < sizeof gfx.jmp_table / sizeof *gfx.jmp_table; u++) {
598 gfx.jmp_table[u] = (gfx.code_seg << 16) + *(uint16_t *) (lowmem + 2 * u);
601 #if 0
602 for(u = 0; u < sizeof gfx.jmp_table / sizeof *gfx.jmp_table; u++) {
603 printf("%d: 0x%08x\n", u, gfx.jmp_table[u]);
605 #endif
607 // we are ready to start
609 r.esi.l = (uint32_t) &gfx_config;
610 __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_INIT], &r, &r);
612 if((r.eflags.l & EFLAGS_CF)) {
613 printf("graphics initialization failed\n");
615 return 1;
618 if((gfx_config.features & 3) != 3) {
619 gfx_done();
621 printf("%s: boot graphics code too old, please use newer version\n", file);
623 return 1;
627 return 0;
631 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
632 int gfx_menu_init(void)
634 com32sys_t r;
636 r.esi.l = (uint32_t) &gfx_menu;
637 __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_MENU_INIT], &r, &r);
639 return 0;
643 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
644 void gfx_done(void)
646 com32sys_t r;
648 gfx_progress_done();
650 __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_DONE], &r, &r);
654 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
655 // Run gfxboot main loop.
657 // return:
658 // boot menu index (-1: go to text mode prompt)
660 int gfx_input(void)
662 com32sys_t r;
664 r.edi.l = (uint32_t) cmdline;
665 r.ecx.l = sizeof cmdline;
666 r.eax.l = timeout * 182 / 100;
667 timeout = 0; // use timeout only first time
668 __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_INPUT], &r, &r);
669 if((r.eflags.l & EFLAGS_CF)) r.eax.l = 1;
671 if(r.eax.l == 1) return -1;
673 return r.ebx.l;
677 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
678 void gfx_infobox(int type, char *str1, char *str2)
680 com32sys_t r;
682 r.eax.l = type;
683 r.esi.l = (uint32_t) str1;
684 r.edi.l = (uint32_t) str2;
685 __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_INFOBOX_INIT], &r, &r);
686 r.edi.l = r.eax.l = 0;
687 __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_INPUT], &r, &r);
688 __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_INFOBOX_DONE], &r, &r);
692 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
693 void gfx_progress_init(ssize_t kernel_size, char *label)
695 com32sys_t r;
697 if(!progress_active) {
698 r.eax.l = kernel_size >> gfx_config.sector_shift; // in sectors
699 r.esi.l = (uint32_t) label;
700 __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_PROGRESS_INIT], &r, &r);
703 progress_active = 1;
707 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
708 void gfx_progress_update(ssize_t advance)
710 com32sys_t r;
712 if(progress_active) {
713 r.eax.l = advance >> gfx_config.sector_shift; // in sectors
714 __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_PROGRESS_UPDATE], &r, &r);
719 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
720 void gfx_progress_done(void)
722 com32sys_t r;
724 if(progress_active) {
725 __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_PROGRESS_DONE], &r, &r);
728 progress_active = 0;
732 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
733 // Like read(2) but preserve bounce buffer.
735 ssize_t save_read(int fd, void *buf, size_t size)
737 ssize_t i;
739 memcpy(save_buf, lowmem_buf, save_buf_size);
740 i = read(fd, buf, size);
741 memcpy(lowmem_buf, save_buf, save_buf_size);
743 return i;
747 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
748 // Read file and update progress bar.
750 void *load_one(char *file, ssize_t *file_size)
752 int fd;
753 void *buf = NULL;
754 char *str;
755 struct stat sbuf;
756 ssize_t size = 0, cur, i;
758 *file_size = 0;
760 if((fd = open(file, O_RDONLY)) == -1) {
761 asprintf(&str, "%s: file not found", file);
762 gfx_infobox(0, str, NULL);
763 free(str);
764 return buf;
767 if(!fstat(fd, &sbuf) && S_ISREG(sbuf.st_mode)) size = sbuf.st_size;
769 i = 0;
771 if(size) {
772 buf = malloc(size);
773 for(i = 1, cur = 0 ; cur < size && i > 0; cur += i) {
774 i = save_read(fd, buf + cur, min(CHUNK_SIZE, size - cur));
775 if(i == -1) break;
776 gfx_progress_update(i);
779 else {
780 do {
781 buf = realloc(buf, size + CHUNK_SIZE);
782 i = save_read(fd, buf + size, CHUNK_SIZE);
783 if(i == -1) break;
784 size += i;
785 gfx_progress_update(i);
786 } while(i > 0);
789 close(fd);
791 if(i == -1) {
792 asprintf(&str, "%s: read error @ %d", file, size);
793 gfx_infobox(0, str, NULL);
794 free(str);
795 free(buf);
796 buf = NULL;
797 size = 0;
800 *file_size = size;
802 return buf;
806 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
807 // Boot menu entry.
809 // cmdline can optionally start with label string.
811 void boot(int index)
813 char *arg, *alt_kernel;
814 menu_t *menu_ptr;
815 int i, label_len;
816 unsigned ipapp;
817 const struct syslinux_ipappend_strings *ipappend;
819 for(menu_ptr = menu; menu_ptr; menu_ptr = menu_ptr->next, index--) {
820 if(!index) break;
823 // invalid index or menu entry
824 if(!menu_ptr || !menu_ptr->menu_label) return;
826 arg = skip_spaces(cmdline);
827 label_len = strlen(menu_ptr->menu_label);
829 // if it does not start with label string, assume first word is kernel name
830 if(strncmp(arg, menu_ptr->menu_label, label_len)) {
831 alt_kernel = arg;
832 arg = skip_nonspaces(arg);
833 if(*arg) *arg++ = 0;
834 if(*alt_kernel) menu_ptr->alt_kernel = alt_kernel;
836 else {
837 arg += label_len;
840 arg = skip_spaces(arg);
842 // handle IPAPPEND
843 if(menu_ptr->ipappend && (ipapp = atoi(menu_ptr->ipappend))) {
844 ipappend = syslinux_ipappend_strings();
845 for(i = 0; i < ipappend->count; i++) {
846 if((ipapp & (1 << i)) && ipappend->ptr[i]) {
847 sprintf(arg + strlen(arg), " %s", ipappend->ptr[i]);
852 boot_entry(menu_ptr, arg);
854 gfx_progress_done();
858 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
859 // Load & run kernel.
861 // Returns only on error.
863 void boot_entry(menu_t *menu_ptr, char *arg)
865 void *kernel, *initrd_buf;
866 ssize_t kernel_size = 0, initrd_size = 0;
867 struct initramfs *initrd = NULL;
868 char *file, *cmd_buf;
869 int fd;
870 struct stat sbuf;
871 char *s, *s0, *t, *initrd_arg;
873 if(!menu_ptr) return;
875 if(menu_ptr->localboot) {
876 gfx_done();
877 syslinux_local_boot(strtol(menu_ptr->localboot, NULL, 0));
879 return;
882 file = menu_ptr->alt_kernel;
883 if(!file) file = menu_ptr->kernel;
884 if(!file) file = menu_ptr->linux;
885 if(!file) {
886 gfx_done();
887 asprintf(&cmd_buf, "%s %s", menu_ptr->label, arg);
888 syslinux_run_command(cmd_buf);
889 return;
892 // first, load kernel
894 kernel_size = 0;
896 if((fd = open(file, O_RDONLY)) >= 0) {
897 if(!fstat(fd, &sbuf) && S_ISREG(sbuf.st_mode)) kernel_size = sbuf.st_size;
898 close(fd);
901 gfx_progress_init(kernel_size, file);
903 kernel = load_one(file, &kernel_size);
905 if(!kernel) {
906 return;
909 if(kernel_size < 1024 || *(uint32_t *) (kernel + 0x202) != 0x53726448) {
910 // not a linux kernel
911 gfx_done();
912 asprintf(&cmd_buf, "%s %s", menu_ptr->label, arg);
913 syslinux_run_command(cmd_buf);
914 return;
917 // printf("kernel = %p, size = %d\n", kernel, kernel_size);
919 // parse cmdline for "initrd" option
921 initrd_arg = menu_ptr->initrd;
923 s = s0 = strdup(arg);
925 while(*s && strncmp(s, "initrd=", sizeof "initrd=" - 1)) {
926 s = skip_spaces(skip_nonspaces(s));
929 if(*s) {
930 s += sizeof "initrd=" - 1;
931 *skip_nonspaces(s) = 0;
932 initrd_arg = s;
935 if(initrd_arg) {
936 initrd = initramfs_init();
938 while((t = strsep(&s, ","))) {
939 initrd_buf = load_one(t, &initrd_size);
941 if(!initrd_buf) {
942 printf("%s: read error\n", t);
943 free(s0);
944 return;
947 initramfs_add_data(initrd, initrd_buf, initrd_size, initrd_size, 4);
949 // printf("initrd = %p, size = %d\n", initrd_buf, initrd_size);
953 free(s0);
955 gfx_done();
957 syslinux_boot_linux(kernel, kernel_size, initrd, arg);