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 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 * 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 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 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