| /* |
| * |
| * gfxboot.c |
| * |
| * A com32 module to load gfxboot graphics. |
| * |
| * Copyright (c) 2009 Steffen Winterfeldt. |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation, Inc., 53 Temple Place Ste 330, Boston MA |
| * 02111-1307, USA; either version 2 of the License, or (at your option) any |
| * later version; incorporated herein by reference. |
| * |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <fcntl.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <minmax.h> |
| #include <ctype.h> |
| |
| #include <syslinux/loadfile.h> |
| #include <syslinux/config.h> |
| #include <syslinux/linux.h> |
| #include <syslinux/boot.h> |
| #include <console.h> |
| #include <com32.h> |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| #define MAX_CONFIG_LINE_LEN 2048 |
| #define MAX_CMDLINE_LEN 2048 |
| |
| // buffer for realmode callback |
| // must be at least block size; can in theory be larger than 4k, but there's |
| // not enough space left |
| #define REALMODE_BUF_SIZE 4096 |
| #define LOWMEM_BUF_SIZE 65536 |
| |
| // gfxboot working memory in MB |
| #define GFX_MEMORY_SIZE 7 |
| |
| // read chunk size for progress bar |
| #define CHUNK_SIZE (64 << 10) |
| |
| // callback function numbers |
| #define GFX_CB_INIT 0 |
| #define GFX_CB_DONE 1 |
| #define GFX_CB_INPUT 2 |
| #define GFX_CB_MENU_INIT 3 |
| #define GFX_CB_INFOBOX_INIT 4 |
| #define GFX_CB_INFOBOX_DONE 5 |
| #define GFX_CB_PROGRESS_INIT 6 |
| #define GFX_CB_PROGRESS_DONE 7 |
| #define GFX_CB_PROGRESS_UPDATE 8 |
| #define GFX_CB_PROGRESS_LIMIT 9 // unused |
| #define GFX_CB_PASSWORD_INIT 10 |
| #define GFX_CB_PASSWORD_DONE 11 |
| |
| // real mode code chunk, will be placed into lowmem buffer |
| extern const char realmode_callback_start[], realmode_callback_end[]; |
| |
| // gets in the way |
| #undef linux |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| // gfxboot config data (64 bytes) |
| typedef struct __attribute__ ((packed)) { |
| uint8_t bootloader; // 0: boot loader type (0: lilo, 1: syslinux, 2: grub) |
| uint8_t sector_shift; // 1: sector shift |
| uint8_t media_type; // 2: media type (0: disk, 1: floppy, 2: cdrom) |
| uint8_t failsafe; // 3: turn on failsafe mode (bitmask) |
| // 0: SHIFT pressed |
| // 1: skip gfxboot |
| // 2: skip monitor detection |
| uint8_t sysconfig_size; // 4: size of sysconfig data |
| uint8_t boot_drive; // 5: BIOS boot drive |
| uint16_t callback; // 6: offset to callback handler |
| uint16_t bootloader_seg; // 8: code/data segment used by bootloader; must follow gfx_callback |
| uint16_t serial_port; // 10: syslinux initialized serial port from 'serial' option |
| uint32_t user_info_0; // 12: data for info box |
| uint32_t user_info_1; // 16: data for info box |
| uint32_t bios_mem_size; // 20: BIOS memory size (in bytes) |
| uint16_t xmem_0; // 24: extended mem area 0 (start:size in MB; 12:4 bits) - obsolete |
| uint16_t xmem_1; // 26: extended mem area 1 - obsolete |
| uint16_t xmem_2; // 28: extended mem area 2 - obsolete |
| uint16_t xmem_3; // 30: extended mem area 3 - obsolete |
| uint32_t file; // 32: start of gfx file |
| uint32_t archive_start; // 36: start of cpio archive |
| uint32_t archive_end; // 40: end of cpio archive |
| uint32_t mem0_start; // 44: low free memory start |
| uint32_t mem0_end; // 48: low free memory end |
| uint32_t xmem_start; // 52: extended mem start |
| uint32_t xmem_end; // 56: extended mem end |
| uint16_t features; // 60: feature flags returned by GFX_CB_INIT |
| // 0: GFX_CB_MENU_INIT accepts 32 bit addresses |
| // 1: knows about xmem_start, xmem_end |
| uint16_t reserved_1; // 62: |
| uint32_t gfxboot_cwd; // 64: if set, points to current gfxboot working directory relative |
| // to syslinux working directory |
| } gfx_config_t; |
| |
| |
| // gfxboot menu description (18 bytes) |
| typedef struct __attribute__ ((packed)) { |
| uint16_t entries; |
| char *default_entry; |
| char *label_list; |
| uint16_t label_size; |
| char *arg_list; |
| uint16_t arg_size; |
| } gfx_menu_t; |
| |
| |
| // menu description |
| typedef struct menu_s { |
| struct menu_s *next; |
| char *label; // config entry name |
| char *menu_label; // text to show in boot menu |
| char *kernel; // name of program to load |
| char *alt_kernel; // alternative name in case user has replaced it |
| char *linux; // de facto an alias for 'kernel' |
| char *localboot; // boot from local disk |
| char *initrd; // initrd as separate line (instead of as part of 'append') |
| char *append; // kernel args |
| char *ipappend; // append special pxelinux args (see doc) |
| } menu_t; |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| gfx_config_t gfx_config; |
| gfx_menu_t gfx_menu; |
| |
| menu_t *menu; |
| menu_t *menu_default; |
| static menu_t *menu_ptr, **menu_next; |
| |
| struct { |
| uint32_t jmp_table[12]; |
| uint16_t code_seg; |
| char fname_buf[64]; |
| } gfx; |
| |
| void *lowmem_buf; |
| |
| int timeout; |
| |
| char cmdline[MAX_CMDLINE_LEN]; |
| |
| // progress bar is visible |
| unsigned progress_active; |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| void show_message(char *file); |
| char *get_config_file_name(void); |
| char *skip_nonspaces(char *s); |
| void chop_line(char *s); |
| int read_config_file(const char *filename); |
| unsigned magic_ok(unsigned char *buf, unsigned *code_size); |
| unsigned find_file(unsigned char *buf, unsigned len, unsigned *gfx_file_start, unsigned *file_len, unsigned *code_size); |
| int gfx_init(char *file); |
| int gfx_menu_init(void); |
| void gfx_done(void); |
| int gfx_input(void); |
| void gfx_infobox(int type, char *str1, char *str2); |
| void gfx_progress_init(ssize_t kernel_size, char *label); |
| void gfx_progress_update(ssize_t size); |
| void gfx_progress_done(void); |
| void *load_one(char *file, ssize_t *file_size); |
| void boot(int index); |
| void boot_entry(menu_t *menu_ptr, char *arg); |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| int main(int argc, char **argv) |
| { |
| int menu_index; |
| const union syslinux_derivative_info *sdi; |
| char working_dir[256]; |
| |
| openconsole(&dev_stdcon_r, &dev_stdcon_w); |
| |
| lowmem_buf = lmalloc(LOWMEM_BUF_SIZE); |
| if (!lowmem_buf) { |
| printf("Could not allocate memory.\n"); |
| return 1; |
| } |
| |
| sdi = syslinux_derivative_info(); |
| |
| gfx_config.sector_shift = sdi->disk.sector_shift; |
| gfx_config.boot_drive = sdi->disk.drive_number; |
| |
| if(sdi->c.filesystem == SYSLINUX_FS_PXELINUX) { |
| gfx_config.sector_shift = 11; |
| gfx_config.boot_drive = 0; |
| } |
| |
| gfx_config.media_type = gfx_config.boot_drive < 0x80 ? 1 : 0; |
| |
| if(sdi->c.filesystem == SYSLINUX_FS_ISOLINUX) { |
| gfx_config.media_type = sdi->iso.cd_mode ? 0 : 2; |
| } |
| |
| gfx_config.bootloader = 1; |
| gfx_config.sysconfig_size = sizeof gfx_config; |
| gfx_config.bootloader_seg = 0; // apparently not needed |
| |
| if(argc < 2) { |
| printf("Usage: gfxboot.c32 bootlogo_file [message_file]\n"); |
| if(argc > 2) show_message(argv[2]); |
| |
| return 0; |
| } |
| |
| if(read_config_file("~")) { |
| printf("Error reading config file\n"); |
| if(argc > 2) show_message(argv[2]); |
| |
| return 0; |
| } |
| |
| if(getcwd(working_dir, sizeof working_dir)) { |
| gfx_config.gfxboot_cwd = (uint32_t) working_dir; |
| } |
| |
| if(gfx_init(argv[1])) { |
| printf("Error setting up gfxboot\n"); |
| if(argc > 2) show_message(argv[2]); |
| |
| return 0; |
| } |
| |
| gfx_menu_init(); |
| |
| for(;;) { |
| menu_index = gfx_input(); |
| |
| // abort gfx, return to text mode prompt |
| if(menu_index == -1) { |
| gfx_done(); |
| break; |
| } |
| |
| // does not return if it succeeds |
| boot(menu_index); |
| } |
| |
| if(argc > 2) show_message(argv[2]); |
| |
| return 0; |
| } |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| void show_message(char *file) |
| { |
| int c; |
| FILE *f; |
| |
| if(!(f = fopen(file, "r"))) return; |
| |
| while((c = getc(f)) != EOF) { |
| if(c < ' ' && c != '\n' && c != '\t') continue; |
| printf("%c", c); |
| } |
| |
| fclose(f); |
| } |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| char *skip_nonspaces(char *s) |
| { |
| while(*s && *s != ' ' && *s != '\t') s++; |
| |
| return s; |
| } |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| void chop_line(char *s) |
| { |
| int i = strlen(s); |
| |
| if(!i) return; |
| |
| while(--i >= 0) { |
| if(s[i] == ' ' || s[i] == '\t' || s[i] == '\n') { |
| s[i] = 0; |
| } |
| else { |
| break; |
| } |
| } |
| } |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| // Read and parse syslinux config file. |
| // |
| // return: |
| // 0: ok, 1: error |
| // |
| int read_config_file(const char *filename) |
| { |
| FILE *f; |
| char *s, *t, buf[MAX_CONFIG_LINE_LEN]; |
| unsigned u, top_level = 0, text = 0; |
| |
| if(!strcmp(filename, "~")) { |
| top_level = 1; |
| filename = syslinux_config_file(); |
| gfx_menu.entries = 0; |
| gfx_menu.label_size = 0; |
| gfx_menu.arg_size = 0; |
| menu_ptr = NULL; |
| menu_next = &menu; |
| menu_default = calloc(1, sizeof *menu_default); |
| } |
| |
| if(!(f = fopen(filename, "r"))) return 1; |
| |
| while((s = fgets(buf, sizeof buf, f))) { |
| chop_line(s); |
| s = skipspace(s); |
| if(!*s || *s == '#') continue; |
| t = skip_nonspaces(s); |
| if(*t) *t++ = 0; |
| t = skipspace(t); |
| |
| if(!strcasecmp(s, "endtext")) { |
| text = 0; |
| continue; |
| } |
| |
| if (text) |
| continue; |
| |
| if(!strcasecmp(s, "timeout")) { |
| timeout = atoi(t); |
| continue; |
| } |
| |
| if(!strcasecmp(s, "default")) { |
| menu_default->label = strdup(t); |
| u = strlen(t); |
| if(u > gfx_menu.label_size) gfx_menu.label_size = u; |
| continue; |
| } |
| |
| if(!strcasecmp(s, "label")) { |
| menu_ptr = *menu_next = calloc(1, sizeof **menu_next); |
| menu_next = &menu_ptr->next; |
| gfx_menu.entries++; |
| menu_ptr->label = menu_ptr->menu_label = strdup(t); |
| u = strlen(t); |
| if(u > gfx_menu.label_size) gfx_menu.label_size = u; |
| continue; |
| } |
| |
| if(!strcasecmp(s, "kernel") && menu_ptr) { |
| menu_ptr->kernel = strdup(t); |
| continue; |
| } |
| |
| if(!strcasecmp(s, "linux") && menu_ptr) { |
| menu_ptr->linux = strdup(t); |
| continue; |
| } |
| |
| if(!strcasecmp(s, "localboot") && menu_ptr) { |
| menu_ptr->localboot = strdup(t); |
| continue; |
| } |
| |
| if(!strcasecmp(s, "initrd") && menu_ptr) { |
| menu_ptr->initrd = strdup(t); |
| continue; |
| } |
| |
| if(!strcasecmp(s, "append")) { |
| (menu_ptr ?: menu_default)->append = strdup(t); |
| u = strlen(t); |
| if(u > gfx_menu.arg_size) gfx_menu.arg_size = u; |
| continue; |
| } |
| |
| if(!strcasecmp(s, "ipappend") || !strcasecmp(s, "sysappend")) { |
| (menu_ptr ?: menu_default)->ipappend = strdup(t); |
| continue; |
| } |
| |
| if(!strcasecmp(s, "text")) { |
| text = 1; |
| continue; |
| } |
| |
| if(!strcasecmp(s, "menu") && menu_ptr) { |
| s = skipspace(t); |
| t = skip_nonspaces(s); |
| if(*t) *t++ = 0; |
| t = skipspace(t); |
| |
| if(!strcasecmp(s, "label")) { |
| menu_ptr->menu_label = strdup(t); |
| u = strlen(t); |
| if(u > gfx_menu.label_size) gfx_menu.label_size = u; |
| continue; |
| } |
| |
| if(!strcasecmp(s, "include")) { |
| goto do_include; |
| } |
| } |
| |
| if (!strcasecmp(s, "include")) { |
| do_include: |
| s = t; |
| t = skip_nonspaces(s); |
| if (*t) *t = 0; |
| read_config_file(s); |
| } |
| } |
| |
| fclose(f); |
| |
| if (!top_level) |
| return 0; |
| |
| if (gfx_menu.entries == 0) { |
| printf("No LABEL keywords found.\n"); |
| return 1; |
| } |
| |
| // final '\0' |
| gfx_menu.label_size++; |
| gfx_menu.arg_size++; |
| |
| // ensure we have a default entry |
| if(!menu_default->label) menu_default->label = menu->label; |
| |
| if(menu_default->label) { |
| for(menu_ptr = menu; menu_ptr; menu_ptr = menu_ptr->next) { |
| if(!strcmp(menu_default->label, menu_ptr->label)) { |
| menu_default->menu_label = menu_ptr->menu_label; |
| break; |
| } |
| } |
| } |
| |
| gfx_menu.default_entry = menu_default->menu_label; |
| gfx_menu.label_list = calloc(gfx_menu.entries, gfx_menu.label_size); |
| gfx_menu.arg_list = calloc(gfx_menu.entries, gfx_menu.arg_size); |
| |
| for(u = 0, menu_ptr = menu; menu_ptr; menu_ptr = menu_ptr->next, u++) { |
| if(!menu_ptr->append) menu_ptr->append = menu_default->append; |
| if(!menu_ptr->ipappend) menu_ptr->ipappend = menu_default->ipappend; |
| |
| if(menu_ptr->menu_label) strcpy(gfx_menu.label_list + u * gfx_menu.label_size, menu_ptr->menu_label); |
| if(menu_ptr->append) strcpy(gfx_menu.arg_list + u * gfx_menu.arg_size, menu_ptr->append); |
| } |
| |
| return 0; |
| } |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| // Check header and return code start offset. |
| // |
| unsigned magic_ok(unsigned char *buf, unsigned *code_size) |
| { |
| if( |
| *(unsigned *) buf == 0x0b2d97f00 && // magic id |
| (buf[4] == 8) // version 8 |
| ) { |
| *code_size = *(unsigned *) (buf + 12); |
| return *(unsigned *) (buf + 8); |
| } |
| |
| return 0; |
| } |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| // Search (cpio archive) for gfx file. |
| // |
| unsigned find_file(unsigned char *buf, unsigned len, unsigned *gfx_file_start, unsigned *file_len, unsigned *code_size) |
| { |
| unsigned i, fname_len, code_start = 0; |
| |
| *gfx_file_start = 0; |
| *code_size = 0; |
| |
| if((code_start = magic_ok(buf, code_size))) return code_start; |
| |
| for(i = 0; i < len;) { |
| if((len - i) >= 0x1a && (buf[i] + (buf[i + 1] << 8)) == 0x71c7) { |
| fname_len = *(unsigned short *) (buf + i + 20); |
| *file_len = *(unsigned short *) (buf + i + 24) + (*(unsigned short *) (buf + i + 22) << 16); |
| i += 26 + fname_len; |
| i = ((i + 1) & ~1); |
| if((code_start = magic_ok(buf + i, code_size))) { |
| *gfx_file_start = i; |
| return code_start; |
| } |
| i += *file_len; |
| i = ((i + 1) & ~1); |
| } |
| else { |
| break; |
| } |
| } |
| |
| return code_start; |
| } |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| // Initialize gfxboot code. |
| // |
| // return: |
| // 0: ok, 1: error |
| // |
| int gfx_init(char *file) |
| { |
| size_t archive_size = 0; |
| void *archive; |
| unsigned code_start, code_size, file_start, file_len, u; |
| com32sys_t r; |
| void *lowmem = lowmem_buf; |
| unsigned lowmem_size = LOWMEM_BUF_SIZE; |
| |
| memset(&r,0,sizeof(r)); |
| progress_active = 0; |
| |
| printf("Loading %s...\n", file); |
| if(loadfile(file, &archive, &archive_size)) return 1; |
| |
| if(!archive_size) return 1; |
| |
| // printf("%s: %d\n", file, archive_size); |
| |
| gfx_config.archive_start = (uint32_t) archive; |
| gfx_config.archive_end = gfx_config.archive_start + archive_size; |
| |
| // locate file inside cpio archive |
| if(!(code_start = find_file(archive, archive_size, &file_start, &file_len, &code_size))) { |
| printf("%s: invalid file format\n", file); |
| return 1; |
| } |
| |
| #if 0 |
| printf( |
| "code_start = 0x%x, code_size = 0x%x\n" |
| "archive_start = 0x%x, archive size = 0x%x\n" |
| "file_start = 0x%x, file_len = 0x%x\n", |
| code_start, code_size, |
| gfx_config.archive_start, archive_size, |
| file_start, file_len |
| ); |
| #endif |
| |
| gfx_config.file = gfx_config.archive_start + file_start; |
| |
| u = realmode_callback_end - realmode_callback_start; |
| u = (u + REALMODE_BUF_SIZE + 0xf) & ~0xf; |
| |
| if(u + code_size > lowmem_size) { |
| printf("lowmem buffer too small: size %u, needed %u\n", lowmem_size, u + code_size); |
| return 1; |
| } |
| |
| memcpy(lowmem + REALMODE_BUF_SIZE, realmode_callback_start, |
| realmode_callback_end - realmode_callback_start); |
| |
| // fill in buffer size and location |
| *(uint16_t *) (lowmem + REALMODE_BUF_SIZE) = REALMODE_BUF_SIZE; |
| *(uint16_t *) (lowmem + REALMODE_BUF_SIZE + 2) = (uint32_t) lowmem >> 4; |
| |
| gfx_config.bootloader_seg = ((uint32_t) lowmem + REALMODE_BUF_SIZE) >> 4; |
| gfx_config.callback = 4; // start address |
| |
| lowmem += u; |
| lowmem_size -= u; |
| |
| memcpy(lowmem, archive + file_start + code_start, code_size); |
| |
| gfx_config.mem0_start = (uint32_t) lowmem + code_size; |
| gfx_config.mem0_end = (uint32_t) lowmem + lowmem_size; |
| // align a bit |
| gfx_config.mem0_start = (gfx_config.mem0_start + 0xf) & ~0xf; |
| |
| gfx_config.xmem_start = (uint32_t) malloc(GFX_MEMORY_SIZE << 20); |
| if(gfx_config.xmem_start) { |
| gfx_config.xmem_end = gfx_config.xmem_start + (GFX_MEMORY_SIZE << 20); |
| } |
| |
| // fake; not used anyway |
| gfx_config.bios_mem_size = 256 << 20; |
| |
| gfx.code_seg = (uint32_t) lowmem >> 4; |
| |
| for(u = 0; u < sizeof gfx.jmp_table / sizeof *gfx.jmp_table; u++) { |
| gfx.jmp_table[u] = (gfx.code_seg << 16) + *(uint16_t *) (lowmem + 2 * u); |
| } |
| |
| #if 0 |
| for(u = 0; u < sizeof gfx.jmp_table / sizeof *gfx.jmp_table; u++) { |
| printf("%d: 0x%08x\n", u, gfx.jmp_table[u]); |
| } |
| #endif |
| |
| // we are ready to start |
| |
| r.esi.l = (uint32_t) &gfx_config; |
| __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_INIT], &r, &r); |
| |
| if((r.eflags.l & EFLAGS_CF)) { |
| printf("graphics initialization failed\n"); |
| |
| return 1; |
| } |
| |
| if((gfx_config.features & 3) != 3) { |
| gfx_done(); |
| |
| printf("%s: boot graphics code too old, please use newer version\n", file); |
| |
| return 1; |
| } |
| |
| |
| return 0; |
| } |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| int gfx_menu_init(void) |
| { |
| com32sys_t r; |
| |
| memset(&r,0,sizeof(r)); |
| r.esi.l = (uint32_t) &gfx_menu; |
| __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_MENU_INIT], &r, &r); |
| |
| return 0; |
| } |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| void gfx_done(void) |
| { |
| com32sys_t r; |
| |
| memset(&r,0,sizeof(r)); |
| gfx_progress_done(); |
| |
| __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_DONE], &r, &r); |
| } |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| // Run gfxboot main loop. |
| // |
| // return: |
| // boot menu index (-1: go to text mode prompt) |
| // |
| int gfx_input(void) |
| { |
| com32sys_t r; |
| |
| memset(&r,0,sizeof(r)); |
| r.edi.l = (uint32_t) cmdline; |
| r.ecx.l = sizeof cmdline; |
| r.eax.l = timeout * 182 / 100; |
| timeout = 0; // use timeout only first time |
| __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_INPUT], &r, &r); |
| if((r.eflags.l & EFLAGS_CF)) r.eax.l = 1; |
| |
| if(r.eax.l == 1) return -1; |
| |
| return r.ebx.l; |
| } |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| void gfx_infobox(int type, char *str1, char *str2) |
| { |
| com32sys_t r; |
| |
| memset(&r,0,sizeof(r)); |
| r.eax.l = type; |
| r.esi.l = (uint32_t) str1; |
| r.edi.l = (uint32_t) str2; |
| __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_INFOBOX_INIT], &r, &r); |
| r.edi.l = r.eax.l = 0; |
| __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_INPUT], &r, &r); |
| __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_INFOBOX_DONE], &r, &r); |
| } |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| void gfx_progress_init(ssize_t kernel_size, char *label) |
| { |
| com32sys_t r; |
| |
| memset(&r,0,sizeof(r)); |
| if(!progress_active) { |
| r.eax.l = kernel_size >> gfx_config.sector_shift; // in sectors |
| r.esi.l = (uint32_t) label; |
| __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_PROGRESS_INIT], &r, &r); |
| } |
| |
| progress_active = 1; |
| } |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| void gfx_progress_update(ssize_t advance) |
| { |
| com32sys_t r; |
| |
| memset(&r,0,sizeof(r)); |
| if(progress_active) { |
| r.eax.l = advance >> gfx_config.sector_shift; // in sectors |
| __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_PROGRESS_UPDATE], &r, &r); |
| } |
| } |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| void gfx_progress_done(void) |
| { |
| com32sys_t r; |
| |
| memset(&r,0,sizeof(r)); |
| if(progress_active) { |
| __farcall(gfx.code_seg, gfx.jmp_table[GFX_CB_PROGRESS_DONE], &r, &r); |
| } |
| |
| progress_active = 0; |
| } |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| // Read file and update progress bar. |
| // |
| void *load_one(char *file, ssize_t *file_size) |
| { |
| int fd; |
| void *buf = NULL; |
| char *str; |
| struct stat sbuf; |
| ssize_t size = 0, cur, i; |
| |
| *file_size = 0; |
| |
| if((fd = open(file, O_RDONLY)) == -1) { |
| asprintf(&str, "%s: file not found", file); |
| gfx_infobox(0, str, NULL); |
| free(str); |
| return buf; |
| } |
| |
| if(!fstat(fd, &sbuf) && S_ISREG(sbuf.st_mode)) size = sbuf.st_size; |
| |
| i = 0; |
| |
| if(size) { |
| buf = malloc(size); |
| for(i = 1, cur = 0 ; cur < size && i > 0; cur += i) { |
| i = read(fd, buf + cur, min(CHUNK_SIZE, size - cur)); |
| if(i == -1) break; |
| gfx_progress_update(i); |
| } |
| } |
| else { |
| do { |
| buf = realloc(buf, size + CHUNK_SIZE); |
| i = read(fd, buf + size, CHUNK_SIZE); |
| if(i == -1) break; |
| size += i; |
| gfx_progress_update(i); |
| } while(i > 0); |
| } |
| |
| close(fd); |
| |
| if(i == -1) { |
| asprintf(&str, "%s: read error @ %d", file, size); |
| gfx_infobox(0, str, NULL); |
| free(str); |
| free(buf); |
| buf = NULL; |
| size = 0; |
| } |
| |
| *file_size = size; |
| |
| return buf; |
| } |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| // Boot menu entry. |
| // |
| // cmdline can optionally start with label string. |
| // |
| void boot(int index) |
| { |
| char *arg, *alt_kernel; |
| menu_t *menu_ptr; |
| int i, label_len; |
| unsigned ipapp; |
| const struct syslinux_ipappend_strings *ipappend; |
| char *gfxboot_cwd = (char *) gfx_config.gfxboot_cwd; |
| |
| if(gfxboot_cwd) { |
| chdir(gfxboot_cwd); |
| gfx_config.gfxboot_cwd = 0; |
| } |
| |
| for(menu_ptr = menu; menu_ptr; menu_ptr = menu_ptr->next, index--) { |
| if(!index) break; |
| } |
| |
| // invalid index or menu entry |
| if(!menu_ptr || !menu_ptr->menu_label) return; |
| |
| arg = skipspace(cmdline); |
| label_len = strlen(menu_ptr->menu_label); |
| |
| // if it does not start with label string, assume first word is kernel name |
| if(strncmp(arg, menu_ptr->menu_label, label_len)) { |
| alt_kernel = arg; |
| arg = skip_nonspaces(arg); |
| if(*arg) *arg++ = 0; |
| if(*alt_kernel) menu_ptr->alt_kernel = alt_kernel; |
| } |
| else { |
| arg += label_len; |
| } |
| |
| arg = skipspace(arg); |
| |
| // handle IPAPPEND |
| if(menu_ptr->ipappend && (ipapp = atoi(menu_ptr->ipappend))) { |
| ipappend = syslinux_ipappend_strings(); |
| for(i = 0; i < ipappend->count; i++) { |
| if((ipapp & (1 << i)) && ipappend->ptr[i]) { |
| sprintf(arg + strlen(arg), " %s", ipappend->ptr[i]); |
| } |
| } |
| } |
| |
| boot_entry(menu_ptr, arg); |
| |
| gfx_progress_done(); |
| } |
| |
| |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| // Load & run kernel. |
| // |
| // Returns only on error. |
| // |
| void boot_entry(menu_t *menu_ptr, char *arg) |
| { |
| void *kernel, *initrd_buf; |
| ssize_t kernel_size = 0, initrd_size = 0; |
| struct initramfs *initrd = NULL; |
| char *file, *cmd_buf; |
| int fd; |
| struct stat sbuf; |
| char *s, *s0, *t, *initrd_arg; |
| |
| if(!menu_ptr) return; |
| |
| if(menu_ptr->localboot) { |
| gfx_done(); |
| syslinux_local_boot(strtol(menu_ptr->localboot, NULL, 0)); |
| |
| return; |
| } |
| |
| file = menu_ptr->alt_kernel; |
| if(!file) file = menu_ptr->kernel; |
| if(!file) file = menu_ptr->linux; |
| if(!file) { |
| gfx_done(); |
| asprintf(&cmd_buf, "%s %s", menu_ptr->label, arg); |
| syslinux_run_command(cmd_buf); |
| return; |
| } |
| |
| // first, load kernel |
| |
| kernel_size = 0; |
| |
| if((fd = open(file, O_RDONLY)) >= 0) { |
| if(!fstat(fd, &sbuf) && S_ISREG(sbuf.st_mode)) kernel_size = sbuf.st_size; |
| close(fd); |
| } |
| |
| gfx_progress_init(kernel_size, file); |
| |
| kernel = load_one(file, &kernel_size); |
| |
| if(!kernel) { |
| return; |
| } |
| |
| if(kernel_size < 1024 || *(uint32_t *) (kernel + 0x202) != 0x53726448) { |
| // not a linux kernel |
| gfx_done(); |
| asprintf(&cmd_buf, "%s %s", menu_ptr->label, arg); |
| syslinux_run_command(cmd_buf); |
| return; |
| } |
| |
| // printf("kernel = %p, size = %d\n", kernel, kernel_size); |
| |
| // parse cmdline for "initrd" option |
| |
| initrd_arg = menu_ptr->initrd; |
| |
| s = s0 = strdup(arg); |
| |
| while(*s && strncmp(s, "initrd=", sizeof "initrd=" - 1)) { |
| s = skipspace(skip_nonspaces(s)); |
| } |
| |
| if(*s) { |
| s += sizeof "initrd=" - 1; |
| *skip_nonspaces(s) = 0; |
| initrd_arg = s; |
| } |
| else if(initrd_arg) { |
| free(s0); |
| initrd_arg = s0 = strdup(initrd_arg); |
| } |
| |
| if(initrd_arg) { |
| initrd = initramfs_init(); |
| |
| while((t = strsep(&initrd_arg, ","))) { |
| initrd_buf = load_one(t, &initrd_size); |
| |
| if(!initrd_buf) { |
| printf("%s: read error\n", t); |
| free(s0); |
| return; |
| } |
| |
| initramfs_add_data(initrd, initrd_buf, initrd_size, initrd_size, 4); |
| |
| // printf("initrd = %p, size = %d\n", initrd_buf, initrd_size); |
| } |
| } |
| |
| free(s0); |
| |
| gfx_done(); |
| |
| syslinux_boot_linux(kernel, kernel_size, initrd, NULL, arg); |
| } |
| |
| |