• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
main(int argc,char ** argv)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 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
show_message(char * file)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 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
skip_nonspaces(char * s)280 char *skip_nonspaces(char *s)
281 {
282   while(*s && *s != ' ' && *s != '\t') s++;
283 
284   return s;
285 }
286 
287 
288 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
chop_line(char * s)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 //
read_config_file(const char * filename)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 //
magic_ok(unsigned char * buf,unsigned * code_size)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 //
find_file(unsigned char * buf,unsigned len,unsigned * gfx_file_start,unsigned * file_len,unsigned * code_size)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 //
gfx_init(char * file)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 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
gfx_menu_init(void)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 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
gfx_done(void)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 //
gfx_input(void)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 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
gfx_infobox(int type,char * str1,char * str2)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 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
gfx_progress_init(ssize_t kernel_size,char * label)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 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
gfx_progress_update(ssize_t advance)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 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
gfx_progress_done(void)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 //
load_one(char * file,ssize_t * file_size)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 //
boot(int index)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 //
boot_entry(menu_t * menu_ptr,char * arg)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