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