1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "efi-string.h"
#include "smbios.h"
#include "util.h"
#define SMBIOS_TABLE_GUID \
GUID_DEF(0xeb9d2d31, 0x2d88, 0x11d3, 0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d)
#define SMBIOS3_TABLE_GUID \
GUID_DEF(0xf2fd1544, 0x9794, 0x4a2c, 0x99, 0x2e, 0xe5, 0xbb, 0xcf, 0x20, 0xe3, 0x94)
typedef struct {
uint8_t anchor_string[4];
uint8_t entry_point_structure_checksum;
uint8_t entry_point_length;
uint8_t major_version;
uint8_t minor_version;
uint16_t max_structure_size;
uint8_t entry_point_revision;
uint8_t formatted_area[5];
uint8_t intermediate_anchor_string[5];
uint8_t intermediate_checksum;
uint16_t table_length;
uint32_t table_address;
uint16_t number_of_smbios_structures;
uint8_t smbios_bcd_revision;
} _packed_ SmbiosEntryPoint;
typedef struct {
uint8_t anchor_string[5];
uint8_t entry_point_structure_checksum;
uint8_t entry_point_length;
uint8_t major_version;
uint8_t minor_version;
uint8_t docrev;
uint8_t entry_point_revision;
uint8_t reserved;
uint32_t table_maximum_size;
uint64_t table_address;
} _packed_ Smbios3EntryPoint;
typedef struct {
uint8_t type;
uint8_t length;
uint8_t handle[2];
} _packed_ SmbiosHeader;
typedef struct {
SmbiosHeader header;
uint8_t vendor;
uint8_t bios_version;
uint16_t bios_segment;
uint8_t bios_release_date;
uint8_t bios_size;
uint64_t bios_characteristics;
uint8_t bios_characteristics_ext[2];
} _packed_ SmbiosTableType0;
typedef struct {
SmbiosHeader header;
uint8_t manufacturer;
uint8_t product_name;
uint8_t version;
uint8_t serial_number;
EFI_GUID uuid;
uint8_t wake_up_type;
uint8_t sku_number;
uint8_t family;
} _packed_ SmbiosTableType1;
typedef struct {
SmbiosHeader header;
uint8_t manufacturer;
uint8_t product_name;
uint8_t version;
uint8_t serial_number;
} _packed_ SmbiosTableType2;
typedef struct {
SmbiosHeader header;
uint8_t count;
char contents[];
} _packed_ SmbiosTableType11;
static const void* find_smbios_configuration_table(uint64_t *ret_size) {
assert(ret_size);
const Smbios3EntryPoint *entry3 = find_configuration_table(MAKE_GUID_PTR(SMBIOS3_TABLE));
if (entry3 && memcmp(entry3->anchor_string, "_SM3_", 5) == 0 &&
entry3->entry_point_length <= sizeof(*entry3)) {
*ret_size = entry3->table_maximum_size;
return PHYSICAL_ADDRESS_TO_POINTER(entry3->table_address);
}
const SmbiosEntryPoint *entry = find_configuration_table(MAKE_GUID_PTR(SMBIOS_TABLE));
if (entry && memcmp(entry->anchor_string, "_SM_", 4) == 0 &&
entry->entry_point_length <= sizeof(*entry)) {
*ret_size = entry->table_length;
return PHYSICAL_ADDRESS_TO_POINTER(entry->table_address);
}
*ret_size = 0;
return NULL;
}
static const SmbiosHeader* get_smbios_table(uint8_t type, size_t min_size, uint64_t *ret_size_left) {
uint64_t size;
const uint8_t *p = find_smbios_configuration_table(&size);
if (!p)
goto not_found;
for (;;) {
if (size < sizeof(SmbiosHeader))
goto not_found;
const SmbiosHeader *header = (const SmbiosHeader *) p;
/* End of table. */
if (header->type == 127)
goto not_found;
if (size < header->length)
goto not_found;
if (header->type == type) {
/* Table is smaller than the minimum expected size? Refuse */
if (header->length < min_size)
goto not_found;
if (ret_size_left)
*ret_size_left = size;
return header; /* Yay! */
}
/* Skip over formatted area. */
size -= header->length;
p += header->length;
/* Special case: if there are no strings appended, we'll see two NUL bytes, skip over them */
if (size >= 2 && p[0] == 0 && p[1] == 0) {
size -= 2;
p += 2;
continue;
}
/* Skip over a populated string table. */
bool first = true;
for (;;) {
const uint8_t *e = memchr(p, 0, size);
if (!e)
goto not_found;
if (!first && e == p) {/* Double NUL byte means we've reached the end of the string table. */
p++;
size--;
break;
}
size -= e + 1 - p;
p = e + 1;
first = false;
}
}
not_found:
if (ret_size_left)
*ret_size_left = 0;
return NULL;
}
bool smbios_in_hypervisor(void) {
/* Look up BIOS Information (Type 0). */
const SmbiosTableType0 *type0 = (const SmbiosTableType0 *) get_smbios_table(0, sizeof(SmbiosTableType0), /* ret_size_left= */ NULL);
if (!type0)
return false;
/* Bit 4 of 2nd BIOS characteristics extension bytes indicates virtualization. */
return FLAGS_SET(type0->bios_characteristics_ext[1], 1 << 4);
}
const char* smbios_find_oem_string(const char *name, const char *after) {
uint64_t left;
assert(name);
const SmbiosTableType11 *type11 = (const SmbiosTableType11 *) get_smbios_table(11, sizeof(SmbiosTableType11), &left);
if (!type11)
return NULL;
assert(left >= type11->header.length); /* get_smbios_table() already validated this */
left -= type11->header.length;
for (const char *p = type11->contents, *limit = type11->contents + left; p < limit; ) {
const char *e = memchr(p, 0, limit - p);
if (!e || e == p) /* Double NUL byte means we've reached the end of the OEM strings. */
break;
const char *suffix = startswith8(p, name);
if (suffix && (!after || suffix > after))
return suffix;
p = e + 1;
}
return NULL;
}
static const char* smbios_get_string(const SmbiosHeader *header, size_t nr, uint64_t left) {
const char *s = (const char *) ASSERT_PTR(header);
/* We assume that get_smbios_table() already validated the header size making some superficial sense */
assert(left >= header->length);
s += header->length;
left -= header->length;
size_t index = 1;
for (const char *p = s, *limit = s + left; index <= nr && p < limit; index++) {
const char *e = memchr(p, 0, limit - p);
if (!e || e == p) /* Double NUL byte means we've reached the end of the strings. */
break;
if (index == nr)
return p;
p = e + 1;
}
return NULL;
}
void smbios_raw_info_populate(RawSmbiosInfo *ret_info) {
uint64_t left;
assert(ret_info);
const SmbiosTableType1 *type1 = (const SmbiosTableType1 *) get_smbios_table(1, sizeof(SmbiosTableType1), &left);
if (type1) {
ret_info->manufacturer = smbios_get_string(&type1->header, type1->manufacturer, left);
ret_info->product_name = smbios_get_string(&type1->header, type1->product_name, left);
ret_info->product_sku = smbios_get_string(&type1->header, type1->sku_number, left);
ret_info->family = smbios_get_string(&type1->header, type1->family, left);
} else {
ret_info->manufacturer = NULL;
ret_info->product_name = NULL;
ret_info->product_sku = NULL;
ret_info->family = NULL;
}
const SmbiosTableType2 *type2 = (const SmbiosTableType2 *) get_smbios_table(2, sizeof(SmbiosTableType2), &left);
if (type2) {
ret_info->baseboard_manufacturer = smbios_get_string(&type2->header, type2->manufacturer, left);
ret_info->baseboard_product = smbios_get_string(&type2->header, type2->product_name, left);
} else {
ret_info->baseboard_manufacturer = NULL;
ret_info->baseboard_product = NULL;
}
}
void smbios_raw_info_get_cached(RawSmbiosInfo *ret_info) {
static RawSmbiosInfo info = {};
static bool cached = false;
assert(ret_info);
if (!cached) {
smbios_raw_info_populate(&info);
cached = true;
}
*ret_info = info;
}
|