Newsgroups: comp.os.linux.announce
From: kevin@frobozz.sccsi.com
Subject: Root device selector (0.99.10)
Message-ID: <1993Jul12.134137.23057@dg-rtp.dg.com>
Approved: linux-announce@tc.cornell.edu (Matt Welsh)
Date: Mon, 12 Jul 93 13:41:37 GMT

Some time ago, I ran across a patch (by Dan Bernstein, I believe) that would
allow one to select the root device at runtime.  The patch doesn't apply to
newer kernels, so I had to roll my own.  What follows is the end result.

Per the discussions about runtime kernel configuration (e.g., VGA modes),
I have incorporated a 15 second timeout into the root device selection
mechanism, using the internal kernel timer mechanism.

When the kernel comes up, it will ask you to press SPACE if you want to
change the root device from the displayed default (which is the compiled-in
default set by tools/build that we all know and love).  If the default
device isn't the floppy, and you don't press SPACE in 15 seconds or less,
the kernel will go ahead and use the default root device.

Pressing SPACE causes the kernel to ask you what the major and minor numbers
of the root device are.  Just type them in.  The backspace/delete key works,
but doesn't change the display the way you might expect (I wasn't able to
find a good, easy way to get the cursor to move from within the kernel, so
I settled for printing the previous character, if any).

The end result is that you can create a boot floppy and boot from *any*
device.  If the kernel fails to mount the root file system, it will ask you
to specify another.  I assume that it is safe to do this, though this feature
can be easily commented out.


You need to cd into the linux source directory and do a patch -p with the
patch as standard input to make the changes.  Standard stuff.  The patch
below is relative to virgin 0.99.10.  It probably won't patch cleanly
against anything else.

The code in keyboard.c is general enough that you can use it to get strings
from the keyboard (via kgets) from pretty much anywhere in the kernel, do
timed waits for keypresses, etc.  This might be useful for other things
similar to what I'm doing.

Note that the compiler will give you a few warnings when compiling the
patched kernel.  I figured I'd let Linus clean it up however he likes.  :-)

Given the usefulness of this particular patch, and given that the major
objection of there being no timeout is no longer valid, I hereby request
that this patch be made part of the standard kernel distribution.  It has
saved me many times.

Many thanks to Dan Bernstein for the original inspiration to do this, and
of course to Linus and the rest of the Linuxers out there, without whom
writing a patch to specify the root device would be merely a dream.  :-)

				Kevin Brown
				kevin@frobozz.sccsi.com


*** fs/super.c~	Tue Jun  1 18:14:05 1993
--- fs/super.c	Mon Jul 12 01:43:39 1993
***************
*** 473,490 ****
  	return retval;
  }
  
  void mount_root(void)
  {
  	struct file_system_type * fs_type;
  	struct super_block * sb;
  	struct inode * inode;
! 
! 	memset(super_block, 0, sizeof(super_block));
! 	fcntl_init_locks();
! 	if (MAJOR(ROOT_DEV) == 2) {
! 		printk("VFS: Insert root floppy and press ENTER");
! 		wait_for_keypress();
! 	}
  	for (fs_type = file_systems; fs_type->read_super; fs_type++) {
  		if (!fs_type->requires_dev)
  			continue;
--- 473,562 ----
  	return retval;
  }
  
+ 
+ 
+ 
+ /* find out where the user wants to boot from */
+ void ask_for_root(void)
+ {
+ int major=0,minor=0,c;
+ char *buf;
+ 
+     printk("\n\nEnter device number to boot from (<esc> at any time to retry).\n");
+     printk ( "Zero or <enter> for major gets you the floppy drive.\n" );
+ retry_major:
+     printk("\nMajor device # (default = floppy)? ");
+     major = 0;
+     buf = kgets ( 0 );
+     if ( ! buf )
+     {
+ 	printk ("\n");
+ 	goto retry_major;
+     }
+     for ( ; c = *buf ; ++buf )
+     {
+ 	if ( c >= '0' && c <= '9' )
+ 	{
+ 	    major*=10;
+ 	    major+=c-'0';
+ 	}
+     }
+     if ( major )
+     {
+ 	minor = 0; 
+ 	printk("Minor device # (default = 0)? ");
+ 	buf = kgets ( 0 );
+ 	if ( ! buf )
+ 	{
+ 	    printk ("\n");
+ 	    goto retry_major;
+ 	}
+ 	for ( ; c = *buf ; ++buf )
+ 	{
+ 	    if ( c >= '0' && c <= '9' )
+ 	    {
+ 		minor*=10;
+ 		minor+=c-'0';
+ 	    }
+ 	}
+     }
+     else
+     {
+ 	major = 2;
+ 	minor = 0;
+     }
+     printk("\n");
+     ROOT_DEV=(major<<8)+minor;
+ }
+ 
+ 
+ 
+ 
  void mount_root(void)
  {
  	struct file_system_type * fs_type;
  	struct super_block * sb;
  	struct inode * inode;
! 	int timeout;
! 
! 	timeout = 15;
! retry:
! 	memset(super_block, 0, sizeof(super_block));
! 	fcntl_init_locks();
! 	if ( MAJOR(ROOT_DEV) == 2) {
! 		printk("\nInsert root floppy and press ENTER to boot off it\n");
! 		timeout = 0;
! 	} else {
! 		printk("\nPress ENTER to boot from the default device (major = %d, minor = %d)\n",
! 		MAJOR(ROOT_DEV), MINOR(ROOT_DEV));
! 	}
! 	printk("or hit SPACE to select an alternative boot device ");
! 	if (kgetkey( timeout )==' ') {
! 		timeout = 0;
! 		ask_for_root();
! 		goto retry;
! 	}		
! 	printk ("\n");
  	for (fs_type = file_systems; fs_type->read_super; fs_type++) {
  		if (!fs_type->requires_dev)
  			continue;
***************
*** 502,506 ****
  			return;
  		}
  	}
! 	panic("VFS: Unable to mount root");
! }
--- 574,591 ----
  			return;
  		}
  	}
! #if 1
! 	/*
! 	 * I'm not yet confident of the stability of the running kernel when
! 	 * going back to retry a root-mount, since I don't know if there's
! 	 * some preinitialized data space that gets changed during a mount.
! 	 * If you think that such an operation is stable, uncomment the
! 	 * following out.
! 	 */
! 	printk("VFS: Unable to mount root!\n");
! 	ask_for_root();
! 	goto retry;
! #else
! 	panic("VFS: Unable to mount root");
! #endif
! }
*** kernel/chr_drv/tty_io.c~	Fri May 28 18:48:36 1993
--- kernel/chr_drv/tty_io.c	Mon Jul 12 00:48:36 1993
***************
*** 461,471 ****
  	complete_change_console(new_console);
  }
  
- void wait_for_keypress(void)
- {
- 	sleep_on(&keypress_wait);
- }
- 
  void copy_to_cooked(struct tty_struct * tty)
  {
  	int c, special_flag;
--- 461,466 ----
*** kernel/chr_drv/keyboard.c~	Sat Jun  5 09:36:21 1993
--- kernel/chr_drv/keyboard.c	Mon Jul 12 03:00:22 1993
***************
*** 15,20 ****
--- 15,21 ----
  
  #include <linux/sched.h>
  #include <linux/tty.h>
+ #include <linux/timer.h>
  #include <linux/mm.h>
  #include <linux/ptrace.h>
  #include <linux/keyboard.h>
***************
*** 59,64 ****
--- 60,67 ----
  unsigned long kbd_dead_keys = 0;
  unsigned long kbd_prev_dead_keys = 0;
  
+ unsigned char extern_scancode = 0;
+ 
  static int want_console = -1;
  static int last_console = 0;		/* last used VC */
  static char rep = 0;			/* flag telling character repeat */
***************
*** 126,131 ****
--- 129,135 ----
  	if (!(inb_p(0x64) & 0x01))
  		goto end_kbd_intr;
  	scancode = inb(0x60);
+ 	extern_scancode = scancode;
  	mark_bh(KEYBOARD_BH);
  	if (scancode == 0xfa) {
  		acknowledge = 1;
***************
*** 748,750 ****
--- 752,878 ----
  	mark_bh(KEYBOARD_BH);
  	return kmem_start;
  }
+ 
+ 
+ int get_mapped_key ( void )
+ {
+ int k;
+ 
+     k = key_map[0][extern_scancode];
+     if ( k == K_ENTER ) return 13;
+     return k;
+ }
+ 
+ 
+ /*
+  * Various functions for getting keystrokes, strings, etc., from the
+  * keyboard.  Perhaps this should be moved into keyboard.c.
+  */
+  
+ void keypress_timeout ( int *timeout_flag_ptr )
+ {
+     *timeout_flag_ptr = 1;
+     wake_up ( &keypress_wait );
+ }
+ 
+ 
+ /*
+  * A return value of zero means that we timed out.
+  */
+ int wait_for_keypress(int time)
+ {
+ int retval;
+ static struct timer_list my_timer;
+ static int timed_out;
+ 
+ 	if ( time )
+ 	{
+ 	    my_timer.function = &keypress_timeout;
+ 	    my_timer.expires = HZ * time;
+ 	    my_timer.data = &timed_out;
+ 	    add_timer ( &my_timer );
+ 	}
+ 	timed_out = 0;
+ 	sleep_on(&keypress_wait);
+ 	/*
+ 	 * If the timer expired, the timer entry has presumably been removed
+ 	 * from the timer queue, and del_timer won't do anything.  Otherwise,
+ 	 * we have to get rid of the timer entry to avoid seeing a bogus
+ 	 * keystroke.
+ 	 */
+ 	if ( time ) del_timer ( &my_timer );
+ 	return ( ! timed_out );
+ }
+ 
+ 
+ int kgetchar ( int time )
+ {
+ int c;
+ 
+ 	if ( ! wait_for_keypress( time ) ) return -1;
+ 	c = get_mapped_key();
+ 	if ( TTY_TABLE(0) && L_ECHO(TTY_TABLE(0)) ) return c;
+ 	if ( c == 8 ) return c;
+ 	if ( c < 32 && c != '\r' && c != '\n' )
+ 	    printk ( "^%c", c + 64 );
+ 	else switch ( c )
+ 	{
+ 	    case '\r':
+ 		printk ( "\n" );
+ 		break;
+ 	    case 127:
+ 	    case 8:
+ 		break;
+ 	    default:
+ 		printk ( "%c", c );
+ 	}
+ 	return c;
+ }
+ 
+ int kgetkey ( int time )
+ {
+     if ( ! wait_for_keypress( time ) ) return -1;
+     return ( get_mapped_key() );
+ }
+ 
+ 
+ char *kgets ( char *buf )
+ {
+ static char mybuf[256];
+ char *p = buf;
+ int c, index = 0;
+ 
+     if ( ! p ) p = mybuf;
+     while ( 1 )
+     {
+ 	c = kgetchar( 0 );
+ 	if ( index > 255 ) continue;
+ 	switch ( c )
+ 	{
+ 	case 8:
+ 	case 127:
+ 	    if ( index )
+ 	    {
+ 		p--;
+ 		index --;
+ 		if ( index ) printk ( "%c", *(p-1) );
+ 	    }
+ 	    break;
+ 
+ 	case '\r':
+ 	case '\n':
+ 	    *p = 0;
+ 	    return ( buf ? buf : mybuf );
+ 
+ 	case 27:
+ 	    *p = 0;	/* Just in case... */
+ 	    return 0;
+ 
+ 	default:
+ 	    *p++ = c;
+ 	    index++;
+ 	    break;
+ 	}
+     }
+ }
+ 

--
Send submissions for comp.os.linux.announce to: linux-announce@tc.cornell.edu
