106 строки
3.0 KiB
JavaScript
106 строки
3.0 KiB
JavaScript
const fs = require("fs")
|
|
const path = require("path")
|
|
const child_process = require("child_process")
|
|
let verbose = 1
|
|
if (process.argv[2] == "-q") {
|
|
process.argv.shift()
|
|
verbose = 0
|
|
}
|
|
const fn = process.argv[2]
|
|
const buf = fs.readFileSync(fn)
|
|
|
|
const flash_size = parseInt(process.argv[3])
|
|
const bl_size = parseInt(process.argv[4])
|
|
const profiles_path = process.argv[5]
|
|
|
|
const DEV_INFO_MAGIC = 0xf6a0e4b6
|
|
|
|
if (isNaN(bl_size) || !profiles_path) {
|
|
throw "USAGE: node patch-bin.js file.elf flash_size_in_k bootloader_size_in_k profiles_path"
|
|
}
|
|
|
|
let pos = 0
|
|
|
|
function log(msg) {
|
|
if (verbose)
|
|
console.log(msg)
|
|
}
|
|
|
|
let w0 = buf.readUInt32LE(0)
|
|
if (w0 == 0x464c457f) {
|
|
const out = child_process.execSync("arm-none-eabi-objdump -h " + fn, { encoding: "utf-8" })
|
|
const m = /^\s*\d+\s+\.text\s.*\s([0-9a-fA-F]{8})\s+\d\*\*/m.exec(out)
|
|
if (!m) {
|
|
throw ("invalid output: " + out)
|
|
}
|
|
pos = parseInt(m[1], 16)
|
|
log("detected ELF file, text at " + pos.toString(16))
|
|
} else {
|
|
log("assuming BIN file")
|
|
}
|
|
|
|
function fnv1a(data) {
|
|
let h = 0x811c9dc5
|
|
for (let i = 0; i < data.length; ++i) {
|
|
h = Math.imul(h ^ data.charCodeAt(i), 0x1000193)
|
|
}
|
|
return h
|
|
}
|
|
|
|
w0 = buf.readUInt32LE(pos)
|
|
if ((w0 & 0xff000000) == 0x20000000) {
|
|
log("app mode")
|
|
|
|
const flashBase = 0x08000000
|
|
|
|
const basename = fn.replace(/\.elf$/, "")
|
|
|
|
if (profiles_path.indexOf("\\_") > 0 || profiles_path.indexOf("/_") > 0)
|
|
throw "folder names cannot start with _"
|
|
|
|
// figure out device class
|
|
const profile_name = basename.replace(/.*\/app-/, "")
|
|
const profile_fn = profiles_path + "/" + profile_name + ".c"
|
|
const src = fs.readFileSync(profile_fn, "utf8")
|
|
const m = /FIRMWARE_IDENTIFIER\((0x3[0-9a-f]+),\s*"([^"]+)"\)/.exec(src)
|
|
if (!m)
|
|
throw "FIRMWARE_IDENTIFIER(0x3..., \"...\") missing"
|
|
let dev_class = parseInt(m[1])
|
|
const dev_class_name = m[2]
|
|
log(`device class: 0x${dev_class.toString(16)} "${dev_class_name}"`)
|
|
|
|
const reset = buf.readUInt32LE(pos + 4)
|
|
const app_reset = buf.readInt32LE(pos + 13 * 4)
|
|
if (app_reset == 0 || app_reset == -1) {
|
|
buf.writeUInt32LE(reset, pos + 13 * 4)
|
|
log("patching app_reset to " + reset.toString(16))
|
|
}
|
|
|
|
const bl_reset_handler = flashBase + (flash_size - bl_size) * 1024 + 8 * 4 + 1
|
|
buf.writeUInt32LE(bl_reset_handler, pos + 4)
|
|
log("setting global reset to " + bl_reset_handler.toString(16))
|
|
|
|
buf.fill(0xff, pos + 7 * 4, pos + (7 + 4) * 4)
|
|
buf.writeUInt32LE(dev_class, pos + 8 * 4)
|
|
log("clearing devinfo area")
|
|
|
|
const vm = /"([^"]+)"/.exec(fs.readFileSync(path.join(path.dirname(fn), "version.c"), "utf8"))
|
|
const fw_version = vm[1]
|
|
|
|
fs.writeFileSync(fn + ".json", JSON.stringify({
|
|
"0xc8a729": dev_class,
|
|
"0x0be9f7": 1024,
|
|
"0x650d9d": dev_class_name,
|
|
"0x9fc7bc": fw_version
|
|
}))
|
|
|
|
} else if (w0 == DEV_INFO_MAGIC) {
|
|
log("setting random seed")
|
|
require("crypto").randomFillSync(buf, pos + 16, 8)
|
|
} else {
|
|
throw "can't detect file type"
|
|
}
|
|
|
|
|
|
fs.writeFileSync(fn, buf)
|