[PATCH 31/33] x86_64 boot: Add serial output support to the decompressor

From: Eric W. Biederman
Date: Tue Aug 01 2006 - 07:13:02 EST


This patch does two very simple things.
It adds a serial output capability to the decompressor.
It adds a command line parser for the early_printk
option so we know which output method to use for the decompressor.

This makes debugging the decompressor a little easier, and
keeps us from assuming we always have a vga console on all
hardware.

Signed-off-by: Eric W. Biederman <ebiederm@xxxxxxxxxxxx>
---
arch/x86_64/boot/compressed/Makefile | 2
arch/x86_64/boot/compressed/misc.c | 262 ++++++++++++++++++++++++++++++++--
2 files changed, 248 insertions(+), 16 deletions(-)

diff --git a/arch/x86_64/boot/compressed/Makefile b/arch/x86_64/boot/compressed/Makefile
index f89d96f..8987c97 100644
--- a/arch/x86_64/boot/compressed/Makefile
+++ b/arch/x86_64/boot/compressed/Makefile
@@ -11,7 +11,7 @@ EXTRA_AFLAGS := -traditional -m32

# cannot use EXTRA_CFLAGS because base CFLAGS contains -mkernel which conflicts with
# -m32
-CFLAGS := -m32 -D__KERNEL__ -Iinclude -O2 -fno-strict-aliasing
+CFLAGS := -m32 -D__KERNEL__ -Iinclude -O2 -fno-strict-aliasing -fno-builtin
LDFLAGS := -m elf_i386

LDFLAGS_vmlinux := -Ttext $(IMAGE_OFFSET) -e startup_32 -m elf_i386
diff --git a/arch/x86_64/boot/compressed/misc.c b/arch/x86_64/boot/compressed/misc.c
index 259bb05..2cbd7cb 100644
--- a/arch/x86_64/boot/compressed/misc.c
+++ b/arch/x86_64/boot/compressed/misc.c
@@ -10,8 +10,10 @@
*/

#include <linux/screen_info.h>
+#include <linux/serial_reg.h>
#include <asm/io.h>
#include <asm/page.h>
+#include <asm/setup.h>

/*
* gzip declarations
@@ -76,12 +78,17 @@ static void gzip_release(void **);
* This is set up by the setup-routine at boot-time
*/
static unsigned char *real_mode; /* Pointer to real-mode data */
+static char saved_command_line[COMMAND_LINE_SIZE];

#define RM_EXT_MEM_K (*(unsigned short *)(real_mode + 0x2))
#ifndef STANDARD_MEMORY_BIOS_CALL
#define RM_ALT_MEM_K (*(unsigned long *)(real_mode + 0x1e0))
#endif
#define RM_SCREEN_INFO (*(struct screen_info *)(real_mode+0))
+#define RM_NEW_CL_POINTER ((char *)(unsigned long)(*(unsigned *)(real_mode+0x228)))
+#define RM_OLD_CL_MAGIC (*(unsigned short *)(real_mode + 0x20))
+#define RM_OLD_CL_OFFSET (*(unsigned short *)(real_mode + 0x22))
+#define OLD_CL_MAGIC 0xA33F

extern unsigned char input_data[];
extern int input_len;
@@ -95,8 +102,12 @@ static void free(void *where);

static void *memset(void *s, int c, unsigned n);
static void *memcpy(void *dest, const void *src, unsigned n);
+static int memcmp(const void *s1, const void *s2, unsigned n);
+static size_t strlen(const char *str);
+static char *strstr(const char *haystack, const char *needle);

static void putstr(const char *);
+static unsigned simple_strtou(const char *cp, char **endp, unsigned base);

extern int end;
static long free_mem_ptr = (long)&end;
@@ -110,10 +121,21 @@ static unsigned int low_buffer_end, low_
static int high_loaded =0;
static uch *high_buffer_start /* = (uch *)(((ulg)&end) + HEAP_SIZE)*/;

-static char *vidmem = (char *)0xb8000;
+static char *vidmem;
static int vidport;
static int lines, cols;

+/* The early serial console */
+
+#define DEFAULT_BAUD 9600
+#define DEFAULT_BASE 0x3f8 /* ttyS0 */
+static unsigned serial_base = DEFAULT_BASE;
+
+#define CONSOLE_NOOP 0
+#define CONSOLE_VID 1
+#define CONSOLE_SERIAL 2
+static int console = CONSOLE_NOOP;
+
#include "../../../../lib/inflate.c"

static void *malloc(int size)
@@ -148,7 +170,8 @@ static void gzip_release(void **ptr)
free_mem_ptr = (long) *ptr;
}

-static void scroll(void)
+/* The early video console */
+static void vid_scroll(void)
{
int i;

@@ -157,7 +180,7 @@ static void scroll(void)
vidmem[i] = ' ';
}

-static void putstr(const char *s)
+static void vid_putstr(const char *s)
{
int x,y,pos;
char c;
@@ -169,7 +192,7 @@ static void putstr(const char *s)
if ( c == '\n' ) {
x = 0;
if ( ++y >= lines ) {
- scroll();
+ vid_scroll();
y--;
}
} else {
@@ -177,7 +200,7 @@ static void putstr(const char *s)
if ( ++x >= cols ) {
x = 0;
if ( ++y >= lines ) {
- scroll();
+ vid_scroll();
y--;
}
}
@@ -194,6 +217,178 @@ static void putstr(const char *s)
outb_p(0xff & (pos >> 1), vidport+1);
}

+static void vid_console_init(void)
+{
+ if (RM_SCREEN_INFO.orig_video_mode == 7) {
+ vidmem = (char *) 0xb0000;
+ vidport = 0x3b4;
+ } else {
+ vidmem = (char *) 0xb8000;
+ vidport = 0x3d4;
+ }
+
+ lines = RM_SCREEN_INFO.orig_video_lines;
+ cols = RM_SCREEN_INFO.orig_video_cols;
+}
+
+/* The early serial console */
+static void serial_putc(int ch)
+{
+ if (ch == '\n') {
+ serial_putc('\r');
+ }
+ /* Wait until I can send a byte */
+ while ((inb(serial_base + UART_LSR) & UART_LSR_THRE) == 0)
+ ;
+
+ /* Send the byte */
+ outb(ch, serial_base + UART_TX);
+
+ /* Wait until the byte is transmitted */
+ while (!(inb(serial_base + UART_LSR) & UART_LSR_TEMT))
+ ;
+}
+
+static void serial_putstr(const char *str)
+{
+ int ch;
+ while((ch = *str++) != '\0') {
+ if (ch == '\n') {
+ serial_putc('\r');
+ }
+ serial_putc(ch);
+ }
+}
+
+static void serial_console_init(char *s)
+{
+ unsigned base = DEFAULT_BASE;
+ unsigned baud = DEFAULT_BAUD;
+ unsigned divisor;
+ char *e;
+
+ if (*s == ',')
+ ++s;
+ if (*s && (*s != ' ')) {
+ if (memcmp(s, "0x", 2) == 0) {
+ base = simple_strtou(s, &e, 16);
+ } else {
+ static const unsigned bases[] = { 0x3f8, 0x2f8 };
+ unsigned port;
+
+ if (memcmp(s, "ttyS", 4) == 0)
+ s += 4;
+ port = simple_strtou(s, &e, 10);
+ if ((port > 1) || (s == e))
+ port = 0;
+ base = bases[port];
+ }
+ s = e;
+ if (*s == ',')
+ ++s;
+ }
+ if (*s && (*s != ' ')) {
+ baud = simple_strtou(s, &e, 0);
+ if ((baud == 0) || (s == e))
+ baud = DEFAULT_BAUD;
+ }
+ divisor = 115200 / baud;
+ serial_base = base;
+
+ outb(0x00, serial_base + UART_IER); /* no interrupt */
+ outb(0x00, serial_base + UART_FCR); /* no fifo */
+ outb(0x03, serial_base + UART_MCR); /* DTR + RTS */
+
+ /* Set Baud Rate divisor */
+ outb(0x83, serial_base + UART_LCR);
+ outb(divisor & 0xff, serial_base + UART_DLL);
+ outb(divisor >> 8, serial_base + UART_DLM);
+ outb(0x03, serial_base + UART_LCR); /* 8n1 */
+
+}
+
+static void putstr(const char *str)
+{
+ if (console == CONSOLE_VID) {
+ vid_putstr(str);
+ } else if (console == CONSOLE_SERIAL) {
+ serial_putstr(str);
+ }
+}
+
+static void console_init(char *cmdline)
+{
+ cmdline = strstr(cmdline, "earlyprintk=");
+ if (!cmdline)
+ return;
+ cmdline += 12;
+ if (memcmp(cmdline, "vga", 3) == 0) {
+ vid_console_init();
+ console = CONSOLE_VID;
+ } else if (memcmp(cmdline, "serial", 6) == 0) {
+ serial_console_init(cmdline + 6);
+ console = CONSOLE_SERIAL;
+ } else if (memcmp(cmdline, "ttyS", 4) == 0) {
+ serial_console_init(cmdline);
+ console = CONSOLE_SERIAL;
+ }
+}
+
+static inline int tolower(int ch)
+{
+ return ch | 0x20;
+}
+
+static inline int isdigit(int ch)
+{
+ return (ch >= '0') && (ch <= '9');
+}
+
+static inline int isxdigit(int ch)
+{
+ ch = tolower(ch);
+ return isdigit(ch) || ((ch >= 'a') && (ch <= 'f'));
+}
+
+
+static inline int digval(int ch)
+{
+ return isdigit(ch)? (ch - '0') : tolower(ch) - 'a' + 10;
+}
+
+/**
+ * simple_strtou - convert a string to an unsigned
+ * @cp: The start of the string
+ * @endp: A pointer to the end of the parsed string will be placed here
+ * @base: The number base to use
+ */
+static unsigned simple_strtou(const char *cp, char **endp, unsigned base)
+{
+ unsigned result = 0,value;
+
+ if (!base) {
+ base = 10;
+ if (*cp == '0') {
+ base = 8;
+ cp++;
+ if ((tolower(*cp) == 'x') && isxdigit(cp[1])) {
+ cp++;
+ base = 16;
+ }
+ }
+ } else if (base == 16) {
+ if (cp[0] == '0' && tolower(cp[1]) == 'x')
+ cp += 2;
+ }
+ while (isxdigit(*cp) && ((value = digval(*cp)) < base)) {
+ result = result*base + value;
+ cp++;
+ }
+ if (endp)
+ *endp = (char *)cp;
+ return result;
+}
+
static void* memset(void* s, int c, unsigned n)
{
int i;
@@ -212,6 +407,37 @@ static void* memcpy(void* dest, const vo
return dest;
}

+static int memcmp(const void *s1, const void *s2, unsigned n)
+{
+ const unsigned char *str1 = s1, *str2 = s2;
+ size_t i;
+ int result = 0;
+ for(i = 0; (result == 0) && (i < n); i++) {
+ result = *str1++ - *str2++;
+ }
+ return result;
+}
+
+static size_t strlen(const char *str)
+{
+ size_t len = 0;
+ while (*str++)
+ len++;
+ return len;
+}
+
+static char *strstr(const char *haystack, const char *needle)
+{
+ size_t len;
+ len = strlen(needle);
+ while(*haystack) {
+ if (memcmp(haystack, needle, len) == 0)
+ return (char *)haystack;
+ haystack++;
+ }
+ return NULL;
+}
+
/* ===========================================================================
* Fill the input buffer. This is called only when the buffer is empty
* and at least one byte is really needed.
@@ -331,20 +557,26 @@ static void close_output_buffer_if_we_ru
}
}

+static void save_command_line(void)
+{
+ /* Find the command line */
+ char *cmdline;
+ cmdline = saved_command_line;
+ if (RM_NEW_CL_POINTER) {
+ cmdline = RM_NEW_CL_POINTER;
+ } else if (OLD_CL_MAGIC == RM_OLD_CL_MAGIC) {
+ cmdline = real_mode + RM_OLD_CL_OFFSET;
+ }
+ memcpy(saved_command_line, cmdline, COMMAND_LINE_SIZE);
+ saved_command_line[COMMAND_LINE_SIZE - 1] = '\0';
+}
+
int decompress_kernel(struct moveparams *mv, void *rmode)
{
real_mode = rmode;

- if (RM_SCREEN_INFO.orig_video_mode == 7) {
- vidmem = (char *) 0xb0000;
- vidport = 0x3b4;
- } else {
- vidmem = (char *) 0xb8000;
- vidport = 0x3d4;
- }
-
- lines = RM_SCREEN_INFO.orig_video_lines;
- cols = RM_SCREEN_INFO.orig_video_cols;
+ save_command_line();
+ console_init(saved_command_line);

if (free_mem_ptr < 0x100000) setup_normal_output_buffer();
else setup_output_buffer_if_we_run_high(mv);
--
1.4.2.rc2.g5209e

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