From 5d11489d749d7263e38507f843e3c246fbaafe09 Mon Sep 17 00:00:00 2001 From: Carl-Daniel Hailfinger Date: Wed, 13 Feb 2008 15:48:37 +0000 Subject: [PATCH] Make printk() log to a buffer. Tested on Qemu and Geode LX. Benefits of this patch: - printk() now works directly after printk_buffer_init(), even before the serial port is set up. - If all you want is a log, you don't have to bother with serial output. - A payload can read and analyze the log. - You can build on this and buffer log until serial is available, then flush the messages buffered so far. The printk buffer is configurable with a default-on Kconfig variable. If you want to dump the buffer from the Qemu monitor after CAR has been disabled, use this command: memsave 0x90000 65536 memdump.bin Signed-off-by: Carl-Daniel Hailfinger Acked-by: Peter Stuge Acked-by: Stefan Reinauer Acked-by: Marc Jones git-svn-id: svn://coreboot.org/repository/coreboot-v3@590 f3766cd6-281f-0410-b1cd-43a5c92072e9 --- arch/x86/geodelx/stage0.S | 7 ++++ arch/x86/stage0_i586.S | 8 ++++- arch/x86/stage1.c | 18 +++++++++- include/arch/x86/cpu.h | 10 ++++++ include/console.h | 13 +++++++ lib/Kconfig | 7 ++++ lib/console.c | 75 ++++++++++++++++++++++++++++++++++++++- 7 files changed, 135 insertions(+), 3 deletions(-) diff --git a/arch/x86/geodelx/stage0.S b/arch/x86/geodelx/stage0.S index 0301b7ad17..773fa9c013 100644 --- a/arch/x86/geodelx/stage0.S +++ b/arch/x86/geodelx/stage0.S @@ -361,6 +361,13 @@ DCacheSetupGood: movw %ax, %ss lout: +#ifdef CONFIG_CONSOLE_BUFFER + /* Store pointer to start of printk buffer, should really use + * PRINTK_BUF_ADDR_CAR instead. + */ + movl $CONFIG_CARBASE, %eax + pushl %eax /* printk buffer */ +#endif /* Restore the BIST result. */ movl %ebp, %eax diff --git a/arch/x86/stage0_i586.S b/arch/x86/stage0_i586.S index 2346567b1d..cb56664665 100644 --- a/arch/x86/stage0_i586.S +++ b/arch/x86/stage0_i586.S @@ -433,7 +433,13 @@ clear_fixed_var_mtrr_out: movw %ax, %ss lout: - +#ifdef CONFIG_CONSOLE_BUFFER + /* Store pointer to start of printk buffer, should really use + * PRINTK_BUF_ADDR_CAR instead. + */ + movl $CONFIG_CARBASE, %eax + pushl %eax /* printk buffer */ +#endif /* Restore the BIST result */ movl %ebp, %eax /* We need to set ebp ? No need */ diff --git a/arch/x86/stage1.c b/arch/x86/stage1.c index 2354a3dda6..62b3affd21 100644 --- a/arch/x86/stage1.c +++ b/arch/x86/stage1.c @@ -26,6 +26,7 @@ #include #include #include +#include /* ah, well, what a mess! This is a hard code. FIX ME but how? * By getting rid of ELF ... @@ -67,6 +68,12 @@ void init_archive(struct mem_file *archive) } +void *bottom_of_stack(void) +{ + /* -4-4 because CONFIG_CARBASE + CONFIG_CARSIZE - 4 is initial %esp */ + return (void *)(CONFIG_CARBASE + CONFIG_CARSIZE - 4 - 4); +} + void dump_mem_range(int msg_level, unsigned char *buf, int size) { int i; @@ -129,6 +136,11 @@ void __attribute__((stdcall)) stage1_main(u32 bist) // We have cache as ram running and can start executing code in C. +#ifdef CONFIG_CONSOLE_BUFFER + /* Initialize the printk buffer. */ + printk_buffer_init(); +#endif + hardware_stage1(); // @@ -173,10 +185,14 @@ void __attribute__((stdcall)) stage1_main(u32 bist) printk(BIOS_DEBUG, "Done RAM init code\n"); - /* Turn off Cache-As-Ram */ disable_car(); +#ifdef CONFIG_CONSOLE_BUFFER + /* Move the printk buffer to PRINTK_BUF_ADDR_RAM */ + printk_buffer_move((void *)PRINTK_BUF_ADDR_RAM, PRINTK_BUF_SIZE_RAM); +#endif + entry = load_file_segments(&archive, "normal/stage2"); if (entry == (void *)-1) die("FATAL: Failed loading stage2."); diff --git a/include/arch/x86/cpu.h b/include/arch/x86/cpu.h index be50a190cc..25ccad464c 100644 --- a/include/arch/x86/cpu.h +++ b/include/arch/x86/cpu.h @@ -24,6 +24,7 @@ #include #include +#include #define X86_VENDOR_INTEL 0 #define X86_VENDOR_CYRIX 1 @@ -196,4 +197,13 @@ static inline __attribute__((always_inline)) void hlt(void) __asm__ __volatile__("hlt" : : : "memory"); } +SHARED(bottom_of_stack, void *, void); + +#ifdef CONFIG_CONSOLE_BUFFER +#define PRINTK_BUF_SIZE_CAR (CONFIG_CARSIZE / 2) +#define PRINTK_BUF_ADDR_CAR CONFIG_CARBASE +#define PRINTK_BUF_SIZE_RAM 65536 +#define PRINTK_BUF_ADDR_RAM 0x90000 +#endif + #endif /* ARCH_X86_CPU_H */ diff --git a/include/console.h b/include/console.h index c3644132e2..304fbbd7ad 100644 --- a/include/console.h +++ b/include/console.h @@ -37,6 +37,10 @@ void console_tx_flush(void); unsigned char console_rx_byte(void); int console_tst_byte(void); void die(const char *msg); +#ifdef CONFIG_CONSOLE_BUFFER +void printk_buffer_init(void); +void printk_buffer_move(void *newaddr, int newsize); +#endif struct console_driver { void (*init)(void); @@ -46,6 +50,15 @@ struct console_driver { int (*tst_byte)(void); }; +#ifdef CONFIG_CONSOLE_BUFFER +struct printk_buffer { + int len; + int readoffset; + int writeoffset; + char buffer[]; +}; +#endif + SHARED_WITH_ATTRIBUTES(printk, int, __attribute__((format (printf, 2, 3))), int msg_level, const char *fmt, ...); SHARED(banner, void, int msg_level, const char *msg); diff --git a/lib/Kconfig b/lib/Kconfig index c6b007f0f0..af65f9dc50 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -223,5 +223,12 @@ config CONSOLE_PREFIX When you enable this option, coreboot will prefix each line of console output with '(LB)'. +config CONSOLE_BUFFER + boolean "Console memory buffer support" + default y + depends CONSOLE + help + Save coreboot output in a memory buffer. + endmenu diff --git a/lib/console.c b/lib/console.c index 87c2e0defb..943185a5b6 100644 --- a/lib/console.c +++ b/lib/console.c @@ -3,6 +3,7 @@ #include #include #include +#include int vtxprintf(void (*)(unsigned char, void *arg), void *arg, const char *, va_list); @@ -12,8 +13,80 @@ static int console_loglevel(void) return CONFIG_DEFAULT_CONSOLE_LOGLEVEL; } +#ifdef CONFIG_CONSOLE_BUFFER +void printk_buffer_move(void *newaddr, int newsize) +{ + struct printk_buffer **p; + struct printk_buffer *oldbuf, *newbuf; + int copylen; + p = bottom_of_stack(); + oldbuf = *p; + newbuf = newaddr; + newbuf->len = newsize; + newbuf->readoffset = 0; + /* Check for wraparound */ + if (oldbuf->writeoffset < oldbuf->readoffset) { + /* Copy from readoffset to end of buffer. */ + copylen = oldbuf->len - oldbuf->readoffset; + } else { + /* Copy from readoffset to writeoffset (exclusive).*/ + copylen = oldbuf->writeoffset - oldbuf->readoffset; + } + if (copylen > newsize) + copylen = newsize; + /* If memcpy() ever uses printk we will see pretty explosions. */ + memcpy(&newbuf->buffer[0], &oldbuf->buffer[oldbuf->readoffset], + copylen); + newbuf->writeoffset = copylen; + /* Check for wraparound */ + if (oldbuf->writeoffset < oldbuf->readoffset) { + /* Copy from start of buffer to writeoffset (exclusive). */ + copylen = (copylen + oldbuf->writeoffset > newsize) + ? newsize - copylen : oldbuf->writeoffset; + memcpy(&newbuf->buffer[newbuf->writeoffset], + &oldbuf->buffer[0], copylen); + newbuf->writeoffset += copylen; + } + *p = newbuf; + return; +} + +struct printk_buffer *printk_buffer_addr(void) +{ + struct printk_buffer **p; + p = bottom_of_stack(); + return *p; +} + +void printk_buffer_init(void) +{ + struct printk_buffer *buf = printk_buffer_addr(); + buf->len = PRINTK_BUF_SIZE_CAR - sizeof(struct printk_buffer); + buf->readoffset = 0; + buf->writeoffset = 0; + return; +} + +void buffer_tx_byte(unsigned char byte, void *arg) +{ + struct printk_buffer *buf = printk_buffer_addr(); + buf->buffer[buf->writeoffset++] = byte; + buf->writeoffset %= buf->len; + /* Make sure writeoffset is always ahead of readoffset here. */ + if (buf->writeoffset == buf->readoffset) { + buf->readoffset++; + buf->readoffset %= buf->len; + } + return; +} +#endif + void console_tx_byte(unsigned char byte, void *arg) { +#ifdef CONFIG_CONSOLE_BUFFER + buffer_tx_byte(byte, arg); +#endif +#ifdef CONFIG_CONSOLE_SERIAL if (byte == '\n') { uart8250_tx_byte(TTYSx_BASE, '\r'); #if defined(CONFIG_CONSOLE_PREFIX) && (CONFIG_CONSOLE_PREFIX == 1) @@ -26,8 +99,8 @@ void console_tx_byte(unsigned char byte, void *arg) return; #endif } - uart8250_tx_byte(TTYSx_BASE, byte); +#endif } int printk(int msg_level, const char *fmt, ...)