--- docs/grub.texi Mon Apr 29 02:34:43 2002 +++ docs/grub.texi Wed Jul 17 14:05:25 2002 @@ -1812,6 +1812,7 @@ * default:: Set the default entry * fallback:: Set the fallback entry * hiddenmenu:: Hide the menu interface +* gfxmenu:: Use graphical menu interface * timeout:: Set the timeout * title:: Start a menu entry @end menu @@ -1843,6 +1844,15 @@ @end deffn +@node gfxmenu +@subsection gfxmenu + +@deffn Command gfxmenu file +Use the graphical menu interface. The graphics data are taken from +@var{file} and must be created using 'mkbootmsg' from the gfxboot package. +@end deffn + + @node hiddenmenu @subsection hiddenmenu --- grub/asmstub.c Sat Jan 19 20:05:43 2002 +++ grub/asmstub.c Wed Jul 17 11:45:46 2002 @@ -473,6 +473,32 @@ return 0; } +/* graphical menu functions . */ +int +gfx_init (gfx_data_t *gfx_data) +{ + return 0; +} + +int +gfx_done (gfx_data_t *gfx_data) +{ + return 0; +} + +int +gfx_input (gfx_data_t *gfx_data, int *menu_entry) +{ + return 0; +} + +int +gfx_setup_menu (gfx_data_t *gfx_data) +{ + return 0; +} + + /* low-level timing info */ int getrtsecs (void) --- stage2/asm.S Mon Nov 12 07:57:29 2001 +++ stage2/asm.S Thu Jul 18 14:35:59 2002 @@ -1682,6 +1682,262 @@ popl %ebp ret + +/* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * + * graphical menu functions + * + */ + +/* + * int gfx_init (gfx_data_t *gfx_data) + * + * init gfx things + * + * return vales: + * 0: ok + * 1: failed + * sets gfx_data->ok + */ + +ENTRY(gfx_init) + pushl %ebp + movl %esp, %ebp + + pushl %edi + pushl %esi + pushl %ebx + + movl 8(%ebp),%edx + movl %edx,%edi + andl $0xf,%edi + shrl $4,%edx + + pushl %ebp + + call EXT_C(prot_to_real) + .code16 + + pushw %ds + + movw %dx,%ds + shll $4,%edx + leal gfx_ofs_sys_cfg(%di),%esi + movl gfx_ofs_mem_start(%di),%eax + movl gfx_ofs_mem_cur(%di),%ebx + movl gfx_ofs_mem_max(%di),%ecx + movw %ds,%dx + + lcall *gfx_ofs_jmp_table + 4 * 0 (%di) + + movl $0,%ebx + adcl $0,%ebx + + popw %ds + + DATA32 call EXT_C(real_to_prot) + .code32 + + popl %ebp + + movl %ebx,%eax + negl %ebx + incl %ebx + movl 8(%ebp),%edx + movl %ebx,gfx_ofs_ok(%edx) + + popl %ebx + popl %esi + popl %edi + + popl %ebp + ret + + +/* + * int gfx_done (gfx_data_t *gfx_data) + * + * shut down gfx things + * + * return vales: + * always 0 + * sets gfx_data->ok + */ + +ENTRY(gfx_done) + pushl %ebp + movl %esp, %ebp + + pushl %edi + pushl %esi + pushl %ebx + + movl 8(%ebp),%edx + movl %edx,%ebx + andl $0xf,%ebx + shrl $4,%edx + + pushl %ebp + + call EXT_C(prot_to_real) + .code16 + + pushw %ds + + movw %dx,%ds + + lcall *gfx_ofs_jmp_table + 4 * 1 (%bx) + + popw %ds + + DATA32 call EXT_C(real_to_prot) + .code32 + + popl %ebp + + xorl %eax,%eax + movl 8(%ebp),%edx + movl %eax,gfx_ofs_ok(%edx) + + popl %ebx + popl %esi + popl %edi + + popl %ebp + ret + + +/* + * int gfx_input (gfx_data_t *gfx_data, int *menu_entry) + * + * let user enter a command line + * + * uses gfx_data->cmdline as buffer + * + * return values: + * 1: abort + * 2: boot + * menu_entry: selected entry + */ + +ENTRY(gfx_input) + pushl %ebp + movl %esp, %ebp + + pushl %edi + pushl %esi + pushl %ebx + + movl 8(%ebp),%edx + movl %edx,%ebx + andl $0xf,%ebx + shrl $4,%edx + + pushl %ebp + + call EXT_C(prot_to_real) + .code16 + + pushw %ds + + movw %dx,%ds + shll $4,%edx + movl gfx_ofs_cmdline(%bx),%edi + subl %edx,%edi + movw gfx_ofs_cmdline_len(%bx),%cx + movw gfx_ofs_timeout(%bx),%ax + imulw $18,%ax + + pushl %ebp + lcall *gfx_ofs_jmp_table + 4 * 2 (%bx) + popl %ebp + movl %eax,%ecx + + popw %ds + + DATA32 call EXT_C(real_to_prot) + .code32 + + popl %ebp + + movl 12(%ebp),%edx + movl %ebx,(%edx) + + movl %ecx,%eax + + popl %ebx + popl %esi + popl %edi + + popl %ebp + ret + + +/* + * int gfx_setup_menu (gfx_data_t *gfx_data) + * + * draw boot menu + * + * return values: + * always 0 + */ + +ENTRY(gfx_setup_menu) + pushl %ebp + movl %esp, %ebp + + pushl %edi + pushl %esi + pushl %ebx + + movl 8(%ebp),%edx + movl %edx,%ebx + andl $0xf,%ebx + shrl $4,%edx + + call EXT_C(prot_to_real) + .code16 + + pushw %ds + + movw %dx,%ds + shll $4,%edx + movl gfx_ofs_menu_list(%bx),%edi + movl gfx_ofs_menu_default_entry(%bx),%esi + movl gfx_ofs_args_list(%bx),%ebp + subl %edx,%edi + subl %edx,%esi + subl %edx,%ebp + movl gfx_ofs_menu_entry_len(%bx),%edx + movl gfx_ofs_args_entry_len(%bx),%ecx + movl gfx_ofs_menu_entries(%bx),%eax + + xchg %bx,%bp + lcall %ds: *gfx_ofs_jmp_table + 4 * 3 (%bp) + + popw %ds + + DATA32 call EXT_C(real_to_prot) + .code32 + + xorl %eax,%eax + + popl %ebx + popl %esi + popl %edi + + popl %ebp + ret + + +/* + * + * end graphics stuff + * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + */ + /* * gateA20(int linear) --- stage2/builtins.c Mon Apr 29 02:26:49 2002 +++ stage2/builtins.c Wed Jul 17 14:11:30 2002 @@ -62,6 +62,8 @@ int fallback_entry = -1; /* The number of current entry. */ int current_entryno; +/* graphics file */ +char graphics_file[64]; /* The address for Multiboot command-line buffer. */ static char *mb_cmdline; /* The password. */ @@ -1327,6 +1329,26 @@ }; +/* graphics */ +static int +gfxmenu_func (char *arg, int flags) +{ + memmove(graphics_file, arg, sizeof graphics_file - 1); + graphics_file[sizeof graphics_file - 1] = 0; + + return 0; +} + +static struct builtin builtin_gfxmenu = +{ + "gfxmenu", + gfxmenu_func, + BUILTIN_MENU | BUILTIN_HELP_LIST, + "gfxmenu FILE", + "Use the graphical menu from FILE." +}; + + /* geometry */ static int geometry_func (char *arg, int flags) @@ -4605,6 +4627,7 @@ &builtin_find, &builtin_fstest, &builtin_geometry, + &builtin_gfxmenu, &builtin_halt, &builtin_help, &builtin_hiddenmenu, --- stage2/shared.h Mon Mar 25 22:43:55 2002 +++ stage2/shared.h Wed Jul 17 12:18:36 2002 @@ -379,6 +379,25 @@ #endif /* WITHOUT_LIBC_STUBS */ +/* see typedef gfx_data_t below */ +#define gfx_ofs_ok 0x00 +#define gfx_ofs_mem_start 0x04 +#define gfx_ofs_mem_cur 0x08 +#define gfx_ofs_mem_max 0x0c +#define gfx_ofs_code_seg 0x10 +#define gfx_ofs_jmp_table 0x14 +#define gfx_ofs_sys_cfg 0x44 +#define gfx_ofs_cmdline 0x48 +#define gfx_ofs_cmdline_len 0x4c +#define gfx_ofs_menu_list 0x50 +#define gfx_ofs_menu_default_entry 0x54 +#define gfx_ofs_menu_entries 0x58 +#define gfx_ofs_menu_entry_len 0x5c +#define gfx_ofs_args_list 0x60 +#define gfx_ofs_args_entry_len 0x64 +#define gfx_ofs_timeout 0x68 + + #ifndef ASM_FILE /* * Below this should be ONLY defines and other constructs for C code. @@ -596,6 +615,39 @@ extern int default_entry; extern int current_entryno; + +/* + * graphics menu stuff + * + * Note: gfx_data and all data referred to in it must lie within a 64k area. + */ +typedef struct { + unsigned ok; /* set while we're in graphics mode */ + unsigned mem_start, mem_cur, mem_max; + unsigned code_seg; /* code segment of binary graphics code */ + unsigned jmp_table[12]; /* link to graphics functions */ + unsigned char sys_cfg[4]; /* sys_cfg[3]: identifies boot loader (grub == 2) */ + char *cmdline; /* command line returned by gfx_input() */ + unsigned cmdline_len; /* length of the above */ + char *menu_list; /* list of menu entries, each of fixed length (menu_entry_len) */ + char *menu_default_entry; /* the default entry */ + unsigned menu_entries; /* number of entries in menu_list */ + unsigned menu_entry_len; /* one entry */ + char *args_list; /* same structure as menu_list, menu_entries entries */ + unsigned args_entry_len; /* one entry */ + unsigned timeout; /* in seconds (0: no timeout) */ +} __attribute__ ((packed)) gfx_data_t; + +extern gfx_data_t *graphics_data; + +/* pointer to graphics image data */ +extern char graphics_file[64]; + +int gfx_init(gfx_data_t *gfx_data); +int gfx_done(gfx_data_t *gfx_data); +int gfx_input(gfx_data_t *gfx_data, int *menu_entry); +int gfx_setup_menu(gfx_data_t *gfx_data); + /* The constants for password types. */ typedef enum { --- stage2/stage2.c Sun Mar 24 13:28:54 2002 +++ stage2/stage2.c Thu Jul 18 14:34:14 2002 @@ -21,6 +21,8 @@ grub_jmp_buf restart_env; +gfx_data_t *graphics_data; + #if defined(PRESET_MENU_STRING) || defined(SUPPORT_DISKLESS) # if defined(PRESET_MENU_STRING) @@ -845,6 +847,334 @@ } + +/* for debugging */ +static void hexdump(unsigned char *buf, unsigned len) +{ + int i, j = 0; + char s[17]; + unsigned addr = (unsigned) buf; + + s[16] = 0; + while(len--) { + i = buf[j]; + i = i & 0xff; + s[j & 15] = (i >= 0x20 && i <= 0x7e) ? i : '.'; + if(!(j & 15)) { + printf("%x ", j + addr); + } + if(!(j & 7) && (j & 15)) printf(" "); + /* stupid grub_printf */ + printf("%x", (i >> 4) & 0x0f); + printf("%x ", i & 0x0f); + if(!(++j & 15)) { + printf(" %s\n", s); + } + } + + if(j & 15) { + s[j & 15] = 0; + if(!(j & 8)) printf(" "); + i = 1 + 3 * (16 - (j & 15)); + while(i--) printf(" "); + printf("%s\n", s); + } +} + + +/* + * Go through config entry and find kernel args, if any. + */ +static char *get_kernel_args(char *cfg) +{ + int j; + char *s, *t = ""; + + for(j = 0; ; j++) { + s = get_entry(cfg, j, 0); + if(!*s) break; + if(!memcmp(s, "kernel", 6) && (s[6] == ' ' || s[6] == '\t')) { + t = skip_to(0, s); + if(*t) t = skip_to(0, t); + break; + } + } + + return t; +} + + +/* + * Leave that much space on the heap. Everything else goes to the graphics + * functions. + * + * 0x2000 is _not_ enough + */ +#define MIN_HEAP_SIZE 0x4000 + +/* gfx code needs at least this much free memory */ +#define MIN_GFX_FREE 0x4000 + +/* + * Does normally not return. + */ +static void +run_graphics_menu (char *menu_entries, char *config_entries, int num_entries, + char *heap, int entryno) +{ + unsigned char *buf; + unsigned buf_size, code_start; + char *s, *t, *cfg, *new_config; + int i, j, max_len; + int selected_entry; + gfx_data_t *gfx_data; + + /* + * check gfx_data_t struct offsets for consistency; gcc will optimize away + * the whole block + */ + + /* dummy function to make ld fail */ + { + extern void wrong_struct_size(void); + #define gfx_ofs_check(a) if(gfx_ofs_##a != (char *) &gfx_data->##a - (char *) gfx_data) wrong_struct_size(); + gfx_ofs_check(ok); + gfx_ofs_check(mem_start); + gfx_ofs_check(mem_cur); + gfx_ofs_check(mem_max); + gfx_ofs_check(code_seg); + gfx_ofs_check(jmp_table); + gfx_ofs_check(sys_cfg); + gfx_ofs_check(cmdline); + gfx_ofs_check(cmdline_len); + gfx_ofs_check(menu_list); + gfx_ofs_check(menu_default_entry); + gfx_ofs_check(menu_entries); + gfx_ofs_check(menu_entry_len); + gfx_ofs_check(args_list); + gfx_ofs_check(args_entry_len); + gfx_ofs_check(timeout); + #undef gfx_ofs_check + } + + if(!num_entries) return; + + graphics_data = gfx_data = (gfx_data_t *) heap; + heap += sizeof *gfx_data; + memset(gfx_data, 0, sizeof *gfx_data); + + gfx_data->sys_cfg[2] = 2; /* bootloader: grub */ + gfx_data->timeout = grub_timeout >= 0 ? grub_timeout : 0; + + + /* setup command line edit buffer */ + + gfx_data->cmdline_len = 256; + + gfx_data->cmdline = heap; + heap += gfx_data->cmdline_len; + memset(gfx_data->cmdline, 0, gfx_data->cmdline_len); + + + /* setup menu entries */ + + for(i = max_len = 0; i < num_entries; i++) { + j = strlen(get_entry(menu_entries, i, 0)); + if(j > max_len) max_len = j; + } + + if(!max_len) return; + + gfx_data->menu_entry_len = max_len + 1; + gfx_data->menu_entries = num_entries; + + gfx_data->menu_list = heap; + heap += gfx_data->menu_entry_len * gfx_data->menu_entries; + + memset(gfx_data->menu_list, 0, gfx_data->menu_entry_len * gfx_data->menu_entries); + + for(i = 0; i < gfx_data->menu_entries; i++) { + strcpy(gfx_data->menu_list + i * gfx_data->menu_entry_len, get_entry(menu_entries, i, 0)); + } + + gfx_data->menu_default_entry = gfx_data->menu_list + entryno * gfx_data->menu_entry_len; + + + /* setup list of kernel args */ + + for(i = max_len = 0; i < num_entries; i++) { + s = get_kernel_args(get_entry(config_entries, i, 1)); + j = strlen(s); + if(j > max_len) max_len = j; + } + + gfx_data->args_entry_len = max_len + 1; + + gfx_data->args_list = heap; + heap += gfx_data->args_entry_len * gfx_data->menu_entries; + + memset(gfx_data->args_list, 0, gfx_data->args_entry_len * gfx_data->menu_entries); + + for(i = 0; i < gfx_data->menu_entries; i++) { + strcpy(gfx_data->args_list + i* gfx_data->args_entry_len, get_kernel_args(get_entry(config_entries, i, 1))); + } + + + /* get memory area to be used by graphics functions */ + + buf = (unsigned char *) (((unsigned) heap + 0xf) & ~0xf); + + buf_size = (unsigned char *) &buf - buf - MIN_HEAP_SIZE; + buf_size &= ~0xf; + + /* too small */ + if(buf_size < 0x10000) return; + + gfx_data->mem_start = (unsigned) buf; + gfx_data->mem_max = gfx_data->mem_start + buf_size; + +#if 0 + printf("graphics menu\n"); + printf( + "heap = 0x%x, buf = 0x%x (0x%x bytes), graphics_file = %s\n", + heap, gfx_data->mem_start, buf_size, graphics_file + ); + getkey(); +#endif + + heap += buf_size; + + + /* read the file */ + + if(!grub_open(graphics_file)) { + printf("graphics file \"%s\" missing, press a key to continue...\n", graphics_file); + getkey(); + return; + } + + i = grub_read(buf, buf_size); + + grub_close(); + + if(i <= 0) { + printf("error reading \"%s\", press a key to continue...\n", graphics_file); + getkey(); + return; + } + + /* besides the file, we need some working memory, too */ + if(i + MIN_GFX_FREE >= buf_size) { + printf("file \"%s\" too large, press a key to continue...\n", graphics_file); + getkey(); + return; + } + + gfx_data->mem_cur = gfx_data->mem_start + ((i + 3) & ~3); /* align it */ + + // printf("image: %d bytes (%d bytes left)\n", i, gfx_data->mem_max - gfx_data->mem_cur); + // getkey(); + + if( + *(unsigned *) buf != 0x0b2d97f00 || /* magic id */ + buf[4] != 3 || /* version 3 */ + !(code_start = *(unsigned *) (buf + 8)) || + (code_start & 0xf) /* check alignment */ + ) { + printf("\"%s\" has wrong format, press a key to continue...\n", graphics_file); + getkey(); + return; + } + + + /* init interface to graphics functions */ + + code_start += gfx_data->mem_start; + + gfx_data->code_seg = code_start >> 4; + + // printf("code start = 0x%x, code_seg = 0x%x\n", code_start, gfx_data->code_seg); + + for(i = 0; i < sizeof gfx_data->jmp_table / sizeof *gfx_data->jmp_table; i++) { + gfx_data->jmp_table[i] = (gfx_data->code_seg << 16) + ((unsigned short *) code_start)[i]; + } + +#if 0 + for(i = 0; i < 12; i++) { + printf("%d: 0x%x\n", i, gfx_data->jmp_table[i]); + } + + for(i = 0; i < gfx_data->menu_entries; i++) { + printf(">%s< - >%s<\n", + gfx_data->menu_list + i * gfx_data->menu_entry_len, + gfx_data->args_list + i * gfx_data->args_entry_len + ); + } + + printf("def: >%s<\n", gfx_data->menu_default_entry); +#endif + + + /* switch to graphics mode */ + + if(gfx_init(gfx_data)) return; + + gfx_setup_menu(gfx_data); + + i = gfx_input(gfx_data, &selected_entry); + + /* ESC -> show text menu */ + if(i == 1) { + gfx_done(gfx_data); + grub_timeout = -1; + + return; + } + + gfx_done(gfx_data); + + // printf("cmdline: >%s<, entry = %d\n", gfx_data->cmdline, selected_entry); + + if(selected_entry < 0 || selected_entry > num_entries) return; + + + /* create new config with modified kernel option */ + + cfg = get_entry(config_entries, selected_entry, 1); + + new_config = heap; + + for(i = 0; ; i++) { + s = get_entry(cfg, i, 0); + if(!*s) { + if(!i) *heap++ = 0; + *heap++ = 0; + break; + } + if(!memcmp(s, "kernel", 6) && (s[6] == ' ' || s[6] == '\t')) { + t = skip_to(0, s); + if(*t) t = skip_to(0, t); + memmove(heap, s, t - s); + heap += t - s; + *heap++ = ' '; + strcpy(heap, gfx_data->cmdline); + heap += strlen(gfx_data->cmdline) + 1; + } + else { + strcpy(heap, s); + heap += strlen(s) + 1; + } + } + + *heap++ = 0; + + // hexdump(new_config, heap - new_config); + // getkey(); + + run_script(new_config, heap); +} + + static int get_line_from_config (char *cmdline, int maxlen, int read_from_file) { @@ -1078,9 +1408,12 @@ } else { - /* Run menu interface. */ - run_menu (menu_entries, config_entries, num_entries, - menu_entries + menu_len, default_entry); + if (*graphics_file) + { + run_graphics_menu(menu_entries, config_entries, num_entries,menu_entries + menu_len, default_entry); + } + /* Run menu interface. */ + run_menu (menu_entries, config_entries, num_entries, menu_entries + menu_len, default_entry); } } }