#include "image.h"
#include "info.h" // esp::xor8
ESPFirmwareImage::ESPFirmwareImage(char chip,const char*filename) {
memset(this,0,sizeof*this);
hdr.magic = MAGIC;
m_chip = chip;
if (filename) load(filename);
}
const ESPFirmwareImage::SEGHDR*ESPFirmwareImage::IMGHDR::next(bool kurz) const {
return reinterpret_cast<const SEGHDR*>(kurz||wp_pin!=0xEE?&wp_pin:(const byte*)(this+1));
}
// Lader
void ESPFirmwareImage::load(const char*filename) {
FILE*f = fopen(filename,"rb");
if (!f) Fatal("Cannot open %s for reading",filename);
load(f);
fclose(f);
}
// Checks header only, not the checksum and not the length
bool ESPFirmwareImage::valid(const void*fdata,size_t flen) {
const IMGHDR&h = *reinterpret_cast<const IMGHDR*>(fdata);
return flen>=sizeof h && h.magic==MAGIC && h.nseg && h.nseg<=16 && h.fmode<4;
}
char ESPFirmwareImage::IMGHDR::detectchip() const{
if (wp_pin!=0xEE) return 66; // kann RAM-Image sein!
if (chipH || chipL==32 || chipL==66) return 0;
return chipL?chipL:32;
}
void ESPFirmwareImage::checkchip() {
char chip=hdr.detectchip();
const esp::Info1*info1=esp::find1(chip);
if (chip) {
if (info1) {
printf("Chip ESP%s encoded in image",info1->name,chip,chip==m_chip?"as expected.":"Unexpected!");
printf(m_chip?chip==m_chip?" as expected.":chip==66?", sure?":": Unexpected!":".");
putchar('\n');
if (chip!=66 || !m_chip)
m_chip=chip; // Für nachfolgende Dekodierung setzen
}else printf("Unknown chip (%d) encoded in image!\n",chip);
}else printf("Unknown chip encoded in image!\n");
}
void ESPFirmwareImage::load(FILE*f) {
size_t hdrlen = m_chip==66 ? sizeof(IMGHDR) : sizeof hdr;
memset(&hdr,0,sizeof hdr);
// some sanity check
if (fread(&hdr,1,hdrlen,f)!=hdrlen
|| hdr.magic!=MAGIC
|| !hdr.nseg
|| hdr.nseg > 16) Fatal("Invalid firmware image");
// Was für ein Bullshit:
// "After the 8-byte header comes the extended header for chips others than ESP8266."
// Aber woher den Chiptyp kennen?? Von der Kommandozeile? Dankeschön!!
if (m_chip!=66 && hdr.wp_pin!=0xEE) { // Kurzer Header für ESP8266 - oder für RAM-Images!
fseek(f,-16,1);
// m_chip=66; // must be 8266
hdrlen=sizeof(IMGHDR);
}
checkchip();
Sha256 sha;
sha.update(&hdr.magic,hdrlen);
segments = new SEG[hdr.nseg];
for (int i=0; i<hdr.nseg; i++) {
SEG&cur = segments[i];
if (fread(&cur,1,8,f)!=8
// || cur.offset > 0x40200000
// || cur.offset < 0x3ffe0000
|| cur.size > 1024*1024)
Fatal("Suspicious segment #%u@0x%XL%u",i,cur.offset,cur.size);
size_t blocksize=(cur.size+3)&~3;
cur.data=new byte[blocksize];
size_t br=fread(cur.data,1,blocksize,f);
if (br!=blocksize)
Fatal("End of file reading segment #%u@0x%XL%u (actual length %u)",i,cur.offset,blocksize,br);
sha.update((byte*)&cur,8);
sha.update(cur.data,cur.size);
}
byte padding[16];
// Skip the padding. The checksum is stored in the last byte so that the
// file is a multiple of 16 bytes.
size_t padlen = 16 - ftell(f)%16U;
size_t br = fread(padding,1,padlen,f);
check_avail=!!br;
if (!br) printf("No CRC. Looks like a RAM image.\n");
else{
if (br!=padlen) Fatal("Missing CRC");
sha.update(padding,br);
}
calchash = sha.final();
check=padding[padlen-1];
// Just after the CRC byte, a 32-byte SHA256 hash follows (ESP32+ only)
if (hdr.hash_append==1 && fread(&filehash,1,32,f)!=32) Fatal("Missing SHA-256 hash");
if (fread(&padlen,1,sizeof padlen,f)) Fatal("Extra data after end of image");
}
// Parsen mit segmentweisem Callback. Beim Laden aus Ressource (stets 3 Segmente) ist flen==0
// Die Startadresse wird früh gesetzt, d.h. auch bei return false verändert.
bool ESPFirmwareImage::parse(const Dumpfile&fd,cb2_t cb,void*cbd,uint32*sadr) {
const IMGHDR&h = *reinterpret_cast<const IMGHDR*>(fd.data());
if (sadr) *sadr=h.entry;
const SEGHDR*sh = h.next();
for (int i=0; i<h.nseg; i++) {
Dumpfile sub(const_cast<byte*>(sh->data()),sh->size,sh->offset);
if (!cb(cbd,sub)) return false;
sh=sh->next();
}
return true;
}
// While reading flash, check whether it's a firmware image,
// and, if so, return a reduced <fsize> value.
// When sure it is NOT a firmware image, return 0 to indicate failure.
// Otherwise, this function returns <fsize> unchanged, i.e. continue reading
uint32 ESPFirmwareImage::detectSize(const void*fdata,uint32 fsize) {
if (!valid(fdata,fsize)) return 0; // kein Firmware-Image
const IMGHDR&h=*reinterpret_cast<const IMGHDR*>(fdata);
if (fsize<sizeof h) return 1; // unsicher
const SEGHDR*sh=h.next();
uint32 o=uint32(size_t(sh)-size_t(fdata));
for (unsigned i=0; i<h.nseg; i++) {
if (fsize < o+8) return 1; // unvollständig, kann next() nicht ausführen
sh=sh->next();
o=uint32(size_t(sh)-size_t(fdata)); // In der letzten Runde nicht testen!
}
// Checksum must be present here! No idea how to handle other case.
o=(o+16)&~15; // align to next 16-byte boundary
if (h.wp_pin==0xEE && h.hash_append==1) o+=32;
return o<=fsize ? o : 1;
}
static const char*nstr(const char*s,unsigned n) {
for(;n;n--) s+=strlen(s)+1;
return s;
}
const char*ESPFirmwareImage::fmode_str() const{
const char*modes="QIO\0QOUT\0DIO\0DOUT";
return nstr(modes,hdr.fmode&3);
}
String<8>ESPFirmwareImage::ffreq_str() const{
MAKEPRINTBUF(8)
static const byte
f [4]={40,26,20,80},
f_c2[4]={30,20,15,60},
f_c6[4]={80,40,20},
f_h2[4]={24,16,12,48};
const byte*pf=f; // ESP8266 und alle übrigen ESP32
switch (m_chip) {
case 12: pf=f_c2; break; // ESP32C2e0, ESP32C2e1
case 7: // ESP32C6b
case 13: pf=f_c6; break; // ESP32C6
case 10: // ESP32H2b1
case 14: // ESP32H2b2
case 16: pf=f_h2; break; // ESP32H2
}
print("%u MHz",pf[hdr.ffreq&3]);
return buf;
}
String<16>ESPFirmwareImage::fsize_str() const{
MAKEPRINTBUF(16)
byte b=hdr.fsize;
const char*suffix="";
if (m_chip==66) {
if (b<2) print("%u KByte",1<<(9-b));
else{
if (byte(b-5)<2)
b-=2;
if (b>=3) {
b-=2;
if (b>3) --b;
else suffix="-c1";
}
}
}
print("%u MByte%s",1<<b,suffix);
return buf;
}
// <data> wird „wegkopiert“ und nicht weiter referenziert
bool ESPFirmwareImage::add_segment(const Dumpfile&df) {
if (!df.size) return true;
if (df.size>65536) Fatal("add_segment: Zu große Datenlänge (%u)",df.size);
// Data should be aligned on word boundary
int nseg = hdr.nseg;
if (nseg>=16) Fatal("add_segment: Zu viele Segmente");
segments = (SEG*)realloc(segments,++hdr.nseg*sizeof*segments);
SEG&cur=segments[nseg]; // Neu erstelltes Segment
uint32 alen = (df.size+3)&~3; // auf DWORD aufrunden
cur.offset = df.addr;
cur.size = alen; // aufgerundet ablegen
cur.data = new byte[alen];
memcpy(cur.data,df.data(),df.size);
memset(cur.data+df.size,0,alen-df.size); // Mit 0 auffüllen, falls alen>dlen
return true;
}
// Speichert die per add_segment() abgelegten Speicherblöcke in eine Datei
// und generiert die Prüfsumme
void ESPFirmwareImage::save(const char*filename) const{
FILE*f = fopen(filename,"wb");
if (!f) Fatal("Cannot create file %s",filename);
fwrite(&hdr,1,sizeof hdr,f);
byte ck = esp::XOR8_INIT;
for (int i=0; i<hdr.nseg; i++) {
SEG&cur=segments[i];
fwrite(&cur,1,8,f);
fwrite(cur.data,1,cur.size,f);
ck = esp::xor8(cur.data,cur.size,ck);
}
int align = 15 - ftell(f)%16U;
fseek(f,align,1);
fwrite(&ck,1,1,f);
fclose(f);
}
ESPFirmwareImage::~ESPFirmwareImage() {
if (!segments) return;
for (int i=0; i<hdr.nseg; i++) {
delete[] segments[i].data;
}
delete[] segments;
segments=0;
}
Detected encoding: ANSI (CP1252) | 4
|
|