| 1 | /* |
| 2 | Copyright 2018 Intel Corporation |
| 3 | |
| 4 | Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | you may not use this file except in compliance with the License. |
| 6 | You may obtain a copy of the License at |
| 7 | |
| 8 | http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | |
| 10 | Unless required by applicable law or agreed to in writing, software |
| 11 | distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | See the License for the specific language governing permissions and |
| 14 | limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | try { Object.defineProperty(Array.prototype, "peek", { value: function () { return (this.length > 0 ? this[this.length - 1] : undefined); } }); } catch (e) { } |
| 18 | try { Object.defineProperty(String.prototype, "replaceAll", { value: function replaceAll(oldVal, newVal) { return (this.split(oldVal).join(newVal)); } }); } catch (e) { } |
| 19 | |
| 20 | var RSMB = 1381190978; |
| 21 | var memoryLocation = { 0x1: 'Other', 0x2: 'Unknown', 0x3: 'System Board', 0x4: 'ISA', 0x5: 'EISA', 0x6: 'PCI', 0x7: 'MCA', 0x8: 'PCMCIA', 0x9: 'Proprietary', 0xA: 'NuBus', 0xA0: 'PC-98/C20', 0xA1: 'PC-98/C24', 0xA2: 'PC-98/E', 0xA3: 'PC-98/LB' }; |
| 22 | var wakeReason = ['Reserved', 'Other', 'Unknown', 'APM Timer', 'Modem Ring', 'LAN', 'Power Switch', 'PCI', 'AC Power']; |
| 23 | |
| 24 | // Fill the left with zeros until the string is of a given length |
| 25 | function zeroLeftPad(str, len) |
| 26 | { |
| 27 | if ((len == null) && (typeof (len) != 'number')) { return null; } |
| 28 | if (str == null) str = ''; // If null, this is to generate zero leftpad string |
| 29 | var zlp = ''; |
| 30 | for (var i = 0; i < len - str.length; i++) { zlp += '0'; } |
| 31 | return zlp + str; |
| 32 | } |
| 33 | |
| 34 | function SMBiosTables() |
| 35 | { |
| 36 | this._ObjectID = 'SMBiosTable'; |
| 37 | if (process.platform == 'win32') { |
| 38 | this._marshal = require('_GenericMarshal'); |
| 39 | this._native = this._marshal.CreateNativeProxy("Kernel32.dll"); |
| 40 | |
| 41 | this._native.CreateMethod('EnumSystemFirmwareTables'); |
| 42 | this._native.CreateMethod('GetSystemFirmwareTable'); |
| 43 | } |
| 44 | if (process.platform == 'linux') { |
| 45 | this._canonicalizeData = function _canonicalizeData(data) { |
| 46 | var lines = data.toString().split('Header and Data:\x0A'); |
| 47 | var MemoryStream = require('MemoryStream'); |
| 48 | var ms = new MemoryStream(); |
| 49 | |
| 50 | for (var i = 1; i < lines.length; ++i) { |
| 51 | var tokens = lines[i].split('Strings:\x0A'); |
| 52 | var header = tokens[0].split('\x0A\x0A')[0].replaceAll('\x0A', '').trim().replaceAll(' ', '').replaceAll('\x09', ''); |
| 53 | ms.write(Buffer.from(header, 'hex')); |
| 54 | if (tokens.length > 1) { |
| 55 | var strings = tokens[1].split('\x0A\x0A')[0].split('\x0A'); |
| 56 | var stringsFinal = []; |
| 57 | for (var strx in strings) { |
| 58 | var tmp = strings[strx].trim().replaceAll(' ', '').replaceAll('\x09', ''); |
| 59 | if (tmp && tmp[0] !== '"' && /^[0-9a-fA-F]+$/.test(tmp)) { stringsFinal.push(tmp); } |
| 60 | } |
| 61 | ms.write(Buffer.from(stringsFinal.join(''), 'hex')); |
| 62 | ms.write(Buffer.from('00', 'hex')); |
| 63 | } |
| 64 | else { |
| 65 | ms.write(Buffer.from('0000', 'hex')); |
| 66 | } |
| 67 | } |
| 68 | var retVal = ms.buffer; |
| 69 | retVal.ms = ms; |
| 70 | return (retVal); |
| 71 | }; |
| 72 | } |
| 73 | this._parse = function _parse(SMData) { |
| 74 | var ret = {}; |
| 75 | var pbyte; |
| 76 | var i = 0 |
| 77 | var SMData; |
| 78 | var structcount = 0; |
| 79 | |
| 80 | while (SMData && i < SMData.length) |
| 81 | { |
| 82 | var SMtype = SMData[i]; |
| 83 | var SMlength = SMData[i + 1]; |
| 84 | |
| 85 | if (!ret[SMtype]) { ret[SMtype] = []; } |
| 86 | ret[SMtype].push(SMData.slice(i + 4, i + SMlength)); |
| 87 | if (process.platform == 'win32') { ret[SMtype].peek()._ext = pbyte; } |
| 88 | i += SMlength; |
| 89 | |
| 90 | ret[SMtype].peek()._strings = []; |
| 91 | |
| 92 | while (SMData[i] != 0 && i <= SMData.length) |
| 93 | { |
| 94 | var strstart = i; |
| 95 | |
| 96 | // Start of String, find end of string |
| 97 | while (SMData[i++] != 0 && i <= SMData.length); |
| 98 | try |
| 99 | { |
| 100 | ret[SMtype].peek()._strings.push(SMData.slice(strstart, i).toString().trim()); |
| 101 | } |
| 102 | catch (ee) |
| 103 | { |
| 104 | } |
| 105 | } |
| 106 | i += (ret[SMtype].peek()._strings.length == 0) ? 2 : 1; |
| 107 | ++structcount; |
| 108 | //console.log('End of Table[' + SMtype + ']: ' + i); |
| 109 | } |
| 110 | //console.log('Struct Count = ' + structcount); |
| 111 | return (ret); |
| 112 | }; |
| 113 | this.get = function get(callback) { |
| 114 | if (process.platform == 'win32') { |
| 115 | var size = this._native.GetSystemFirmwareTable(RSMB, 0, 0, 0).Val; |
| 116 | //console.log('Table Size: ' + size); |
| 117 | |
| 118 | var PtrSize = this._marshal.CreatePointer()._size; |
| 119 | var buffer = this._marshal.CreateVariable(size); |
| 120 | var written = this._native.GetSystemFirmwareTable(RSMB, 0, buffer, size).Val; |
| 121 | //console.log('Written Size: ' + written); |
| 122 | |
| 123 | var rawBuffer = buffer.toBuffer(); |
| 124 | var length = buffer.Deref(4, 4).toBuffer().readUInt32LE(0); |
| 125 | |
| 126 | pbyte = buffer.Deref(8, length); |
| 127 | SMData = pbyte.toBuffer(); |
| 128 | |
| 129 | if (callback) { callback.apply(this, [this._parse(SMData)]); return; } else { return (this._parse(SMData)); } |
| 130 | } |
| 131 | if (process.platform == 'linux') { |
| 132 | var MemoryStream = require('MemoryStream'); |
| 133 | this.child = require('child_process').execFile('/usr/sbin/dmidecode', ['dmidecode', '-u']); |
| 134 | this.child.SMBiosTable = this; |
| 135 | this.child.ms = new MemoryStream(); |
| 136 | this.child.ms.callback = callback; |
| 137 | this.child.ms.child = this.child; |
| 138 | this.child.stdout.on('data', function (buffer) { this.parent.ms.write(buffer); }); |
| 139 | this.child.on('exit', function () { this.ms.end(); }); |
| 140 | this.child.ms.on('end', function () { |
| 141 | //console.log('read ' + this.buffer.length + ' bytes'); |
| 142 | if (this.buffer.length < 300) { |
| 143 | //console.log('Not enough permission to read SMBiosTable'); |
| 144 | if (this.callback) { this.callback.apply(this.child.SMBiosTable, []); } |
| 145 | } |
| 146 | else { |
| 147 | var SMData = this.child.SMBiosTable._canonicalizeData(this.buffer); |
| 148 | var j = this.child.SMBiosTable._parse(SMData); |
| 149 | if (this.callback) { this.callback.apply(this.child.SMBiosTable, [j]); } |
| 150 | } |
| 151 | }); |
| 152 | return; |
| 153 | } |
| 154 | if (callback) { callback.apply(this, [null]); return; } else { return (null); } |
| 155 | }; |
| 156 | this.parse = function parse(data) { |
| 157 | var r = {}; |
| 158 | try |
| 159 | { |
| 160 | r.processorInfo = this.processorInfo(data); |
| 161 | } |
| 162 | catch(e) |
| 163 | { |
| 164 | } |
| 165 | try |
| 166 | { |
| 167 | r.memoryInfo = this.memoryInfo(data); |
| 168 | } |
| 169 | catch(e) |
| 170 | { |
| 171 | } |
| 172 | try |
| 173 | { |
| 174 | r.systemInfo = this.systemInfo(data); |
| 175 | } |
| 176 | catch(e) |
| 177 | { |
| 178 | } |
| 179 | try |
| 180 | { |
| 181 | r.systemSlots = this.systemSlots(data); |
| 182 | } |
| 183 | catch(e) |
| 184 | { |
| 185 | } |
| 186 | try |
| 187 | { |
| 188 | r.amtInfo = this.amtInfo(data); |
| 189 | } |
| 190 | catch(e) |
| 191 | { |
| 192 | } |
| 193 | try |
| 194 | { |
| 195 | if (JSON.stringify(r).length > 65535) { r = {}; } |
| 196 | } |
| 197 | catch(ee) |
| 198 | {} |
| 199 | return r; |
| 200 | } |
| 201 | this.processorInfo = function processorInfo(data) { |
| 202 | if (!data) { throw ('no data'); } |
| 203 | var ret = []; |
| 204 | var ptype = ['ERROR', 'Other', 'Unknown', 'CPU', 'ALU', 'DSP', 'GPU']; |
| 205 | var statusString = ['Unknown', 'Enabled', 'Disabled by user', 'Disabled by BIOS', 'Idle', 'Reserved', 'Reserved', 'Other']; |
| 206 | var cpuid = 0; |
| 207 | while (data[4] && data[4].length > 0) { |
| 208 | var p = data[4].pop(); |
| 209 | var populated = p[20] & 0x40; |
| 210 | var status = p[20] & 0x07 |
| 211 | if (populated) { |
| 212 | var j = { _ObjectID: 'SMBiosTables.processorInfo' }; |
| 213 | j.Processor = ptype[p[1]]; |
| 214 | j.MaxSpeed = p.readUInt16LE(16) + ' Mhz'; |
| 215 | if (p[31]) { j.Cores = p[31]; } |
| 216 | if (p[33]) { j.Threads = p[33]; } |
| 217 | j.Populated = 1; |
| 218 | j.Status = statusString[status]; |
| 219 | j.Socket = p._strings[p[0] - 1]; |
| 220 | j.Manufacturer = p._strings[p[3] - 1]; |
| 221 | j.Version = p._strings[p[12] - 1]; |
| 222 | ret.push(j); |
| 223 | } |
| 224 | } |
| 225 | return (ret); |
| 226 | }; |
| 227 | this.memoryInfo = function memoryInfo(data) { |
| 228 | if (!data) { throw ('no data'); } |
| 229 | var retVal = { _ObjectID: 'SMBiosTables.memoryInfo' }; |
| 230 | if (data[16]) { |
| 231 | var m = data[16].peek(); |
| 232 | retVal.location = memoryLocation[m[0]]; |
| 233 | if ((retVal.maxCapacityKb = m.readUInt32LE(3)) == 0x80000000) { |
| 234 | retVal.maxCapacityKb = 'A really big number'; |
| 235 | } |
| 236 | } |
| 237 | return (retVal); |
| 238 | }; |
| 239 | this.systemInfo = function systemInfo(data) |
| 240 | { |
| 241 | if (!data) { throw ('no data'); } |
| 242 | var retVal = { _ObjectID: 'SMBiosTables.systemInfo' }; |
| 243 | if (data[1]) |
| 244 | { |
| 245 | var si = data[1].peek(); |
| 246 | var uuid = si.slice(4, 20); |
| 247 | |
| 248 | retVal.uuid = [zeroLeftPad(uuid.readUInt32LE(0).toString(16), 8), |
| 249 | zeroLeftPad(uuid.readUInt16LE(4).toString(16), 4), |
| 250 | zeroLeftPad(uuid.readUInt16LE(6).toString(16), 4), |
| 251 | zeroLeftPad(uuid.readUInt16BE(8).toString(16), 4), |
| 252 | zeroLeftPad(uuid.slice(10).toString('hex').toLowerCase(), 12)].join('-'); |
| 253 | |
| 254 | retVal.wakeReason = wakeReason[si[20]]; |
| 255 | } |
| 256 | return (retVal); |
| 257 | }; |
| 258 | this.systemSlots = function systemSlots(data) { |
| 259 | if (!data) { throw ('no data'); } |
| 260 | var retVal = []; |
| 261 | if (data[9]) { |
| 262 | while (data[9].length > 0) { |
| 263 | var ss = data[9].pop(); |
| 264 | retVal.push({ name: ss._strings[ss[0] - 1] }); |
| 265 | } |
| 266 | } |
| 267 | return (retVal); |
| 268 | }; |
| 269 | this.amtInfo = function amtInfo(data) { |
| 270 | if (!data) { throw ('no data'); } |
| 271 | var retVal = { AMT: false }; |
| 272 | if (data[130] && data[130].peek().slice(0, 4).toString() == '$AMT') |
| 273 | { |
| 274 | var amt = data[130].peek(); |
| 275 | retVal.AMT = amt[4] ? true : false; |
| 276 | if (retVal.AMT) |
| 277 | { |
| 278 | retVal.enabled = amt[5] ? true : false; |
| 279 | retVal.storageRedirection = amt[6] ? true : false; |
| 280 | retVal.serialOverLan = amt[7] ? true : false; |
| 281 | retVal.kvm = amt[14] ? true : false; |
| 282 | if (data[131].peek() && data[131].peek().slice(52, 56).toString() == 'vPro') |
| 283 | { |
| 284 | var settings = data[131].peek(); |
| 285 | if (settings[0] & 0x04) { retVal.TXT = (settings[0] & 0x08) ? true : false; } |
| 286 | if (settings[0] & 0x10) { retVal.VMX = (settings[0] & 0x20) ? true : false; } |
| 287 | retVal.MEBX = settings.readUInt16LE(4).toString() + '.' + settings.readUInt16LE(6).toString() + '.' + settings.readUInt16LE(8).toString() + '.' + settings.readUInt16LE(10).toString(); |
| 288 | |
| 289 | var mecap = settings.slice(20, 32); |
| 290 | retVal.ManagementEngine = mecap.readUInt16LE(6).toString() + '.' + mecap.readUInt16LE(4).toString() + '.' + mecap.readUInt16LE(10).toString() + '.' + mecap.readUInt16LE(8).toString(); |
| 291 | |
| 292 | //var lan = settings.slice(36, 48); |
| 293 | //console.log(lan.toString('hex')); |
| 294 | //retVal.LAN = (lan.readUInt16LE(10) & 0x03).toString() + '/' + ((lan.readUInt16LE(10) & 0xF8) >> 3).toString(); |
| 295 | |
| 296 | //console.log(lan.readUInt16LE(3)); |
| 297 | //retVal.WLAN = (lan.readUInt16LE(3) & 0x07).toString() + '/' + ((lan.readUInt16LE(3) & 0xF8) >> 3).toString() + '/' + (lan.readUInt16LE(3) >> 8).toString(); |
| 298 | } |
| 299 | } |
| 300 | } |
| 301 | if (!retVal.AMT) |
| 302 | { |
| 303 | if (data[131].peek() && data[131].peek().slice(52, 56).toString() == 'vPro') |
| 304 | { |
| 305 | var settings = data[131].peek(); |
| 306 | if ((settings[20] & 0x08) == 0x08) { retVal.AMT = true; } |
| 307 | } |
| 308 | } |
| 309 | return (retVal); |
| 310 | }; |
| 311 | this.smTableTypes = { |
| 312 | 0: 'BIOS information', |
| 313 | 1: 'System information', |
| 314 | 2: 'Baseboard (or Module) information', |
| 315 | 4: 'Processor information', |
| 316 | 5: 'memory controller information', |
| 317 | 6: 'Memory module information', |
| 318 | 7: 'Cache information', |
| 319 | 8: 'Port connector information', |
| 320 | 9: 'System slots', |
| 321 | 10: 'On board devices information', |
| 322 | 11: 'OEM strings', |
| 323 | 12: 'System configuration options', |
| 324 | 13: 'BIOS language information', |
| 325 | 14: 'Group associations', |
| 326 | 15: 'System event log', |
| 327 | 16: 'Physical memory array', |
| 328 | 17: 'Memory device', |
| 329 | 18: '32bit memory error information', |
| 330 | 19: 'Memory array mapped address', |
| 331 | 20: 'Memory device mapped address', |
| 332 | 21: 'Built-in pointing device', |
| 333 | 22: 'Portable battery', |
| 334 | 23: 'System reset', |
| 335 | 24: 'Hardware security', |
| 336 | 25: 'System power controls', |
| 337 | 26: 'Voltage probe', |
| 338 | 27: 'Cooling device', |
| 339 | 28: 'Temperature probe', |
| 340 | 29: 'Electrical current probe', |
| 341 | 30: 'Out-of-band remote access', |
| 342 | 31: 'Boot integrity services (BIS) entry point', |
| 343 | 32: 'System boot information', |
| 344 | 33: '64bit memory error information', |
| 345 | 34: 'Management device', |
| 346 | 35: 'Management device component', |
| 347 | 36: 'Management device threshold data', |
| 348 | 37: 'Memory channel', |
| 349 | 38: 'IPMI device information', |
| 350 | 39: 'System power supply', |
| 351 | 40: 'Additional information', |
| 352 | 41: 'Onboard devices extended information', |
| 353 | 42: 'Management controller host interface', |
| 354 | 126: 'Inactive', |
| 355 | 127: 'End-of-table' |
| 356 | } |
| 357 | } |
| 358 | |
| 359 | module.exports = new SMBiosTables(); |