use chip id for all image checks

This commit is contained in:
andreas 2023-09-09 17:11:47 +02:00
parent c87c38fca4
commit f800893ac8
5 changed files with 99 additions and 82 deletions

View File

@ -268,10 +268,7 @@ def prebuild(env):
genereateUserTasks(os.path.join(outPath(), TASK_INCLUDE))
generateFile(os.path.join(basePath(),XDR_FILE),os.path.join(outPath(),XDR_INCLUDE),generateXdrMappings)
version="dev"+datetime.now().strftime("%Y%m%d")
env.Append(CPPDEFINES=[
('GWDEVVERSION',version),
('PIO_ENV_BOARD',env.get('BOARD_MCU'))
])
env.Append(CPPDEFINES=[('GWDEVVERSION',version)])
def cleangenerated(source, target, env):
od=outPath()
@ -293,4 +290,3 @@ env.Append(
)
#script does not run on clean yet - maybe in the future
env.AddPostAction("clean",cleangenerated)
#print(env.Dump())

View File

@ -72,7 +72,7 @@ def main():
tk.Label(frame,textvariable=self.fileInfo).grid(row=row,column=0,columnspan=2,sticky="ew")
row+=1
self.flashInfo=tk.StringVar()
self.flashInfo.set("Address 0x1000")
self.flashInfo.set("Full Flash")
tk.Label(frame,textvariable=self.flashInfo).grid(row=row,column=0,columnspan=2,sticky='ew',pady=10)
row+=1
btFrame=tk.Frame(frame)
@ -96,7 +96,7 @@ def main():
def updateFlashInfo(self):
if self.mode.get() == 1:
#full
self.flashInfo.set("Address 0x1000")
self.flashInfo.set("Full Flash")
else:
self.flashInfo.set("Erase(otadata): 0xe000...0xffff, Address 0x10000")
def changeMode(self):
@ -141,50 +141,67 @@ def main():
self.interrupt=False
raise Exception("User cancel")
FULLOFFSET=61440
UPDATE_ADDR = 0x10000
HDROFFSET = 288
VERSIONOFFSET = 16
NAMEOFFSET = 48
IDOFFSET=12 #2 byte chipid
MINSIZE = HDROFFSET + NAMEOFFSET + 32
CHECKBYTES = {
0: 0xe9, # image magic
288: 0x32, # app header magic
289: 0x54,
290: 0xcd,
291: 0xab
}
#flash addresses for full images based on chip id
FLASH_ADDR={
0: 0x1000,
9: 0
}
def getString(self,buffer, offset, len):
return buffer[offset:offset + len].rstrip(b'\0').decode('utf-8')
def getFirmwareInfo(self,ih,imageFile,offset):
buffer = ih.read(self.MINSIZE)
if len(buffer) != self.MINSIZE:
return self.setErr("invalid image file %s, to short"%imageFile)
for k, v in self.CHECKBYTES.items():
if buffer[k] != v:
return self.setErr("invalid magic at %d, expected %d got %d"
def getFirmwareInfo(self,filename,isFull):
with open(filename,"rb") as ih:
buffer = ih.read(self.MINSIZE)
if len(buffer) != self.MINSIZE:
return self.setErr("invalid image file %s, to short"%filename)
if buffer[0] != 0xe9:
return self.setErr("invalid magic in file, expected 0xe9 got 0x%02x"%buffer[0])
chipid= buffer[self.IDOFFSET]+256*buffer[self.IDOFFSET+1]
flashoffset=self.FLASH_ADDR.get(chipid)
if flashoffset is None:
return self.setErr("unknown chip id in image %d",chipid);
if isFull:
offset=self.UPDATE_ADDR-flashoffset;
offset-=self.MINSIZE
ih.seek(offset,os.SEEK_CUR)
buffer=ih.read(self.MINSIZE)
if len(buffer) != self.MINSIZE:
return self.setErr("invalid image file %s, to short"%filename)
if buffer[0] != 0xe9:
return self.setErr("invalid magic in file, expected 0xe9 got 0x%02x"%buffer[0])
for k, v in self.CHECKBYTES.items():
if buffer[k] != v:
return self.setErr("invalid magic at %d, expected %d got %d"
% (k+offset, v, buffer[k]))
name = self.getString(buffer, self.HDROFFSET + self.NAMEOFFSET, 32)
version = self.getString(buffer, self.HDROFFSET + self.VERSIONOFFSET, 32)
return {'error':False,'info':"%s:%s"%(name,version)}
name = self.getString(buffer, self.HDROFFSET + self.NAMEOFFSET, 32)
version = self.getString(buffer, self.HDROFFSET + self.VERSIONOFFSET, 32)
chipid= buffer[self.IDOFFSET]+256*buffer[self.IDOFFSET+1]
return {
'error':False,
'info':"%s:%s"%(name,version),
'chipid':chipid,
'flashbase':flashoffset if isFull else self.UPDATE_ADDR
}
def setErr(self,err):
return {'error':True,'info':err}
def checkImageFile(self,filename,isFull):
if not os.path.exists(filename):
return self.setErr("file %s not found"%filename)
with open(filename,"rb") as fh:
offset=0
if isFull:
b=fh.read(1)
if len(b) != 1:
return self.setErr("unable to read header")
if b[0] != 0xe9:
return self.setErr("invalid magic in file, expected 0xe9 got 0x%02x"%b[0])
st=fh.seek(self.FULLOFFSET)
offset=self.FULLOFFSET
return self.getFirmwareInfo(fh,filename,offset)
return self.getFirmwareInfo(filename,isFull)
def runCheck(self):
self.text_widget.delete("1.0", "end")
@ -202,7 +219,7 @@ def main():
if info['error']:
print("ERROR: %s" % info['info'])
return
return {'port':port,'isFull':isFull}
return {'port':port,'isFull':isFull,'info':info}
def runEspTool(self,command):
for b in self.actionButtons:
@ -219,25 +236,36 @@ def main():
for b in self.actionButtons:
b.configure(state=tk.NORMAL)
self.cancelButton.configure(state=tk.DISABLED)
def buttonCheck(self):
def verifyChip(self):
param = self.runCheck()
if not param:
print("check failed")
return
print("Settings OK")
command = ['--chip', 'ESP32', '--port', param['port'], 'chip_id']
self.runEspTool(command)
imageChipId=param['info']['chipid']
chip=esptool.ESPLoader.detect_chip(param['port'])
print("Detected chip %s, id=%d"%(chip.CHIP_NAME,chip.IMAGE_CHIP_ID))
if (chip.IMAGE_CHIP_ID != imageChipId):
print("##Error: chip id in image %d does not match detected chip"%imageChipId)
return
print("Checks OK")
param['chipname']=chip.CHIP_NAME
return param
def buttonCheck(self):
param = self.verifyChip()
def buttonFlash(self):
param=self.runCheck()
param=self.verifyChip()
if not param:
return
if param['isFull']:
command=['--chip','ESP32','--port',param['port'],'write_flash','0x1000',self.filename.get()]
command=['--chip',param['chipname'],'--port',param['port'],'write_flash',str(param['info']['flashbase']),self.filename.get()]
self.runEspTool(command)
else:
command=['--chip','ESP32','--port',param['port'],'erase_region','0xe000','0x2000']
command=['--chip',param['chipname'],'--port',param['port'],'erase_region','0xe000','0x2000']
self.runEspTool(command)
command = ['--chip', 'ESP32', '--port', param['port'], 'write_flash', '0x10000', self.filename.get()]
command = ['--chip', param['chipname'], '--port', param['port'], 'write_flash', str(param['info']['flashbase']), self.filename.get()]
self.runEspTool(command)

View File

@ -1564,14 +1564,14 @@ function uploadBin(ev){
.then(function (result) {
let currentType;
let currentVersion;
let chiptype;
let chipid;
forEl('.status-version', function (el) { currentVersion = el.textContent });
forEl('.status-fwtype', function (el) { currentType = el.textContent });
forEl('.status-chiptype', function (el) { chiptype = el.textContent });
forEl('.status-chipid', function (el) { chipid = el.textContent });
let confirmText = 'Ready to update firmware?\n';
if (result.chip.match(/^@@/) && (result.chip.substr(2) != chiptype)){
confirmText += "WARNING: the chiptype in the image ("+result.chip.substr(2);
confirmText +=") does not match the current chip type ("+chiptype+").\n";
if (result.chipId != chipid){
confirmText += "WARNING: the chipid in the image ("+result.chipId;
confirmText +=") does not match the current chip id ("+chipid+").\n";
}
if (currentType != result.fwtype) {
confirmText += "WARNING: image has different type: " + result.fwtype + "\n";
@ -1649,8 +1649,8 @@ function uploadBin(ev){
let HDROFFSET=288;
let VERSIONOFFSET=16;
let NAMEOFFSET=48;
let CHIPOFFSET=NAMEOFFSET+64;
let MINSIZE = HDROFFSET + CHIPOFFSET + 32;
let MINSIZE = HDROFFSET + NAMEOFFSET + 32;
let CHIPIDOFFSET=12; //2 bytes chip id here
let imageCheckBytes={
0: 0xe9, //image magic
288: 0x32, //app header magic
@ -1669,6 +1669,10 @@ function decodeFromBuffer(buffer,start,length){
start+length));
return rt;
}
function getChipId(buffer){
if (buffer.length < CHIPIDOFFSET+2) return -1;
return buffer[CHIPIDOFFSET]+256*buffer[CHIPIDOFFSET+1];
}
function checkImageFile(file){
return new Promise(function(resolve,reject){
if (! file) reject("no file");
@ -1685,11 +1689,11 @@ function checkImageFile(file){
}
let version=decodeFromBuffer(content,HDROFFSET+VERSIONOFFSET,32);
let fwtype=decodeFromBuffer(content,HDROFFSET+NAMEOFFSET,32);
let chip=decodeFromBuffer(content,HDROFFSET+CHIPOFFSET,32);
let chipId=getChipId(content);
let rt={
fwtype:fwtype,
version: version,
chip:chip
chipId:chipId
};
resolve(rt);
});
@ -1751,10 +1755,7 @@ window.addEventListener('load', function () {
checkImageFile(file)
.then(function(res){
forEl('#imageProperties',function(iel){
let txt="";
if (res.chip.match(/^@@/)){
txt=res.chip.substr(2)+", "
}
let txt="["+res.chipId+"] ";
txt+=res.fwtype+", "+res.version;
iel.textContent=txt;
iel.classList.remove("error");

View File

@ -10,8 +10,7 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm";
const HDROFFSET = 288;
const VERSIONOFFSET = 16;
const NAMEOFFSET = 48;
const CHIPOFFSET=NAMEOFFSET+64;
const MINSIZE = HDROFFSET + CHIPOFFSET + 32;
const MINSIZE = HDROFFSET + NAMEOFFSET + 32;
const CHIPIDOFFSET=12; //2 bytes chip id here
const imageMagic=0xe9; //at byte 0
const imageCheckBytes = {
@ -22,6 +21,7 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm";
};
/**
* map of our known chip ids to flash starts for full images
* 0 - esp32 - starts at 0x1000
* 9 - esp32s3 - starts at 0
*/
const FLASHSTART={
@ -47,8 +47,8 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm";
let prfx=isFull?"full":"update";
let startOffset=0;
let flashStart=UPDATE_START;
let chipId=getChipId(content);
if (isFull){
let chipId=getChipId(content);
if (chipId < 0) throw new Error(prfx+"image: no valid chip id found");
let flashStart=FLASHSTART[chipId];
if (flashStart === undefined) throw new Error(prfx+"image: unknown chip id "+chipId);
@ -69,11 +69,10 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm";
}
let version = decodeFromBuffer(content, startOffset+ HDROFFSET + VERSIONOFFSET, 32);
let fwtype = decodeFromBuffer(content, startOffset+ HDROFFSET + NAMEOFFSET, 32);
let chip=decodeFromBuffer(content,startOffset+HDROFFSET+CHIPOFFSET,32);
let rt = {
fwtype: fwtype,
version: version,
chip:chip,
chipId:chipId,
flashStart: flashStart
};
return rt;
@ -133,22 +132,13 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm";
hFrame.textContent='';
let h=addEl('h2',undefined,hFrame,`ESP32 Install ${info}`)
}
const checkChip= async (chipFamily,data,isFull)=>{
const checkChip= async (chipId,data,isFull)=>{
let info=checkImage(data,isFull);
if (info.chip && info.chip.match(/^@@/)){
let chip=info.chip.substr(2);
let compare=chipFamily.toLowerCase().replace(/[^a-z0-9]*/g,'');
if (compare !== chip){
let res=confirm("different chip signatures - image("+chip+"), chip ("+compare+")\nUse this image any way?");
if (! res) throw new Error("user abort");
}
return;
if (info.chipId != chipId){
let res=confirm("different chip signatures - image("+chipId+"), chip ("+info.chipId+")\nUse this image any way?");
if (! res) throw new Error("user abort");
}
//for now only ESP32/ESP32-S3
if (chipFamily != "ESP32" && chipFamily != "ESP32-S3"){
throw new Error(`unexpected chip family ${chipFamily}, expected ESP32/ESP32-S3`);
}
return;
return info;
}
const baudRates=[1200,
2400,
@ -339,7 +329,6 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm";
await espInstaller.runFlash(
true,
fullData,
vinfo.flashStart,
version,
checkChip
)
@ -353,7 +342,6 @@ import * as zip from "https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.29/+esm";
await espInstaller.runFlash(
false,
updateData,
uinfo.flashStart,
version,
checkChip
)

View File

@ -61,6 +61,7 @@ class ESPInstaller{
this.base=import.meta.url.replace(/[^/]*$/,"install.php");
this.consoleDevice=undefined;
this.consoleReader=undefined;
this.imageChipId=undefined;
}
/**
* get an URL query parameter
@ -133,6 +134,7 @@ class ESPInstaller{
this.espLoaderTerminal.writeLine(`chip: ${foundChip}`);
await this.esploader.flash_id();
this.chipFamily = this.esploader.chip.CHIP_NAME;
this.imageChipId = this.esploader.chip.IMAGE_CHIP_ID;
this.espLoaderTerminal.writeLine(`chipFamily: ${this.chipFamily}`);
} catch (e) {
this.disconnect();
@ -185,6 +187,10 @@ class ESPInstaller{
this.checkConnected();
return this.chipFamily;
}
getChipId(){
this.checkConnected();
return this.imageChipId;
}
/**
* flass the device
* @param {*} fileList : an array of entries {data:blob,address:number}
@ -236,7 +242,7 @@ class ESPInstaller{
* @param {*} repo
* @param {*} version
* @param {*} assetName
* @param {*} checkChip will be called with the found chip and the data and the isFull flag
* @param {*} checkChip will be called with the found chipId and the data and the isFull flag
* must return an info with the flashStart being set
* @returns
*/
@ -247,7 +253,7 @@ class ESPInstaller{
if (!imageData || imageData.length == 0) {
throw new Error(`no image data fetched`);
}
let info=await checkChip(this.getChipFamily(),imageData,isFull);
let info=await checkChip(this.getChipId(),imageData,isFull);
let fileList = [
{ data: imageData, address: info.flashAddress }
];
@ -267,20 +273,18 @@ class ESPInstaller{
/**
* directly run the flash
* @param {*} isFull
* @param {*} address
* @param {*} imageData the data to be flashed
* @param {*} version the info shown in the dialog
* @param {*} checkChip will be called with the found chip and the data
* @param {*} checkChip will be called with the found chipId and the data
* must return an info with flashAddress
* @returns
*/
async runFlash(isFull,imageData,address,version,checkChip){
async runFlash(isFull,imageData,version,checkChip){
try {
await this.connect();
if (checkChip) {
await checkChip(this.getChipFamily(),imageData,isFull); //just check
}
let info= await checkChip(this.getChipId(),imageData,isFull); //just check
let fileList = [
{ data: imageData, address: address }
{ data: imageData, address: info.flashAddress }
];
let txt = isFull ? "baseImage (all data will be erased)" : "update";
if (!confirm(`ready to install ${version}\n${txt}`)) {