xref: /haiku/src/add-ons/kernel/generic/smbios/smbios.cpp (revision 97f2b91169c13d1fde0403013f366c44267915be)
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 
27 static device_manager_info* gDeviceManager;
28 static char* sHardwareVendor = NULL;
29 static char* sHardwareProduct = NULL;
30 
31 struct 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 
52 struct 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 
68 struct 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 
78 enum {
79 	SMBIOS_TYPE_BIOS 	= 0,
80 	SMBIOS_TYPE_SYSTEM,
81 };
82 
83 
84 struct 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 
97 static bool
smbios_match_vendor_product(const char * vendor,const char * product)98 smbios_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 
112 static const char *
smbios_get_string(struct smbios_structure_header * table,uint8 * tableEnd,uint8 index)113 smbios_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 
130 static void
smbios_scan()131 smbios_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);
214 err:
215 	delete_area(smbiosArea);
216 }
217 
218 
219 static status_t
std_ops(int32 op,...)220 std_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 
237 module_dependency module_dependencies[] = {
238 	{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager},
239 	{}
240 };
241 
242 
243 static 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 
254 module_info *modules[] = {
255 	(module_info*)&sSMBIOSModule,
256 	NULL
257 };
258