gfxboot: handle INITRD config line
[profile/ivi/syslinux.git] / com32 / gfxboot / gfxboot.c
1 /*
2  *
3  * gfxboot.c
4  *
5  * A com32 module to load gfxboot graphics.
6  *
7  * Copyright (c) 2009 Steffen Winterfeldt.
8  *
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.
14  *
15  */
16
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>
25 #include <ctype.h>
26
27 #include <syslinux/loadfile.h>
28 #include <syslinux/config.h>
29 #include <syslinux/linux.h>
30 #include <syslinux/boot.h>
31 #include <console.h>
32 #include <com32.h>
33
34
35 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
36 #define MAX_CONFIG_LINE_LEN     2048
37 #define MAX_CMDLINE_LEN         2048
38
39 // buffer for realmode callback
40 // must be at least block size; can in theory be larger than 4k, but there's
41 // not enough space left
42 #define REALMODE_BUF_SIZE       4096
43 #define LOWMEM_BUF_SIZE         65536
44
45 // gfxboot working memory in MB
46 #define GFX_MEMORY_SIZE         7
47
48 // read chunk size for progress bar
49 #define CHUNK_SIZE      (64 << 10)
50
51 // callback function numbers
52 #define GFX_CB_INIT             0
53 #define GFX_CB_DONE             1
54 #define GFX_CB_INPUT            2
55 #define GFX_CB_MENU_INIT        3
56 #define GFX_CB_INFOBOX_INIT     4
57 #define GFX_CB_INFOBOX_DONE     5
58 #define GFX_CB_PROGRESS_INIT    6
59 #define GFX_CB_PROGRESS_DONE    7
60 #define GFX_CB_PROGRESS_UPDATE  8
61 #define GFX_CB_PROGRESS_LIMIT   9               // unused
62 #define GFX_CB_PASSWORD_INIT    10
63 #define GFX_CB_PASSWORD_DONE    11
64
65 // real mode code chunk, will be placed into lowmem buffer
66 extern const char realmode_callback_start[], realmode_callback_end[];
67
68 // gets in the way
69 #undef linux
70
71
72 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
73 // gfxboot config data (64 bytes)
74 typedef struct __attribute__ ((packed)) {
75   uint8_t bootloader;           //  0: boot loader type (0: lilo, 1: syslinux, 2: grub)
76   uint8_t sector_shift;         //  1: sector shift
77   uint8_t media_type;           //  2: media type (0: disk, 1: floppy, 2: cdrom)
78   uint8_t failsafe;             //  3: turn on failsafe mode (bitmask)
79                                 //    0: SHIFT pressed
80                                 //    1: skip gfxboot
81                                 //    2: skip monitor detection
82   uint8_t sysconfig_size;       //  4: size of sysconfig data
83   uint8_t boot_drive;           //  5: BIOS boot drive
84   uint16_t callback;            //  6: offset to callback handler
85   uint16_t bootloader_seg;      //  8: code/data segment used by bootloader; must follow gfx_callback
86   uint16_t serial_port;         // 10: syslinux initialized serial port from 'serial' option
87   uint32_t user_info_0;         // 12: data for info box
88   uint32_t user_info_1;         // 16: data for info box
89   uint32_t bios_mem_size;       // 20: BIOS memory size (in bytes)
90   uint16_t xmem_0;              // 24: extended mem area 0 (start:size in MB; 12:4 bits) - obsolete
91   uint16_t xmem_1;              // 26: extended mem area 1 - obsolete
92   uint16_t xmem_2;              // 28: extended mem area 2 - obsolete
93   uint16_t xmem_3;              // 30: extended mem area 3 - obsolete
94   uint32_t file;                // 32: start of gfx file
95   uint32_t archive_start;       // 36: start of cpio archive
96   uint32_t archive_end;         // 40: end of cpio archive
97   uint32_t mem0_start;          // 44: low free memory start
98   uint32_t mem0_end;            // 48: low free memory end
99   uint32_t xmem_start;          // 52: extended mem start
100   uint32_t xmem_end;            // 56: extended mem end
101   uint16_t features;            // 60: feature flags returned by GFX_CB_INIT
102                                 //    0: GFX_CB_MENU_INIT accepts 32 bit addresses
103                                 //    1: knows about xmem_start, xmem_end
104   uint16_t reserved_1;          // 62:
105 } gfx_config_t;
106
107
108 // gfxboot menu description (18 bytes)
109 typedef struct __attribute__ ((packed)) {
110   uint16_t entries;
111   char *default_entry;
112   char *label_list;
113   uint16_t label_size;
114   char *arg_list;
115   uint16_t arg_size;
116 } gfx_menu_t;
117
118
119 // menu description
120 typedef struct menu_s {
121   struct menu_s *next;
122   char *label;          // config entry name
123   char *menu_label;     // text to show in boot menu
124   char *kernel;         // name of program to load
125   char *alt_kernel;     // alternative name in case user has replaced it
126   char *linux;          // de facto an alias for 'kernel'
127   char *localboot;      // boot from local disk
128   char *initrd;         // initrd as separate line (instead of as part of 'append')
129   char *append;         // kernel args
130   char *ipappend;       // append special pxelinux args (see doc)
131 } menu_t;
132
133
134 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
135 gfx_config_t gfx_config;
136 gfx_menu_t gfx_menu;
137
138 menu_t *menu;
139 menu_t *menu_default;
140 static menu_t *menu_ptr, **menu_next;
141
142 struct {
143   uint32_t jmp_table[12];
144   uint16_t code_seg;
145   char fname_buf[64];
146 } gfx;
147
148 void *lowmem_buf;
149
150 int timeout;
151
152 char cmdline[MAX_CMDLINE_LEN];
153
154 // progress bar is visible
155 unsigned progress_active;
156
157
158 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
159 void show_message(char *file);
160 char *get_config_file_name(void);
161 char *skip_nonspaces(char *s);
162 void chop_line(char *s);
163 int read_config_file(const char *filename);
164 unsigned magic_ok(unsigned char *buf, unsigned *code_size);
165 unsigned find_file(unsigned char *buf, unsigned len, unsigned *gfx_file_start, unsigned *file_len, unsigned *code_size);
166 int gfx_init(char *file);
167 int gfx_menu_init(void);
168 void gfx_done(void);
169 int gfx_input(void);
170 void gfx_infobox(int type, char *str1, char *str2);
171 void gfx_progress_init(ssize_t kernel_size, char *label);
172 void gfx_progress_update(ssize_t size);
173 void gfx_progress_done(void);
174 void *load_one(char *file, ssize_t *file_size);
175 void boot(int index);
176 void boot_entry(menu_t *menu_ptr, char *arg);
177
178
179 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
180 int main(int argc, char **argv)
181 {
182   int menu_index;
183   const union syslinux_derivative_info *sdi;
184
185   openconsole(&dev_stdcon_r, &dev_stdcon_w);
186
187   lowmem_buf = lmalloc(LOWMEM_BUF_SIZE);
188   if (!lowmem_buf) {
189     printf("Could not allocate memory.\n");
190     return 1;
191   }
192
193   sdi = syslinux_derivative_info();
194
195   gfx_config.sector_shift = sdi->disk.sector_shift;
196   gfx_config.boot_drive = sdi->disk.drive_number;
197
198   if(sdi->c.filesystem == SYSLINUX_FS_PXELINUX) {
199     gfx_config.sector_shift = 11;
200     gfx_config.boot_drive = 0;
201   }
202
203   gfx_config.media_type = gfx_config.boot_drive < 0x80 ? 1 : 0;
204
205   if(sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) {
206     gfx_config.media_type = sdi->iso.cd_mode ? 0 : 2;
207   }
208
209   gfx_config.bootloader = 1;
210   gfx_config.sysconfig_size = sizeof gfx_config;
211   gfx_config.bootloader_seg = 0;        // apparently not needed
212
213   if(argc < 2) {
214     printf("Usage: gfxboot.c32 bootlogo_file [message_file]\n");
215     if(argc > 2) show_message(argv[2]);
216
217     return 0;
218   }
219
220   if(read_config_file("~")) {
221     printf("Error reading config file\n");
222     if(argc > 2) show_message(argv[2]);
223
224     return 0;
225   }
226
227   if(gfx_init(argv[1])) {
228     printf("Error setting up gfxboot\n");
229     if(argc > 2) show_message(argv[2]);
230
231     return 0;
232   }
233
234   gfx_menu_init();
235
236   for(;;) {
237     menu_index = gfx_input();
238
239     // abort gfx, return to text mode prompt
240     if(menu_index == -1) {
241       gfx_done();
242       break;
243     }
244
245     // does not return if it succeeds
246     boot(menu_index);
247   }
248
249   if(argc > 2) show_message(argv[2]);
250
251   return 0;
252 }
253
254
255 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
256 void show_message(char *file)
257 {
258   int c;
259   FILE *f;
260
261   if(!(f = fopen(file, "r"))) return;
262
263   while((c = getc(f)) != EOF) {
264     if(c < ' ' && c != '\n' && c != '\t') continue;
265     printf("%c", c);
266   }
267
268   fclose(f);
269 }
270
271
272 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
273 char *skip_nonspaces(char *s)
274 {
275   while(*s && *s != ' ' && *s != '\t') s++;
276
277   return s;
278 }
279
280
281 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
282 void chop_line(char *s)
283 {
284   int i = strlen(s);
285
286   if(!i) return;
287
288   while(--i >= 0) {
289     if(s[i] == ' ' || s[i] == '\t' || s[i] == '\n') {
290       s[i] = 0;
291     }
292     else {
293       break;
294     }
295   }
296 }
297
298
299 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
300 // Read and parse syslinux config file.
301 //
302 // return:
303 //   0: ok, 1: error
304 //
305 int read_config_file(const char *filename)
306 {
307   FILE *f;
308   char *s, *t, buf[MAX_CONFIG_LINE_LEN];
309   unsigned u, top_level = 0, text = 0;
310
311   if(!strcmp(filename, "~")) {
312     top_level = 1;
313     filename = syslinux_config_file();
314     gfx_menu.entries = 0;
315     gfx_menu.label_size = 0;
316     gfx_menu.arg_size = 0;
317     menu_ptr = NULL;
318     menu_next = &menu;
319     menu_default = calloc(1, sizeof *menu_default);
320   }
321
322   if(!(f = fopen(filename, "r"))) return 1;
323
324   while((s = fgets(buf, sizeof buf, f))) {
325     chop_line(s);
326     s = skipspace(s);
327     if(!*s || *s == '#') continue;
328     t = skip_nonspaces(s);
329     if(*t) *t++ = 0;
330     t = skipspace(t);
331
332     if(!strcasecmp(s, "endtext")) {
333       text = 0;
334       continue;
335     }
336
337     if (text)
338       continue;
339
340     if(!strcasecmp(s, "timeout")) {
341       timeout = atoi(t);
342       continue;
343     }
344
345     if(!strcasecmp(s, "default")) {
346       menu_default->label = strdup(t);
347       u = strlen(t);
348       if(u > gfx_menu.label_size) gfx_menu.label_size = u;
349       continue;
350     }
351
352     if(!strcasecmp(s, "label")) {
353       menu_ptr = *menu_next = calloc(1, sizeof **menu_next);
354       menu_next = &menu_ptr->next;
355       gfx_menu.entries++;
356       menu_ptr->label = menu_ptr->menu_label = strdup(t);
357       u = strlen(t);
358       if(u > gfx_menu.label_size) gfx_menu.label_size = u;
359       continue;
360     }
361
362     if(!strcasecmp(s, "kernel") && menu_ptr) {
363       menu_ptr->kernel = strdup(t);
364       continue;
365     }
366
367     if(!strcasecmp(s, "linux") && menu_ptr) {
368       menu_ptr->linux = strdup(t);
369       continue;
370     }
371
372     if(!strcasecmp(s, "localboot") && menu_ptr) {
373       menu_ptr->localboot = strdup(t);
374       continue;
375     }
376
377     if(!strcasecmp(s, "initrd") && menu_ptr) {
378       menu_ptr->initrd = strdup(t);
379       continue;
380     }
381
382     if(!strcasecmp(s, "append")) {
383       (menu_ptr ?: menu_default)->append = strdup(t);
384       u = strlen(t);
385       if(u > gfx_menu.arg_size) gfx_menu.arg_size = u;
386       continue;
387     }
388
389     if(!strcasecmp(s, "ipappend")) {
390       (menu_ptr ?: menu_default)->ipappend = strdup(t);
391       continue;
392     }
393
394     if(!strcasecmp(s, "text")) {
395       text = 1;
396       continue;
397     }
398
399     if(!strcasecmp(s, "menu") && menu_ptr) {
400       s = skipspace(t);
401       t = skip_nonspaces(s);
402       if(*t) *t++ = 0;
403       t = skipspace(t);
404
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;
410       }
411
412       if(!strcasecmp(s, "include")) {
413         goto do_include;
414       }
415     }
416
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);
423     }
424   }
425
426   fclose(f);
427
428   if (!top_level)
429     return 0;
430
431   if (gfx_menu.entries == 0) {
432     printf("No LABEL keywords found.\n");
433     return 1;
434   }
435
436   // final '\0'
437   gfx_menu.label_size++;
438   gfx_menu.arg_size++;
439
440   // ensure we have a default entry
441   if(!menu_default->label) menu_default->label = menu->label;
442
443   if(menu_default->label) {
444     for(menu_ptr = menu; menu_ptr; menu_ptr = menu_ptr->next) {
445       if(!strcmp(menu_default->label, menu_ptr->label)) {
446         menu_default->menu_label = menu_ptr->menu_label;
447         break;
448       }
449     }
450   }
451
452   gfx_menu.default_entry = menu_default->menu_label;
453   gfx_menu.label_list = calloc(gfx_menu.entries, gfx_menu.label_size);
454   gfx_menu.arg_list = calloc(gfx_menu.entries, gfx_menu.arg_size);
455
456   for(u = 0, menu_ptr = menu; menu_ptr; menu_ptr = menu_ptr->next, u++) {
457     if(!menu_ptr->append) menu_ptr->append = menu_default->append;
458     if(!menu_ptr->ipappend) menu_ptr->ipappend = menu_default->ipappend;
459
460     if(menu_ptr->menu_label) strcpy(gfx_menu.label_list + u * gfx_menu.label_size, menu_ptr->menu_label);
461     if(menu_ptr->append) strcpy(gfx_menu.arg_list + u * gfx_menu.arg_size, menu_ptr->append);
462   }
463
464   return 0;
465 }
466
467
468 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
469 // Check header and return code start offset.
470 //
471 unsigned magic_ok(unsigned char *buf, unsigned *code_size)
472 {
473   if(
474     *(unsigned *) buf == 0x0b2d97f00 &&         // magic id
475     (buf[4] == 8)                               // version 8
476   ) {
477     *code_size = *(unsigned *) (buf + 12);
478     return *(unsigned *) (buf + 8);
479   }
480
481   return 0;
482 }
483
484
485 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
486 // Search (cpio archive) for gfx file.
487 //
488 unsigned find_file(unsigned char *buf, unsigned len, unsigned *gfx_file_start, unsigned *file_len, unsigned *code_size)
489 {
490   unsigned i, fname_len, code_start = 0;
491
492   *gfx_file_start = 0;
493   *code_size = 0;
494
495   if((code_start = magic_ok(buf, code_size))) return code_start;
496
497   for(i = 0; i < len;) {
498     if((len - i) >= 0x1a && (buf[i] + (buf[i + 1] << 8)) == 0x71c7) {
499       fname_len = *(unsigned short *) (buf + i + 20);
500       *file_len = *(unsigned short *) (buf + i + 24) + (*(unsigned short *) (buf + i + 22) << 16);
501       i += 26 + fname_len;
502       i = ((i + 1) & ~1);
503       if((code_start = magic_ok(buf + i, code_size))) {
504         *gfx_file_start = i;
505         return code_start;
506       }
507       i += *file_len;
508       i = ((i + 1) & ~1);
509     }
510     else {
511       break;
512     }
513   }
514
515   return code_start;
516 }
517
518
519 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
520 // Initialize gfxboot code.
521 //
522 // return:
523 //   0: ok, 1: error
524 //
525 int gfx_init(char *file)
526 {
527   size_t archive_size = 0;
528   void *archive;
529   unsigned code_start, code_size, file_start, file_len, u;
530   com32sys_t r;
531   void *lowmem = lowmem_buf;
532   unsigned lowmem_size = LOWMEM_BUF_SIZE;
533
534   progress_active = 0;
535
536   printf("Loading %s...\n", file);
537   if(loadfile(file, &archive, &archive_size)) return 1;
538
539   if(!archive_size) return 1;
540
541   // printf("%s: %d\n", file, archive_size);
542
543   gfx_config.archive_start = (uint32_t) archive;
544   gfx_config.archive_end = gfx_config.archive_start + archive_size;
545
546   // locate file inside cpio archive
547   if(!(code_start = find_file(archive, archive_size, &file_start, &file_len, &code_size))) {
548     printf("%s: invalid file format\n", file);
549     return 1;
550   }
551
552 #if 0
553   printf(
554     "code_start = 0x%x, code_size = 0x%x\n"
555     "archive_start = 0x%x, archive size = 0x%x\n"
556     "file_start = 0x%x, file_len = 0x%x\n",
557     code_start, code_size,
558     gfx_config.archive_start, archive_size,
559     file_start, file_len
560   );
561 #endif
562
563   gfx_config.file = gfx_config.archive_start + file_start;
564
565   u = realmode_callback_end - realmode_callback_start;
566   u = (u + REALMODE_BUF_SIZE + 0xf) & ~0xf;
567
568   if(u + code_size > lowmem_size) {
569     printf("lowmem buffer too small: size %u, needed %u\n", lowmem_size, u + code_size);
570     return 1;
571   }
572
573   memcpy(lowmem + REALMODE_BUF_SIZE, realmode_callback_start,
574          realmode_callback_end - realmode_callback_start);
575
576   // fill in buffer size and location
577   *(uint16_t *) (lowmem + REALMODE_BUF_SIZE) = REALMODE_BUF_SIZE;
578   *(uint16_t *) (lowmem + REALMODE_BUF_SIZE + 2) = (uint32_t) lowmem >> 4;
579
580   gfx_config.bootloader_seg = ((uint32_t) lowmem + REALMODE_BUF_SIZE) >> 4;
581   gfx_config.callback = 4;      // start address
582
583   lowmem += u;
584   lowmem_size -= u;
585
586   memcpy(lowmem, archive + file_start + code_start, code_size);
587
588   gfx_config.mem0_start = (uint32_t) lowmem + code_size;
589   gfx_config.mem0_end = (uint32_t) lowmem + lowmem_size;
590   // align a bit
591   gfx_config.mem0_start = (gfx_config.mem0_start + 0xf) & ~0xf;
592
593   gfx_config.xmem_start = (uint32_t) malloc(GFX_MEMORY_SIZE << 20);
594   if(gfx_config.xmem_start) {
595     gfx_config.xmem_end = gfx_config.xmem_start + (GFX_MEMORY_SIZE << 20);
596   }
597
598   // fake; not used anyway
599   gfx_config.bios_mem_size = 256 << 20;
600
601   gfx.code_seg = (uint32_t) lowmem >> 4;
602
603   for(u = 0; u < sizeof gfx.jmp_table / sizeof *gfx.jmp_table; u++) {
604     gfx.jmp_table[u] = (gfx.code_seg << 16) + *(uint16_t *) (lowmem + 2 * u);
605   }
606
607 #if 0
608   for(u = 0; u < sizeof gfx.jmp_table / sizeof *gfx.jmp_table; u++) {
609     printf("%d: 0x%08x\n", u, gfx.jmp_table[u]);
610   }
611 #endif
612
613   // we are ready to start
614
615   r.esi.l = (uint32_t) &gfx_config;
616   __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_INIT], &r, &r);
617
618   if((r.eflags.l & EFLAGS_CF)) {
619     printf("graphics initialization failed\n");
620
621     return 1;
622   }
623
624   if((gfx_config.features & 3) != 3) {
625     gfx_done();
626
627     printf("%s: boot graphics code too old, please use newer version\n", file);
628
629     return 1;
630   }
631
632
633   return 0;
634 }
635
636
637 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
638 int gfx_menu_init(void)
639 {
640   com32sys_t r;
641
642   r.esi.l = (uint32_t) &gfx_menu;
643   __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_MENU_INIT], &r, &r);
644
645   return 0;
646 }
647
648
649 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
650 void gfx_done(void)
651 {
652   com32sys_t r;
653
654   gfx_progress_done();
655
656   __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_DONE], &r, &r);
657 }
658
659
660 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
661 // Run gfxboot main loop.
662 //
663 // return:
664 //   boot menu index (-1: go to text mode prompt)
665 //
666 int gfx_input(void)
667 {
668   com32sys_t r;
669
670   r.edi.l = (uint32_t) cmdline;
671   r.ecx.l = sizeof cmdline;
672   r.eax.l = timeout * 182 / 100;
673   timeout = 0;          // use timeout only first time
674   __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_INPUT], &r, &r);
675   if((r.eflags.l & EFLAGS_CF)) r.eax.l = 1;
676
677   if(r.eax.l == 1) return -1;
678
679   return r.ebx.l;
680 }
681
682
683 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
684 void gfx_infobox(int type, char *str1, char *str2)
685 {
686   com32sys_t r;
687
688   r.eax.l = type;
689   r.esi.l = (uint32_t) str1;
690   r.edi.l = (uint32_t) str2;
691   __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_INFOBOX_INIT], &r, &r);
692   r.edi.l = r.eax.l = 0;
693   __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_INPUT], &r, &r);
694   __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_INFOBOX_DONE], &r, &r);
695 }
696
697
698 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
699 void gfx_progress_init(ssize_t kernel_size, char *label)
700 {
701   com32sys_t r;
702
703   if(!progress_active) {
704     r.eax.l = kernel_size >> gfx_config.sector_shift;           // in sectors
705     r.esi.l = (uint32_t) label;
706     __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_PROGRESS_INIT], &r, &r);
707   }
708
709   progress_active = 1;
710 }
711
712
713 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
714 void gfx_progress_update(ssize_t advance)
715 {
716   com32sys_t r;
717
718   if(progress_active) {
719     r.eax.l = advance >> gfx_config.sector_shift;               // in sectors
720     __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_PROGRESS_UPDATE], &r, &r);
721   }
722 }
723
724
725 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
726 void gfx_progress_done(void)
727 {
728   com32sys_t r;
729
730   if(progress_active) {
731     __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_PROGRESS_DONE], &r, &r);
732   }
733
734   progress_active = 0;
735 }
736
737
738 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
739 // Read file and update progress bar.
740 //
741 void *load_one(char *file, ssize_t *file_size)
742 {
743   int fd;
744   void *buf = NULL;
745   char *str;
746   struct stat sbuf;
747   ssize_t size = 0, cur, i;
748
749   *file_size = 0;
750
751   if((fd = open(file, O_RDONLY)) == -1) {
752     asprintf(&str, "%s: file not found", file);
753     gfx_infobox(0, str, NULL);
754     free(str);
755     return buf;
756   }
757
758   if(!fstat(fd, &sbuf) && S_ISREG(sbuf.st_mode)) size = sbuf.st_size;
759
760   i = 0;
761
762   if(size) {
763     buf = malloc(size);
764     for(i = 1, cur = 0 ; cur < size && i > 0; cur += i) {
765       i = read(fd, buf + cur, min(CHUNK_SIZE, size - cur));
766       if(i == -1) break;
767       gfx_progress_update(i);
768     }
769   }
770   else {
771     do {
772       buf = realloc(buf, size + CHUNK_SIZE);
773       i = read(fd, buf + size, CHUNK_SIZE);
774       if(i == -1) break;
775       size += i;
776       gfx_progress_update(i);
777     } while(i > 0);
778   }
779
780   close(fd);
781
782   if(i == -1) {
783     asprintf(&str, "%s: read error @ %d", file, size);
784     gfx_infobox(0, str, NULL);
785     free(str);
786     free(buf);
787     buf = NULL;
788     size = 0;
789   }
790
791   *file_size = size;
792
793   return buf;
794 }
795
796
797 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
798 // Boot menu entry.
799 //
800 // cmdline can optionally start with label string.
801 //
802 void boot(int index)
803 {
804   char *arg, *alt_kernel;
805   menu_t *menu_ptr;
806   int i, label_len;
807   unsigned ipapp;
808   const struct syslinux_ipappend_strings *ipappend;
809
810   for(menu_ptr = menu; menu_ptr; menu_ptr = menu_ptr->next, index--) {
811     if(!index) break;
812   }
813
814   // invalid index or menu entry
815   if(!menu_ptr || !menu_ptr->menu_label) return;
816
817   arg = skipspace(cmdline);
818   label_len = strlen(menu_ptr->menu_label);
819
820   // if it does not start with label string, assume first word is kernel name
821   if(strncmp(arg, menu_ptr->menu_label, label_len)) {
822     alt_kernel = arg;
823     arg = skip_nonspaces(arg);
824     if(*arg) *arg++ = 0;
825     if(*alt_kernel) menu_ptr->alt_kernel = alt_kernel;
826   }
827   else {
828     arg += label_len;
829   }
830
831   arg = skipspace(arg);
832
833   // handle IPAPPEND
834   if(menu_ptr->ipappend && (ipapp = atoi(menu_ptr->ipappend))) {
835     ipappend = syslinux_ipappend_strings();
836     for(i = 0; i < ipappend->count; i++) {
837       if((ipapp & (1 << i)) && ipappend->ptr[i]) {
838         sprintf(arg + strlen(arg), " %s", ipappend->ptr[i]);
839       }
840     }
841   }
842
843   boot_entry(menu_ptr, arg);
844
845   gfx_progress_done();
846 }
847
848
849 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
850 // Load & run kernel.
851 //
852 // Returns only on error.
853 //
854 void boot_entry(menu_t *menu_ptr, char *arg)
855 {
856   void *kernel, *initrd_buf;
857   ssize_t kernel_size = 0, initrd_size = 0;
858   struct initramfs *initrd = NULL;
859   char *file, *cmd_buf;
860   int fd;
861   struct stat sbuf;
862   char *s, *s0, *t, *initrd_arg;
863
864   if(!menu_ptr) return;
865
866   if(menu_ptr->localboot) {
867     gfx_done();
868     syslinux_local_boot(strtol(menu_ptr->localboot, NULL, 0));
869
870     return;
871   }
872
873   file = menu_ptr->alt_kernel;
874   if(!file) file = menu_ptr->kernel;
875   if(!file) file = menu_ptr->linux;
876   if(!file) {
877     gfx_done();
878     asprintf(&cmd_buf, "%s %s", menu_ptr->label, arg);
879     syslinux_run_command(cmd_buf);
880     return;
881   }
882
883   // first, load kernel
884
885   kernel_size = 0;
886
887   if((fd = open(file, O_RDONLY)) >= 0) {
888     if(!fstat(fd, &sbuf) && S_ISREG(sbuf.st_mode)) kernel_size = sbuf.st_size;
889     close(fd);
890   }
891
892   gfx_progress_init(kernel_size, file);
893
894   kernel = load_one(file, &kernel_size);
895
896   if(!kernel) {
897     return;
898   }
899
900   if(kernel_size < 1024 || *(uint32_t *) (kernel + 0x202) != 0x53726448) {
901     // not a linux kernel
902     gfx_done();
903     asprintf(&cmd_buf, "%s %s", menu_ptr->label, arg);
904     syslinux_run_command(cmd_buf);
905     return;
906   }
907
908   // printf("kernel = %p, size = %d\n", kernel, kernel_size);
909
910   // parse cmdline for "initrd" option
911
912   initrd_arg = menu_ptr->initrd;
913
914   s = s0 = strdup(arg);
915
916   while(*s && strncmp(s, "initrd=", sizeof "initrd=" - 1)) {
917     s = skipspace(skip_nonspaces(s));
918   }
919
920   if(*s) {
921     s += sizeof "initrd=" - 1;
922     *skip_nonspaces(s) = 0;
923     initrd_arg = s;
924   }
925   else if(initrd_arg) {
926     free(s0);
927     initrd_arg = s0 = strdup(initrd_arg);
928   }
929
930   if(initrd_arg) {
931     initrd = initramfs_init();
932
933     while((t = strsep(&initrd_arg, ","))) {
934       initrd_buf = load_one(t, &initrd_size);
935
936       if(!initrd_buf) {
937         printf("%s: read error\n", t);
938         free(s0);
939         return;
940       }
941
942       initramfs_add_data(initrd, initrd_buf, initrd_size, initrd_size, 4);
943
944       // printf("initrd = %p, size = %d\n", initrd_buf, initrd_size);
945     }
946   }
947
948   free(s0);
949
950   gfx_done();
951
952   syslinux_boot_linux(kernel, kernel_size, initrd, arg);
953 }
954
955