#!/usr/bin/env node /* This builds a js file containing the addresses of a chip's PERIPHERALS and the values of the bits in each register. Can be used for quickly building ways to access the underlying hardware from JS. This has only been tested with STM32 boards so far */ // =================================================================================== // These files are the source of information for the JS files - change depending on STM32F1/F4 chip //var INPUTFILE = "targetlibs/stm32f1/lib/stm32f10x.h"; //var DEFINITIONS = {"STM32F10X_XL":true}; //var INPUTFILE = "targetlibs/stm32f4/lib/stm32f411xe.h"; //var DEFINITIONS = {"STM32F401xx":true}; var INPUTFILE = "targetlibs/nrf5x_12/components/device/nrf52.h"; var DEFINITIONS = {"STM32F401xx":true}; // The PERIPHERALS you need to output code for //var PERIPHERALS = ["RCC","RTC","PWR"]; var PERIPHERALS = []; var OUTPUT_GHIDRA = "nrf52.pspec"; // =================================================================================== var fs = require('fs'); var structContents = undefined; var structSize = 0; var typeSizes = { "uint32_t" : 4, "int32_t" : 4, "uint16_t" : 2, "uint8_t" : 1, "CAN_FIFOMailBox_TypeDef" : NaN, "CAN_TxMailBox_TypeDef" : NaN, "CAN_FilterRegister_TypeDef" : NaN, }; var ifDefs = []; var structs = {}; ///< structures representing periphgerals var periphs = {}; ///< Defined PERIPHERALS { name : ..., type : ..., base : ... } var header = fs.readFileSync(INPUTFILE).toString().replace(/(\/\*[^\n*]*)\r\n(.*\*\/)/g,"$1$2"); header = header.replace(/\/\*[^\*]*\*\//g,""); // remove comments header.split("\n").forEach(function(oline) { //console.log(JSON.stringify(oline)); var line = oline.trim(); if (line=="") return; if (line.indexOf("#ifndef ")==0) { ifDefs.push(!(line.substr(8).trim() in DEFINITIONS)); return; } else if (line.indexOf("#ifdef ")==0) { ifDefs.push((line.substr(7).trim() in DEFINITIONS)); return; } else if (line.indexOf("#if ")==0) { console.warn("Uh-oh, we have a #if - assuming it is true:"+line); ifDefs.push(true); return; } else if (line.indexOf("#else")==0) { ifDefs[ifDefs.length-1] = !ifDefs[ifDefs.length-1]; return; } else if (line.indexOf("#endif")==0) { ifDefs.pop(); return; } for (var i in ifDefs) if (!ifDefs[i]) return; if (line.indexOf("#error ")==0) { console.warn(line); } if (line.indexOf("#define")==0) { line = line.substr(7).trim(); if (line.includes("*")) { var m = line.match(/(\w*)\s*\(\((\w*)\s*\*\)\s*(\w*)\)/); if (m) periphs[m[1]] = { name : m[1], type : m[2], base : m[3] }; } else { var i = line.indexOf(" "); var dkey = line.substr(0,i); var dval = line.substr(i).trim(); dval = dval.replace(/\(uint32_t[ ]*\)/,""); dval = dval.replace(/UL$/,""); console.log(dkey,dval); DEFINITIONS[dkey] = dval; } } else if (line=="typedef struct" || line=="typedef struct {") { structContents = {}; structSize = 0; } else if (line[0]=="{") { } else if (line[0]=="}") { if (line.indexOf("_TypeDef;")>=0) { var name = line.substring(line.lastIndexOf(" ")+1,line.indexOf("_TypeDef;")); structs[name] = structContents; typeSizes[name] = structSize; } if (line.indexOf("_Type;")>=0) { var name = line.substring(line.lastIndexOf(" ")+1,line.indexOf("_Type;")+5); structs[name] = structContents; typeSizes[name] = structSize; } structContents = undefined; structSize = 0; } else if (structContents) { if (line.substr(0,2)!="/*") { if (line.substr(0,2)=="__") line=line.substr(line.indexOf(" ")+1).trim(); var type = line.substr(0,line.indexOf(" ")); line = line.substr(line.indexOf(" ")+1).trim(); var name = line.substr(0,line.indexOf(";")); line = line.substr(line.indexOf(";")+1).trim(); if (!(type in typeSizes)) { console.log(typeSizes); throw new Error("Unknown type "+type+" in "+JSON.stringify(oline)); } var size = typeSizes[type]; var elSize = size; if (name.indexOf("[")>=0) { var c = name.substring(name.indexOf("[")+1, name.indexOf("]")); size *= c; } structContents[name] = { offset : structSize, size : size, elSize : elSize }; structSize += size; } } }); //console.log(structs) // find base addresses var bases = {}; for (var def in DEFINITIONS) { if (def.substr(-5)=="_BASE") { var v = DEFINITIONS[def]. replace(/(\()([A-Z])/,"$1 $2"). replace(/([A-Z])(\))/,"$1 $2"). split(" "); v = v.map(function(tk) { if (tk in bases) return bases[tk]; return tk; }); bases[def] = eval(v.join("").replace(/UL$/,"")); } } function out(s) { console.log(s); } //console.log(periphs); PERIPHERALS.forEach(function(periph) { var base = bases[periph+"_BASE"]; var data = { a : {}, f : {} }; var periphType = periph.replace(/[0-9]/g,""); var periphStruct = structs[periphType]; for (var key in periphStruct) { if (key.substr(0,8)=="RESERVED") continue; // address data.a[key] = base+periphStruct[key].offset; // flags var pref = periph+"_"+key+"_"; for (var d in DEFINITIONS) { if (d.substr(0,pref.length)==pref) { if (!data.f[key]) data.f[key] = {}; try { data.f[key][d.substr(pref.length)] = eval(DEFINITIONS[d]); } catch (e) {} } } } // output out("var "+periph+" = "+JSON.stringify(data,null,2)+";"); }); if (OUTPUT_GHIDRA) { console.log("Outputting Ghidra XML -> "+OUTPUT_GHIDRA); var xml = ` `; function sym(name,addr) { xml += ` \n`; } Object.keys(periphs).forEach(function(periphName) { //console.log(periphName); periph = periphs[periphName]; var periphStruct = structs[periph.type]; var periphBase = bases[periph.base]; //console.log(periphStruct, periphBase); sym(periphName, periphBase); for (var key in periphStruct) { if (key.substr(0,8)=="RESERVED") continue; // address var addr = periphBase+periphStruct[key].offset; // flags var l = key.match(/([^\[]*)\[(\d+)\]/); if (l) { var name = l[1]; var n = 0|l[2]; for (var a=0;a \n`; fs.writeFileSync(OUTPUT_GHIDRA, xml); }