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