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