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