physical floppy disk is present in the system.
* Enable the 16550A FIFOs, if present, when doing serial
console.
+ * New "totaltimeout" command establishes a timeout without
+ regard for any user input.
Changes in 3.10:
* gcc 4.0.1 compilation fixes.
--- /dev/null
+/*
+ * arch/i386/include/klibc/archsetjmp.h
+ */
+
+#ifndef _KLIBC_ARCHSETJMP_H
+#define _KLIBC_ARCHSETJMP_H
+
+struct __jmp_buf {
+ unsigned int __ebx;
+ unsigned int __esp;
+ unsigned int __ebp;
+ unsigned int __esi;
+ unsigned int __edi;
+ unsigned int __eip;
+};
+
+typedef struct __jmp_buf jmp_buf[1];
+
+#endif /* _SETJMP_H */
--- /dev/null
+/*
+ * setjmp.h
+ */
+
+#ifndef _SETJMP_H
+#define _SETJMP_H
+
+#include <klibc/extern.h>
+#include <klibc/compiler.h>
+#include <stddef.h>
+#include <signal.h>
+
+#include <klibc/archsetjmp.h>
+
+__extern int setjmp(jmp_buf);
+__extern __noreturn longjmp(jmp_buf, int);
+
+/*
+ Whose bright idea was it to add unrelated functionality to just about
+ the only function in the standard C library (setjmp) which cannot be
+ wrapped by an ordinary function wrapper? Anyway, the damage is done,
+ and therefore, this wrapper *must* be inline. However, gcc will
+ complain if this is an inline function for unknown reason, and
+ therefore sigsetjmp() needs to be a macro.
+*/
+
+struct __sigjmp_buf {
+ jmp_buf __jmpbuf;
+ sigset_t __sigs;
+};
+
+typedef struct __sigjmp_buf sigjmp_buf[1];
+
+#define sigsetjmp(__env, __save) \
+({ \
+ struct __sigjmp_buf *__e = (__env); \
+ sigprocmask(0, NULL, &__e->__sigs); \
+ setjmp(__e->__jmpbuf); \
+})
+
+__extern __noreturn siglongjmp(sigjmp_buf, int);
+
+#endif /* _SETJMP_H */
LIBOBJS = \
abort.o atexit.o atoi.o atol.o atoll.o calloc.o creat.o \
ctypes.o errno.o fgetc.o fgets.o fopen.o fprintf.o fputc.o \
- putchar.o \
+ putchar.o setjmp.o \
fputs.o fread2.o fread.o free.o fwrite2.o fwrite.o getopt.o \
lrand48.o malloc.o stack.o memccpy.o memchr.o memcmp.o \
memcpy.o memmem.o memmove.o memset.o memswap.o exit.o onexit.o \
.text
.align 4
+
+ .globl _setjmp
+ .type _setjmp, @function
+_setjmp: # gcc 4.0.1 wants this as an alias?
+
.globl setjmp
.type setjmp, @function
setjmp:
#include <consoles.h>
#include <getkey.h>
#include <minmax.h>
-#include <time.h>
-#include <sys/times.h>
-#include <unistd.h>
+#include <setjmp.h>
+#include <limits.h>
#include <sha1.h>
#include <base64.h>
#ifdef __COM32__
return !memcmp(sha1, pwdsha1, 20);
}
+static jmp_buf timeout_jump;
+
+static int mygetkey(clock_t timeout)
+{
+ clock_t t0, t;
+ clock_t tto, to;
+ int key;
+
+ if ( !totaltimeout )
+ return get_key(stdin, timeout);
+
+ for (;;) {
+ tto = min(totaltimeout, INT_MAX);
+ to = timeout ? min(tto, timeout) : tto;
+
+ t0 = times(NULL);
+ key = get_key(stdin, to);
+ t = times(NULL) - t0;
+
+ if ( totaltimeout <= t )
+ longjmp(timeout_jump, 1);
+
+ totaltimeout -= t;
+
+ if ( key != KEY_NONE )
+ return key;
+
+ if ( timeout ) {
+ if ( timeout <= t )
+ return KEY_NONE;
+
+ timeout -= t;
+ }
+ }
+}
+
static int
ask_passwd(const char *menu_entry)
{
p = user_passwd;
while ( !done ) {
- key = get_key(stdin, 0);
+ key = mygetkey(0);
switch ( key ) {
case KEY_ENTER:
redraw = 0;
}
- key = get_key(stdin, 0);
+ key = mygetkey(0);
switch( key ) {
case KEY_CTRL('L'):
{
int key;
int done = 0;
- int entry = defentry, prev_entry = -1;
+ volatile int entry = defentry, prev_entry = -1;
int top = 0, prev_top = -1;
int clear = 1, to_clear;
const char *cmdline = NULL;
- clock_t key_timeout, timeout_left, this_timeout;
+ volatile clock_t key_timeout, timeout_left, this_timeout;
- /* Convert timeout from deciseconds to clock ticks */
/* Note: for both key_timeout and timeout == 0 means no limit */
- timeout_left = key_timeout = (clock_t)(CLK_TCK*timeout+9)/10;
+ timeout_left = key_timeout = timeout;
+
+ /* Handle both local and global timeout */
+ if ( setjmp(timeout_jump) ) {
+ entry = defentry;
+
+ if ( top < 0 || top < entry-MENU_ROWS+1 )
+ top = max(0, entry-MENU_ROWS+1);
+ else if ( top > entry || top > max(0,nentries-MENU_ROWS) )
+ top = min(entry, max(0,nentries-MENU_ROWS));
+
+ draw_menu(ontimeout ? -1 : entry, top, 1);
+ cmdline = ontimeout ? ontimeout : menu_entries[entry].cmdline;
+ done = 1;
+ }
while ( !done ) {
if ( entry < 0 )
prev_entry = entry; prev_top = top;
- if ( key_timeout && entry == defentry ) {
+ /* Cursor movement cancels timeout */
+ if ( entry != defentry )
+ key_timeout = 0;
+
+ if ( key_timeout ) {
int tol = timeout_left/CLK_TCK;
int nc = snprintf(NULL, 0, " Automatic boot in %d seconds ", tol);
printf("\033[%d;%dH%s Automatic boot in %s%d%s seconds ",
to_clear = 0;
}
- this_timeout = min(timeout_left, CLK_TCK);
- key = get_key(stdin, this_timeout);
+ this_timeout = min(min(key_timeout, timeout_left), CLK_TCK);
+ key = mygetkey(this_timeout);
if ( key != KEY_NONE ) {
timeout_left = key_timeout;
Warning: a timeout will boot the default entry without any
password! */
- timeout_left -= this_timeout;
-
- if ( timeout_left == 0 ) {
- if ( entry != defentry ) {
- entry = defentry;
- timeout_left = key_timeout;
- } else {
- cmdline = menu_entries[defentry].cmdline;
- done = 1;
- }
+ if ( key_timeout ) {
+ if ( timeout_left <= this_timeout )
+ longjmp(timeout_jump, 1);
+
+ timeout_left -= this_timeout;
}
break;
case KEY_ENTER:
case KEY_CTRL('J'):
+ key_timeout = 0; /* Cancels timeout */
if ( menu_entries[entry].passwd ) {
clear = 1;
done = ask_passwd(menu_entries[entry].passwd);
if ( allowedit ) {
int ok = 1;
+ key_timeout = 0; /* Cancels timeout */
draw_row(entry-top+4, -1, top, 0, 0);
if ( to_clear ) {
if ( allowedit ) {
done = 1;
clear = 1;
+ key_timeout = 0;
draw_row(entry-top+4, -1, top, 0, 0);
if ( key > 0 && key < 0xFF ) {
key &= ~0x20; /* Upper case */
if ( menu_hotkeys[key] ) {
+ key_timeout = 0;
entry = menu_hotkeys[key] - menu_entries;
/* Should we commit at this point? */
}
/* If this returns, something went bad; return to menu */
#else
/* For testing... */
- printf("\n>>> %s\n", cmdline);
+ printf("\n\033[0m>>> %s\n", cmdline);
exit(0);
#endif
}
#ifndef MENU_H
#define MENU_H
+#include <time.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <inttypes.h>
+#include <unistd.h>
+
+#ifndef CLK_TCK
+# define CLK_TCK sysconf(_SC_CLK_TCK)
+#endif
+
struct menu_entry {
char *displayname;
char *label;
extern int defentry;
extern int allowedit;
extern int timeout;
+extern long long totaltimeout;
extern char *menu_title;
extern char *ontimeout;
#include "menu.h"
-int nentries = 0;
-int defentry = 0;
-int allowedit = 1; /* Allow edits of the command line */
-int timeout = 0;
+int nentries = 0;
+int defentry = 0;
+int allowedit = 1; /* Allow edits of the command line */
+int timeout = 0;
+long long totaltimeout = 0;
char *menu_title = "";
char *ontimeout = NULL;
ld.kernel = strdup(skipspace(p+6));
}
} else if ( looking_at(p, "timeout") ) {
- timeout = atoi(skipspace(p+7));
+ timeout = (atoi(skipspace(p+7))*CLK_TCK+9)/10;
+ } else if ( looking_at(p, "totaltimeout") ) {
+ totaltimeout = (atoll(skipspace(p+13))*CLK_TCK+9)/10;
} else if ( looking_at(p, "ontimeout") ) {
ontimeout = strdup(skipspace(p+9));
} else if ( looking_at(p, "allowoptions") ) {
serial
console
timeout
+totaltimeout
allowoptions
ontimeout
onerror
keyword say, pc_say
keyword serial, pc_serial
keyword console, pc_setint16, DisplayCon
- keyword timeout, pc_timeout
+ keyword timeout, pc_timeout, KbdTimeout
+ keyword totaltimeout, pc_timeout, TotalTimeout
keyword ontimeout, pc_ontimeout
keyword onerror, pc_onerror
keyword allowoptions, pc_setint16, AllowOptions
.err: ret
;
-; "timeout" command
+; "timeout", "totaltimeout" command
;
-pc_timeout: call getint
+; N.B. 1/10 s ~ 1.D2162AABh clock ticks
+;
+pc_timeout: push ax
+ call getint
+ pop si
jc .err
- mov ax,0D215h ; There are approx 1.D215h
- mul bx ; clock ticks per 1/10 s
- add bx,dx
- mov [KbdTimeOut],bx
+ mov eax,0D2162AABh
+ mul ebx ; clock ticks per 1/10 s
+ add ebx,edx
+ mov [si],ebx
.err: ret
+
+;
+; "totaltimeout" command
+;
+pc_totaltimeout:
+
;
; Generic integer variable setting commands:
; "prompt", "implicit"
section .data
vk_overflow_msg db 'Out of memory parsing config file', CR, LF, 0
- align 2, db 0
+ align 4, db 0
+KbdTimeout dd 0 ; Keyboard timeout (if any)
+TotalTimeout dd 0 ; Total timeout (if any)
AppendLen dw 0 ; Bytes in append= command
OntimeoutLen dw 0 ; Bytes in ontimeout command
OnerrorLen dw 0 ; Bytes in onerror command
-KbdTimeOut dw 0 ; Keyboard timeout (if any)
CmdLinePtr dw cmd_line_here ; Command line advancing pointer
ForcePrompt dw 0 ; Force prompt
NoEscape dw 0 ; No escape
begun. A timeout of zero will disable the timeout completely,
this is also the default.
- NOTE: The maximum possible timeout value is 35996; corresponding to
- just below one hour.
+TOTALTIMEOUT timeout
+ Indicates how long to wait until booting automatically, in
+ units of 1/10 s. This timeout is *not* cancelled by user
+ input, and can thus be used to deal with serial port glitches
+ or "the user walked away" type situations. A timeout of zero
+ will disable the timeout completely, this is also the default.
+
+ Both TIMEOUT and TOTALTIMEOUT can be used together, for
+ example:
+
+ # Wait 5 seconds unless the user types something, but
+ # always boot after 15 minutes.
+ TIMEOUT 50
+ TOTALTIMEOUT 9000
ONTIMEOUT kernel options...
Sets the command line invoked on a timeout. Normally this is
mov byte [FuncFlag],0 ; <Ctrl-F> not pressed
mov di,command_line
+
;
; get the very first character -- we can either time
; out, or receive a character press at this time. Some dorky BIOSes stuff
mov ah,10h ; Get char
int 16h
jmp short clear_buffer
-get_char_time:
- call vgashowcursor
- RESET_IDLE
- mov cx,[KbdTimeOut]
- and cx,cx
- jz get_char ; Timeout == 0 -> no timeout
- inc cx ; The first loop will happen
- ; immediately as we don't
- ; know the appropriate DX value
-time_loop: push cx
-tick_loop: push dx
- call pollchar
- jnz get_char_pop
- mov dx,[BIOS_timer] ; Get time "of day"
- pop ax
- cmp dx,ax ; Has the timer advanced?
- je tick_loop
- pop cx
- DO_IDLE
- loop time_loop ; If so, decrement counter
- ; Timeout!!!!
- call vgahidecursor
- mov si,Ontimeout ; Copy ontimeout command
- mov cx,[OntimeoutLen] ; if we have one...
- rep movsb
-.stddefault:
- jmp command_done
+ ; For the first character, both KbdTimeout and
+ ; TotalTimeout apply; after that, only TotalTimeout.
+
+get_char_time:
+ mov eax,[TotalTimeout]
+ mov [ThisTotalTo],eax
+ mov eax,[KbdTimeout]
+ mov [ThisKbdTo],eax
-get_char_pop: pop eax ; Clear stack
get_char:
- call vgashowcursor
- call getchar
- call vgahidecursor
+ call getchar_timeout
+ and dword [ThisKbdTo],0 ; For the next time...
+
and al,al
jz func_key
;
kernel_corrupt: mov si,err_notkernel
jmp abort_load
+
+;
+; Get a key, observing ThisKbdTO and ThisTotalTO -- those are timeouts
+; which can be adjusted by the caller based on the corresponding
+; master variables; on return they're updated.
+;
+; This cheats. If we say "no timeout" we actually get a timeout of
+; 7.5 years.
+;
+getchar_timeout:
+ call vgashowcursor
+ RESET_IDLE
+
+.loop:
+ push word [BIOS_timer]
+ call pollchar
+ jnz .got_char
+ pop ax
+ cmp ax,[BIOS_timer] ; Has the timer advanced?
+ je .loop
+ DO_IDLE
+
+ dec dword [ThisKbdTo]
+ jz .timeout
+ dec dword [ThisTotalTo]
+ jnz .loop
+
+.timeout:
+ ; Timeout!!!!
+ pop cx ; Discard return address
+ call vgahidecursor
+ mov si,Ontimeout ; Copy ontimeout command
+ mov cx,[OntimeoutLen] ; if we have one...
+ rep movsb
+ jmp command_done
+
+.got_char:
+ pop cx ; Discard
+ call getchar
+ call vgahidecursor
+ ret
+
;
; This is it! We have a name (and location on the disk)... let's load
; that sucker!! First we have to decide what kind of file this is; base
; Otherwise Linux kernel
section .bss
- alignb 2
+ alignb 4
+ThisKbdTo resd 1 ; Temporary holder for KbdTimeout
+ThisTotalTo resd 1 ; Temporary holder for TotalTimeout
KernelExtPtr resw 1 ; During search, final null pointer
CmdOptPtr resw 1 ; Pointer to first option on cmd line
KbdFlags resb 1 ; Check for keyboard escapes