public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Yinghai Lu <yinghai@kernel.org>
To: Pekka Enberg <penberg@cs.helsinki.fi>
Cc: hpa@zytor.com, x86@kernel.org, linux-kernel@vger.kernel.org,
	Cyrill Gorcunov <gorcunov@gmail.com>,
	Ingo Molnar <mingo@redhat.com>
Subject: Re: [PATCH v2] x86: Early-boot serial I/O support
Date: Sat, 10 Jul 2010 13:49:23 -0700	[thread overview]
Message-ID: <4C38DCD3.8020302@kernel.org> (raw)
In-Reply-To: <1278790820-1817-1-git-send-email-penberg@cs.helsinki.fi>

On 07/10/2010 12:40 PM, Pekka Enberg wrote:
> This patch adds serial I/O support to very early boot printf(). It's useful for
> debugging boot code when running Linux under KVM, for example. The actual code
> was lifted from early printk.
> 
> Cc: Cyrill Gorcunov <gorcunov@gmail.com>
> Cc: Ingo Molnar <mingo@redhat.com>
> Cc: Yinghai Lu <yinghai@kernel.org>
> Signed-off-by: Pekka Enberg <penberg@cs.helsinki.fi>
> ---
> v1 -> v2:
> 
>   - Use 'earlyprintk' kernel parameter to determine whether to use
>     early serial or not as suggested by Yinghai and hpa.
> 
>  arch/x86/boot/boot.h   |   16 +++++++
>  arch/x86/boot/main.c   |    3 +
>  arch/x86/boot/string.c |   41 ++++++++++++++++++
>  arch/x86/boot/tty.c    |  111 +++++++++++++++++++++++++++++++++++++++++++++---
>  4 files changed, 165 insertions(+), 6 deletions(-)
> 
> diff --git a/arch/x86/boot/boot.h b/arch/x86/boot/boot.h
> index 98239d2..f05b5ac 100644
> --- a/arch/x86/boot/boot.h
> +++ b/arch/x86/boot/boot.h
> @@ -37,6 +37,8 @@
>  extern struct setup_header hdr;
>  extern struct boot_params boot_params;
>  
> +#define cpu_relax()	asm volatile("rep; nop" ::: "memory")
> +
>  /* Basic port I/O */
>  static inline void outb(u8 v, u16 port)
>  {
> @@ -203,6 +205,17 @@ static inline int isdigit(int ch)
>  	return (ch >= '0') && (ch <= '9');
>  }
>  
> +static inline int isxdigit(int ch)
> +{
> +	if (isdigit(ch))
> +		return true;
> +
> +	if ((ch >= 'a') && (ch <= 'f'))
> +		return true;
> +
> +	return (ch >= 'A') && (ch <= 'F');
> +}
> +
>  /* Heap -- available for dynamic lists. */
>  extern char _end[];
>  extern char *HEAP;
> @@ -329,10 +342,13 @@ void initregs(struct biosregs *regs);
>  
>  /* string.c */
>  int strcmp(const char *str1, const char *str2);
> +int strncmp(const char *cs, const char *ct, size_t count);
>  size_t strnlen(const char *s, size_t maxlen);
>  unsigned int atou(const char *s);
> +unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base);
>  
>  /* tty.c */
> +void console_init(void);
>  void puts(const char *);
>  void putchar(int);
>  int getchar(void);
> diff --git a/arch/x86/boot/main.c b/arch/x86/boot/main.c
> index 140172b..4ef1a33 100644
> --- a/arch/x86/boot/main.c
> +++ b/arch/x86/boot/main.c
> @@ -130,6 +130,9 @@ void main(void)
>  	/* First, copy the boot header into the "zeropage" */
>  	copy_boot_params();
>  
> +	/* Initialize the early-boot console */
> +	console_init();
> +
>  	/* End of heap check */
>  	init_heap();
>  
> diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c
> index f94b7a0..aba29df 100644
> --- a/arch/x86/boot/string.c
> +++ b/arch/x86/boot/string.c
> @@ -30,6 +30,22 @@ int strcmp(const char *str1, const char *str2)
>  	return 0;
>  }
>  
> +int strncmp(const char *cs, const char *ct, size_t count)
> +{
> +	unsigned char c1, c2;
> +
> +	while (count) {
> +		c1 = *cs++;
> +		c2 = *ct++;
> +		if (c1 != c2)
> +			return c1 < c2 ? -1 : 1;
> +		if (!c1)
> +			break;
> +		count--;
> +	}
> +	return 0;
> +}
> +
>  size_t strnlen(const char *s, size_t maxlen)
>  {
>  	const char *es = s;
> @@ -48,3 +64,28 @@ unsigned int atou(const char *s)
>  		i = i * 10 + (*s++ - '0');
>  	return i;
>  }
> +
> +/* Works only for digits and letters, but small and fast */
> +#define TOLOWER(x) ((x) | 0x20)
> +
> +unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)
> +{
> +	unsigned long long result = 0;
> +
> +	if (base == 16 && cp[0] == '0' && TOLOWER(cp[1]) == 'x')
> +		cp += 2;
> +
> +	while (isxdigit(*cp)) {
> +		unsigned int value;
> +
> +		value = isdigit(*cp) ? *cp - '0' : TOLOWER(*cp) - 'a' + 10;
> +		if (value >= base)
> +			break;
> +		result = result * base + value;
> +		cp++;
> +	}
> +	if (endp)
> +		*endp = (char *)cp;
> +
> +	return result;
> +}
> diff --git a/arch/x86/boot/tty.c b/arch/x86/boot/tty.c
> index 01ec69c..f3ceee2 100644
> --- a/arch/x86/boot/tty.c
> +++ b/arch/x86/boot/tty.c
> @@ -10,23 +10,51 @@
>   * ----------------------------------------------------------------------- */
>  
>  /*
> - * Very simple screen I/O
> - * XXX: Probably should add very simple serial I/O?
> + * Very simple screen and serial I/O
>   */
>  
>  #include "boot.h"
>  
> +#define DEFAULT_SERIAL_PORT 0x3f8 /* ttyS0 */
> +
> +static int	early_serial_base;
> +
> +#define XMTRDY          0x20
> +
> +#define DLAB		0x80
> +
> +#define TXR             0       /*  Transmit register (WRITE) */
> +#define RXR             0       /*  Receive register  (READ)  */
> +#define IER             1       /*  Interrupt Enable          */
> +#define IIR             2       /*  Interrupt ID              */
> +#define FCR             2       /*  FIFO control              */
> +#define LCR             3       /*  Line control              */
> +#define MCR             4       /*  Modem control             */
> +#define LSR             5       /*  Line Status               */
> +#define MSR             6       /*  Modem Status              */
> +#define DLL             0       /*  Divisor Latch Low         */
> +#define DLH             1       /*  Divisor latch High        */
> +
> +#define DEFAULT_BAUD 9600
> +
>  /*
>   * These functions are in .inittext so they can be used to signal
>   * error during initialization.
>   */
>  
> -void __attribute__((section(".inittext"))) putchar(int ch)
> +static void __attribute__((section(".inittext"))) serial_putchar(int ch)
>  {
> -	struct biosregs ireg;
> +	unsigned timeout = 0xffff;
>  
> -	if (ch == '\n')
> -		putchar('\r');	/* \n -> \r\n */
> +	while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout)
> +		cpu_relax();
> +
> +	outb(ch, early_serial_base + TXR);
> +}
> +
> +static void __attribute__((section(".inittext"))) bios_putchar(int ch)
> +{
> +	struct biosregs ireg;
>  
>  	initregs(&ireg);
>  	ireg.bx = 0x0007;
> @@ -36,6 +64,17 @@ void __attribute__((section(".inittext"))) putchar(int ch)
>  	intcall(0x10, &ireg, NULL);
>  }
>  
> +void __attribute__((section(".inittext"))) putchar(int ch)
> +{
> +	if (ch == '\n')
> +		putchar('\r');	/* \n -> \r\n */
> +
> +	bios_putchar(ch);
> +
> +	if (early_serial_base != 0)
> +		serial_putchar(ch);
> +}
> +
>  void __attribute__((section(".inittext"))) puts(const char *str)
>  {
>  	while (*str)
> @@ -112,3 +151,63 @@ int getchar_timeout(void)
>  
>  	return 0;		/* Timeout! */
>  }
> +
> +static void early_serial_init(int baud)
> +{
> +	unsigned char c;
> +	unsigned divisor;
> +
> +	outb(0x3, early_serial_base + LCR);	/* 8n1 */
> +	outb(0, early_serial_base + IER);	/* no interrupt */
> +	outb(0, early_serial_base + FCR);	/* no fifo */
> +	outb(0x3, early_serial_base + MCR);	/* DTR + RTS */
> +
> +	divisor	= 115200 / baud;
> +	c = inb(early_serial_base + LCR);
> +	outb(c | DLAB, early_serial_base + LCR);
> +	outb(divisor & 0xff, early_serial_base + DLL);
> +	outb((divisor >> 8) & 0xff, early_serial_base + DLH);
> +	outb(c & ~DLAB, early_serial_base + LCR);
> +}
> +
> +void console_init(void)
> +{
> +	int baud = DEFAULT_BAUD;
> +	char arg[32];
> +	int pos = 0;
> +
> +	if (cmdline_find_option("earlyprintk", arg, sizeof arg) > 0) {


> +		char *e;
> +
> +		if (!strncmp(arg, "serial", 6)) {
> +			early_serial_base = DEFAULT_SERIAL_PORT;
> +			pos += 6;
> +		}
> +
> +		if (arg[pos] == ',')
> +			pos++;
> +
> +		if (!strncmp(arg, "ttyS", 4)) {
> +			static const int bases[] = { 0x3f8, 0x2f8 };
> +			int port = 0;
> +
> +			if (!strncmp(arg + pos, "ttyS", 4))
> +				pos += 4;
> +
> +			if (arg[pos++] == '1')
> +				port = 1;
> +
> +			early_serial_base = bases[port];
> +		}
> +
> +		if (arg[pos] == ',')
> +			pos++;
> +
> +		baud = simple_strtoull(arg + pos, &e, 0);
> +		if (baud == 0 || arg + pos == e)
> +			baud = DEFAULT_BAUD;
> +	}
> +
> +	if (early_serial_base != 0)
> +		early_serial_init(baud);
> +}


can you analyze "console=uart8250,io,0x3f8,115200n8" instead?

that is equal to "earlyprintk=ttyS0,115200 console=ttyS0,115200"

so we only use one for all.

also like to kill earlyprintk=ttyS0,115200 to favor earlycon

Thanks

Yinghai

  reply	other threads:[~2010-07-10 20:54 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-07-10 19:40 [PATCH v2] x86: Early-boot serial I/O support Pekka Enberg
2010-07-10 20:49 ` Yinghai Lu [this message]
2010-07-10 21:17   ` Pekka Enberg
2010-07-10 21:27     ` H. Peter Anvin
2010-07-10 21:32       ` Pekka Enberg
2010-07-10 21:07 ` Cyrill Gorcunov
2010-07-10 21:18   ` Pekka Enberg
2010-07-10 21:32     ` Cyrill Gorcunov
2010-07-10 21:36       ` H. Peter Anvin
2010-07-10 21:37   ` H. Peter Anvin
2010-07-10 21:55     ` Cyrill Gorcunov
2010-07-10 22:30       ` H. Peter Anvin
2010-07-11  7:10         ` Pekka Enberg

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4C38DCD3.8020302@kernel.org \
    --to=yinghai@kernel.org \
    --cc=gorcunov@gmail.com \
    --cc=hpa@zytor.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@redhat.com \
    --cc=penberg@cs.helsinki.fi \
    --cc=x86@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox