1/*
2 * Copyright 2020, J��r��me Duval, jerome.duval@gmail.com.
3 * Distributed under the terms of the MIT license.
4 */
5
6
7#include "smbios.h"
8
9#include <device_manager.h>
10#include <KernelExport.h>
11#include <module.h>
12
13#include <stdlib.h>
14#include <string.h>
15
16#include <vm/vm.h>
17
18
19#define TRACE_SMBIOS
20#ifdef TRACE_SMBIOS
21#	define TRACE(x...) dprintf (x)
22#else
23#	define TRACE(x...) ;
24#endif
25
26
27static device_manager_info* gDeviceManager;
28static char* sHardwareVendor = NULL;
29static char* sHardwareProduct = NULL;
30
31struct smbios {
32	uint32		anchor_string;
33	uint8		entry_point_checksum;
34	uint8		entry_point_length;
35	struct {
36		uint8	major;
37		uint8	minor;
38	} version;
39	uint16		maximum_size;
40	uint8		entry_point_revision;
41	uint8		formatted_area[5];
42
43	uint8		dmi_anchor_string[5];
44	uint8		intermediate_checksum;
45	uint16		structure_table_size;
46	uint32		structure_table;
47	uint16		num_structures;
48	uint8		bcd_revision;
49} _PACKED;
50
51
52struct smbios3 {
53	uint8		anchor_string[5];
54	uint8		entry_point_checksum;
55	uint8		entry_point_length;
56	struct {
57		uint8	major;
58		uint8	minor;
59		uint8	doc;
60	} version;
61	uint8		entry_point_revision;
62	uint8		reserved;
63	uint32		structure_table_size;
64	uint64		structure_table;
65} _PACKED;
66
67
68struct smbios_structure_header {
69	uint8		type;
70	uint8		length;
71	uint16		handle;
72} _PACKED;
73
74
75#define SMBIOS	"_SM_"
76#define SMBIOS3	"_SM3_"
77
78enum {
79	SMBIOS_TYPE_BIOS 	= 0,
80	SMBIOS_TYPE_SYSTEM,
81};
82
83
84struct smbios_system {
85	struct smbios_structure_header header;
86	uint8		manufacturer;
87	uint8		product_name;
88	uint8		version;
89	uint8		serial_number;
90	uint8		uuid[16];
91	uint8		wakeup_type;
92	uint8		sku_number;
93	uint8		family;
94} _PACKED;
95
96
97static bool
98smbios_match_vendor_product(const char* vendor, const char* product)
99{
100	if (vendor == NULL && product == NULL)
101		return false;
102
103	bool match = true;
104	if (vendor != NULL && sHardwareVendor != NULL)
105		match = strcmp(vendor, sHardwareVendor) == 0;
106	if (match && product != NULL && sHardwareProduct != NULL)
107		match = strcmp(product, sHardwareProduct) == 0;
108	return match;
109}
110
111
112static const char *
113smbios_get_string(struct smbios_structure_header* table, uint8* tableEnd,
114	uint8 index)
115{
116	uint8* addr = (uint8*)table + table->length;
117	uint8 i = 1;
118	for (; addr < tableEnd && i < index && *addr != 0; i++) {
119		while (*addr != 0 && addr < tableEnd)
120			addr++;
121		addr++;
122	}
123	if (i == index)
124		return (const char*)addr;
125
126	return NULL;
127}
128
129
130static void
131smbios_scan()
132{
133	TRACE("smbios_scan\n");
134	static bool scanDone = false;
135	if (scanDone)
136		return;
137
138	// map SMBIOS area 0xf0000 - 0xfffff
139	addr_t smBiosBase;
140	area_id smbiosArea = map_physical_memory("pc bios", 0xf0000, 0x10000,
141		B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA, (void **)&smBiosBase);
142	if (smbiosArea < 0)
143		return;
144
145	struct smbios *smbios = NULL;
146	struct smbios3 *smbios3 = NULL;
147	for (addr_t offset = 0; offset <= 0xffe0; offset += 0x10) {
148		void* p = (void*)(smBiosBase + offset);
149		if (memcmp(p, SMBIOS3, 5) == 0) {
150			smbios3 = (struct smbios3 *)p;
151			break;
152		} else if (memcmp(p, SMBIOS, 4) == 0) {
153			smbios = (struct smbios *)p;
154		}
155	}
156
157	phys_addr_t tablePhysAddr = 0;
158	size_t tablePhysLength = 0;
159	void* table;
160	status_t status;
161	uint8* tableEnd;
162
163	if (smbios != NULL) {
164		tablePhysAddr = smbios->structure_table;
165		tablePhysLength = smbios->structure_table_size;
166	} else if (smbios3 != NULL) {
167		tablePhysAddr = smbios3->structure_table;
168		tablePhysLength = smbios3->structure_table_size;
169	}
170
171	if (tablePhysAddr == 0)
172		goto err;
173
174	table = malloc(tablePhysLength);
175	if (table == NULL)
176		goto err;
177	status = vm_memcpy_from_physical(table, tablePhysAddr,
178		tablePhysLength, false);
179	if (status != B_OK)
180		goto err;
181
182	tableEnd = (uint8*)table + tablePhysLength;
183	for (uint8* addr = (uint8*)table;
184		(addr + sizeof(struct smbios_structure_header)) < tableEnd;) {
185		struct smbios_structure_header* table
186			= (struct smbios_structure_header*)addr;
187
188		if (table->type == SMBIOS_TYPE_SYSTEM) {
189			struct smbios_system *system = (struct smbios_system*)table;
190			TRACE("found System Information at %p\n", table);
191			TRACE("found vendor %u product %u\n", system->manufacturer,
192				system->product_name);
193			const char* vendor = smbios_get_string(table, tableEnd,
194				system->manufacturer);
195			const char* product = smbios_get_string(table, tableEnd,
196				system->product_name);
197			if (vendor != NULL)
198				sHardwareVendor = strdup(vendor);
199			if (product != NULL)
200				sHardwareProduct = strdup(product);
201			break;
202		}
203		addr += table->length;
204		for (; addr + 1 < tableEnd; addr++) {
205			if (*addr == 0 && *(addr + 1) == 0)
206				break;
207		}
208		addr += 2;
209	}
210
211	scanDone = true;
212	TRACE("smbios_scan found vendor %s product %s\n", sHardwareVendor,
213		sHardwareProduct);
214err:
215	delete_area(smbiosArea);
216}
217
218
219static status_t
220std_ops(int32 op, ...)
221{
222	switch (op) {
223		case B_MODULE_INIT:
224			smbios_scan();
225			return B_OK;
226		case B_MODULE_UNINIT:
227			free(sHardwareVendor);
228			free(sHardwareProduct);
229			return B_OK;
230		default:
231			return B_ERROR;
232	}
233}
234
235
236
237module_dependency module_dependencies[] = {
238	{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager},
239	{}
240};
241
242
243static smbios_module_info sSMBIOSModule = {
244	{
245		SMBIOS_MODULE_NAME,
246		B_KEEP_LOADED,
247		std_ops
248	},
249
250	smbios_match_vendor_product,
251};
252
253
254module_info *modules[] = {
255	(module_info*)&sSMBIOSModule,
256	NULL
257};
258