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