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