summaryrefslogtreecommitdiffstats
path: root/lib/smbios-parser.c
blob: b89f988ef9f3150e49794f248e71130ddb01990c (plain)
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
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2020, Bachmann electronic GmbH
 */

#include <common.h>
#include <smbios.h>

static inline int verify_checksum(const struct smbios_entry *e)
{
	/*
	 * Checksums for SMBIOS tables are calculated to have a value, so that
	 * the sum over all bytes yields zero (using unsigned 8 bit arithmetic).
	 */
	u8 *byte = (u8 *)e;
	u8 sum = 0;

	for (int i = 0; i < e->length; i++)
		sum += byte[i];

	return sum;
}

const struct smbios_entry *smbios_entry(u64 address, u32 size)
{
	const struct smbios_entry *entry = (struct smbios_entry *)(uintptr_t)address;

	if (!address | !size)
		return NULL;

	if (memcmp(entry->anchor, "_SM_", 4))
		return NULL;

	if (verify_checksum(entry))
		return NULL;

	return entry;
}

static const struct smbios_header *next_header(const struct smbios_header *curr)
{
	u8 *pos = ((u8 *)curr) + curr->length;

	/* search for _double_ NULL bytes */
	while (!((*pos == 0) && (*(pos + 1) == 0)))
		pos++;

	/* step behind the double NULL bytes */
	pos += 2;

	return (struct smbios_header *)pos;
}

const struct smbios_header *smbios_header(const struct smbios_entry *entry, int type)
{
	const unsigned int num_header = entry->struct_count;
	const struct smbios_header *header = (struct smbios_header *)entry->struct_table_address;

	for (unsigned int i = 0; i < num_header; i++) {
		if (header->type == type)
			return header;

		header = next_header(header);
	}

	return NULL;
}

static const char *string_from_smbios_table(const struct smbios_header *header,
                                           int idx)
{
	unsigned int i = 1;
	u8 *pos;

	if (!header)
		return NULL;

	pos = ((u8 *)header) + header->length;

	while (i < idx) {
		if (*pos == 0x0)
			i++;

		pos++;
	}

	return (const char *)pos;
}

const char *smbios_string(const struct smbios_header *header, int index)
{
	if (!header)
		return NULL;

	return string_from_smbios_table(header, index);
}