diff -urN linux-2.5.1-pre2-virgin/arch/i386/config.in linux-2.5.1-pre2/arch/i386/config.in
--- linux-2.5.1-pre2-virgin/arch/i386/config.in	Mon Nov 12 11:58:08 2001
+++ linux-2.5.1-pre2/arch/i386/config.in	Tue Nov 27 15:05:51 2001
@@ -407,6 +407,10 @@
    bool '  Magic SysRq key' CONFIG_MAGIC_SYSRQ
    bool '  Spinlock debugging' CONFIG_DEBUG_SPINLOCK
    bool '  Verbose BUG() reporting (adds 70K)' CONFIG_DEBUG_BUGVERBOSE
+   bool '  Early printk() on serial I/O port 0x3F8' CONFIG_EARLY_CONSOLE_3F8
+   bool '  Early printk() on serial I/O port 0x3E8' CONFIG_EARLY_CONSOLE_3E8
+   bool '  Early printk() on VGA text console' CONFIG_EARLY_CONSOLE_VGA
+   bool '  Early printk() via the bochs 0xE9 hack' CONFIG_EARLY_CONSOLE_BOCHS_E9_HACK
 fi
 
 endmenu
diff -urN linux-2.5.1-pre2-virgin/drivers/char/Makefile linux-2.5.1-pre2/drivers/char/Makefile
--- linux-2.5.1-pre2-virgin/drivers/char/Makefile	Sun Nov 11 10:09:32 2001
+++ linux-2.5.1-pre2/drivers/char/Makefile	Tue Nov 27 15:05:51 2001
@@ -16,7 +16,8 @@
 
 O_TARGET := char.o
 
-obj-y	 += mem.o tty_io.o n_tty.o tty_ioctl.o raw.o pty.o misc.o random.o
+obj-y	 += mem.o tty_io.o n_tty.o tty_ioctl.o raw.o pty.o misc.o random.o \
+		early_consoles.o
 
 # All of the (potential) objects that export symbols.
 # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
diff -urN linux-2.5.1-pre2-virgin/drivers/char/early_consoles.c linux-2.5.1-pre2/drivers/char/early_consoles.c
--- linux-2.5.1-pre2-virgin/drivers/char/early_consoles.c	Wed Dec 31 16:00:00 1969
+++ linux-2.5.1-pre2/drivers/char/early_consoles.c	Tue Nov 27 15:05:51 2001
@@ -0,0 +1,384 @@
+/*
+ * Early console drivers.
+ * (C) Nov 2001, William Irwin, IBM
+ *
+ * These are low-level pseudodrivers to enable early console output
+ * to aid in debugging during early boot.
+ *
+ * They are crude, but hopefully effective. They rely on the fact
+ * that consoles are largely unused prior to the true console_init(),
+ * and that printk() uses the ->write callback and that callback
+ * only during its operation.
+ *
+ * Serial port routines are derived from Linux serial.c, and
+ * vga_putc() is derived from vsta, (C) Andrew Valencia.
+ */
+
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/serial_reg.h>
+#include <asm/io.h>
+
+/*
+ * I/O ports are not linearly mapped on all architectures.
+ * On IA64 in particular, port I/O is just reading/writing from
+ * an uncached address, but ioremap there requires ia64_io_base
+ * to be initialized, which does not happen until the middle of
+ * setup_arch(). So a port remapping macro is provided here.
+ *
+ * The IA64 case is not handled here, although the port remapping
+ * is demonstrated for the purposes of understanding its necessity.
+ * The IO_BASE is taken from Lion systems; in general, this varies.
+ * True handling for IA64 will be merged in given testing.
+ */
+
+#ifdef CONFIG_IA64
+
+#define IO_BASE   0xC0000FFFFC000000UL
+#define MK_PORT(port) ((char *)(IO_BASE|(((port)>>2)<<12)|((port) & 0xFFF)))
+
+#else
+
+/*
+ * This works for i386, but not everywhere.
+ * Other architectures with port I/O mapping needs will need to
+ * add to the preprocessor case analysis above.
+ */
+
+#define MK_PORT(port) (port)
+
+#endif
+
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+
+/*
+ * This serial output driver derived from the one appearing
+ * in serial.c
+ *
+ * It is a simple "bitbanging" style output routine, with
+ * initialization performed at every call.
+ */
+
+#if defined(CONFIG_EARLY_CONSOLE_3F8) || defined(CONFIG_EARLY_CONSOLE_3E8)
+
+static inline __init void wait_for_readiness(unsigned port)
+{
+	unsigned retries;
+	unsigned char status;
+
+	/*
+	 * Wait for transmitter holding and shift registers to empty,
+	 * which is required for output to succeed. If the retries are
+	 * exceeded, this deliberately fails to ensure termination.
+	 */
+	for(retries = 0; retries < 65536; ++retries) {
+		status = inb(MK_PORT(port + 5));
+		if((status & BOTH_EMPTY) == BOTH_EMPTY)
+			break;
+	}
+}
+
+static void __init write_serial_io_port(unsigned port,
+					const char *s,
+					unsigned n)
+{
+	unsigned k;
+
+	wait_for_readiness(port);
+
+	/*
+	 * Disable interrupts.
+	 */
+	outb(0x0, MK_PORT(port + 1));
+
+	/*
+	 * Set the baud rate divisor's LSB.
+	 */
+	outb(0x83, MK_PORT(port + 3));
+
+	/*
+	 * Set the baud rate divisor's MSB.
+	 */
+	outb(0xC, MK_PORT(port));
+
+	/*
+	 * Set no parity, 8 bits, 1 stop bit, and select
+	 * interrupt enable register.
+	 */
+	outb(0x3, MK_PORT(port + 3));
+
+	/*
+	 * Set data terminal ready and request to send.
+	 */
+
+	for(k = 0; k < n; ++k) {
+		wait_for_readiness(port);
+		outb(s[k], MK_PORT(port));
+		if(s[k] == '\n') {
+			wait_for_readiness(port);
+			outb('\r', MK_PORT(port));
+		}
+	}
+}
+
+#endif /* CONFIG_EARLY_CONSOLE_3F8  || CONFIG_EARLY_CONSOLE_3E8 */
+
+
+
+/*
+ * On Intel-derived architectures it is customary for onboard serial
+ * ports to have I/O ports at these two port addresses.
+ */
+
+#ifdef CONFIG_EARLY_CONSOLE_3F8
+static void __init write_3F8(struct console *c, const char *s, unsigned n)
+{
+	write_serial_io_port(0x3F8, s, n);
+}
+
+static struct console __initdata early_console_3F8 =
+{
+	write: write_3F8
+};
+#endif
+
+#ifdef CONFIG_EARLY_CONSOLE_3E8
+static void __init write_3E8(struct console *c, const char *s, unsigned n)
+{
+	write_serial_io_port(0x3E8, s, n);
+}
+
+static struct console __initdata early_console_3E8 =
+{
+	write: write_3E8
+};
+#endif
+
+
+
+
+/*
+ * This should work for a variety of Intel-derived architectures,
+ * as it is customary for VGA memory to reside in this address range.
+ * vga_putc() is derived from vsta sources, (C) Andrew Valencia.
+ *
+ * Several forms of functionality are intentionally omitted in the
+ * interest of robustness, in particular, cursor movement and cursor
+ * position determination.
+ */
+
+#ifdef CONFIG_EARLY_CONSOLE_VGA
+
+#define VGA_MAXCOL 80
+#define VGA_MAXROW 25
+#define VGA_SCRNSZ (VGA_MAXCOL * VGA_MAXROW)
+#define VGA_REG_PORT 0x3D4
+#define VGA_VAL_PORT 0x3D5
+#define VGA_TEXT_BUFFER 0xB8000
+
+#define VGA_CHAR(_row_, _col_) vga_mem[(_row_)*VGA_MAXCOL + (_col_)].c
+
+struct vga_char_desc
+{
+	unsigned char c;
+	unsigned char color;
+};
+
+static struct vga_char_desc * __initdata vga_mem = 
+	(struct vga_char_desc *)(VGA_TEXT_BUFFER + PAGE_OFFSET);
+
+/*
+ * The characters displayed at a screen position can be discerned by
+ * reading from the corresponding memory location. This can be used
+ * to simulate scrolling movement. Line blanking is simulated by
+ * overwriting the displayed characters with the space character.
+ *
+ * In the interest of robustness, cursor movement is also omitted.
+ */
+static inline void __init vga_scroll_up(void)
+{
+	unsigned k;
+
+	for(k = 0; k < (VGA_SCRNSZ - VGA_MAXCOL); ++k)
+		vga_mem[k].c = vga_mem[k + VGA_MAXCOL].c;
+
+	for(k = VGA_SCRNSZ - VGA_MAXCOL; k < VGA_SCRNSZ; ++k)
+		vga_mem[k].c = ' ';
+}
+
+/*
+ * The screen position can actually be determined by port I/O,
+ * but in the interest of robustness, these are always initialized
+ * to the (0, 0) position. These position indices must always be
+ * strictly less than the bounds VGA_MAXROW and VGA_MAXCOL.
+ */
+static unsigned short __initdata row;
+static unsigned short __initdata col;
+
+
+/*
+ * Line advancement must preserve the invariant that the row and
+ * column indices are in-bounds. The semantics of this mean that
+ * when line advancement "beyond" the last line results in scrolling.
+ */
+static inline void __init vga_line_advance(void)
+{
+	++row;
+
+	if(row >= VGA_MAXROW) {
+		row = VGA_MAXROW - 1;
+		vga_scroll_up();
+	}
+}
+
+
+/*
+ * Character advancement must once again preserve the in-bounds
+ * invariants, and in so doing line wrapping and advancement may occur.
+ */
+static inline void __init vga_char_advance(void)
+{
+	++col;
+
+	if(col >= VGA_MAXCOL) {
+		col = 0;
+		vga_line_advance();
+	}
+}
+
+
+/*
+ * Derived from vsta sources (C) Andrew Valencia.
+ * Here the interpretation of several common special characters occurs,
+ * namely linefeeds, newlines, tabs, and backspaces. The position
+ * indices are updated using the vga_char_advance() and vga_line_advance()
+ * routines, and a vga_char_advance() is triggered on the printing of
+ * each ordinary character. The special characters have specialized
+ * position update semantics in order to be faithful to their customary
+ * cursor movement effects, although the cursor position is not updated.
+ */
+static void __init vga_putc(char c)
+{
+	unsigned k;
+	switch(c) {
+		case '\t':
+			for(k = 0; k < 8; ++k) {
+				VGA_CHAR(row, col) = ' ';
+				vga_char_advance();
+			}
+			break;
+
+		case '\r':
+			col = 0;
+			break;
+
+		case '\n':
+			col = 0;
+			vga_line_advance();
+			break;
+
+		case '\b':
+			if(col > 0) {
+				--col;
+				VGA_CHAR(row, col) = ' ';
+			}
+			break;
+
+		default:
+			VGA_CHAR(row, col) = c;
+			vga_char_advance();
+			break;
+	}
+}
+
+
+/*
+ * write_vga(), given a NUL-terminated character array, writes
+ * characters to VGA space in bulk, and is the callback used for the
+ * driver structure.
+ */
+static void __init write_vga(struct console *c, const char *s, unsigned n)
+{
+	unsigned k;
+	for(k = 0; k < n; ++k)
+		vga_putc(s[k]);
+}
+
+static struct console __initdata early_console_vga =
+{
+	write: write_vga
+};
+
+#endif /* CONFIG_EARLY_CONSOLE_VGA */
+
+
+/*
+ * The bochs x86 simulator has an optional feature for enabling
+ * debugging output through a normally unused ISA I/O port. The
+ * protocol for communicating with the simulated device is simply
+ * using port I/O writes to write a stream of characters to the
+ * device, and these are then relayed by the simulator to the
+ * controlling terminal of the simulator process.
+ */
+#ifdef CONFIG_EARLY_CONSOLE_BOCHS_E9_HACK
+static void __init write_bochs(struct console *c, const char *s, unsigned n)
+{
+	unsigned k;
+
+	for(k = 0; k < n; ++k)
+		outb(s[k], MK_PORT(0xE9));
+}
+
+static struct console __initdata early_console_bochs =
+{
+	write: write_bochs
+};
+#endif /* CONFIG_EARLY_CONSOLE_BOCHS_E9_HACK */
+
+
+/*
+ * In order to minimize the number of #ifdefs whch must
+ * appear in-line, this direct-mapped, NULL-terminated table
+ * of console entries is used to provide a configuration-independent
+ * structure which may be traversed to discover all of the available
+ * early console devices for registration and unregistration.
+ *
+ * This is the ugliest part of the code, thanks to #ifdef
+ */
+static struct console * __initdata early_console_table[] =
+	{
+#ifdef CONFIG_EARLY_CONSOLE_3F8
+		&early_console_3F8,
+#endif
+#ifdef CONFIG_EARLY_CONSOLE_3E8
+		&early_console_3E8,
+#endif
+#ifdef CONFIG_EARLY_CONSOLE_VGA
+		&early_console_vga,
+#endif
+#ifdef CONFIG_EARLY_CONSOLE_BOCHS_E9_HACK
+		&early_console_bochs,
+#endif
+		NULL
+	};
+
+
+/*
+ * The above implementations are quite far from complete console
+ * devices, but printk() only requires the ->write callback, so this is
+ * somewhat deceptive, but still cleaner than editing printk.c itself.
+ */
+void __init register_early_consoles(void)
+{
+	struct console **c = early_console_table;
+	while(*c)
+		register_console(*c++);
+}
+
+void __init unregister_early_consoles(void)
+{
+	struct console **c = early_console_table;
+	while(*c)
+		unregister_console(*c++);
+}
diff -urN linux-2.5.1-pre2-virgin/include/linux/kernel.h linux-2.5.1-pre2/include/linux/kernel.h
--- linux-2.5.1-pre2-virgin/include/linux/kernel.h	Thu Nov 22 11:46:18 2001
+++ linux-2.5.1-pre2/include/linux/kernel.h	Tue Nov 27 15:51:17 2001
@@ -79,6 +79,9 @@
 
 extern int session_of_pgrp(int pgrp);
 
+extern void register_early_consoles(void);
+extern void unregister_early_consoles(void);
+
 asmlinkage int printk(const char * fmt, ...)
 	__attribute__ ((format (printf, 1, 2)));
 
diff -urN linux-2.5.1-pre2-virgin/init/main.c linux-2.5.1-pre2/init/main.c
--- linux-2.5.1-pre2-virgin/init/main.c	Fri Nov  9 14:15:00 2001
+++ linux-2.5.1-pre2/init/main.c	Tue Nov 27 15:05:51 2001
@@ -549,6 +549,7 @@
  * enable them
  */
 	lock_kernel();
+	register_early_consoles();
 	printk(linux_banner);
 	setup_arch(&command_line);
 	printk("Kernel command line: %s\n", saved_command_line);
@@ -564,6 +565,7 @@
 	 * we've done PCI setups etc, and console_init() must be aware of
 	 * this. But we do want output early, in case something goes wrong.
 	 */
+	unregister_early_consoles();
 	console_init();
 #ifdef CONFIG_MODULES
 	init_modules();
