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