static caddr_t smbios_sigsearch(const caddr_t addr, const uint32_t len) { caddr_t cp; /* Search on 16-byte boundaries. */ for (cp = addr; cp < addr + len; cp += SMBIOS_STEP) if (strncmp(cp, SMBIOS_SIG, 4) == 0 && smbios_checksum(cp, SMBIOS_GET8(cp, 0x05)) == 0 && strncmp(cp + 0x10, SMBIOS_DMI_SIG, 5) == 0 && smbios_checksum(cp + 0x10, 0x0f) == 0) return (cp); return (NULL); }
unsigned long smbios_write_tables(unsigned long current) { struct smbios_entry *se; unsigned long tables; int len = 0; int max_struct_size = 0; int handle = 0; current = ALIGN(current, 16); printk(BIOS_DEBUG, "%s: %08lx\n", __func__, current); se = (struct smbios_entry *)current; current += sizeof(struct smbios_entry); current = ALIGN(current, 16); tables = current; update_max(len, max_struct_size, smbios_write_type0(¤t, handle++)); update_max(len, max_struct_size, smbios_write_type1(¤t, handle++)); update_max(len, max_struct_size, smbios_write_type2(¤t, handle++)); update_max(len, max_struct_size, smbios_write_type3(¤t, handle++)); update_max(len, max_struct_size, smbios_write_type4(¤t, handle++)); update_max(len, max_struct_size, smbios_write_type11(¤t, &handle)); #if CONFIG_ELOG update_max(len, max_struct_size, elog_smbios_write_type15(¤t, handle++)); #endif update_max(len, max_struct_size, smbios_write_type17(¤t, &handle)); update_max(len, max_struct_size, smbios_write_type32(¤t, handle++)); update_max(len, max_struct_size, smbios_walk_device_tree(all_devices, &handle, ¤t)); update_max(len, max_struct_size, smbios_write_type127(¤t, handle++)); memset(se, 0, sizeof(struct smbios_entry)); memcpy(se->anchor, "_SM_", 4); se->length = sizeof(struct smbios_entry); se->major_version = 2; se->minor_version = 7; se->max_struct_size = max_struct_size; se->struct_count = handle; memcpy(se->intermediate_anchor_string, "_DMI_", 5); se->struct_table_address = (u32)tables; se->struct_table_length = len; se->intermediate_checksum = smbios_checksum((u8 *)se + 0x10, sizeof(struct smbios_entry) - 0x10); se->checksum = smbios_checksum((u8 *)se, sizeof(struct smbios_entry)); return current; }
/* * Common code to copy out the SMBIOS snapshot used for both read and mmap. * The caller must validate uio_offset for us since semantics differ there. * The copy is done in two stages, either of which can be skipped based on the * offset and length: first we copy the entry point, with 'staddr' recalculated * to indicate the offset of the data buffer, and second we copy the table. */ static int smb_uiomove(smb_clone_t *cp, uio_t *uio) { off_t off = uio->uio_offset; size_t len = uio->uio_resid; int err = 0; if (off + len > cp->c_eplen + cp->c_stlen) len = cp->c_eplen + cp->c_stlen - off; if (off < cp->c_eplen) { smbios_entry_t *ep = kmem_zalloc(cp->c_eplen, KM_SLEEP); size_t eprlen = MIN(len, cp->c_eplen - off); smbios_info_smbios(cp->c_hdl, ep); ep->smbe_staddr = (uint32_t)cp->c_eplen; smbios_checksum(cp->c_hdl, ep); err = uiomove((char *)ep + off, eprlen, UIO_READ, uio); kmem_free(ep, cp->c_eplen); off += eprlen; len -= eprlen; } if (err == 0 && off >= cp->c_eplen) { char *buf = (char *)smbios_buf(cp->c_hdl); size_t bufoff = off - cp->c_eplen; err = uiomove(buf + bufoff, MIN(len, cp->c_stlen - bufoff), UIO_READ, uio); } return (err); }
int smbios_write(smbios_hdl_t *shp, int fd) { smbios_entry_t ep; off64_t off = lseek64(fd, 0, SEEK_CUR) + P2ROUNDUP(sizeof (ep), 16); if (off > UINT32_MAX) return (smb_set_errno(shp, EOVERFLOW)); bcopy(&shp->sh_ent, &ep, sizeof (ep)); ep.smbe_staddr = (uint32_t)off; smbios_checksum(shp, &ep); if (smbios_xwrite(shp, fd, &ep, sizeof (ep)) == -1 || lseek64(fd, off, SEEK_SET) != off || smbios_xwrite(shp, fd, shp->sh_buf, shp->sh_buflen) == -1) return (-1); return (0); }
smbios_hdl_t * smbios_bufopen(const smbios_entry_t *ep, const void *buf, size_t len, int version, int flags, int *errp) { smbios_hdl_t *shp = smb_zalloc(sizeof (smbios_hdl_t)); const smb_header_t *hp, *nhp; const uchar_t *p, *q, *s; uint_t i, h; switch (version) { case SMB_VERSION_23: case SMB_VERSION_24: case SMB_VERSION_25: case SMB_VERSION_26: case SMB_VERSION_27: case SMB_VERSION_28: break; default: return (smb_open_error(shp, errp, ESMB_VERSION)); } if (ep == NULL || buf == NULL || len == 0 || (flags & ~SMB_O_MASK)) return (smb_open_error(shp, errp, ESMB_INVAL)); if (shp == NULL) return (smb_open_error(shp, errp, ESMB_NOMEM)); if (_smb_debug) shp->sh_flags |= SMB_FL_DEBUG; if (strncmp(ep->smbe_eanchor, SMB_ENTRY_EANCHOR, SMB_ENTRY_EANCHORLEN)) return (smb_open_error(shp, errp, ESMB_HEADER)); if (strncmp(ep->smbe_ianchor, SMB_ENTRY_IANCHOR, SMB_ENTRY_IANCHORLEN)) return (smb_open_error(shp, errp, ESMB_HEADER)); smb_dprintf(shp, "opening SMBIOS version %u.%u bcdrev 0x%x\n", ep->smbe_major, ep->smbe_minor, ep->smbe_bcdrev); if (!(flags & SMB_O_NOVERS)) { if (ep->smbe_major > SMB_MAJOR(SMB_VERSION)) return (smb_open_error(shp, errp, ESMB_NEW)); if (ep->smbe_major < SMB_MAJOR(SMB_VERSION_23) || ( ep->smbe_major == SMB_MAJOR(SMB_VERSION_23) && ep->smbe_minor < SMB_MINOR(SMB_VERSION_23))) return (smb_open_error(shp, errp, ESMB_OLD)); } if (len < sizeof (smb_header_t) || ep->smbe_stlen < sizeof (smb_header_t) || len < ep->smbe_stlen) return (smb_open_error(shp, errp, ESMB_SHORT)); if (!(flags & SMB_O_NOCKSUM)) { uint8_t esum = 0, isum = 0; q = (uchar_t *)ep; for (p = q; p < q + ep->smbe_elen; p++) esum += *p; for (p = (uchar_t *)ep->smbe_ianchor; p < q + sizeof (*ep); p++) isum += *p; if (esum != 0 || isum != 0) { smb_dprintf(shp, "bad cksum: e=%x i=%x\n", esum, isum); return (smb_open_error(shp, errp, ESMB_CKSUM)); } } /* * Copy the entry point into our handle. The underlying entry point * may be larger than our structure definition, so reset smbe_elen * to our internal size and recompute good checksums for our copy. */ bcopy(ep, &shp->sh_ent, sizeof (smbios_entry_t)); shp->sh_ent.smbe_elen = sizeof (smbios_entry_t); smbios_checksum(shp, &shp->sh_ent); shp->sh_buf = buf; shp->sh_buflen = len; shp->sh_structs = smb_alloc(sizeof (smb_struct_t) * ep->smbe_stnum); shp->sh_nstructs = 0; shp->sh_hashlen = _smb_hashlen; shp->sh_hash = smb_zalloc(sizeof (smb_struct_t *) * shp->sh_hashlen); shp->sh_libvers = version; shp->sh_smbvers = SMB_MAJMIN(ep->smbe_major, ep->smbe_minor); if (shp->sh_structs == NULL || shp->sh_hash == NULL) return (smb_open_error(shp, errp, ESMB_NOMEM)); hp = shp->sh_buf; q = (const uchar_t *)buf + MIN(ep->smbe_stlen, len); for (i = 0; i < ep->smbe_stnum; i++, hp = nhp) { smb_struct_t *stp = &shp->sh_structs[i]; uint_t n = 0; if ((const uchar_t *)hp + sizeof (smb_header_t) > q) return (smb_open_error(shp, errp, ESMB_CORRUPT)); smb_dprintf(shp, "struct [%u] type %u len %u hdl %u at %p\n", i, hp->smbh_type, hp->smbh_len, hp->smbh_hdl, (void *)hp); if (hp->smbh_type == SMB_TYPE_EOT) break; /* ignore any entries beyond end-of-table */ if ((const uchar_t *)hp + hp->smbh_len > q - 2) return (smb_open_error(shp, errp, ESMB_CORRUPT)); h = hp->smbh_hdl & (shp->sh_hashlen - 1); p = s = (const uchar_t *)hp + hp->smbh_len; while (p <= q - 2 && (p[0] != '\0' || p[1] != '\0')) { if (*p++ == '\0') n++; /* count strings until \0\0 delimiter */ } if (p > q - 2) return (smb_open_error(shp, errp, ESMB_CORRUPT)); if (p > s) n++; /* add one for final string in string table */ stp->smbst_hdr = hp; stp->smbst_str = s; stp->smbst_end = p; stp->smbst_next = shp->sh_hash[h]; stp->smbst_strtab = smb_alloc(sizeof (uint16_t) * n); stp->smbst_strtablen = n; if (n != 0 && stp->smbst_strtab == NULL) return (smb_open_error(shp, errp, ESMB_NOMEM)); shp->sh_hash[h] = stp; nhp = (void *)(p + 2); shp->sh_nstructs++; for (n = 0, p = s; n < stp->smbst_strtablen; p++) { if (*p == '\0') { stp->smbst_strtab[n++] = (uint16_t)(s - stp->smbst_str); s = p + 1; } } } if (flags & SMB_O_ZIDS) smb_strip(shp); return (shp); }