console/serialice: Add SerialICE

Add Kconfig Option to build SerialICE shell into coreboot stages.
You can select between romstage and ramstage.
The SerialICE shell will be launched instead of coreboot console.
As minimal system initialization is already done in coreboot, it
should work on all boards.

Tested with EHCI Debug on Lenovo T520 and Lenovo X60.

Needs tests:
 * Ramstage

Change-Id: I1fa6eb4f40e0f625c8c8302d1580bcd2664d670b
Signed-off-by: Patrick Rudolph <siro@das-labor.org>
Signed-off-by: Antonello Dettori <dev@dettori.io>
This commit is contained in:
Patrick Rudolph 2016-04-25 11:00:17 +02:00 committed by Alexander Couzens
parent 7079ee113e
commit ae0f552f57
14 changed files with 743 additions and 17 deletions

View File

@ -1045,6 +1045,40 @@ config DEBUG_ADA_CODE
Add the compiler switch `-gnata` to compile code guarded by
`pragma Debug`.
config DEBUG_SERIALICE
bool "Enable SerialICE instead normal boot"
default n
depends on ARCH_X86
depends on CONSOLE_SERIAL || CONSOLE_USB
depends on !GDB_STUB
help
If enabled, SerialICE will be run instead of normal boot.
You need to connect a serial cable or EHCI debug dongle to
communicate with SerialICE shell.
if DEBUG_SERIALICE
choice
prompt "SerialICE bootstage"
default DEBUG_SERIALICE_ROMSTAGE
help
Choose the stage SerialICE to start. You can choose between
ROMSTAGE and RAMSTAGE.
config DEBUG_SERIALICE_ROMSTAGE
bool "Start SerialICE instead of Romstage"
depends on CONSOLE_SERIAL || USBDEBUG_IN_ROMSTAGE
help
Start SerialICE in romstage, right after minimal system initialization.
config DEBUG_SERIALICE_RAMSTAGE
bool "Start SerialICE instead of Ramstage"
help
Start SerialICE in ramstage.
endchoice
endif
endmenu

View File

@ -113,7 +113,7 @@ _start:
andl $0xFFFFFFF0, %esp
#if IS_ENABLED(CONFIG_GDB_WAIT)
call gdb_hw_init
call debug_hw_init
call gdb_stub_breakpoint
#endif
call main

View File

@ -14,6 +14,7 @@
#include <console/console.h>
#include <console/streams.h>
#include <string.h>
#include <debug-com.h>
#if IS_ENABLED(CONFIG_GDB_STUB)
@ -230,17 +231,17 @@ static char out_buffer[BUFMAX];
static inline void stub_putc(int ch)
{
gdb_tx_byte(ch);
debug_tx_byte(ch);
}
static inline void stub_flush(void)
{
gdb_tx_flush();
debug_tx_flush();
}
static inline int stub_getc(void)
{
return gdb_rx_byte();
return debug_rx_byte();
}
static int hex(char ch)

View File

@ -6,6 +6,7 @@ ifeq ($(CONFIG_HWBASE_DEBUG_CB),y)
ramstage-$(CONFIG_RAMSTAGE_LIBHWBASE) += hw-debug_sink.ads
ramstage-$(CONFIG_RAMSTAGE_LIBHWBASE) += hw-debug_sink.adb
endif
ramstage-$(CONFIG_DEBUG_SERIALICE_RAMSTAGE) += serialice.c
smm-$(CONFIG_DEBUG_SMI) += init.c console.c vtxprintf.c printk.c
smm-$(CONFIG_SMM_TSEG) += die.c
@ -21,6 +22,7 @@ romstage-y += vtxprintf.c printk.c vsprintf.c
romstage-y += init.c console.c
romstage-y += post.c
romstage-y += die.c
romstage-$(CONFIG_DEBUG_SERIALICE_ROMSTAGE) += serialice.c
postcar-$(CONFIG_POSTCAR_CONSOLE) += vtxprintf.c printk.c vsprintf.c
postcar-$(CONFIG_POSTCAR_CONSOLE) += init.c console.c

View File

@ -80,7 +80,7 @@ void console_write_line(uint8_t *buffer, size_t number_of_bytes)
}
#if IS_ENABLED(CONFIG_GDB_STUB) && (ENV_ROMSTAGE || ENV_RAMSTAGE)
#if CONFIG_GDB_STUB && (ENV_ROMSTAGE || ENV_RAMSTAGE)
void gdb_hw_init(void)
{
__gdb_hw_init();

View File

@ -14,13 +14,14 @@
* GNU General Public License for more details.
*/
#include <console/console.h>
#include <console/uart.h>
#include <console/streams.h>
#include <device/pci.h>
#include <option.h>
#include <rules.h>
#include <version.h>
#include <console/console.h>
#include <console/uart.h>
#include <console/serialice.h>
#include <console/streams.h>
#include <device/pci.h>
/* Mutable console log level only allowed when RAM comes online. */
#if defined(__PRE_RAM__)
@ -74,6 +75,8 @@ asmlinkage void console_init(void)
console_hw_init();
printk(BIOS_NOTICE, "\n\ncoreboot-%s%s %s " ENV_STRING " starting...\n",
serialice_main();
printk(BIOS_INFO, "\n\ncoreboot-%s%s %s " ENV_STRING " starting...\n",
coreboot_version, coreboot_extra_version, coreboot_build);
}

430
src/console/serialice.c Normal file
View File

@ -0,0 +1,430 @@
/*
* SerialICE
*
* Copyright (C) 2009 coresystems GmbH
* Copyright (C) 2016 Patrick Rudolph <siro@das-labor.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <console/console.h>
#include <console/serialice.h>
#include <arch/io.h>
#include <debug-com.h>
#include "serialice_priv.h"
#define VERSION "1.6"
/* Uart wrapper functions */
static void sio_flush(void)
{
debug_tx_flush();
}
static void sio_putc(u8 byte)
{
debug_tx_byte(byte);
}
static u8 sio_getc(void)
{
u8 val = 0;
val = debug_rx_byte();
if (val) {
#if ECHO_MODE
sio_putc(val);
#endif
return val;
}
return 0;
}
/* String functions */
static void sio_putstring(const char *string)
{
/* Very simple, no %d, %x etc. */
while (*string) {
if (*string == '\n')
sio_putc('\r');
sio_putc(*string);
string++;
}
sio_flush();
}
static void sio_put8(u8 data)
{
u8 c;
c = (data >> 4) & 0xf;
sio_put_nibble(c);
c = data & 0xf;
sio_put_nibble(c);
sio_flush();
}
static void sio_put16(u16 data)
{
int i;
u8 c;
for (i = 12; i >= 0; i -= 4) {
c = (data >> i) & 0xf;
sio_put_nibble(c);
}
sio_flush();
}
static void sio_put32(u32 data)
{
int i;
u8 c;
for (i = 28; i >= 0; i -= 4) {
c = (data >> i) & 0xf;
sio_put_nibble(c);
}
sio_flush();
}
static u8 sio_get_nibble(void)
{
u8 ret = 0;
u8 nibble = sio_getc();
if (nibble >= '0' && nibble <= '9') {
ret = (nibble - '0');
} else if (nibble >= 'a' && nibble <= 'f') {
ret = (nibble - 'a') + 0xa;
} else if (nibble >= 'A' && nibble <= 'F') {
ret = (nibble - 'A') + 0xa;
} else {
sio_putstring("ERROR: parsing number\n");
}
return ret;
}
static u8 sio_get8(void)
{
u8 data;
data = sio_get_nibble();
data = data << 4;
data |= sio_get_nibble();
return data;
}
static u16 sio_get16(void)
{
u16 data;
data = sio_get_nibble();
data = data << 4;
data |= sio_get_nibble();
data = data << 4;
data |= sio_get_nibble();
data = data << 4;
data |= sio_get_nibble();
return data;
}
static u32 sio_get32(void)
{
u32 data;
data = sio_get_nibble();
data = data << 4;
data |= sio_get_nibble();
data = data << 4;
data |= sio_get_nibble();
data = data << 4;
data |= sio_get_nibble();
data = data << 4;
data |= sio_get_nibble();
data = data << 4;
data |= sio_get_nibble();
data = data << 4;
data |= sio_get_nibble();
data = data << 4;
data |= sio_get_nibble();
return data;
}
/* SerialICE interface functions */
static void serialice_read_memory(void)
{
u8 width;
u32 *addr;
// Format:
// *rm00000000.w
addr = (u32 *)sio_get32();
sio_getc(); // skip .
width = sio_getc();
sio_putc('\r'); sio_putc('\n');
switch (width) {
case 'b':
sio_put8(read8(addr));
break;
case 'w':
sio_put16(read16(addr));
break;
case 'l':
sio_put32(read32(addr));
break;
}
}
static void serialice_write_memory(void)
{
u8 width;
u32 *addr;
u32 data;
// Format:
// *wm00000000.w=0000
addr = (u32 *)sio_get32();
sio_getc(); // skip .
width = sio_getc();
sio_getc(); // skip =
switch (width) {
case 'b':
data = sio_get8();
write8(addr, (u8)data);
break;
case 'w':
data = sio_get16();
write16(addr, (u16)data);
break;
case 'l':
data = sio_get32();
write32(addr, (u32)data);
break;
}
}
static void serialice_read_io(void)
{
u8 width;
u16 port;
// Format:
// *ri0000.w
port = sio_get16();
sio_getc(); // skip .
width = sio_getc();
sio_putc('\r'); sio_putc('\n');
switch (width) {
case 'b':
sio_put8(inb(port));
break;
case 'w':
sio_put16(inw(port));
break;
case 'l':
sio_put32(inl(port));
break;
}
}
static void serialice_write_io(void)
{
u8 width;
u16 port;
u32 data;
// Format:
// *wi0000.w=0000
port = sio_get16();
sio_getc(); // skip .
width = sio_getc();
sio_getc(); // skip =
switch (width) {
case 'b':
data = sio_get8();
outb((u8)data, port);
break;
case 'w':
data = sio_get16();
outw((u16)data, port);
break;
case 'l':
data = sio_get32();
outl((u32)data, port);
break;
}
}
static void serialice_read_msr(void)
{
u32 addr, key;
msr_t msr;
// Format:
// *rc00000000.9c5a203a
addr = sio_get32();
sio_getc(); // skip .
key = sio_get32(); // key in %edi
sio_putc('\r'); sio_putc('\n');
msr = rdmsr(addr, key);
sio_put32(msr.hi);
sio_putc('.');
sio_put32(msr.lo);
}
static void serialice_write_msr(void)
{
u32 addr, key;
msr_t msr;
// Format:
// *wc00000000.9c5a203a=00000000.00000000
addr = sio_get32();
sio_getc(); // skip .
key = sio_get32(); // read key in %edi
sio_getc(); // skip =
msr.hi = sio_get32();
sio_getc(); // skip .
msr.lo = sio_get32();
#ifdef __ROMCC__
/* Cheat to avoid register outage */
wrmsr(addr, msr, 0x9c5a203a);
#else
wrmsr(addr, msr, key);
#endif
}
static void serialice_cpuinfo(void)
{
u32 eax, ecx;
u32 reg32;
// Format:
// --EAX--- --ECX---
// *ci00000000.00000000
eax = sio_get32();
sio_getc(); // skip .
ecx = sio_get32();
sio_putc('\r'); sio_putc('\n');
/* This code looks quite crappy but this way we don't
* have to worry about running out of registers if we
* occupy eax, ebx, ecx, edx at the same time
*/
reg32 = s_cpuid_eax(eax, ecx);
sio_put32(reg32);
sio_putc('.');
reg32 = s_cpuid_ebx(eax, ecx);
sio_put32(reg32);
sio_putc('.');
reg32 = s_cpuid_ecx(eax, ecx);
sio_put32(reg32);
sio_putc('.');
reg32 = s_cpuid_edx(eax, ecx);
sio_put32(reg32);
}
static void serialice_mainboard(void)
{
int i = 0;
const char mb_string[] = CONFIG_MAINBOARD_VENDOR" "
CONFIG_MAINBOARD_PART_NUMBER;
sio_putc('\r'); sio_putc('\n');
while (i < 32 && mb_string[i] > 0) {
sio_putc(mb_string[i]);
i++;
}
while (i < 32) {
sio_putc(' ');
i++;
}
sio_flush();
}
static void serialice_version(void)
{
sio_putstring("\nSerialICE v" VERSION "\n");
}
void serialice_main(void)
{
u16 c;
serialice_version();
while (1) {
sio_putstring("\n> ");
c = sio_getc();
if (c != '*')
continue;
c = sio_getc() << 8;
c |= sio_getc();
switch (c) {
case (('r' << 8)|'m'): // Read Memory *rm
serialice_read_memory();
break;
case (('w' << 8)|'m'): // Write Memory *wm
serialice_write_memory();
break;
case (('r' << 8)|'i'): // Read IO *ri
serialice_read_io();
break;
case (('w' << 8)|'i'): // Write IO *wi
serialice_write_io();
break;
case (('r' << 8)|'c'): // Read CPU MSR *rc
serialice_read_msr();
break;
case (('w' << 8)|'c'): // Write CPU MSR *wc
serialice_write_msr();
break;
case (('c' << 8)|'i'): // Read CPUID *ci
serialice_cpuinfo();
break;
case (('m' << 8)|'b'): // Read mainboard type *mb
serialice_mainboard();
break;
case (('v' << 8)|'i'): // Read version info *vi
serialice_version();
break;
default:
sio_putstring("ERROR\n");
break;
}
}
}

View File

@ -0,0 +1,105 @@
/*
* SerialICE
*
* Copyright (C) 2009 coresystems GmbH
* Copyright (C) 2016 Patrick Rudolph <siro@das-labor.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef CONSOLE_SERIALICE_PRIV_H
#define CONSOLE_SERIALICE_PRIV_H
#include <stdint.h>
/* String functions */
#define sio_put_nibble(nibble) \
do { \
if (nibble > 9) \
nibble += ('a' - 10); \
else \
nibble += '0'; \
sio_putc(nibble); \
} while (0)
/* MSR functions */
typedef struct { u32 lo, hi; } msr_t;
static inline msr_t rdmsr(u32 index, u32 key)
{
msr_t result;
__asm__ __volatile__ (
"rdmsr"
: "=a" (result.lo), "=d" (result.hi)
: "c" (index), "D" (key)
);
return result;
}
static inline void wrmsr(u32 index, msr_t msr, u32 key)
{
__asm__ __volatile__ (
"wrmsr"
: /* No outputs */
: "c" (index), "a" (msr.lo), "d" (msr.hi), "D" (key)
);
}
/* CPUID functions */
static inline u32 s_cpuid_eax(u32 op, u32 op2)
{
u32 eax;
__asm__("cpuid"
: "=a" (eax)
: "a" (op), "c" (op2)
: "ebx", "edx");
return eax;
}
static inline u32 s_cpuid_ebx(u32 op, u32 op2)
{
u32 ebx;
__asm__("cpuid"
: "=b" (ebx)
: "a" (op), "c" (op2)
: "edx");
return ebx;
}
static inline u32 s_cpuid_ecx(u32 op, u32 op2)
{
u32 ecx;
__asm__("cpuid"
: "=c" (ecx)
: "a" (op), "c" (op2)
: "ebx", "edx");
return ecx;
}
static inline u32 s_cpuid_edx(u32 op, u32 op2)
{
u32 edx;
__asm__("cpuid"
: "=d" (edx)
: "a" (op), "c" (op2)
: "ebx");
return edx;
}
#endif

View File

@ -0,0 +1,30 @@
/*
* SerialICE
*
* Copyright (C) 2009 coresystems GmbH
* Copyright (C) 2016 Patrick Rudolph <siro@das-labor.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef CONSOLE_SERIALICE_H
#define CONSOLE_SERIALICE_H
#define ECHO_MODE 1
#if (ENV_RAMSTAGE && CONFIG_DEBUG_SERIALICE_RAMSTAGE) || \
(ENV_ROMSTAGE && CONFIG_DEBUG_SERIALICE_ROMSTAGE)
void serialice_main(void);
#else
static inline void serialice_main(void) { }
#endif
#endif

View File

@ -27,10 +27,4 @@ void console_tx_flush(void);
*/
void console_write_line(uint8_t *buffer, size_t number_of_bytes);
/* For remote GDB debugging. */
void gdb_hw_init(void);
void gdb_tx_byte(unsigned char byte);
void gdb_tx_flush(void);
unsigned char gdb_rx_byte(void);
#endif /* _CONSOLE_STREAMS_H_ */

View File

@ -33,7 +33,6 @@ int usb_can_rx_byte(int idx);
ENV_RAMSTAGE))
#define USB_PIPE_FOR_CONSOLE 0
#define USB_PIPE_FOR_GDB 0
#if __CONSOLE_USB_ENABLE__
static inline void __usbdebug_init(void) { usbdebug_init(); }

24
src/include/debug-com.h Normal file
View File

@ -0,0 +1,24 @@
/*
* This file is part of the coreboot project.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _DEBUG_COM_H_
#define _DEBUG_COM_H_
#include <stdint.h>
void debug_hw_init(void);
void debug_tx_byte(unsigned char byte);
void debug_tx_flush(void);
unsigned char debug_rx_byte(void);
#endif /* _DEBUG_COM_H_ */

View File

@ -251,6 +251,11 @@ postcar-y += prog_ops.c
postcar-y += rmodule.c
postcar-$(CONFIG_COLLECT_TIMESTAMPS) += timestamp.c
romstage-$(CONFIG_DEBUG_SERIALICE_ROMSTAGE) += debug-com.c
ramstage-$(CONFIG_DEBUG_SERIALICE_RAMSTAGE) += debug-com.c
romstage-$(CONFIG_GDB_STUB) += debug-com.c
ramstage-$(CONFIG_GDB_STUB) += debug-com.c
# Use program.ld for all the platforms which use C fo the bootblock.
bootblock-$(CONFIG_C_ENVIRONMENT_BOOTBLOCK) += program.ld

99
src/lib/debug-com.c Normal file
View File

@ -0,0 +1,99 @@
/*
* This file is part of the coreboot project.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <rules.h>
#include <stdint.h>
#include <console/uart.h>
#include <console/usb.h>
#include <debug-com.h>
#define CONFIG_UART_FOR_GDB 0
#define USB_PIPE_FOR_GDB 0
#define CONFIG_UART_FOR_SERIALICE 0
#define USB_PIPE_FOR_SERIALICE 0
/* Configure functions to use GDB arguments */
#if __CONSOLE_USB_ENABLE__ && CONFIG_GDB_STUB
static inline void __debug_hw_init(void) { usbdebug_init(); }
static inline void __debug_tx_byte(u8 data)
{ usb_tx_byte(USB_PIPE_FOR_GDB, data); }
static inline void __debug_tx_flush(void)
{ usb_tx_flush(USB_PIPE_FOR_GDB); }
static inline u8 __debug_rx_byte(void)
{ return usb_rx_byte(USB_PIPE_FOR_GDB); }
#elif __CONSOLE_SERIAL_ENABLE__ && CONFIG_GDB_STUB
static inline void __debug_hw_init(void)
{ uart_init(CONFIG_UART_FOR_GDB); }
static inline void __debug_tx_byte(u8 data)
{ uart_tx_byte(CONFIG_UART_FOR_GDB, data); }
static inline void __debug_tx_flush(void)
{ uart_tx_flush(CONFIG_UART_FOR_GDB); }
static inline u8 __debug_rx_byte(void)
{ return uart_rx_byte(CONFIG_UART_FOR_GDB); }
/* Configure functions to use SerialICE arguments */
#elif __CONSOLE_USB_ENABLE__ && CONFIG_DEBUG_SERIALICE
static inline void __debug_hw_init(void) { usbdebug_init(); }
static inline void __debug_tx_byte(u8 data)
{ usb_tx_byte(USB_PIPE_FOR_SERIALICE, data); }
static inline void __debug_tx_flush(void)
{ usb_tx_flush(USB_PIPE_FOR_SERIALICE); }
static inline u8 __debug_rx_byte(void)
{ return usb_rx_byte(USB_PIPE_FOR_SERIALICE); }
#elif __CONSOLE_SERIAL_ENABLE__ && CONFIG_DEBUG_SERIALICE
static inline void __debug_hw_init(void)
{ uart_init(CONFIG_UART_FOR_SERIALICE); }
static inline void __debug_tx_byte(u8 data)
{ uart_tx_byte(CONFIG_UART_FOR_SERIALICE, data); }
static inline void __debug_tx_flush(void)
{ uart_tx_flush(CONFIG_UART_FOR_SERIALICE); }
static inline u8 __debug_rx_byte(void)
{ return uart_rx_byte(CONFIG_UART_FOR_SERIALICE); }
#else
static inline void __debug_hw_init(void) {}
static inline void __debug_tx_byte(u8 data) {}
static inline void __debug_tx_flush(void) {}
static inline u8 __debug_rx_byte(void) { return 0; }
#endif
void debug_hw_init(void)
{
__debug_hw_init();
}
void debug_tx_byte(unsigned char byte)
{
__debug_tx_byte(byte);
}
void debug_tx_flush(void)
{
__debug_tx_flush();
}
unsigned char debug_rx_byte(void)
{
return __debug_rx_byte();
}