Re: [BK PATCH] console changes 1

From: Martin J. Bligh (mbligh@aracnet.com)
Date: Fri Oct 11 2002 - 21:24:47 EST


>> Are you going to have early console support (ie printk from before
>> what is now console_init) done before the freeze, or should I just
>> submit our version?
>
> On ppc64 Im currently setting a console up very early in arch init code
> and using the CONFIG_EARLY_PRINTK hook to disable it at console_init
> time. Works OK for me, do you guys need something on top of that?

Attatched is what I use when it all goes to hell in a handbasket.

Works for me ... I'd love to see it more generally available, so
we didn't get bug reports saying "it says 'booting linux', then
nothing". I'm not desperately attatched to any one implementation,
as long as it works from really, really early on. The fact that
everyone and his dog (including me once, badly) has reimplemented
this suggests it's very useful, and should go into mainline.

Patch by Bill Irwin and Keith Mannthey. Inlined so you can read it,
attached as well, cause my mail-reader on this PC seems to be eating
things at the moment for some reason. This version supports standard
PC VGA consoles, and serial consoles (essential for remote work),
but should be easy to hook in other interfaces.

Probably needs some cleanup. If people would take a cleaned up version,
I'll clean it up, or get someone to do it ;-)

M.

diff -urN linux-2.5.25/arch/i386/Config.help linux-2.5.25-early/arch/i386/Config.help
--- linux-2.5.25/arch/i386/Config.help 2002-07-05 16:42:04.000000000 -0700
+++ linux-2.5.25-early/arch/i386/Config.help 2002-07-16 13:11:21.000000000 -0700
@@ -941,6 +941,25 @@
   Say Y here if you want to reduce the chances of the tree compiling,
   and are prepared to dig into driver internals to fix compile errors.
 
+
+NO_CONFIG_EARLY_CONSOLE
+ If you are not doing early kernel devolopment NONE is what you want.
+ The other will add a early console for you (in i386)
+ This will allow you see printk output before console_init is called.
+ This is handy if you need to debug the kernel before this point.
+
+ VGA will give you a VGA console, your screen.
+
SERIAL will give you out put on your serial line.
+ Location of serial port
+ CONFIG_EARLY_CONSOLE_SERIAL_PORT
+ 0x3F8 == COMM 1
+ 0x3E8 == COMM 2
+
+CONFIG_EARLY_CONSOLE_SERIAL_BAUD
+ Set the the Baud of your serial console.
+
+
+
 Software Suspend
 CONFIG_SOFTWARE_SUSPEND
   Enable the possibilty of suspendig machine. It doesn't need APM.
diff -urN linux-2.5.25/arch/i386/config.in linux-2.5.25-early/arch/i386/config.in
--- linux-2.5.25/arch/i386/config.in 2002-07-25 10:23:53.000000000 -0700
+++ linux-2.5.25-early/arch/i386/config.in 2002-07-16 13:11:21.000000000 -0700
@@ -424,6 +424,16 @@
    bool ' Spinlock debugging' CONFIG_DEBUG_SPINLOCK
    if [ "$CONFIG_HIGHMEM" = "y" ]; then
       bool ' Highmem debugging' CONFIG_DEBUG_HIGHMEM
+
choice 'Early printk support' \
+ "NONE NO_CONFIG_EARLY_CONSOLE \
+ VGA CONFIG_EARLY_CONSOLE_VGA \
+ Serial_PORT CONFIG_EARLY_CONSOLE_SERIAL\
+ Bochs_0xE9_hack CONFIG_EARLY_CONSOLE_BOCHS_E9_HACK " NONE
+ if [ "$CONFIG_EARLY_CONSOLE_SERIAL" = "y" ]; then
+ hex "Location of serial port " CONFIG_EARLY_CONSOLE_SERIAL_PORT 0x3F8
+ int "Baud rate " CONFIG_EARLY_CONSOLE_SERIAL_BAUD 38400
+ fi
+
    fi
 fi
 
diff -urN linux-2.5.25/arch/i386/kernel/Makefile linux-2.5.25-early/arch/i386/kernel/Makefile
--- linux-2.5.25/arch/i386/kernel/Makefile 2002-07-05 16:42:14.000000000 -0700
+++ linux-2.5.25-early/arch/i386/kernel/Makefile 2002-07-16
13:11:21.000000000 -0700
@@ -11,7 +11,7 @@
 obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \
                 ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \
                 pci-dma.o i386_ksyms.o i387.o bluesmoke.o dmi_scan.o \
- bootflag.o
+ bootflag.o early_consoles.o
 
 obj-y += cpu/
 obj-$(CONFIG_MCA) += mca.o
diff -urN linux-2.5.25/arch/i386/kernel/early_consoles.c linux-2.5.25-early/arch/i386/kernel/early_consoles.c
--- linux-2.5.25/arch/i386/kernel/early_consoles.c 1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.25-early/arch/i386/kernel/early_consoles.c 2002-07-16 13:19:34.000000000 -0700
@@ -0,0 +1,418 @@
+/*
+ * 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.
+ */
+
+#ifdef CONFIG_EARLY_CONSOLE_SERIAL
+#define MAX_BAUD 115200
+#define BAUD_MSB (MAX_BAUD/CONFIG_EARLY_CONSOLE_SERIAL_BAUD)/0xFF
+#define BAUD_LSB (MAX_BAUD/CONFIG_EARLY_CONSOLE_SERIAL_BAUD)%0xFF
+
+
+static inline 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 int serial_init = 0;
+
+static void init_serial_io_port(unsigned port)
+{
+ serial_init = 1;
+
+ wait_for_readiness(port);
+
+ /*
+ * Disable interrupts.
+ */
+ outb(0x0, MK_PORT(port + 1));
+
+ /*
+ * Set the baud rate divisor's LSB.
+ */
+ outb(BAUD_MSB, MK_PORT(port + 3));
+
+ /*
+ * Set the baud rate divisor's MSB.
+ */
+ outb(BAUD_LSB, MK_PORT(port));
+
+ /*
+ * Set no parity, 8 bits, 1 stop bit, and select
+ * interrupt enable register.
+ */
+ outb(0x3, MK_PORT(port + 3));
+}
+

+
+static void write_serial_io_port(unsigned port,
+ const char *s,
+ unsigned n)
+{
+ unsigned k;
+
+ if (serial_init==0)
+ {
+ init_serial_io_port(port);
+ }
+
+ /*
+ * 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));
+ }
+ }
+}
+
+
+
+/*
+ * On Intel-derived architectures it is customary for onboard serial
+ * ports to have I/O ports at these two port addresses.
+ */
+
+static void write_serial(struct console *c, const char *s, unsigned n)
+{
+ write_serial_io_port(CONFIG_EARLY_CONSOLE_SERIAL_PORT, s, n);
+}
+
+static struct console early_console_serial =
+{
+
        write: write_serial
+};
+#endif
+
+
+#ifdef CONFIG_EARLY_CONSOLE_VGA
+
+/*
+ * 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.
+ */
+
+#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 * vga_mem =
+ (struct vga_char_desc *)(VGA_TEXT_BUFFER + PAGE_OFFSET);
+
+/*
+ ** 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 row;
+static unsigned short col;
+
+
+/*from martin bligh's early_printk */
+static inline void update_cursor(void)
+{
+ int pos = (col + VGA_MAXCOL*row) *2;
+
+ outb_p(14, VGA_REG_PORT);
+ outb_p(0xff & (pos >> 9), VGA_VAL_PORT);
+ outb_p(15, VGA_REG_PORT);
+ outb_p(0xff & (pos >> 1),
VGA_VAL_PORT);
+}
+
+
+void clear_vga_mem(void)
+{
+ int x, y;
+
+ for (x = 0; x < 80; x++) {
+ for (y = 0; y < 25; y++) {
+ VGA_CHAR(y,x) = ' ';
+ }
+ }
+ row =0;
+ col =0;
+ update_cursor();
+}
+
+
+
+
+/*
+ * 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 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 = ' ';
+}
+
+
+/*
+ * 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 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 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 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 write_vga(struct console *c, const char *s, unsigned n)
+{
+ unsigned k;
+
+ for(k = 0; k < n; ++k)
+ {
+ vga_putc(s[k]);
+ update_cursor();
+ }
+}
+
+static struct console early_console_vga =
+{
+ write: write_vga
+};
+
+#endif /*END 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 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 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 * early_console_table[] =
+ {
+#ifdef CONFIG_EARLY_CONSOLE_SERIAL
+ &early_console_serial,
+#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 add_early_consoles(void)
+{
+
+ struct console **c = early_console_table;
+ while(*c)
+ {
+ register_early_console(*c++);
+ printk ("consoled added!\n\n");
+ }
+#ifdef CONFIG_EARLY_CONSOLE_VGA
+ clear_vga_mem();
+#endif
+
+}
+
diff -urN linux-2.5.25/arch/i386/kernel/setup.c linux-2.5.25-early/arch/i386/kernel/setup.c
--- linux-2.5.25/arch/i386/kernel/setup.c 2002-07-05 16:42:23.000000000 -0700
+++ linux-2.5.25-early/arch/i386/kernel/setup.c 2002-07-16 13:11:21.000000000 -0700
@@ -598,7 +598,9 @@
         int i;
 
         early_cpu_init();
-
+
+ add_early_consoles();
+
 #ifdef CONFIG_VISWS
         visws_get_board_type_and_rev();
 #endif
diff -urN linux-2.5.25/include/linux/console.h
linux-2.5.25-early/include/linux/console.h
--- linux-2.5.25/include/linux/console.h 2002-07-05 16:42:22.000000000 -0700
+++ linux-2.5.25-early/include/linux/console.h 2002-07-16 13:11:21.000000000 -0700
@@ -90,7 +90,7 @@
 #define CON_PRINTBUFFER (1)
 #define CON_CONSDEV (2) /* Last on the command line */
 #define CON_ENABLED (4)
-
+#define EARLY_CONSOLE (8) /* Early flag. Remove in con_init */
 struct console
 {
         char name[8];
@@ -105,6 +105,7 @@
         struct console *next;
 };
 
+
 extern void register_console(struct console *);
 extern int unregister_console(struct console *);
 extern struct console *console_drivers;
@@ -112,6 +113,9 @@
 extern void release_console_sem(void);
 extern void console_conditional_schedule(void);
 extern void
console_unblank(void);
+extern int clear_early_consoles(void);
+extern void register_early_console(struct console *);
+
 
 /* VESA Blanking Levels */
 #define VESA_NO_BLANKING 0
diff -urN linux-2.5.25/kernel/printk.c linux-2.5.25-early/kernel/printk.c
--- linux-2.5.25/kernel/printk.c 2002-07-05 16:42:23.000000000 -0700
+++ linux-2.5.25-early/kernel/printk.c 2002-07-16 13:11:21.000000000 -0700
@@ -579,6 +579,10 @@
         int i;
         unsigned long flags;
 
+#ifndef NO_CONFIG_EARLY_CONSOLE
+ clear_early_consoles();
+#endif
+
         /*
          * See if we want to use this console driver. If we
          * didn't select a console we take the first one
@@ -676,6 +680,50 @@
 }
 EXPORT_SYMBOL(unregister_console);
         
+/* This will clear any and all console_drivers that

+ use only in con_init to remove any consoles that setup_arch
+ may have added for early console support
+ */
+int clear_early_consoles(void)
+{
+ struct console *a, *b;
+ int removed = 0;
+
+ if(console_drivers)
+ {
+ acquire_console_sem();
+
+
+ for (a=console_drivers->next, b=console_drivers; a; b=a,a=b->next)
+ {
+ if (a->flags & EARLY_CONSOLE)
+ {
+ b->next = a->next;
+ removed++;
+ }
+ }
+
+ if (console_drivers->flags & EARLY_CONSOLE)
+ {
+ console_drivers=console_drivers->next;
+ removed++;
+
+ }
+
+ if (console_drivers == NULL)
+ preferred_console = -1;
+
+ release_console_sem();
+ }
+ return removed;
+}
+
+void register_early_console(struct console *early_con)
+{
+
early_con->flags |= EARLY_CONSOLE;
+ register_console(early_con);
+}
+
 /**
  * tty_write_message - write a message to a certain tty, not just the console.
  *



-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Tue Oct 15 2002 - 22:00:42 EST