Serial console patch for 2.0.23

Nicholas J. Leon (nicholas@binary9.net)
Sun, 10 Nov 1996 18:39:23 -0500 (EST)


Someone asked about a patch that applies cleanly to 2.0.25? Well, I
don't know about .25, but this one goes against .23 perfectly. 'Been
using it on 3 servers for over 3 weeks with no problem.

diff -ruN linux-2.0.20.orig/Documentation/serial-console.txt linux-2.0.20/Documentation/serial-console.txt
--- linux-2.0.20.orig/Documentation/serial-console.txt Thu Jan 1 01:00:00 1970
+++ linux-2.0.20/Documentation/serial-console.txt Sat Sep 21 14:55:52 1996
@@ -0,0 +1,75 @@
+ Linux Serial Console
+
+These examples are valid if you want to use /dev/ttyS1 (COM2)
+as the serial console. Replace as needed.
+
+1. Tell LILO to use the serial port.
+ In lilo.conf (global section):
+
+ serial = 1,9600n8 (ttyS1, 9600 bd, no parity, 8 bits)
+
+2. Adjust to kernel flags for the new kernel,
+ again in lilo.conf (kernel section)
+
+ append = "console=1,9600n8"
+
+3. Link /dev/console to the serial port.
+
+ Your probably want to save your old /dev/console (the "master" virtual
+ console). Check if it is a symbolic link first. If not, `mv' it to
+ `/dev/tty0':
+
+ ls -l /dev/console
+ mv /dev/console /dev/tty0
+
+ Now link the serial port you are going to use as the console to
+ /dev/console, for example ttyS1:
+
+ ln -s /dev/ttyS1 /dev/console
+
+ On some systems you might want to edit your bootup scripts to make sure
+ they don't reset this arrangement on boot. (On Debian, check
+ /etc/rc.boot/console and /etc/default/console). You probably also want
+ to put a getty on either /dev/console or /dev/ttyS1.
+
+4. Init and /dev/console.
+ Sysvinit will open /dev/console on boot. If this does not point
+ to the serial console device, the startup messages will be printed
+ to the wrong device. The kernel also passes the environment variable
+ "CONSOLE" to the init program. sysvinit-2.64 reckognizes this, and
+ opens that device instead. Boot scripts as mentioned in (3) can
+ also check this variable to see what device the system console is.
+ If CONSOLE is not set you can assume the console is /dev/tty0.
+
+ Sysvinit remembers its stty settings in a file in /etc, called
+ `/etc/ioctl.save'. REMOVE THIS FILE before using the serial
+ console for the first time, because otherwise init will probably
+ set the baudrate to 38400 (baudrate of the virtual console).
+
+5. /dev/console and X
+ Programs that want to do something with the virtual console usually
+ open /dev/console. XF86 does this, and probably SVGALIB as well.
+ IMO this is wrong; they should open /dev/tty0.
+ I have binary patched /usr/bin/X11/XF86_SVGA to use "tty0"
+ instead of "console".
+
+6. Notes.
+
+ If you compile the next little program, you will be able
+ to really "halt" the system. It will enter a little
+ monitor :)
+
+ main() { reboot(0xfee1dead, 672274793, 0xCDEF0123); }
+
+ This is just a call to the new "halt" function that later
+ kernels have. This is included the "halt" command of
+ the recent sysvinit versions.
+
+ The monitor will also be entered at a kernel panic, or
+ when you press "break". That last function does not
+ work at the moment I think, but it would be useful for
+ kernel debugging. You don't have alt-scrollock on a serial
+ console to find out the current EIP...
+
+Miquel van Smoorenburg <miquels@cistron.nl>, 21-Jun-1996
+
diff -ruN linux-2.0.20.orig/drivers/char/Config.in linux-2.0.20/drivers/char/Config.in
--- linux-2.0.20.orig/drivers/char/Config.in Sat Aug 10 20:44:18 1996
+++ linux-2.0.20/drivers/char/Config.in Sat Sep 21 14:38:17 1996
@@ -5,6 +5,9 @@
comment 'Character devices'

tristate 'Standard/generic serial support' CONFIG_SERIAL
+if [ "$CONFIG_SERIAL" = y ]; then
+ bool 'Serial console support' CONFIG_SERIAL_CONSOLE
+fi
bool 'Digiboard PC/Xx Support' CONFIG_DIGI
tristate 'Cyclades async mux support' CONFIG_CYCLADES
bool 'Stallion multiport serial support' CONFIG_STALDRV
diff -ruN linux-2.0.20.orig/drivers/char/console.c linux-2.0.20/drivers/char/console.c
--- linux-2.0.20.orig/drivers/char/console.c Fri Sep 13 23:54:29 1996
+++ linux-2.0.20/drivers/char/console.c Sat Sep 21 14:38:17 1996
@@ -118,6 +118,10 @@
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif

+#ifdef CONFIG_SERIAL_CONSOLE
+extern int serial_console;
+#endif
+
struct tty_driver console_driver;
static int console_refcount;
static struct tty_struct *console_table[MAX_NR_CONSOLES];
@@ -2104,8 +2108,13 @@
* initialization and register_console() event from
* within the bus probing code... :-(
*/
+#ifdef CONFIG_SERIAL_CONSOLE
+ if (!serial_console && video_type != VIDEO_TYPE_TGAC)
+ register_console(console_print);
+#else
if (video_type != VIDEO_TYPE_TGAC)
register_console(console_print);
+#endif

init_bh(CONSOLE_BH, console_bh);
return kmem_start;
diff -ruN linux-2.0.20.orig/drivers/char/serial.c linux-2.0.20/drivers/char/serial.c
--- linux-2.0.20.orig/drivers/char/serial.c Sat Jul 6 19:50:35 1996
+++ linux-2.0.20/drivers/char/serial.c Sat Sep 21 14:38:17 1996
@@ -42,11 +42,17 @@
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
+#ifdef CONFIG_SERIAL_CONSOLE
+#include <linux/kmon.h>
+#endif

#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/bitops.h>
+#ifdef CONFIG_SERIAL_CONSOLE
+#include <asm/irq.h>
+#endif

static char *serial_name = "Serial driver";
static char *serial_version = "4.13";
@@ -56,6 +62,19 @@
struct tty_driver serial_driver, callout_driver;
static int serial_refcount;

+#ifdef CONFIG_SERIAL_CONSOLE
+extern void register_console(void (*proc)(const char *));
+
+static void serial_print(const char *);
+static void init_serial_console(struct async_struct *, unsigned int);
+static void register_serial_monio(void);
+
+static struct async_struct *serial_console_info = NULL;
+static unsigned int serial_console_cflag = 0;
+static int serial_console_int_disabled = 0;
+int serial_console = 0;
+#endif
+
/* serial subtype definitions */
#define SERIAL_TYPE_NORMAL 1
#define SERIAL_TYPE_CALLOUT 2
@@ -392,7 +411,8 @@
}

static _INLINE_ void receive_chars(struct async_struct *info,
- int *status)
+ int *status,
+ struct pt_regs * regs)
{
struct tty_struct *tty = info->tty;
unsigned char ch;
@@ -412,6 +432,10 @@
#ifdef SERIAL_DEBUG_INTR
printk("handling break....");
#endif
+#ifdef CONFIG_SERIAL_CONSOLE
+ if (info == serial_console_info)
+ kernel_monitor(regs, "break", NULL);
+#endif
*tty->flip.flag_buf_ptr++ = TTY_BREAK;
if (info->flags & ASYNC_SAK)
do_SAK(tty);
@@ -574,7 +598,7 @@
printk("status = %x...", status);
#endif
if (status & UART_LSR_DR)
- receive_chars(info, &status);
+ receive_chars(info, &status, regs);
check_modem_status(info);
if (status & UART_LSR_THRE)
transmit_chars(info, 0);
@@ -629,7 +653,7 @@
printk("status = %x...", status);
#endif
if (status & UART_LSR_DR)
- receive_chars(info, &status);
+ receive_chars(info, &status, regs);
check_modem_status(info);
if (status & UART_LSR_THRE)
transmit_chars(info, 0);
@@ -688,7 +712,7 @@
printk("status = %x...", status);
#endif
if (status & UART_LSR_DR)
- receive_chars(info, &status);
+ receive_chars(info, &status, regs);
check_modem_status(info);
if (status & UART_LSR_THRE)
transmit_chars(info, 0);
@@ -2852,6 +2876,13 @@
}
}
register_symtab(&serial_syms);
+#ifdef CONFIG_SERIAL_CONSOLE
+ if (serial_console) {
+ init_serial_console(serial_console_info, serial_console_cflag);
+ register_console(serial_print);
+ register_serial_monio();
+ }
+#endif
return 0;
}

@@ -2928,6 +2959,296 @@
printk(KERN_INFO "tty%02d unloaded\n", info->line);
restore_flags(flags);
}
+
+#ifdef CONFIG_SERIAL_CONSOLE
+
+/*
+ * This is here to set the speed etc. for a non-initialized
+ * line. We also ALWAYS disable FIFO processing for simplicity.
+ * It will get enabled as soon as some process does an open()
+ * on the serial device.
+ * We have no termios struct yet, so we just pass "cflag".
+ */
+static void init_serial_console(struct async_struct *info, unsigned int cflag)
+{
+ unsigned short port;
+ unsigned cval,fcr;
+ int i;
+ int quot = 0;
+ unsigned long flags;
+
+ /* Calculate speed divisor. */
+ if (!(port = info->port))
+ return;
+
+ info->normal_termios.c_cflag = cflag;
+
+ i = cflag & CBAUD;
+ if (i & CBAUDEX) {
+ i &= ~CBAUDEX;
+ if (i == 1 || i == 2) i += 15;
+ }
+ /* We do not support baudrates of 0. */
+ if (!baud_table[i]) i = 13; /* 9600 */
+
+ if (baud_table[i] == 134)
+ quot = (2*info->baud_base / 269);
+ else
+ quot = info->baud_base / baud_table[i];
+
+ /* byte size and parity */
+ cval = cflag & (CSIZE | CSTOPB);
+ cval >>= 4;
+ if (cflag & PARENB)
+ cval |= UART_LCR_PARITY;
+ if (!(cflag & PARODD))
+ cval |= UART_LCR_EPAR;
+
+ /* We do not support FIFO mode, so clear the buffers. */
+ if (info->type == PORT_16650 || info->type == PORT_16550A)
+ fcr = UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT;
+ else
+ fcr = 0;
+
+ /* Right, program the UART. */
+ save_flags(flags); cli();
+
+ /* Clear ports to be sure. */
+ (void) serial_inp(info, UART_LSR);
+ (void) serial_inp(info, UART_RX);
+ (void) serial_inp(info, UART_IIR);
+ (void) serial_inp(info, UART_MSR);
+
+ /* Disable UART interrupts. */
+ serial_out(info, UART_IER, 0);
+
+ /* Set DTR and RTS high. */
+ serial_out(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
+
+ /* And set speed. */
+ serial_outp(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
+ serial_outp(info, UART_DLL, quot & 0xff); /* LS of divisor */
+ serial_outp(info, UART_DLM, quot >> 8); /* MS of divisor */
+ serial_outp(info, UART_LCR, cval); /* reset DLAB */
+ serial_outp(info, UART_FCR, fcr); /* set fcr */
+
+ (void)serial_in(info, UART_RX); /* read data port to reset things */
+
+ restore_flags(flags);
+}
+
+/* Serial console setup. Called from linux/init/main.c */
+void serial_setup(char *str, int *ints)
+{
+ char *s;
+ int baud, bits, parity;
+ int cflag = CREAD | HUPCL | CLOCAL;
+
+ /* Sanity check. */
+ if (ints[0] > 3 || ints[1] > 3) return;
+
+ /* Get baud, bits and parity */
+ baud = 2400;
+ bits = 8;
+ parity = 'n';
+ if (ints[2]) baud = ints[2];
+ if ((s = strchr(str, ','))) {
+ do {
+ s++;
+ } while(*s >= '0' && *s <= '9');
+ if (*s) parity = *s++;
+ if (*s) bits = *s - '0';
+ }
+
+ /* Now construct a cflag setting. */
+ switch(baud) {
+ case 1200:
+ cflag |= B1200;
+ break;
+ case 4800:
+ cflag |= B4800;
+ break;
+ case 9600:
+ cflag |= B9600;
+ break;
+ case 19200:
+ cflag |= B19200;
+ break;
+ case 38400:
+ cflag |= B38400;
+ break;
+ case 2400:
+ default:
+ cflag |= B2400;
+ break;
+ }
+ switch(bits) {
+ case 7:
+ cflag |= CS7;
+ break;
+ default:
+ case 8:
+ cflag |= CS8;
+ break;
+ }
+ switch(parity) {
+ case 'o': case 'O':
+ cflag |= PARODD;
+ break;
+ case 'e': case 'E':
+ cflag |= PARENB;
+ break;
+ }
+
+ serial_console_info = &rs_table[ints[1]];
+ serial_console_cflag = cflag;
+ serial_console = ints[1] + 64; /*callout_driver.minor_start*/
+}
+
+/* Stop interrupts. */
+static void serial_console_int_disable(void)
+{
+ if (serial_console_int_disabled) return;
+
+ /* First turn off interrupt on the controller. */
+ if (serial_console_info->tty && serial_console_info->irq)
+ disable_irq(serial_console_info->irq);
+
+ /* Now turn off interrupts on the UART. */
+ serial_out(serial_console_info, UART_IER, 0);
+
+ (void)serial_in(serial_console_info, UART_RX); /* read data port to reset */
+ serial_console_int_disabled = 1;
+}
+
+/* Retart interrupts. */
+static void serial_console_int_enable(void)
+{
+ if (!serial_console_int_disabled) return;
+
+ /* First turn on interrupts on the UART. */
+ serial_out(serial_console_info, UART_IER, serial_console_info->IER);
+
+ /* Now turn on interrupt on the controller. */
+ if (serial_console_info->tty && serial_console_info->irq)
+ enable_irq(serial_console_info->irq);
+
+ serial_console_int_disabled = 0;
+}
+
+/* Wait for transmitter register to empty. */
+static int serial_wait_thre(void)
+{
+ unsigned int tmout = 1000000;
+
+ while(!(serial_inp(serial_console_info, UART_LSR) & UART_LSR_THRE))
+ if (--tmout == 0) break;
+ return(tmout ? 0 : -1);
+}
+
+/* Send one character. */
+static int serial_send_one(unsigned char c)
+{
+ /* Wait for transmit register to empty. */
+ if (serial_wait_thre() < 0) return(-1);
+
+ serial_outp(serial_console_info, UART_TX, c);
+
+ return(0);
+}
+
+/* Send string. */
+static void serial_send_str(const char *b)
+{
+ unsigned char *ptr = (unsigned char *)b;
+
+ for(; *ptr; ptr++) {
+ /* Send CR if needed. */
+ if (*ptr == '\n') serial_send_one('\r');
+
+ /* And output one character. */
+ if (serial_send_one(*ptr) < 0) break;
+ }
+ (void) serial_wait_thre();
+}
+
+/*
+ * Serial console output.
+ * This one is used by printk().
+ */
+void serial_print(const char *b)
+{
+ unsigned char *ptr;
+
+ ptr = (unsigned char *)b;
+
+ /* Sanity check. */
+ if (serial_console_info->port == 0) return;
+
+ /* Turn off interrupts etc. */
+ serial_console_int_disable();
+
+ /* Print the data. */
+ serial_send_str(ptr);
+
+ /* And re-init serial port. */
+ serial_console_int_enable();
+}
+
+/* Read one line from the input. */
+static void serial_gets(char *s, int len)
+{
+ int ch, cnt = 0;
+
+ while(1) {
+ /* Get character. */
+ while(!(serial_inp(serial_console_info, UART_LSR) & UART_LSR_DR))
+ ;
+ ch = serial_inp(serial_console_info, UART_RX);
+
+ /* Check for backspace. */
+ if (ch == 8 || ch == 127) {
+ if (cnt == 0) {
+ serial_send_one('\007');
+ continue;
+ }
+ cnt--;
+ serial_send_str("\010 \010");
+ continue;
+ }
+
+ /* Check for enter. */
+ if (ch == 10 || ch == 13) break;
+
+ /* See if line is too long. */
+ if (cnt >= len + 1) {
+ serial_send_one(7);
+ cnt--;
+ continue;
+ }
+
+ /* Store and echo character. */
+ s[cnt++] = ch;
+ serial_send_one(ch);
+ }
+ /* Print enter. */
+ serial_send_str("\r\n");
+ s[cnt] = 0;
+}
+
+static void register_serial_monio(void)
+{
+ struct kmon_struct km;
+
+ km.cons_gets = serial_gets;
+ km.cons_puts = serial_send_str;
+ km.cons_enable = serial_console_int_disable;
+ km.cons_disable = serial_console_int_enable;
+
+ register_monitor_io(&km);
+}
+
+#endif /* CONFIG_SERIAL_CONSOLE */

#ifdef MODULE
int init_module(void)
diff -ruN linux-2.0.20.orig/drivers/char/tga.c linux-2.0.20/drivers/char/tga.c
--- linux-2.0.20.orig/drivers/char/tga.c Tue Jun 4 21:52:30 1996
+++ linux-2.0.20/drivers/char/tga.c Sat Sep 21 14:38:17 1996
@@ -25,6 +25,7 @@
#include <linux/ioport.h>
#include <linux/bios32.h>
#include <linux/pci.h>
+#include <linux/config.h>

#include <asm/io.h>
#include <asm/system.h>
@@ -39,6 +40,9 @@

extern void register_console(void (*proc)(const char *));
extern void console_print(const char *);
+#ifdef CONFIG_SERIAL_CONSOLE
+extern int serial_console;
+#endif

/* TGA hardware description (minimal) */
/*
@@ -480,7 +484,12 @@
/*
* FINALLY, we can register TGA as console (whew!)
*/
+#ifdef CONFIG_SERIAL_CONSOLE
+ if (!serial_console)
+ register_console(console_print);
+#else
register_console(console_print);
+#endif
}

unsigned char PLLbits[7] = { 0x80, 0x04, 0x00, 0x24, 0x44, 0x80, 0xb8 };
diff -ruN linux-2.0.20.orig/include/linux/kmon.h linux-2.0.20/include/linux/kmon.h
--- linux-2.0.20.orig/include/linux/kmon.h Thu Jan 1 01:00:00 1970
+++ linux-2.0.20/include/linux/kmon.h Sat Sep 21 14:38:17 1996
@@ -0,0 +1,23 @@
+#ifndef _LINUX_KMON_H
+#define _LINUX_KMON_H
+
+/*
+ * 'kmon.h' contains the dispatch structure for the
+ * kernel monitor.
+ */
+
+struct kmon_struct {
+ void (*cons_gets)(char *s, int len);
+ void (*cons_puts)(const char *);
+ void (*cons_enable)(void);
+ void (*cons_disable)(void);
+ int (*kmonitor)(struct pt_regs *);
+};
+
+extern struct kmon_struct kmonitor;
+
+extern void register_monitor_io(struct kmon_struct *);
+int (*register_monitor(int (*newmon)(struct pt_regs *)))(struct pt_regs *);
+extern int kernel_monitor(struct pt_regs *, char *prompt, char *pmsg);
+
+#endif
diff -ruN linux-2.0.20.orig/init/main.c linux-2.0.20/init/main.c
--- linux-2.0.20.orig/init/main.c Fri Sep 13 23:54:42 1996
+++ linux-2.0.20/init/main.c Sat Sep 21 14:45:58 1996
@@ -177,6 +177,13 @@
extern void ipc_init(void);
#endif

+#ifdef CONFIG_SERIAL_CONSOLE
+extern void serial_setup(char *str, int *ints);
+extern int serial_console;
+static char console_env[] = "CONSOLE=/dev/ttySx";
+static char *console_dev = console_env + 8;
+#endif
+
/*
* Boot command-line arguments
*/
@@ -413,6 +420,9 @@
#ifdef CONFIG_BAYCOM
{ "baycom=", baycom_setup },
#endif
+#ifdef CONFIG_SERIAL_CONSOLE
+ { "console=", serial_setup },
+#endif
{ 0, 0 }
};

@@ -639,6 +649,7 @@
}
if (checksetup(line))
continue;
+
/*
* Then check if it's an environment variable or
* an option.
@@ -653,6 +664,13 @@
argv_init[++args] = line;
}
}
+#ifdef CONFIG_SERIAL_CONSOLE
+ if (serial_console) {
+ console_env[17] = serial_console - 64 + '0';
+ if (envs < MAX_INIT_ENVS)
+ envp_init[++envs] = console_env;
+ }
+#endif
argv_init[args+1] = NULL;
envp_init[envs+1] = NULL;
}
@@ -852,6 +870,31 @@
cpu_idle(NULL);
}

+/*
+ * Open the console device.
+ */
+static int open_console(void)
+{
+ int i;
+
+#ifdef CONFIG_SERIAL_CONSOLE
+ if (serial_console && (i = open(console_dev,O_RDWR,0)) >= 0)
+ return i;
+#endif
+
+ /*
+ * This keeps serial console MUCH cleaner, but does assume
+ * the console driver checks there really is a video device
+ * attached (Sparc effectively does).
+ */
+
+ if ((i = open("/dev/tty1",O_RDWR,0)) < 0 &&
+ (i = open("/dev/ttyS0",O_RDWR,0)) < 0)
+ printk("Unable to open an initial console.\n");
+
+ return i;
+}
+
static int printf(const char *fmt, ...)
{
va_list args;
@@ -875,7 +918,7 @@
{
close(0);close(1);close(2);
setsid();
- (void) open("/dev/tty1",O_RDWR,0);
+ (void) open_console();
(void) dup(0);
(void) dup(0);
return execve(shell, argv, envp);
@@ -888,7 +931,7 @@

close(0);close(1);close(2);
setsid();
- (void) open("/dev/tty1",O_RDWR,0);
+ (void) open_console();
(void) dup(0);
(void) dup(0);
return execve(shell, argv, envp_init);
@@ -961,16 +1004,7 @@
}
#endif

- /*
- * This keeps serial console MUCH cleaner, but does assume
- * the console driver checks there really is a video device
- * attached (Sparc effectively does).
- */
-
- if ((open("/dev/tty1",O_RDWR,0) < 0) &&
- (open("/dev/ttyS0",O_RDWR,0) < 0))
- printk("Unable to open an initial console.\n");
-
+ (void) open_console();
(void) dup(0);
(void) dup(0);

diff -ruN linux-2.0.20.orig/kernel/Makefile linux-2.0.20/kernel/Makefile
--- linux-2.0.20.orig/kernel/Makefile Wed Jan 10 08:27:39 1996
+++ linux-2.0.20/kernel/Makefile Sat Sep 21 14:38:18 1996
@@ -19,6 +19,10 @@
OX_OBJS = ksyms.o
endif

+ifeq ($(CONFIG_SERIAL_CONSOLE),y)
+O_OBJS += kmon.o
+endif
+
include $(TOPDIR)/Rules.make

sched.o: sched.c
diff -ruN linux-2.0.20.orig/kernel/kmon.c linux-2.0.20/kernel/kmon.c
--- linux-2.0.20.orig/kernel/kmon.c Thu Jan 1 01:00:00 1970
+++ linux-2.0.20/kernel/kmon.c Sat Sep 21 14:38:18 1996
@@ -0,0 +1,142 @@
+/*
+ * linux/kernel/kmon.c
+ *
+ * Copyright (C) 1995 Miquel van Smoorenburg
+ *
+ * These are the basic routines for a simple kernel monitor,
+ * needed when we use a serial console.
+ *
+ * One can register an external monitor with
+ * register_monitor, it returns a pointer to the previous
+ * function. The monitor will be called with interrupts
+ * disabled, and it can use kmonitor.cons_gets and
+ * kmonitor.cons_puts to do I/O.
+ *
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/kmon.h>
+
+#define cputs kmonitor.cons_puts
+#define cgets kmonitor.cons_gets
+
+struct kmon_struct kmonitor = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+/*
+ * Register the drivers for monitor input and output.
+ */
+void register_monitor_io(struct kmon_struct *km)
+{
+ kmonitor.cons_gets = km->cons_gets;
+ kmonitor.cons_puts = km->cons_puts;
+ kmonitor.cons_enable = km->cons_enable;
+ kmonitor.cons_disable = km->cons_disable;
+}
+
+/*
+ * Register the kernel monitor itself.
+ */
+int (*register_monitor(int (*newmon)(struct pt_regs *)))(struct pt_regs *)
+{
+ int (*omon)(struct pt_regs *);
+
+ omon = kmonitor.kmonitor;
+ kmonitor.kmonitor = newmon;
+
+ return omon;
+}
+
+/*
+ * Help for the "monitor".
+ */
+static void kernel_monitor_help(void)
+{
+ cputs("Linux serial monitor v0.1\n");
+ cputs("b boot\n");
+ cputs("c continue\n");
+ cputs("m show memory\n");
+ cputs("p show panic message\n");
+ cputs("s show state\n");
+ cputs("r show registers\n");
+}
+
+/*
+ * This is the kernel monitor.
+ */
+int kernel_monitor(struct pt_regs * regs, char *prompt, char *pmsg)
+{
+ unsigned long flags;
+ char s[64];
+ extern void hard_reset_now(void);
+ extern void show_mem(void);
+ extern void show_state(void);
+ extern void show_regs(struct pt_regs *);
+
+ /* Sanity check. */
+ if (cgets == NULL || cputs == NULL)
+ return -1;
+
+ /* Enable special console puts/gets code. */
+ if (kmonitor.cons_enable)
+ (*kmonitor.cons_enable)();
+
+ /* Turn off interrupts. */
+ save_flags(flags); cli();
+
+ /* See if another monitor has been registered. */
+ if (kmonitor.kmonitor) {
+ (*kmonitor.kmonitor)(regs);
+ } else {
+
+ /* Say hello to the crowd. */
+ cputs("Linux serial monitor v0.1\n");
+
+ while(1) {
+ if (prompt) cputs(prompt);
+ cputs("> ");
+ cgets(s, 64);
+ if (*s == 0) continue;
+ if (strcmp(s, "c") == 0)
+ break;
+ else if (strcmp(s, "b") == 0)
+ hard_reset_now();
+ else if (strcmp(s, "m") == 0)
+ show_mem();
+ else if (strcmp(s, "s") == 0)
+ show_state();
+ else if (strcmp(s, "r") == 0) {
+ if (regs)
+ show_regs(regs);
+ else
+ cputs("No register data available\n");
+ } else if (strcmp(s, "p") == 0) {
+ if (pmsg)
+ cputs(pmsg);
+ else
+ cputs("No panic message available\n");
+ } else
+ kernel_monitor_help();
+ }
+ }
+
+ /* Turn on interrupts. */
+ restore_flags(flags);
+
+ /* Disable special console puts/gets code. */
+ if (kmonitor.cons_disable)
+ (*kmonitor.cons_disable)();
+
+ return 0;
+}
diff -ruN linux-2.0.20.orig/kernel/panic.c linux-2.0.20/kernel/panic.c
--- linux-2.0.20.orig/kernel/panic.c Sat Apr 13 16:40:51 1996
+++ linux-2.0.20/kernel/panic.c Sat Sep 21 14:38:18 1996
@@ -13,6 +13,10 @@
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/delay.h>
+#include <linux/config.h>
+#ifdef CONFIG_SERIAL_CONSOLE
+#include <linux/kmon.h>
+#endif

asmlinkage void sys_sync(void); /* it's really int */
extern void hard_reset_now(void);
@@ -55,7 +59,12 @@
udelay(1000);
hard_reset_now();
}
- for(;;);
+#ifdef CONFIG_SERIAL_CONSOLE
+ for(;;)
+ kernel_monitor(NULL, "panic", buf);
+#else
+ for(;;);
+#endif
}

/*
diff -ruN linux-2.0.20.orig/kernel/sys.c linux-2.0.20/kernel/sys.c
--- linux-2.0.20.orig/kernel/sys.c Sat Jul 20 12:32:05 1996
+++ linux-2.0.20/kernel/sys.c Sat Sep 21 14:38:18 1996
@@ -24,6 +24,9 @@
#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)
#include <linux/apm_bios.h>
#endif
+#ifdef CONFIG_SERIAL_CONSOLE
+#include <linux/kmon.h>
+#endif

#include <asm/segment.h>
#include <asm/io.h>
@@ -193,6 +196,10 @@
else if (!flag)
C_A_D = 0;
else if (flag == 0xCDEF0123) {
+#ifdef CONFIG_SERIAL_CONSOLE
+ if (kernel_monitor(NULL, "halt", NULL) == 0)
+ return(0);
+#endif
printk(KERN_EMERG "System halted\n");
sys_kill(-1, SIGKILL);
#if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)


N!
------------------------------------------------------------------------------
Nicholas J. Leon nicholas@binary9.net
"Elegance through Simplicity" http://www.binary9.net/nicholas

Under NO circumstances buy a Packard Bell.
They are substandard pieces of shit.