Loader serial baud rate control

Ian Dowse iedowse at iedowse.com
Sun Aug 14 16:35:39 GMT 2005


In message <200508140243.aa50444 at nowhere.iedowse.com>, Ian Dowse writes:
>
>Currently the baud rate used by the i386/amd64 loader for a serial
>console is hard-coded as 9600 baud and you need to recompile the
>loader to change it. Below is a patch that adds a "comconsole_speed"
>loader environment variable so that the speed can be changed from
>loader.conf or manually from the loader prompt. It doesn't quite
>do what you want though, since the loader has already printed a
>number of things by the time it reads loader.conf. Any comments?

Following a few suggestions, here's a more extensive change that
allows you to just write "-h -S19200" for example to /boot.config,
and the setting will propagate to boot2, loader and the kernel.
The rate can be overridden from the 'boot:' prompt and also via
the loader "comconsole_speed" variable

This adds about 50 bytes to the size of boot2, and it required a
few other boot2 changes to limit the size impact.

The loader stage now assumes that the baud rate has already been
set if the previous stage boot loader requested a serial console
(RB_SERIAL or RB_MULTIPLE). I'm not sure if this is a reasonable
assumption in all cases?

Ian

Index: i386/boot2/boot2.c
===================================================================
RCS file: /dump/FreeBSD-CVS/src/sys/boot/i386/boot2/boot2.c,v
retrieving revision 1.71
diff -u -r1.71 boot2.c
--- i386/boot2/boot2.c	18 Sep 2004 02:07:00 -0000	1.71
+++ i386/boot2/boot2.c	14 Aug 2005 16:16:33 -0000
@@ -63,7 +63,6 @@
 #define RBX_NOINTR	0x1c	/* -n */
 /* 0x1d is reserved for log2(RB_MULTIPLE) and is just misnamed here. */
 #define RBX_DUAL	0x1d	/* -D */
-#define RBX_PROBEKBD	0x1e	/* -P */
 /* 0x1f is reserved for log2(RB_BOOTINFO). */
 
 /* pass: -a, -s, -r, -d, -c, -v, -h, -C, -g, -m, -p, -D */
@@ -91,7 +90,7 @@
 
 extern uint32_t _end;
 
-static const char optstr[NOPT] = "DhaCgmnPprsv";
+static const char optstr[NOPT] = "DhaCgmnprsv"; /* Also 'P', 'S' */
 static const unsigned char flags[NOPT] = {
     RBX_DUAL,
     RBX_SERIAL,
@@ -100,7 +99,6 @@
     RBX_GDB,
     RBX_MUTE,
     RBX_NOINTR,
-    RBX_PROBEKBD,
     RBX_PAUSE,
     RBX_DFLTROOT,
     RBX_SINGLE,
@@ -122,6 +120,7 @@
 static char cmd[512];
 static char kname[1024];
 static uint32_t opts;
+static int comspeed = SIOSPD;
 static struct bootinfo bootinfo;
 static uint8_t ioctrl = IO_KEYBOARD;
 
@@ -390,34 +389,47 @@
 parse()
 {
     char *arg = cmd;
-    char *p, *q;
+    char *ep, *p, *q;
     unsigned int drv;
-    int c, i;
+    int c, i, j;
 
     while ((c = *arg++)) {
 	if (c == ' ' || c == '\t' || c == '\n')
 	    continue;
 	for (p = arg; *p && *p != '\n' && *p != ' ' && *p != '\t'; p++);
+	ep = p;
 	if (*p)
 	    *p++ = 0;
 	if (c == '-') {
 	    while ((c = *arg++)) {
+		if (c == 'P') {
+		    if (*(uint8_t *)PTOV(0x496) & 0x10) {
+			q = "yes";
+		    } else {
+			opts |= 1 << RBX_DUAL | 1 << RBX_SERIAL;
+			q = "no";
+		    }
+		    printf("Keyboard: %s\n", q);
+		    continue;
+		} else if (c == 'S') {
+		    j = 0;
+		    while ((unsigned int)(i = *arg++ - '0') <= 9)
+			j = j * 10 + i;
+		    if (j > 0 && i == -'0') {
+			comspeed = j;
+			break;
+		    }
+		    /* Fall through to error below ('S' not in optstr[]). */
+		}
 		for (i = 0; c != optstr[i]; i++)
 		    if (i == NOPT - 1)
 			return -1;
 		opts ^= 1 << flags[i];
 	    }
-	    if (opts & 1 << RBX_PROBEKBD) {
-		i = *(uint8_t *)PTOV(0x496) & 0x10;
-		printf("Keyboard: %s\n", i ? "yes" : "no");
-		if (!i)
-		    opts |= 1 << RBX_DUAL | 1 << RBX_SERIAL;
-		opts &= ~(1 << RBX_PROBEKBD);
-	    }
 	    ioctrl = opts & 1 << RBX_DUAL ? (IO_SERIAL|IO_KEYBOARD) :
 		     opts & 1 << RBX_SERIAL ? IO_SERIAL : IO_KEYBOARD;
 	    if (ioctrl & IO_SERIAL)
-	        sio_init();
+	        sio_init(115200 / comspeed);
 	} else {
 	    for (q = arg--; *q && *q != '('; q++);
 	    if (*q) {
@@ -459,7 +471,7 @@
 			     ? DRV_HARD : 0) + drv;
 		dsk_meta = 0;
 	    }
-	    if ((i = p - arg - !*(p - 1))) {
+	    if ((i = ep - arg)) {
 		if ((size_t)i >= sizeof(kname))
 		    return -1;
 		memcpy(kname, arg, i + 1);
Index: i386/boot2/lib.h
===================================================================
RCS file: /dump/FreeBSD-CVS/src/sys/boot/i386/boot2/lib.h,v
retrieving revision 1.2
diff -u -r1.2 lib.h
--- i386/boot2/lib.h	28 Aug 1999 00:40:02 -0000	1.2
+++ i386/boot2/lib.h	14 Aug 2005 16:02:57 -0000
@@ -17,7 +17,7 @@
  * $FreeBSD: src/sys/boot/i386/boot2/lib.h,v 1.2 1999/08/28 00:40:02 peter Exp $
  */
 
-void sio_init(void);
+void sio_init(int);
 void sio_flush(void);
 void sio_putc(int);
 int sio_getc(void);
Index: i386/boot2/sio.S
===================================================================
RCS file: /dump/FreeBSD-CVS/src/sys/boot/i386/boot2/sio.S,v
retrieving revision 1.9
diff -u -r1.9 sio.S
--- i386/boot2/sio.S	14 May 2004 20:29:29 -0000	1.9
+++ i386/boot2/sio.S	14 Aug 2005 16:02:57 -0000
@@ -17,7 +17,6 @@
 
 		.set SIO_PRT,SIOPRT		# Base port
 		.set SIO_FMT,SIOFMT		# 8N1
-		.set SIO_DIV,(115200/SIOSPD)	# 115200 / SPD
 
 		.globl sio_init
 		.globl sio_flush
@@ -25,14 +24,14 @@
 		.globl sio_getc
 		.globl sio_ischar
 
-/* void sio_init(void) */
+/* void sio_init(int div) */
 
 sio_init:	movw $SIO_PRT+0x3,%dx		# Data format reg
 		movb $SIO_FMT|0x80,%al		# Set format
 		outb %al,(%dx)			#  and DLAB
 		pushl %edx			# Save
 		subb $0x3,%dl			# Divisor latch reg
-		movw $SIO_DIV,%ax		# Set
+		movl 0x8(%esp),%eax		# Set
 		outw %ax,(%dx)			#  BPS
 		popl %edx			# Restore
 		movb $SIO_FMT,%al		# Clear
@@ -41,6 +40,8 @@
 		movb $0x3,%al			# Set RTS,
 		outb %al,(%dx)			#  DTR
 		incl %edx			# Line status reg
+		call sio_flush
+		ret $0x4
 
 /* void sio_flush(void) */
 
Index: i386/libi386/comconsole.c
===================================================================
RCS file: /dump/FreeBSD-CVS/src/sys/boot/i386/libi386/comconsole.c,v
retrieving revision 1.10
diff -u -r1.10 comconsole.c
--- i386/libi386/comconsole.c	16 Sep 2003 11:24:23 -0000	1.10
+++ i386/libi386/comconsole.c	14 Aug 2005 15:59:37 -0000
@@ -35,6 +35,7 @@
 #define COMC_FMT	0x3		/* 8N1 */
 #define COMC_TXWAIT	0x40000		/* transmit timeout */
 #define COMC_BPS(x)	(115200 / (x))	/* speed to DLAB divisor */
+#define COMC_DIV2BPS(x)	(115200 / (x))	/* DLAB divisor to speed */
 
 #ifndef	COMPORT
 #define COMPORT		0x3f8
@@ -47,9 +48,15 @@
 static int	comc_init(int arg);
 static void	comc_putchar(int c);
 static int	comc_getchar(void);
+static int	comc_getspeed(void);
 static int	comc_ischar(void);
+static int	comc_parsespeed(const char *string);
+static void	comc_setup(int speed);
+static int	comc_speed_set(struct env_var *ev, int flags,
+		    const void *value);
 
 static int	comc_started;
+static int	comc_curspeed;
 
 struct console comconsole = {
     "comconsole",
@@ -65,26 +72,50 @@
 static void
 comc_probe(struct console *cp)
 {
+    char speedbuf[16];
+    char *cons, *speedenv;
+    int speed;
+
     /* XXX check the BIOS equipment list? */
     cp->c_flags |= (C_PRESENTIN | C_PRESENTOUT);
+
+    if (comc_curspeed == 0) {
+	comc_curspeed = COMSPEED;
+	/*
+	 * Assume that the speed was set by an earlier boot loader if
+	 * comconsole is already the preferred console.
+	 */
+	cons = getenv("console");
+	if ((cons != NULL && strcmp(cons, comconsole.c_name) == 0) ||
+	    getenv("boot_multicons") != NULL) {
+		comc_curspeed = comc_getspeed();
+	}
+	speedenv = getenv("comconsole_speed");
+	if (speedenv != NULL) {
+	    speed = comc_parsespeed(speedenv);
+	    if (speed > 0)
+		comc_curspeed = speed;
+	}
+
+	sprintf(speedbuf, "%d", comc_curspeed);
+	unsetenv("comconsole_speed");
+	env_setenv("comconsole_speed", EV_VOLATILE, speedbuf, comc_speed_set,
+	    env_nounset);
+    }
 }
 
 static int
 comc_init(int arg)
 {
+    char speedbuf[16];
+    char *cons, *speedenv;
+    int speed;
+
     if (comc_started && arg == 0)
 	return 0;
     comc_started = 1;
 
-    outb(COMPORT + com_cfcr, CFCR_DLAB | COMC_FMT);
-    outb(COMPORT + com_dlbl, COMC_BPS(COMSPEED) & 0xff);
-    outb(COMPORT + com_dlbh, COMC_BPS(COMSPEED) >> 8);
-    outb(COMPORT + com_cfcr, COMC_FMT);
-    outb(COMPORT + com_mcr, MCR_RTS | MCR_DTR);
-
-    do
-        inb(COMPORT + com_data);
-    while (inb(COMPORT + com_lsr) & LSR_RXRDY);
+    comc_setup(comc_curspeed);
 
     return(0);
 }
@@ -112,3 +143,76 @@
 {
     return(inb(COMPORT + com_lsr) & LSR_RXRDY);
 }
+
+static int
+comc_speed_set(struct env_var *ev, int flags, const void *value)
+{
+    int speed;
+
+    if (value == NULL || (speed = comc_parsespeed(value)) <= 0) {
+	printf("Invalid speed\n");
+	return (CMD_ERROR);
+    }
+
+    if (comc_started && comc_curspeed != speed)
+	comc_setup(speed);
+
+    env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
+
+    return (CMD_OK);
+}
+
+static void
+comc_setup(int speed)
+{
+
+    comc_curspeed = speed;
+
+    outb(COMPORT + com_cfcr, CFCR_DLAB | COMC_FMT);
+    outb(COMPORT + com_dlbl, COMC_BPS(speed) & 0xff);
+    outb(COMPORT + com_dlbh, COMC_BPS(speed) >> 8);
+    outb(COMPORT + com_cfcr, COMC_FMT);
+    outb(COMPORT + com_mcr, MCR_RTS | MCR_DTR);
+
+    do
+        inb(COMPORT + com_data);
+    while (inb(COMPORT + com_lsr) & LSR_RXRDY);
+}
+
+static int
+comc_parsespeed(const char *speedstr)
+{
+    char *p;
+    int speed;
+
+    speed = strtol(speedstr, &p, 0);
+    if (p == speedstr || *p != '\0' || speed <= 0)
+	return (-1);
+
+    return (speed);
+}
+
+static int
+comc_getspeed(void)
+{
+	u_int	divisor;
+	u_char	dlbh;
+	u_char	dlbl;
+	u_char	cfcr;
+
+	cfcr = inb(COMPORT + com_cfcr);
+	outb(COMPORT + com_cfcr, CFCR_DLAB | cfcr);
+
+	dlbl = inb(COMPORT + com_dlbl);
+	dlbh = inb(COMPORT + com_dlbh);
+
+	outb(COMPORT + com_cfcr, cfcr);
+
+	divisor = dlbh << 8 | dlbl;
+
+	/* XXX there should be more sanity checking. */
+	if (divisor == 0)
+		return (COMSPEED);
+	return (COMC_DIV2BPS(divisor));
+}
+



More information about the freebsd-current mailing list