#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <uvm/uvm_extern.h>
#include <machine/biosvar.h>
#include <machine/mpbiosvar.h>
#include <machine/smbiosvar.h>
#include <dev/isa/isareg.h>
#include <amd64/include/isa_machdep.h>
#include "acpi.h"
#include "efi.h"
#include "mpbios.h"
#include "pci.h"
struct bios_softc {
struct device sc_dev;
};
struct smbhdr *smbios_find(uint8_t *);
void smbios_info(char *);
int bios_match(struct device *, void *, void *);
void bios_attach(struct device *, struct device *, void *);
int bios_print(void *, const char *);
char *fixstring(char *);
const struct cfattach bios_ca = {
sizeof(struct bios_softc), bios_match, bios_attach
};
struct cfdriver bios_cd = {
NULL, "bios", DV_DULL, CD_COCOVM
};
struct smbios_entry smbios_entry;
const char *smbios_uninfo[] = {
"System",
"Not ",
"To be",
"SYS-"
};
char smbios_bios_date[64];
char smbios_bios_version[64];
char smbios_board_vendor[64];
char smbios_board_prod[64];
char smbios_board_serial[64];
int
bios_match(struct device *parent, void *match , void *aux)
{
struct bios_attach_args *bia = aux;
if (bios_cd.cd_ndevs || strcmp(bia->ba_name, bios_cd.cd_name))
return 0;
return 1;
}
void
bios_attach(struct device *parent, struct device *self, void *aux)
{
struct bios_softc *sc = (struct bios_softc *)self;
struct smbios_struct_bios *sb;
struct smbtable bios;
char scratch[64];
vaddr_t va;
paddr_t pa, end;
uint8_t *p;
int smbiosrev = 0;
struct smbhdr *hdr = NULL;
char *sminfop;
if (bios_efiinfo != NULL && bios_efiinfo->config_smbios != 0)
hdr = smbios_find(PMAP_DIRECT_MAP(
(uint8_t *)bios_efiinfo->config_smbios));
if (hdr == NULL) {
for (p = ISA_HOLE_VADDR(SMBIOS_START);
p < (uint8_t *)ISA_HOLE_VADDR(SMBIOS_END); p+= 16) {
hdr = smbios_find(p);
if (hdr != NULL)
break;
}
}
if (hdr != NULL) {
pa = trunc_page(hdr->addr);
end = round_page(hdr->addr + hdr->size);
va = (vaddr_t)km_alloc(end - pa, &kv_any, &kp_none, &kd_nowait);
if (va == 0)
goto out;
smbios_entry.addr = (uint8_t *)(va + (hdr->addr & PGOFSET));
smbios_entry.len = hdr->size;
smbios_entry.mjr = hdr->majrev;
smbios_entry.min = hdr->minrev;
smbios_entry.count = hdr->count;
for (; pa < end; pa+= NBPG, va+= NBPG)
pmap_kenter_pa(va, pa, PROT_READ);
printf(": SMBIOS rev. %d.%d @ 0x%x (%d entries)",
hdr->majrev, hdr->minrev, hdr->addr, hdr->count);
smbiosrev = hdr->majrev * 100 + hdr->minrev;
if (hdr->minrev < 10)
smbiosrev = hdr->majrev * 100 + hdr->minrev * 10;
bios.cookie = 0;
if (smbios_find_table(SMBIOS_TYPE_BIOS, &bios)) {
sb = bios.tblhdr;
printf("\n%s:", sc->sc_dev.dv_xname);
if ((smbios_get_string(&bios, sb->vendor,
scratch, sizeof(scratch))) != NULL)
printf(" vendor %s",
fixstring(scratch));
if ((smbios_get_string(&bios, sb->version,
scratch, sizeof(scratch))) != NULL) {
sminfop = fixstring(scratch);
if (sminfop != NULL) {
strlcpy(smbios_bios_version,
sminfop,
sizeof(smbios_bios_version));
printf(" version \"%s\"", sminfop);
}
}
if ((smbios_get_string(&bios, sb->release,
scratch, sizeof(scratch))) != NULL) {
sminfop = fixstring(scratch);
if (sminfop != NULL) {
strlcpy(smbios_bios_date,
sminfop,
sizeof(smbios_bios_date));
printf(" date %s", sminfop);
}
}
}
smbios_info(sc->sc_dev.dv_xname);
}
out:
printf("\n");
if (smbiosrev == 0) {
const char *signature = "Soekris Engineering";
for (p = ISA_HOLE_VADDR(SMBIOS_START);
p <= (uint8_t *)ISA_HOLE_VADDR(SMBIOS_END -
(strlen(signature) - 1)); p++)
if (!memcmp(p, signature, strlen(signature))) {
hw_vendor = malloc(strlen(signature) + 1,
M_DEVBUF, M_NOWAIT);
if (hw_vendor)
strlcpy(hw_vendor, signature,
strlen(signature) + 1);
p += strlen(signature);
break;
}
for (; hw_vendor &&
p <= (uint8_t *)ISA_HOLE_VADDR(SMBIOS_END - 6); p++)
if (!memcmp(p, "net6501", 7)) {
hw_prod = malloc(8, M_DEVBUF, M_NOWAIT);
if (hw_prod) {
memcpy(hw_prod, p, 7);
hw_prod[7] = '\0';
}
break;
}
}
#if NEFI > 0
if (bios_efiinfo != NULL) {
struct bios_attach_args ba;
memset(&ba, 0, sizeof(ba));
ba.ba_name = "efi";
ba.ba_memt = X86_BUS_SPACE_MEM;
config_found(self, &ba, bios_print);
}
#endif
#if NACPI > 0
{
struct bios_attach_args ba;
memset(&ba, 0, sizeof(ba));
ba.ba_name = "acpi";
ba.ba_iot = X86_BUS_SPACE_IO;
ba.ba_memt = X86_BUS_SPACE_MEM;
if (bios_efiinfo != NULL)
ba.ba_acpipbase = bios_efiinfo->config_acpi;
config_found(self, &ba, bios_print);
}
#endif
#if NMPBIOS > 0
if (mpbios_probe(self)) {
struct bios_attach_args ba;
memset(&ba, 0, sizeof(ba));
ba.ba_name = "mpbios";
ba.ba_iot = X86_BUS_SPACE_IO;
ba.ba_memt = X86_BUS_SPACE_MEM;
config_found(self, &ba, bios_print);
}
#endif
}
struct smbhdr *
smbios_find(uint8_t *p)
{
struct smbhdr *hdr = (struct smbhdr *)p;
uint8_t chksum;
int i;
if (hdr->sig != SMBIOS_SIGNATURE)
return (NULL);
i = hdr->len;
for (chksum = 0; i--; chksum += p[i])
;
if (chksum != 0)
return (NULL);
p += 0x10;
if (!(p[0] == '_' && p[1] == 'D' && p[2] == 'M' && p[3] == 'I' &&
p[4] == '_'))
return (NULL);
for (chksum = 0, i = 0xf; i--; chksum += p[i])
;
if (chksum != 0)
return (NULL);
return (hdr);
}
int
smbios_find_table(uint8_t type, struct smbtable *st)
{
uint8_t *va, *end;
struct smbtblhdr *hdr;
int ret = 0, tcount = 1;
va = smbios_entry.addr;
end = va + smbios_entry.len;
if ((st->cookie & 0xfff) == type && st->cookie >> 16) {
if ((uint8_t *)st->hdr >= va && (uint8_t *)st->hdr < end) {
hdr = st->hdr;
if (hdr->type == type) {
va = (uint8_t *)hdr + hdr->size;
for (; va + 1 < end; va++)
if (*va == 0 && *(va + 1) == 0)
break;
va += 2;
tcount = st->cookie >> 16;
}
}
}
for (; va + sizeof(struct smbtblhdr) < end &&
tcount <= smbios_entry.count; tcount++) {
hdr = (struct smbtblhdr *)va;
if (hdr->type == type) {
ret = 1;
st->hdr = hdr;
st->tblhdr = va + sizeof(struct smbtblhdr);
st->cookie = (tcount + 1) << 16 | type;
break;
}
if (hdr->type == SMBIOS_TYPE_EOT)
break;
va += hdr->size;
for (; va + 1 < end; va++)
if (*va == 0 && *(va + 1) == 0)
break;
va += 2;
}
return ret;
}
char *
smbios_get_string(struct smbtable *st, uint8_t indx, char *dest, size_t len)
{
uint8_t *va, *end;
char *ret = NULL;
int i;
va = (uint8_t *)st->hdr + st->hdr->size;
end = smbios_entry.addr + smbios_entry.len;
for (i = 1; va < end && i < indx && *va; i++)
while (*va++)
;
if (i == indx) {
if (va + len < end) {
ret = dest;
memcpy(ret, va, len);
ret[len - 1] = '\0';
}
}
return ret;
}
char *
fixstring(char *s)
{
char *p, *e;
int i;
for (i = 0; i < nitems(smbios_uninfo); i++)
if ((strncasecmp(s, smbios_uninfo[i],
strlen(smbios_uninfo[i]))) == 0)
return NULL;
for (p = s; *p == ' '; p++)
;
if (p == s + strlen(s))
return NULL;
for (e = s + strlen(s) - 1; e > s && *e == ' '; e--)
;
if (p > s || e < s + strlen(s) - 1) {
memmove(s, p, e - p + 1);
s[e - p + 1] = '\0';
}
return s;
}
void
smbios_info(char *str)
{
char *sminfop, sminfo[64];
struct smbtable stbl, btbl;
struct smbios_sys *sys;
struct smbios_board *board;
int i, infolen, uuidf, havebb;
char *p;
if (smbios_entry.mjr < 2)
return;
stbl.cookie = btbl.cookie = 0;
if (!smbios_find_table(SMBIOS_TYPE_SYSTEM, &stbl))
return;
havebb = smbios_find_table(SMBIOS_TYPE_BASEBOARD, &btbl);
sys = (struct smbios_sys *)stbl.tblhdr;
if (havebb) {
board = (struct smbios_board *)btbl.tblhdr;
sminfop = NULL;
if ((p = smbios_get_string(&btbl, board->vendor,
sminfo, sizeof(sminfo))) != NULL)
sminfop = fixstring(p);
if (sminfop)
strlcpy(smbios_board_vendor, sminfop,
sizeof(smbios_board_vendor));
sminfop = NULL;
if ((p = smbios_get_string(&btbl, board->product,
sminfo, sizeof(sminfo))) != NULL)
sminfop = fixstring(p);
if (sminfop)
strlcpy(smbios_board_prod, sminfop,
sizeof(smbios_board_prod));
sminfop = NULL;
if ((p = smbios_get_string(&btbl, board->serial,
sminfo, sizeof(sminfo))) != NULL)
sminfop = fixstring(p);
if (sminfop)
strlcpy(smbios_board_serial, sminfop,
sizeof(smbios_board_serial));
}
sminfop = NULL;
if ((p = smbios_get_string(&stbl, sys->vendor, sminfo,
sizeof(sminfo))) != NULL)
sminfop = fixstring(p);
if (sminfop == NULL) {
if (havebb) {
if ((p = smbios_get_string(&btbl, board->vendor,
sminfo, sizeof(sminfo))) != NULL)
sminfop = fixstring(p);
}
}
if (sminfop) {
infolen = strlen(sminfop) + 1;
hw_vendor = malloc(infolen, M_DEVBUF, M_NOWAIT);
if (hw_vendor)
strlcpy(hw_vendor, sminfop, infolen);
sminfop = NULL;
}
if ((p = smbios_get_string(&stbl, sys->product, sminfo,
sizeof(sminfo))) != NULL)
sminfop = fixstring(p);
if (sminfop == NULL) {
if (havebb) {
if ((p = smbios_get_string(&btbl, board->product,
sminfo, sizeof(sminfo))) != NULL)
sminfop = fixstring(p);
}
}
if (sminfop) {
infolen = strlen(sminfop) + 1;
hw_prod = malloc(infolen, M_DEVBUF, M_NOWAIT);
if (hw_prod)
strlcpy(hw_prod, sminfop, infolen);
sminfop = NULL;
}
if (hw_vendor != NULL && hw_prod != NULL)
printf("\n%s: %s %s", str, hw_vendor, hw_prod);
if ((p = smbios_get_string(&stbl, sys->version, sminfo,
sizeof(sminfo))) != NULL)
sminfop = fixstring(p);
if (sminfop) {
infolen = strlen(sminfop) + 1;
hw_ver = malloc(infolen, M_DEVBUF, M_NOWAIT);
if (hw_ver)
strlcpy(hw_ver, sminfop, infolen);
sminfop = NULL;
}
if ((p = smbios_get_string(&stbl, sys->serial, sminfo,
sizeof(sminfo))) != NULL)
sminfop = fixstring(p);
if (sminfop) {
infolen = strlen(sminfop) + 1;
for (i = 0; i < infolen - 1; i++)
enqueue_randomness(sminfop[i]);
hw_serial = malloc(infolen, M_DEVBUF, M_NOWAIT);
if (hw_serial)
strlcpy(hw_serial, sminfop, infolen);
}
if (smbios_entry.mjr > 2 || (smbios_entry.mjr == 2 &&
smbios_entry.min >= 1)) {
uuidf = SMBIOS_UUID_NPRESENT|SMBIOS_UUID_NSET;
for (i = 0; i < sizeof(sys->uuid); i++) {
if (sys->uuid[i] != 0xff)
uuidf &= ~SMBIOS_UUID_NSET;
if (sys->uuid[i] != 0)
uuidf &= ~SMBIOS_UUID_NPRESENT;
}
if (uuidf & SMBIOS_UUID_NPRESENT)
hw_uuid = NULL;
else if (uuidf & SMBIOS_UUID_NSET)
hw_uuid = "Not Set";
else {
for (i = 0; i < sizeof(sys->uuid); i++)
enqueue_randomness(sys->uuid[i]);
hw_uuid = malloc(SMBIOS_UUID_REPLEN, M_DEVBUF,
M_NOWAIT);
if (hw_uuid) {
snprintf(hw_uuid, SMBIOS_UUID_REPLEN,
SMBIOS_UUID_REP,
sys->uuid[0], sys->uuid[1], sys->uuid[2],
sys->uuid[3], sys->uuid[4], sys->uuid[5],
sys->uuid[6], sys->uuid[7], sys->uuid[8],
sys->uuid[9], sys->uuid[10], sys->uuid[11],
sys->uuid[12], sys->uuid[13], sys->uuid[14],
sys->uuid[15]);
}
}
}
}
int
bios_print(void *aux, const char *pnp)
{
struct bios_attach_args *ba = aux;
if (pnp)
printf("%s at %s", ba->ba_name, pnp);
return (UNCONF);
}