]>
Commit | Line | Data |
---|---|---|
c98e55d8 HS |
1 | --- docs/grub.texi |
2 | +++ docs/grub.texi | |
3 | @@ -2118,6 +2118,7 @@ | |
4 | * default:: Set the default entry | |
5 | * fallback:: Set the fallback entry | |
6 | * hiddenmenu:: Hide the menu interface | |
7 | +* gfxmenu:: Use graphical menu interface | |
8 | * timeout:: Set the timeout | |
9 | * title:: Start a menu entry | |
10 | @end menu | |
11 | @@ -2150,6 +2151,15 @@ | |
12 | @end deffn | |
13 | ||
14 | ||
15 | +@node gfxmenu | |
16 | +@subsection gfxmenu | |
17 | + | |
18 | +@deffn Command gfxmenu file | |
19 | +Use the graphical menu interface. The graphics data are taken from | |
20 | +@var{file} and must be created using 'mkbootmsg' from the gfxboot package. | |
21 | +@end deffn | |
22 | + | |
23 | + | |
24 | @node hiddenmenu | |
25 | @subsection hiddenmenu | |
26 | ||
27 | --- grub/asmstub.c | |
28 | +++ grub/asmstub.c | |
29 | @@ -498,6 +498,32 @@ | |
30 | return 0; | |
31 | } | |
32 | ||
33 | +/* graphical menu functions . */ | |
34 | +int | |
35 | +gfx_init (gfx_data_t *gfx_data) | |
36 | +{ | |
37 | + return 0; | |
38 | +} | |
39 | + | |
40 | +int | |
41 | +gfx_done (gfx_data_t *gfx_data) | |
42 | +{ | |
43 | + return 0; | |
44 | +} | |
45 | + | |
46 | +int | |
47 | +gfx_input (gfx_data_t *gfx_data, int *menu_entry) | |
48 | +{ | |
49 | + return 0; | |
50 | +} | |
51 | + | |
52 | +int | |
53 | +gfx_setup_menu (gfx_data_t *gfx_data) | |
54 | +{ | |
55 | + return 0; | |
56 | +} | |
57 | + | |
58 | + | |
59 | /* low-level timing info */ | |
60 | int | |
61 | getrtsecs (void) | |
62 | --- stage2/asm.S | |
63 | +++ stage2/asm.S | |
64 | @@ -1614,6 +1614,286 @@ | |
65 | popl %ebp | |
66 | ret | |
67 | ||
68 | + | |
69 | +/* | |
70 | + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
71 | + * | |
72 | + * graphical menu functions | |
73 | + * | |
74 | + */ | |
75 | + | |
76 | +/* | |
77 | + * int gfx_init (gfx_data_t *gfx_data) | |
78 | + * | |
79 | + * init gfx things | |
80 | + * | |
81 | + * return vales: | |
82 | + * 0: ok | |
83 | + * 1: failed | |
84 | + * sets gfx_data->ok | |
85 | + */ | |
86 | + | |
87 | +ENTRY(gfx_init) | |
88 | + pushl %ebp | |
89 | + movl %esp, %ebp | |
90 | + | |
91 | + pushl %edi | |
92 | + pushl %esi | |
93 | + pushl %ebx | |
94 | + | |
95 | + movl 8(%ebp),%edx | |
96 | + movl %edx,%edi | |
97 | + leal gfx_ofs_sys_cfg(%edx),%esi | |
98 | + andl $0xf,%edi | |
99 | + shrl $4,%edx | |
100 | + | |
101 | + pushl %ebp | |
102 | + | |
103 | + call EXT_C(prot_to_real) | |
104 | + .code16 | |
105 | + | |
106 | + pushw %ds | |
107 | + movw %dx,%ds | |
108 | + | |
109 | + lcall *gfx_ofs_jmp_table + 4 * 0 (%di) | |
110 | + | |
111 | + sbbl %ebx,%ebx | |
112 | + negl %ebx | |
113 | + | |
114 | + popw %ds | |
115 | + | |
116 | + DATA32 call EXT_C(real_to_prot) | |
117 | + .code32 | |
118 | + | |
119 | + popl %ebp | |
120 | + | |
121 | + movl %ebx,%eax | |
122 | + xorl $1,%ebx | |
123 | + movl 8(%ebp),%edx | |
124 | + movl %ebx,gfx_ofs_ok(%edx) | |
125 | + | |
126 | + popl %ebx | |
127 | + popl %esi | |
128 | + popl %edi | |
129 | + | |
130 | + popl %ebp | |
131 | + ret | |
132 | + | |
133 | + | |
134 | +/* | |
135 | + * int gfx_done (gfx_data_t *gfx_data) | |
136 | + * | |
137 | + * shut down gfx things | |
138 | + * | |
139 | + * return vales: | |
140 | + * always 0 | |
141 | + * sets gfx_data->ok | |
142 | + */ | |
143 | + | |
144 | +ENTRY(gfx_done) | |
145 | + pushl %ebp | |
146 | + movl %esp, %ebp | |
147 | + | |
148 | + pushl %edi | |
149 | + pushl %esi | |
150 | + pushl %ebx | |
151 | + | |
152 | + movl 8(%ebp),%edx | |
153 | + movl %edx,%ebx | |
154 | + andl $0xf,%ebx | |
155 | + shrl $4,%edx | |
156 | + | |
157 | + pushl %ebp | |
158 | + | |
159 | + call EXT_C(prot_to_real) | |
160 | + .code16 | |
161 | + | |
162 | + pushw %ds | |
163 | + | |
164 | + movw %dx,%ds | |
165 | + | |
166 | + lcall *gfx_ofs_jmp_table + 4 * 1 (%bx) | |
167 | + | |
168 | + popw %ds | |
169 | + | |
170 | + DATA32 call EXT_C(real_to_prot) | |
171 | + .code32 | |
172 | + | |
173 | + popl %ebp | |
174 | + | |
175 | + xorl %eax,%eax | |
176 | + movl 8(%ebp),%edx | |
177 | + movl %eax,gfx_ofs_ok(%edx) | |
178 | + | |
179 | + popl %ebx | |
180 | + popl %esi | |
181 | + popl %edi | |
182 | + | |
183 | + popl %ebp | |
184 | + ret | |
185 | + | |
186 | + | |
187 | +/* | |
188 | + * int gfx_input (gfx_data_t *gfx_data, int *menu_entry) | |
189 | + * | |
190 | + * let user enter a command line | |
191 | + * | |
192 | + * uses gfx_data->cmdline as buffer | |
193 | + * | |
194 | + * return values: | |
195 | + * 1: abort | |
196 | + * 2: boot | |
197 | + * menu_entry: selected entry | |
198 | + */ | |
199 | + | |
200 | +ENTRY(gfx_input) | |
201 | + pushl %ebp | |
202 | + movl %esp, %ebp | |
203 | + | |
204 | + pushl %edi | |
205 | + pushl %esi | |
206 | + pushl %ebx | |
207 | + | |
208 | + movl 8(%ebp),%edx | |
209 | + movl %edx,%ebx | |
210 | + leal gfx_ofs_sys_cfg(%edx),%esi | |
211 | + andl $0xf,%ebx | |
212 | + shrl $4,%edx | |
213 | + | |
214 | + pushl %ebp | |
215 | + | |
216 | + call EXT_C(prot_to_real) | |
217 | + .code16 | |
218 | + | |
219 | + pushw %ds | |
220 | + | |
221 | + movw %dx,%ds | |
222 | + | |
223 | + movl gfx_ofs_cmdline(%bx),%edi | |
224 | + movl gfx_ofs_cmdline_len(%bx),%ecx | |
225 | + movl gfx_ofs_timeout(%bx),%eax | |
226 | + imull $18,%eax | |
227 | + | |
228 | + lcall *gfx_ofs_jmp_table + 4 * 2 (%bx) | |
229 | + | |
230 | + movl %eax,%ecx | |
231 | + | |
232 | + popw %ds | |
233 | + | |
234 | + DATA32 call EXT_C(real_to_prot) | |
235 | + .code32 | |
236 | + | |
237 | + popl %ebp | |
238 | + | |
239 | + movl 12(%ebp),%edx | |
240 | + movl %ebx,(%edx) | |
241 | + | |
242 | + movl %ecx,%eax | |
243 | + | |
244 | + popl %ebx | |
245 | + popl %esi | |
246 | + popl %edi | |
247 | + | |
248 | + popl %ebp | |
249 | + ret | |
250 | + | |
251 | + | |
252 | +/* | |
253 | + * int gfx_setup_menu (gfx_data_t *gfx_data) | |
254 | + * | |
255 | + * draw boot menu | |
256 | + * | |
257 | + * return values: | |
258 | + * always 0 | |
259 | + */ | |
260 | + | |
261 | +/* menu entry descriptor */ | |
262 | +#define menu_entries 0 | |
263 | +#define menu_default 2 /* seg:ofs */ | |
264 | +#define menu_ent_list 6 /* seg:ofs */ | |
265 | +#define menu_ent_size 10 | |
266 | +#define menu_arg_list 12 /* seg:ofs */ | |
267 | +#define menu_arg_size 16 | |
268 | +#define sizeof_menu_desc 18 | |
269 | + | |
270 | +ENTRY(gfx_setup_menu) | |
271 | + pushl %ebp | |
272 | + movl %esp, %ebp | |
273 | + | |
274 | + pushl %edi | |
275 | + pushl %esi | |
276 | + pushl %ebx | |
277 | + | |
278 | + movl 8(%ebp),%edx | |
279 | + movl %edx,%ebx | |
280 | + andl $0xf,%ebx | |
281 | + shrl $4,%edx | |
282 | + | |
283 | + call EXT_C(prot_to_real) | |
284 | + .code16 | |
285 | + | |
286 | + pushw %ds | |
287 | + | |
288 | + movw %dx,%ds | |
289 | + shll $4,%edx | |
290 | + | |
291 | + subw $sizeof_menu_desc,%sp | |
292 | + movw %esp,%ebp | |
293 | + | |
294 | + movl gfx_ofs_menu_entries(%bx),%eax | |
295 | + movw %ax,menu_entries(%bp) | |
296 | + | |
297 | + movl gfx_ofs_menu_default_entry(%bx),%eax | |
298 | + subl %edx,%eax | |
299 | + movw %ax,menu_default(%bp) | |
300 | + movw %ds,menu_default+2(%bp) | |
301 | + | |
302 | + movl gfx_ofs_menu_list(%bx),%eax | |
303 | + subl %edx,%eax | |
304 | + movw %ax,menu_ent_list(%bp) | |
305 | + movw %ds,menu_ent_list+2(%bp) | |
306 | + | |
307 | + movl gfx_ofs_menu_entry_len(%bx),%eax | |
308 | + movw %ax,menu_ent_size(%bp) | |
309 | + | |
310 | + movl gfx_ofs_args_list(%bx),%eax | |
311 | + subl %edx,%eax | |
312 | + movw %ax,menu_arg_list(%bp) | |
313 | + movw %ds,menu_arg_list+2(%bp) | |
314 | + | |
315 | + movl gfx_ofs_args_entry_len(%bx),%eax | |
316 | + movw %ax,menu_arg_size(%bp) | |
317 | + | |
318 | + movl %ss,%esi | |
319 | + shll $4,%esi | |
320 | + addl %ebp,%esi | |
321 | + | |
322 | + lcall %ds: *gfx_ofs_jmp_table + 4 * 3 (%bx) | |
323 | + | |
324 | + addw $sizeof_menu_desc,%sp | |
325 | + | |
326 | + popw %ds | |
327 | + | |
328 | + DATA32 call EXT_C(real_to_prot) | |
329 | + .code32 | |
330 | + | |
331 | + xorl %eax,%eax | |
332 | + | |
333 | + popl %ebx | |
334 | + popl %esi | |
335 | + popl %edi | |
336 | + | |
337 | + popl %ebp | |
338 | + ret | |
339 | + | |
340 | + | |
341 | +/* | |
342 | + * | |
343 | + * end graphics stuff | |
344 | + * | |
345 | + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
346 | + */ | |
347 | + | |
348 | ||
349 | /* | |
350 | * gateA20(int linear) | |
351 | --- stage2/builtins.c | |
352 | +++ stage2/builtins.c | |
353 | @@ -63,6 +63,8 @@ | |
354 | int fallback_entries[MAX_FALLBACK_ENTRIES]; | |
355 | /* The number of current entry. */ | |
356 | int current_entryno; | |
357 | +/* graphics file */ | |
358 | +char graphics_file[64]; | |
359 | /* The address for Multiboot command-line buffer. */ | |
360 | static char *mb_cmdline; | |
361 | /* The password. */ | |
362 | @@ -1351,6 +1353,26 @@ | |
363 | }; | |
364 | ||
365 | \f | |
366 | +/* graphics */ | |
367 | +static int | |
368 | +gfxmenu_func (char *arg, int flags) | |
369 | +{ | |
370 | + memmove(graphics_file, arg, sizeof graphics_file - 1); | |
371 | + graphics_file[sizeof graphics_file - 1] = 0; | |
372 | + | |
373 | + return 0; | |
374 | +} | |
375 | + | |
376 | +static struct builtin builtin_gfxmenu = | |
377 | +{ | |
378 | + "gfxmenu", | |
379 | + gfxmenu_func, | |
380 | + BUILTIN_MENU | BUILTIN_HELP_LIST, | |
381 | + "gfxmenu FILE", | |
382 | + "Use the graphical menu from FILE." | |
383 | +}; | |
384 | + | |
385 | +\f | |
386 | /* geometry */ | |
387 | static int | |
388 | geometry_func (char *arg, int flags) | |
389 | @@ -4874,6 +4896,7 @@ | |
390 | &builtin_find, | |
391 | &builtin_fstest, | |
392 | &builtin_geometry, | |
393 | + &builtin_gfxmenu, | |
394 | &builtin_halt, | |
395 | &builtin_help, | |
396 | &builtin_hiddenmenu, | |
397 | --- stage2/shared.h | |
398 | +++ stage2/shared.h | |
399 | @@ -374,6 +374,22 @@ | |
400 | #endif /* WITHOUT_LIBC_STUBS */ | |
401 | ||
402 | ||
403 | +/* see typedef gfx_data_t below */ | |
404 | +#define gfx_ofs_ok 0x00 | |
405 | +#define gfx_ofs_code_seg 0x04 | |
406 | +#define gfx_ofs_jmp_table 0x08 | |
407 | +#define gfx_ofs_sys_cfg 0x38 | |
408 | +#define gfx_ofs_cmdline 0x6c | |
409 | +#define gfx_ofs_cmdline_len 0x70 | |
410 | +#define gfx_ofs_menu_list 0x74 | |
411 | +#define gfx_ofs_menu_default_entry 0x78 | |
412 | +#define gfx_ofs_menu_entries 0x7c | |
413 | +#define gfx_ofs_menu_entry_len 0x80 | |
414 | +#define gfx_ofs_args_list 0x84 | |
415 | +#define gfx_ofs_args_entry_len 0x88 | |
416 | +#define gfx_ofs_timeout 0x8c | |
417 | + | |
418 | + | |
419 | #ifndef ASM_FILE | |
420 | /* | |
421 | * Below this should be ONLY defines and other constructs for C code. | |
422 | @@ -595,6 +611,38 @@ | |
423 | extern int default_entry; | |
424 | extern int current_entryno; | |
425 | ||
426 | + | |
427 | +/* | |
428 | + * graphics menu stuff | |
429 | + * | |
430 | + * Note: gfx_data and all data referred to in it must lie within a 64k area. | |
431 | + */ | |
432 | +typedef struct { | |
433 | + unsigned ok; /* set while we're in graphics mode */ | |
434 | + unsigned code_seg; /* code segment of binary graphics code */ | |
435 | + unsigned jmp_table[12]; /* link to graphics functions */ | |
436 | + unsigned char sys_cfg[52]; /* sys_cfg[0]: identifies boot loader (grub == 2) */ | |
437 | + char *cmdline; /* command line returned by gfx_input() */ | |
438 | + unsigned cmdline_len; /* length of the above */ | |
439 | + char *menu_list; /* list of menu entries, each of fixed length (menu_entry_len) */ | |
440 | + char *menu_default_entry; /* the default entry */ | |
441 | + unsigned menu_entries; /* number of entries in menu_list */ | |
442 | + unsigned menu_entry_len; /* one entry */ | |
443 | + char *args_list; /* same structure as menu_list, menu_entries entries */ | |
444 | + unsigned args_entry_len; /* one entry */ | |
445 | + unsigned timeout; /* in seconds (0: no timeout) */ | |
446 | +} __attribute__ ((packed)) gfx_data_t; | |
447 | + | |
448 | +extern gfx_data_t *graphics_data; | |
449 | + | |
450 | +/* pointer to graphics image data */ | |
451 | +extern char graphics_file[64]; | |
452 | + | |
453 | +int gfx_init(gfx_data_t *gfx_data); | |
454 | +int gfx_done(gfx_data_t *gfx_data); | |
455 | +int gfx_input(gfx_data_t *gfx_data, int *menu_entry); | |
456 | +int gfx_setup_menu(gfx_data_t *gfx_data); | |
457 | + | |
458 | /* The constants for password types. */ | |
459 | typedef enum | |
460 | { | |
461 | --- stage2/stage2.c | |
462 | +++ stage2/stage2.c | |
463 | @@ -22,6 +22,8 @@ | |
464 | ||
465 | grub_jmp_buf restart_env; | |
466 | ||
467 | +gfx_data_t *graphics_data; | |
468 | + | |
469 | #if defined(PRESET_MENU_STRING) || defined(SUPPORT_DISKLESS) | |
470 | ||
471 | # if defined(PRESET_MENU_STRING) | |
472 | @@ -310,6 +312,12 @@ | |
473 | ||
474 | if (! auth && password) | |
475 | { | |
476 | + if (*graphics_file) | |
477 | + { | |
478 | + printf ("\ | |
479 | + WARNING: graphical menu doesn\'t work\ | |
480 | + in conjunction with the password feature\n" ); | |
481 | + } | |
482 | printf ("\ | |
483 | Press enter to boot the selected OS or \'p\' to enter a\n\ | |
484 | password to unlock the next set of features."); | |
485 | @@ -753,6 +761,493 @@ | |
486 | } | |
487 | ||
488 | ||
489 | + | |
490 | +#if 0 | |
491 | +/* for debugging */ | |
492 | +static void hexdump(unsigned char *buf, unsigned len) | |
493 | +{ | |
494 | + int i, j = 0; | |
495 | + char s[17]; | |
496 | + unsigned addr = (unsigned) buf; | |
497 | + | |
498 | + s[16] = 0; | |
499 | + while(len--) { | |
500 | + i = buf[j]; | |
501 | + i = i & 0xff; | |
502 | + s[j & 15] = (i >= 0x20 && i <= 0x7e) ? i : '.'; | |
503 | + if(!(j & 15)) { | |
504 | + printf("%x ", j + addr); | |
505 | + } | |
506 | + if(!(j & 7) && (j & 15)) printf(" "); | |
507 | + /* stupid grub_printf */ | |
508 | + printf("%x", (i >> 4) & 0x0f); | |
509 | + printf("%x ", i & 0x0f); | |
510 | + if(!(++j & 15)) { | |
511 | + printf(" %s\n", s); | |
512 | + } | |
513 | + } | |
514 | + | |
515 | + if(j & 15) { | |
516 | + s[j & 15] = 0; | |
517 | + if(!(j & 8)) printf(" "); | |
518 | + i = 1 + 3 * (16 - (j & 15)); | |
519 | + while(i--) printf(" "); | |
520 | + printf("%s\n", s); | |
521 | + } | |
522 | +} | |
523 | +#endif | |
524 | + | |
525 | + | |
526 | +/* kernel + (grub-)module options */ | |
527 | +#define GFX_CMD_BUF_SIZE 512 | |
528 | + | |
529 | +/* command line separator char */ | |
530 | +#define GFX_CMD_SEP 1 | |
531 | + | |
532 | +/* | |
533 | + * Go through config entry and find kernel args, if any. | |
534 | + * Put things into buf and return it. | |
535 | + */ | |
536 | +static char *get_kernel_args(char *cfg, char *buf) | |
537 | +{ | |
538 | + int i, j; | |
539 | + char *s, *t = "", *p, *t2; | |
540 | + | |
541 | + *(p = buf) = 0; | |
542 | + | |
543 | + for(j = 0; ; j++) { | |
544 | + s = get_entry(cfg, j, 0); | |
545 | + if(!*s) break; | |
546 | + if( | |
547 | + (!memcmp(s, "kernel", 6) || !memcmp(s, "module", 6)) && | |
548 | + (s[6] == ' ' || s[6] == '\t') | |
549 | + ) { | |
550 | + t = skip_to(0, s); | |
551 | + t2 = s[0] == 'm' ? strstr(t, "initrd") : NULL; | |
552 | + if(*t) t = skip_to(0, t); | |
553 | + if(t2 && t2 < t) break; /* module is likely a normal initrd -> skip */ | |
554 | + i = strlen(t); | |
555 | + if(p - buf + i > GFX_CMD_BUF_SIZE - 2) break; | |
556 | + *p++ = GFX_CMD_SEP; | |
557 | + strcpy(p, t); | |
558 | + p += i; | |
559 | + | |
560 | + continue; | |
561 | + } | |
562 | + } | |
563 | + | |
564 | + if(*buf) buf++; /* skip initial separator char */ | |
565 | + | |
566 | + return buf; | |
567 | +} | |
568 | + | |
569 | + | |
570 | +/* | |
571 | + * Check header and return code start offset. | |
572 | + */ | |
573 | +static unsigned magic_ok(unsigned char *buf) | |
574 | +{ | |
575 | + if( | |
576 | + *(unsigned *) buf == 0x0b2d97f00 && /* magic id */ | |
577 | + (buf[4] == 8) /* version 8 */ | |
578 | + ) { | |
579 | + return *(unsigned *) (buf + 8); | |
580 | + } | |
581 | + | |
582 | + return 0; | |
583 | +} | |
584 | + | |
585 | + | |
586 | +/* | |
587 | + * Search cpio archive for gfx file. | |
588 | + */ | |
589 | +static unsigned find_file(unsigned char *buf, unsigned len, unsigned *gfx_file_start, unsigned *file_len) | |
590 | +{ | |
591 | + unsigned i, fname_len, code_start = 0; | |
592 | + | |
593 | + *gfx_file_start = 0; | |
594 | + | |
595 | + for(i = 0; i < len;) { | |
596 | + if((len - i) >= 0x1a && (buf[i] + (buf[i + 1] << 8)) == 0x71c7) { | |
597 | + fname_len = *(unsigned short *) (buf + i + 20); | |
598 | + *file_len = *(unsigned short *) (buf + i + 24) + (*(unsigned short *) (buf + i + 22) << 16); | |
599 | + i += 26 + fname_len; | |
600 | + i = ((i + 1) & ~1); | |
601 | + if((code_start = magic_ok(buf + i))) { | |
602 | + *gfx_file_start = i; | |
603 | + return code_start; | |
604 | + } | |
605 | + i += *file_len; | |
606 | + i = ((i + 1) & ~1); | |
607 | + } | |
608 | + else { | |
609 | + break; | |
610 | + } | |
611 | + } | |
612 | + | |
613 | + return code_start; | |
614 | +} | |
615 | + | |
616 | +static inline unsigned char * stack_ptr(void) | |
617 | +{ | |
618 | + unsigned char * u; | |
619 | + | |
620 | + asm("movl %%esp, %0" : "=r" (u)); | |
621 | + | |
622 | + return u; | |
623 | +} | |
624 | + | |
625 | +static void sleep(int delay) | |
626 | +{ | |
627 | + int tick, last_tick = currticks(); | |
628 | + | |
629 | + delay *= 18; | |
630 | + | |
631 | + while(delay--) { | |
632 | + while((tick = currticks()) == last_tick) { } | |
633 | + last_tick = tick; | |
634 | + } | |
635 | +} | |
636 | + | |
637 | +static void wait_for_key() | |
638 | +{ | |
639 | + printf("Press a key to continue..."); | |
640 | + getkey(); | |
641 | + printf("\r \r"); | |
642 | +} | |
643 | + | |
644 | + | |
645 | +/* | |
646 | + * Leave that much space on the heap. Everything else goes to the graphics | |
647 | + * functions. | |
648 | + * | |
649 | + * 0x2000 is _not_ enough | |
650 | + */ | |
651 | +#define MIN_HEAP_SIZE 0x4000 | |
652 | +#define MIN_GFX_FREE 0x1000 | |
653 | + | |
654 | +#define SC_BOOTLOADER 0 | |
655 | +#define SC_FAILSAFE 3 | |
656 | +#define SC_SYSCONFIG_SIZE 4 | |
657 | +#define SC_BOOTLOADER_SEG 8 | |
658 | +#define SC_XMEM_0 24 | |
659 | +#define SC_XMEM_1 26 | |
660 | +#define SC_XMEM_2 28 | |
661 | +#define SC_XMEM_3 30 | |
662 | +#define SC_FILE 32 | |
663 | +#define SC_ARCHIVE_START 36 | |
664 | +#define SC_ARCHIVE_END 40 | |
665 | +#define SC_MEM0_START 44 | |
666 | +#define SC_MEM0_END 48 | |
667 | + | |
668 | +/* | |
669 | + * Does normally not return. | |
670 | + */ | |
671 | +static void | |
672 | +run_graphics_menu (char *menu_entries, char *config_entries, int num_entries, | |
673 | + char *heap, int entryno) | |
674 | +{ | |
675 | + unsigned char *buf, *buf_ext; | |
676 | + unsigned buf_size, buf_ext_size, code_start, file_start; | |
677 | + char *s, *t, *t2, *cfg, *new_config, *p; | |
678 | + char *saved_heap; | |
679 | + int i, j, max_len, gfx_file_size, verbose; | |
680 | + int selected_entry; | |
681 | + gfx_data_t *gfx_data; | |
682 | + char *cmd_buf; | |
683 | + unsigned mem0_start, mem0_end, file_len; | |
684 | + | |
685 | + /* | |
686 | + * check gfx_data_t struct offsets for consistency; gcc will optimize away | |
687 | + * the whole block | |
688 | + */ | |
689 | + | |
690 | + /* dummy function to make ld fail */ | |
691 | + { | |
692 | + extern void wrong_struct_size(void); | |
693 | + #define gfx_ofs_check(a) if(gfx_ofs_##a != (char *) &gfx_data->a - (char *) gfx_data) wrong_struct_size(); | |
694 | + gfx_ofs_check(ok); | |
695 | + gfx_ofs_check(code_seg); | |
696 | + gfx_ofs_check(jmp_table); | |
697 | + gfx_ofs_check(sys_cfg); | |
698 | + gfx_ofs_check(cmdline); | |
699 | + gfx_ofs_check(cmdline_len); | |
700 | + gfx_ofs_check(menu_list); | |
701 | + gfx_ofs_check(menu_default_entry); | |
702 | + gfx_ofs_check(menu_entries); | |
703 | + gfx_ofs_check(menu_entry_len); | |
704 | + gfx_ofs_check(args_list); | |
705 | + gfx_ofs_check(args_entry_len); | |
706 | + gfx_ofs_check(timeout); | |
707 | + #undef gfx_ofs_check | |
708 | + } | |
709 | + | |
710 | + if(!num_entries) return; | |
711 | + | |
712 | + graphics_data = gfx_data = (gfx_data_t *) heap; | |
713 | + heap += sizeof *gfx_data; | |
714 | + memset(gfx_data, 0, sizeof *gfx_data); | |
715 | + | |
716 | + gfx_data->sys_cfg[SC_BOOTLOADER] = 2; /* bootloader: grub */ | |
717 | + gfx_data->sys_cfg[SC_SYSCONFIG_SIZE] = 52; /* config data size */ | |
718 | + *(unsigned short *) (gfx_data->sys_cfg + SC_BOOTLOADER_SEG) = (unsigned) gfx_data >> 4; /* segment */ | |
719 | + gfx_data->sys_cfg[SC_XMEM_0] = 0x21; /* 1MB @ 2MB */ | |
720 | + gfx_data->sys_cfg[SC_XMEM_1] = 0x41; /* 1MB @ 4MB */ | |
721 | + verbose = (*(unsigned char *) 0x417) & 3 ? 1 : 0; /* SHIFT pressed */ | |
722 | + gfx_data->sys_cfg[SC_FAILSAFE] = verbose; | |
723 | + | |
724 | + gfx_data->timeout = grub_timeout >= 0 ? grub_timeout : 0; | |
725 | + | |
726 | + | |
727 | + /* setup command line edit buffer */ | |
728 | + | |
729 | + gfx_data->cmdline_len = 256; | |
730 | + | |
731 | + gfx_data->cmdline = heap; | |
732 | + heap += gfx_data->cmdline_len; | |
733 | + memset(gfx_data->cmdline, 0, gfx_data->cmdline_len); | |
734 | + | |
735 | + cmd_buf = heap; | |
736 | + heap += GFX_CMD_BUF_SIZE; | |
737 | + | |
738 | + /* setup menu entries */ | |
739 | + | |
740 | + for(i = max_len = 0; i < num_entries; i++) { | |
741 | + j = strlen(get_entry(menu_entries, i, 0)); | |
742 | + if(j > max_len) max_len = j; | |
743 | + } | |
744 | + | |
745 | + if(!max_len) return; | |
746 | + | |
747 | + gfx_data->menu_entry_len = max_len + 1; | |
748 | + gfx_data->menu_entries = num_entries; | |
749 | + | |
750 | + gfx_data->menu_list = heap; | |
751 | + heap += gfx_data->menu_entry_len * gfx_data->menu_entries; | |
752 | + | |
753 | + memset(gfx_data->menu_list, 0, gfx_data->menu_entry_len * gfx_data->menu_entries); | |
754 | + | |
755 | + for(i = 0; i < (int) gfx_data->menu_entries; i++) { | |
756 | + strcpy(gfx_data->menu_list + i * gfx_data->menu_entry_len, get_entry(menu_entries, i, 0)); | |
757 | + } | |
758 | + | |
759 | + gfx_data->menu_default_entry = gfx_data->menu_list + entryno * gfx_data->menu_entry_len; | |
760 | + | |
761 | + | |
762 | + /* setup list of kernel args */ | |
763 | + | |
764 | + for(i = max_len = 0; i < num_entries; i++) { | |
765 | + s = get_kernel_args(get_entry(config_entries, i, 1), cmd_buf); | |
766 | + j = strlen(s); | |
767 | + if(j > max_len) max_len = j; | |
768 | + } | |
769 | + | |
770 | + gfx_data->args_entry_len = max_len + 1; | |
771 | + | |
772 | + gfx_data->args_list = heap; | |
773 | + heap += gfx_data->args_entry_len * gfx_data->menu_entries; | |
774 | + | |
775 | + memset(gfx_data->args_list, 0, gfx_data->args_entry_len * gfx_data->menu_entries); | |
776 | + | |
777 | + for(i = 0; i < (int) gfx_data->menu_entries; i++) { | |
778 | + strcpy(gfx_data->args_list + i* gfx_data->args_entry_len, get_kernel_args(get_entry(config_entries, i, 1), cmd_buf)); | |
779 | + } | |
780 | + | |
781 | + | |
782 | + /* go back here when we no longer need the graphics data */ | |
783 | + saved_heap = heap; | |
784 | + | |
785 | + | |
786 | + /* get memory area to be used by graphics functions */ | |
787 | + | |
788 | + /* use 1MB starting at 2MB as file buffer */ | |
789 | + buf_ext = (unsigned char *) (2 << 20); | |
790 | + buf_ext_size = 1 << 20; | |
791 | + | |
792 | + /* must be 16-byte aligned */ | |
793 | + buf = (unsigned char *) (((unsigned) heap + 0xf) & ~0xf); | |
794 | + | |
795 | + buf_size = stack_ptr() - buf - MIN_HEAP_SIZE; | |
796 | + buf_size &= ~0xf; | |
797 | + | |
798 | + mem0_start = (unsigned) buf; | |
799 | + mem0_end = mem0_start + buf_size; | |
800 | + | |
801 | + if(verbose) { | |
802 | + printf("low memory 0x%x - 0x%x (%d bytes)\n", mem0_start, mem0_end, buf_size); | |
803 | + wait_for_key(); | |
804 | + } | |
805 | + | |
806 | + heap += buf_size; | |
807 | + | |
808 | + /* read the file */ | |
809 | + | |
810 | + if(!grub_open(graphics_file)) { | |
811 | + printf("%s: file not found\n", graphics_file); | |
812 | + sleep(5); | |
813 | + heap = saved_heap; | |
814 | + return; | |
815 | + } | |
816 | + | |
817 | + gfx_file_size = grub_read(buf_ext, buf_ext_size); | |
818 | + | |
819 | + grub_close(); | |
820 | + | |
821 | + if(gfx_file_size <= 0) { | |
822 | + printf("%s: read error\n", graphics_file); | |
823 | + sleep(5); | |
824 | + heap = saved_heap; | |
825 | + return; | |
826 | + } | |
827 | + | |
828 | + if(verbose) { | |
829 | + printf("%s: %d bytes (%d bytes left)\n", graphics_file, gfx_file_size, buf_ext_size - gfx_file_size); | |
830 | + wait_for_key(); | |
831 | + } | |
832 | + | |
833 | + /* locate file inside cpio archive */ | |
834 | + if(!(code_start = find_file(buf_ext, gfx_file_size, &file_start, &file_len))) { | |
835 | + printf("%s: invalid file format\n", graphics_file); | |
836 | + sleep(5); | |
837 | + heap = saved_heap; | |
838 | + return; | |
839 | + } | |
840 | + | |
841 | + if(verbose) { | |
842 | + printf("init: start 0x%x, len %d; code offset 0x%x\n", file_start, file_len, code_start); | |
843 | + wait_for_key(); | |
844 | + } | |
845 | + | |
846 | + if(file_len - code_start + MIN_GFX_FREE > buf_size) { | |
847 | + printf("not enough free memory: %d extra bytes need\n", file_len - code_start + MIN_GFX_FREE - buf_size); | |
848 | + sleep(5); | |
849 | + heap = saved_heap; | |
850 | + return; | |
851 | + } | |
852 | + | |
853 | + memcpy((void *) buf, (void *) (buf_ext + file_start + code_start), file_len - code_start); | |
854 | + | |
855 | + mem0_start += file_len - code_start; | |
856 | + mem0_start = (mem0_start + 3) & ~3; /* align */ | |
857 | + | |
858 | + /* init interface to graphics functions */ | |
859 | + | |
860 | + *(unsigned *) (gfx_data->sys_cfg + SC_FILE) = (unsigned) buf_ext + file_start; | |
861 | + *(unsigned *) (gfx_data->sys_cfg + SC_ARCHIVE_START) = (unsigned) buf_ext; | |
862 | + *(unsigned *) (gfx_data->sys_cfg + SC_ARCHIVE_END) = (unsigned) buf_ext + gfx_file_size; | |
863 | + *(unsigned *) (gfx_data->sys_cfg + SC_MEM0_START) = mem0_start; | |
864 | + *(unsigned *) (gfx_data->sys_cfg + SC_MEM0_END) = mem0_end; | |
865 | + | |
866 | + gfx_data->code_seg = (unsigned) buf >> 4; | |
867 | + | |
868 | + if(verbose) { | |
869 | + printf("init 0x%x, archive 0x%x - 0x%x, low mem 0x%x - 0x%x\ncode seg 0x%x\n", | |
870 | + (unsigned) buf_ext + file_start, | |
871 | + (unsigned) buf_ext, (unsigned) buf_ext + gfx_file_size, | |
872 | + mem0_start, mem0_end, gfx_data->code_seg | |
873 | + ); | |
874 | + wait_for_key(); | |
875 | + } | |
876 | + | |
877 | + for(i = 0; (unsigned) i < sizeof gfx_data->jmp_table / sizeof *gfx_data->jmp_table; i++) { | |
878 | + gfx_data->jmp_table[i] = (gfx_data->code_seg << 16) + ((unsigned short *) buf)[i]; | |
879 | + } | |
880 | + | |
881 | + if(verbose) { | |
882 | + for(i = 0; i < 12; i++) { | |
883 | + printf("%d: 0x%x\n", i, gfx_data->jmp_table[i]); | |
884 | + } | |
885 | + | |
886 | + for(i = 0; i < gfx_data->menu_entries; i++) { | |
887 | + printf("\"%s\" -- \"%s\"\n", | |
888 | + gfx_data->menu_list + i * gfx_data->menu_entry_len, | |
889 | + gfx_data->args_list + i * gfx_data->args_entry_len | |
890 | + ); | |
891 | + } | |
892 | + | |
893 | + printf("default: \"%s\"\n", gfx_data->menu_default_entry); | |
894 | + wait_for_key(); | |
895 | + } | |
896 | + | |
897 | + /* switch to graphics mode */ | |
898 | + | |
899 | + if(gfx_init(gfx_data)) { | |
900 | + printf("graphics initialization failed\n"); | |
901 | + sleep(5); | |
902 | + heap = saved_heap; | |
903 | + return; | |
904 | + } | |
905 | + | |
906 | + gfx_setup_menu(gfx_data); | |
907 | + | |
908 | + i = gfx_input(gfx_data, &selected_entry); | |
909 | + | |
910 | + /* ESC -> show text menu */ | |
911 | + if(i == 1) { | |
912 | + gfx_done(gfx_data); | |
913 | + grub_timeout = -1; | |
914 | + | |
915 | + heap = saved_heap; | |
916 | + return; | |
917 | + } | |
918 | + | |
919 | + gfx_done(gfx_data); | |
920 | + | |
921 | + heap = saved_heap; /* free most of the graphics data */ | |
922 | + | |
923 | + // printf("cmdline: >%s<, entry = %d\n", gfx_data->cmdline, selected_entry); | |
924 | + | |
925 | + if(selected_entry < 0 || selected_entry > num_entries) return; | |
926 | + | |
927 | + | |
928 | + /* create new config with modified kernel option */ | |
929 | + | |
930 | + cfg = get_entry(config_entries, selected_entry, 1); | |
931 | + | |
932 | + new_config = heap; | |
933 | + | |
934 | + for(p = gfx_data->cmdline, i = 0; ; i++) { | |
935 | + s = get_entry(cfg, i, 0); | |
936 | + if(!*s) { | |
937 | + if(!i) *heap++ = 0; | |
938 | + *heap++ = 0; | |
939 | + break; | |
940 | + } | |
941 | + /* note: must match get_kernel_args() */ | |
942 | + if( | |
943 | + (!memcmp(s, "kernel", 6) || !memcmp(s, "module", 6)) && | |
944 | + (s[6] == ' ' || s[6] == '\t') | |
945 | + ) { | |
946 | + t = skip_to(0, s); | |
947 | + t2 = s[0] == 'm' ? strstr(t, "initrd") : NULL; | |
948 | + if(*t) t = skip_to(0, t); | |
949 | + if(t2 && t2 < t) { /* module is likely a normal initrd -> skip */ | |
950 | + strcpy(heap, s); | |
951 | + heap += strlen(s) + 1; | |
952 | + continue; | |
953 | + } | |
954 | + memmove(heap, s, t - s); | |
955 | + heap += t - s; | |
956 | + *heap++ = ' '; | |
957 | + while(*p && *p != GFX_CMD_SEP) *heap++ = *p++; | |
958 | + *heap++ = 0; | |
959 | + if(*p == GFX_CMD_SEP) p++; | |
960 | + } | |
961 | + else { | |
962 | + strcpy(heap, s); | |
963 | + heap += strlen(s) + 1; | |
964 | + } | |
965 | + } | |
966 | + | |
967 | + *heap++ = 0; | |
968 | + | |
969 | + // hexdump(new_config, heap - new_config); | |
970 | + // getkey(); | |
971 | + | |
972 | + run_script(new_config, heap); | |
973 | +} | |
974 | + | |
975 | + | |
976 | static int | |
977 | get_line_from_config (char *cmdline, int maxlen, int read_from_file) | |
978 | { | |
979 | @@ -1062,9 +1557,12 @@ | |
980 | } | |
981 | else | |
982 | { | |
983 | - /* Run menu interface. */ | |
984 | - run_menu (menu_entries, config_entries, num_entries, | |
985 | - menu_entries + menu_len, default_entry); | |
986 | + if (*graphics_file && !password && show_menu && grub_timeout) | |
987 | + { | |
988 | + run_graphics_menu(menu_entries, config_entries, num_entries,menu_entries + menu_len, default_entry); | |
989 | + } | |
990 | + /* Run menu interface. */ | |
991 | + run_menu (menu_entries, config_entries, num_entries, menu_entries + menu_len, default_entry); | |
992 | } | |
993 | } | |
994 | } |