From 636d90fc5cc34dc279f5004a47d79deee99dac7d Mon Sep 17 00:00:00 2001 From: Poul-Henning Kamp Date: Thu, 21 Jul 2005 09:48:37 +0000 Subject: [PATCH] Make the facility for recognizing BIOS-signatures more general and return a printable representation. This fixes recognition of the PC Engines WRAP and improves the recognition of the Soekris boards (Bios version can now be seen in the dmesg output for instance). Also, add watchdog support for PCM-582x platforms. Submitted by: Adrian Steinmann Slightly changed by: phk PR: 81360 --- share/man/man9/bios.9 | 50 +++++++++++++++++++++- sys/amd64/amd64/bios.c | 15 ------- sys/amd64/include/pc/bios.h | 17 +++++++- sys/i386/i386/bios.c | 84 ++++++++++++++++++++++++++++++++----- sys/i386/i386/elan-mmcr.c | 23 ++++++++++ sys/i386/i386/geode.c | 67 +++++++++++++++++++++++++---- sys/i386/include/pc/bios.h | 17 +++++++- 7 files changed, 235 insertions(+), 38 deletions(-) diff --git a/share/man/man9/bios.9 b/share/man/man9/bios.9 index b3a2ab64cab..e087f7bdc54 100644 --- a/share/man/man9/bios.9 +++ b/share/man/man9/bios.9 @@ -30,7 +30,8 @@ .Sh NAME .Nm bios_sigsearch , .Nm bios32_SDlookup , -.Nm bios32 +.Nm bios32 , +.Nm bios_oem_strings .Nd interact with PC BIOS .Sh SYNOPSIS .In sys/param.h @@ -50,6 +51,23 @@ .Vt extern struct bios32_SDentry PCIbios ; .Vt extern struct SMBIOS_table SMBIOStable ; .Vt extern struct DMI_table DMItable ; +.Ft int +.Fn bios_oem_strings "struct bios_oem *oem" "u_char *buffer" "size_t maxlen" +.Bd -literal +struct bios_oem_signature { + char * anchor; /* search anchor string in BIOS memory */ + size_t offset; /* offset from anchor (may be negative) */ + size_t totlen; /* total length of BIOS string to copy */ +}; +struct bios_oem_range { + u_int from; /* shouldn't be below 0xe0000 */ + u_int to; /* shouldn't be above 0xfffff */ +}; +struct bios_oem { + struct bios_oem_range range; + struct bios_oem_signature signature[]; +}; +.Ed .Sh DESCRIPTION These functions provide a general-purpose interface for dealing with the BIOS functions and data encountered on x86 PC-architecture systems. @@ -75,6 +93,36 @@ bytes and the search repeated. If the signature is found, its effective physical address is returned. If no signature is found, zero is returned. +.It Fn bios_oem_strings +Searches a given BIOS memory range for one or more strings, +and composes a printable concatenation of those found. +The routine expects a structure describing the BIOS address +.Fa range +(within 0xe0000 - 0xfffff), and a { NULL, 0, 0 } -terminated array of +.Fa bios_oem_signature +structures which define the +.Fa anchor +string, an +.Fa offset +from the beginning of the match (which may be negative), and +.Fa totlen +number of bytes to be collected from BIOS memory starting at that offset. +Unmatched anchors are ignored, whereas matches are copied from BIOS memory +starting at their corresponding +.Fa offset +with unprintable characters being replaced with space, and consecutive spaces +being suppressed. This composed string is stored in +.Fa buffer +up to the given +.Fa maxlen +bytes (including trailing '\\0', and any trailing space surpressed). +If an error is encountered, i.e. trying to read out of said BIOS range, +other invalid input, or +.Fa buffer +overflow, a negative integer is returned, otherwise the +length of the composed string is returned. In particular, a return +value of 0 means that none of the given anchor strings were found in +the specified BIOS memory range. .It Fn BIOS_VADDRTOPADDR Returns the effective physical address which corresponds to the kernel virtual address diff --git a/sys/amd64/amd64/bios.c b/sys/amd64/amd64/bios.c index d08f5ccd160..c8985c1f773 100644 --- a/sys/amd64/amd64/bios.c +++ b/sys/amd64/amd64/bios.c @@ -93,18 +93,3 @@ bios_sigsearch(u_int32_t start, u_char *sig, int siglen, int paralen, int sigofs } return(0); } - -const u_char * -bios_string(u_int from, u_int to, const u_char *string, int len) -{ - const char *t, *te; - - if (len == 0) - len = strlen(string); - t = (const char *)(KERNBASE + from); - te = (const char *)(KERNBASE + to); - for (; t <= te; t++) - if (!memcmp(string, t, len)) - return (t); - return (NULL); -} diff --git a/sys/amd64/include/pc/bios.h b/sys/amd64/include/pc/bios.h index 87279727afe..ffad3695f75 100644 --- a/sys/amd64/include/pc/bios.h +++ b/sys/amd64/include/pc/bios.h @@ -48,7 +48,22 @@ struct bios_smap { u_int32_t type; } __packed; -const u_char *bios_string(u_int from, u_int to, const u_char *string, int len); +struct bios_oem_signature { + char * anchor; /* search anchor string in BIOS memory */ + size_t offset; /* offset from anchor (may be negative) */ + size_t totlen; /* total length of BIOS string to copy */ +} __packed; +struct bios_oem_range { + u_int from; /* shouldn't be below 0xe0000 */ + u_int to; /* shouldn't be above 0xfffff */ +} __packed; +struct bios_oem { + struct bios_oem_range range; + struct bios_oem_signature signature[]; +} __packed; + +extern int +bios_oem_strings(struct bios_oem *oem, u_char *buffer, size_t maxlen); #endif /* _MACHINE_PC_BIOS_H_ */ diff --git a/sys/i386/i386/bios.c b/sys/i386/i386/bios.c index 332bd6c4dcc..9d5df7de160 100644 --- a/sys/i386/i386/bios.c +++ b/sys/i386/i386/bios.c @@ -475,19 +475,81 @@ bios16(struct bios_args *args, char *fmt, ...) return (i); } -const u_char * -bios_string(u_int from, u_int to, const u_char *string, int len) +int bios_oem_strings(struct bios_oem *oem, u_char *buffer, size_t maxlen) { - const char *t, *te; + size_t idx = 0; + struct bios_oem_signature *sig; + u_int from, to; + u_char c, *s, *se, *str, *bios_str; + size_t i, off, len, tot; - if (len == 0) - len = strlen(string); - t = (const char *)(KERNBASE + from); - te = (const char *)(KERNBASE + to); - for (; t <= te; t++) - if (!memcmp(string, t, len)) - return (t); - return (NULL); + if ( !oem || !buffer || maxlen<2 ) + return(-1); + + sig = oem->signature; + if (!sig) + return(-2); + + from = oem->range.from; + to = oem->range.to; + if ( (to<=from) || (from(BIOS_START+BIOS_SIZE)) ) + return(-3); + + while (sig->anchor != NULL) { + str = sig->anchor; + len = strlen(str); + off = sig->offset; + tot = sig->totlen; + /* make sure offset doesn't go beyond bios area */ + if ( (to+off)>(BIOS_START+BIOS_SIZE) || + ((from+off) maxlen - 1) { + printf("sys/i386/i386/bios.c: sig '%s' " + "idx %d + tot %d = %d > maxlen-1 %d\n", + str, idx, tot, idx+tot, maxlen-1); + return(-5); + } + bios_str = NULL; + s = (u_char *)BIOS_PADDRTOVADDR(from); + se = (u_char *)BIOS_PADDRTOVADDR(to-len); + for (; s 0x7E) ) + c = ' '; + if (idx == 0) { + if (c != ' ') + buffer[idx++] = c; + } else if ( (c != ' ') || + ((c == ' ') && (buffer[idx-1] != ' ')) ) + buffer[idx++] = c; + } + } + sig++; + } + /* remove a final trailing space */ + if ( (idx > 1) && (buffer[idx-1] == ' ') ) + idx--; + buffer[idx] = '\0'; + return (idx); } #ifdef DEV_ISA diff --git a/sys/i386/i386/elan-mmcr.c b/sys/i386/i386/elan-mmcr.c index 8e1ffb13225..66c218f2ee4 100644 --- a/sys/i386/i386/elan-mmcr.c +++ b/sys/i386/i386/elan-mmcr.c @@ -61,6 +61,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -77,6 +78,20 @@ static u_int pps_a, pps_d; static u_int echo_a, echo_d; #endif /* CPU_ELAN_PPS */ +#ifdef CPU_SOEKRIS + +static struct bios_oem bios_soekris = { + { 0xf0000, 0xf1000 }, + { + { "Soekris", 0, 8 }, /* Soekris Engineering. */ + { "net4", 0, 8 }, /* net45xx */ + { "comBIOS", 0, 54 }, /* comBIOS ver. 1.26a 20040819 ... */ + { NULL, 0, 0 }, + } +}; + +#endif + static u_int led_cookie[32]; static struct cdev *led_dev[32]; @@ -449,6 +464,11 @@ static void elan_drvinit(void) { +#ifdef CPU_SOEKRIS +#define BIOS_OEM_MAXLEN 72 + static u_char bios_oem[BIOS_OEM_MAXLEN] = "\0"; +#endif /* CPU_SOEKRIS */ + /* If no elan found, just return */ if (mmcrptr == NULL) return; @@ -466,6 +486,9 @@ elan_drvinit(void) UID_ROOT, GID_WHEEL, 0600, "elan-mmcr"); #ifdef CPU_SOEKRIS + if ( bios_oem_strings(&bios_soekris, bios_oem, BIOS_OEM_MAXLEN) > 0 ) + printf("Elan-mmcr %s\n", bios_oem); + /* Create the error LED on GPIO9 */ led_cookie[9] = 0x02000c34; led_dev[9] = led_create(gpio_led, &led_cookie[9], "error"); diff --git a/sys/i386/i386/geode.c b/sys/i386/i386/geode.c index 2702f21eb25..a32d68cdd19 100644 --- a/sys/i386/i386/geode.c +++ b/sys/i386/i386/geode.c @@ -39,6 +39,34 @@ __FBSDID("$FreeBSD$"); #include #include +static struct bios_oem bios_soekris = { + { 0xf0000, 0xf1000 }, + { + { "Soekris", 0, 8 }, /* Soekris Engineering. */ + { "net4", 0, 8 }, /* net45xx */ + { "comBIOS", 0, 54 }, /* comBIOS ver. 1.26a 20040819 ... */ + { NULL, 0, 0 }, + } +}; + +static struct bios_oem bios_pcengines = { + { 0xf9000, 0xfa000 }, + { + { "PC Engines WRAP", 0, 28 }, /* PC Engines WRAP.1C v1.03 */ + { "tinyBIOS", 0, 28 }, /* tinyBIOS V1.4a (C)1997-2003 */ + { NULL, 0, 0 }, + } +}; + +static struct bios_oem bios_advantech = { + { 0xfe000, 0xff000 }, + { + { "**** PCM-582", 5, 33 }, /* PCM-5823 BIOS V1.12 ... */ + { "GXm-Cx5530", -11, 35 }, /* 06/07/2002-GXm-Cx5530... */ + { NULL, 0, 0 }, + } +}; + static unsigned cba; static unsigned gpio; static unsigned geode_counter; @@ -115,9 +143,23 @@ geode_watchdog(void *foo __unused, u_int cmd, int *error) } } +/* + * The Advantech PCM-582x watchdog expects 0x1 at I/O port 0x0443 + * every 1.6 secs +/- 30%. Writing 0x0 disables the watchdog + * NB: reading the I/O port enables the timer as well + */ +static void +advantech_watchdog(void *foo __unused, u_int cmd, int *error) +{ + outb(0x0443, (cmd & WD_INTERVAL) ? 1 : 0); + *error = 0; +} + static int geode_probe(device_t self) { +#define BIOS_OEM_MAXLEN 80 + static u_char bios_oem[BIOS_OEM_MAXLEN] = "\0"; if (pci_get_devid(self) == 0x0515100b) { if (geode_counter == 0) { @@ -139,14 +181,12 @@ geode_probe(device_t self) gpio = pci_read_config(self, PCIR_BAR(0), 4); gpio &= ~0x1f; printf("Geode GPIO@ = %x\n", gpio); - if (NULL != - bios_string(0xf0000, 0xf0100, "Soekris Engineering", 0)) { - printf("Soekris Engineering NET4801 platform\n"); + if ( bios_oem_strings(&bios_soekris, + bios_oem, BIOS_OEM_MAXLEN) > 0 ) { led1b = 20; led1 = led_create(led_func, &led1b, "error"); - } else if (NULL != - bios_string(0xf9000, 0xf9000, "PC Engines WRAP.1C ", 0)) { - printf("PC Engines WRAP.1C platfrom\n"); + } else if ( bios_oem_strings(&bios_pcengines, + bios_oem, BIOS_OEM_MAXLEN) > 0 ) { led1b = -2; led2b = -3; led3b = -18; @@ -154,11 +194,20 @@ geode_probe(device_t self) led2 = led_create(led_func, &led2b, "led2"); led3 = led_create(led_func, &led3b, "led3"); /* - * Turn on first LED so we don't make people think - * their box just died. - */ + * Turn on first LED so we don't make + * people think their box just died. + */ led_func(&led1b, 1); } + if ( strlen(bios_oem) ) + printf("Geode %s\n", bios_oem); + } else if (pci_get_devid(self) == 0x01011078) { + if ( bios_oem_strings(&bios_advantech, + bios_oem, BIOS_OEM_MAXLEN) > 0 ) { + printf("Geode %s\n", bios_oem); + EVENTHANDLER_REGISTER(watchdog_list, advantech_watchdog, + NULL, 0); + } } return (ENXIO); } diff --git a/sys/i386/include/pc/bios.h b/sys/i386/include/pc/bios.h index c7362a96408..4533de75acd 100644 --- a/sys/i386/include/pc/bios.h +++ b/sys/i386/include/pc/bios.h @@ -281,7 +281,22 @@ struct bios_smap { u_int32_t type; } __packed; -const u_char *bios_string(u_int from, u_int to, const u_char *string, int len); +struct bios_oem_signature { + char * anchor; /* search anchor string in BIOS memory */ + size_t offset; /* offset from anchor (may be negative) */ + size_t totlen; /* total length of BIOS string to copy */ +} __packed; +struct bios_oem_range { + u_int from; /* shouldn't be below 0xe0000 */ + u_int to; /* shouldn't be above 0xfffff */ +} __packed; +struct bios_oem { + struct bios_oem_range range; + struct bios_oem_signature signature[]; +} __packed; + +extern int +bios_oem_strings(struct bios_oem *oem, u_char *buffer, size_t maxlen); #endif /* _MACHINE_PC_BIOS_H_ */