APS Logo APS_TUNING [ ABOUT ] [ BLOG ]
[ VISSZA A LOGOKHOZ ]

BMW EDC16: kalibrációs címzés és mapswitch négy- és hathengeres motoroknál

TIMESTAMP: 2026.04.16 | TARGET: BMW DDE5 / DDE6 | AUTHOR: APS TUNING | UPDATED: 2026.10.01 | v10

A Bosch EDC16 az egyik legelterjedtebb dízel motorvezérlő platform. BMW, VW, Audi, Ford, gyakorlatilag bármelyik európai gyártó járművében találhatsz belőle valamilyen változatot. A szoftver-architektúra közös, de a visszafejtés korántsem egyszerű. A kalibrációs adatok, a térképek, a karakterisztikák és a konstansok többszintű indirekción át rejtőznek a kódban.

Több BMW DDE5 és DDE6 szoftvert vizsgáltam, köztük négyhengeres, belső flash nélküli és hathengeres, külön belső flash-t használó változatokat. A vizsgált DDE6 szoftverek több közös kalibrációs címzési mintát használnak. A hasonló kód azonban nem jelent azonos hardvert, memóriaelrendezést vagy perifériakezelést. Új portnál ezeket külön ellenőrizni kell.

A korábbi, itt vizsgált MPC555-alapú DDE5 minta külön eset: más memóriaelrendezést és közvetlen kalibrációs címzést használ. A konkrét ECU hardverét és a szoftvert mindig azonosítani kell, a DDE vagy EDC16 családnév önmagában nem elég.

Bosch EDC16 MPC562-563 (DDE6.0 / DDE6.2)

A cikkben tárgyalt DDE6 változatok az MPC5xx család 32 bites PowerPC architektúráját használják. Az MPC562 és MPC563 nem azonos flash-konfigurációjú: az MPC562 nem tartalmazza az MPC563 512 KiB belső flash memóriáját. Az alábbi táblázat a vizsgált, külön belső flash-t használó hathengeres elrendezés példája, nem minden EDC16 univerzális memóriatérképe.

BlokkCímMéretTartalom
Külső Flash 0x000000 - 0x1FFFFF2 MBAlkalmazás kód + kalibrációs terület
Belső Flash 0x400000 - 0x47FFFF512 KiBBosch Funktionsbibliothek (OS + standard rutinok)

A vizsgált belső flash-es szoftverekben a belső flash jelentős részét Bosch könyvtári rutinok, matematikai és interpolációs függvények, valamint rendszerkód foglalja el. A könyvtárak között lehetnek nagyfokú egyezések, de címeik és bináris tartalmuk nem tekinthető a vizsgált DDE6 mintábannak. A hívások a processzor címterében értendők, nem a mentési fájl offsetjei szerint.

Négyhengeres DDE6, külön iFlash nélkül

A tárgyalt 18d 122 LE és 20d 163 LE négyhengeres DDE6 változatoknál nincs külön belső program-flash, ezért külön iFlash-tartalom sem szükséges. A program és a kalibráció a külső flash-ben található. Ezeknél az EEPROM és a teljes eFlash mentés képezi a kiindulási alapot.

A cikkben bemutatott, belső flash-es hathengeres változatoknál EEPROM, iFlash és eFlash együtt szükséges az összetartozó tartalom vizsgálatához. A mentőeszköz fájlnevei helyett mindig a tényleges hardvert, a fájlméreteket és a beágyazott szoftverazonosítókat ellenőrzöm.

Vizsgált változatProgramtartalomKiinduló mentés
Négyhengeres DDE6, külön iFlash nélkülProgram és kalibráció az eFlash-benEEPROM és teljes eFlash
Hathengeres DDE6, belső flash-es kivitelÖsszetartozó iFlash és eFlashEEPROM, iFlash és teljes eFlash

Az APS mapswitch négyhengeres kialakításánál 3, hathengeres kialakításánál 4 kalibrációs bank is elérhető. A konkrét patch lehet kevesebb bankos is. A kiosztást a rendelkezésre álló flash-terület és a szoftver felépítése alapján kell meghatározni, a hengerszám önmagában nem memóriatérkép.

Hardver memória architektúra (A2L validációval)

Az MPC562/MPC563 család 32 KiB CALRAM területét és a külső RAM címablakait külön kell kezelni. A vizsgált A2L a 0x3F9800 - 0x3FFFFF közötti 26 KiB tartományban is tartalmaz adatokat. A változólista önmagában nem bizonyítja a teljes RAM-kiosztást, a fizikai chipméreteket vagy az egyes rutinok futtatási helyét. A flash-ben tárolt alkalmazáskód RAM-ban tárolt futási állapotokkal dolgozik.

Az alábbi címablakok egy 5660 változót tartalmazó A2L elemzéséből és kódreferenciákból származó példák. A funkciómegjelölések támpontok. Nem minden címablak jelent külön fizikai SRAM chipet, és a felsorolt teljes tartományok írhatóságát új hardveren mérni kell.

RégióCímtartományMéretFunkció (A2L elemzés alapján)
INT_CALRAM (on-chip)0x3F9800 - 0x3FFFFF26 KBBelső futási adatok, a pontos felhasználás kódellenőrzést igényel
CS0 - External Flash0x000000 - 0x1FFFFF2 MBApplikációs kód + Kalibrációs terület
EXT_SRAM_A0x6F8000 - 0x6F87FF2 KBMérőpont puffer (kis, kiemelt): 99.2% _mp suffix (Messpunkt), ~128 db kritikus measurement point
EXT_SRAM_B0x7F8000 - 0x7FFFFF32 KBFő alkalmazói RAM (SDA, r13-relatív): 2469 futási változó, state machines, FrmMng, InjCrv, Lambda...
EXT_SRAM_C0x800000 - 0x807FFF32 KBStack + RTOS workspace, és az r13 pozitív offset tartománya (0x7FFFF0 + max 0x7FFF = 0x807FEF). Az A2L-ben nincs itt nevesített változó, de a futó kód használja: a 730d DDE626-on itt vannak például a CAL RAM cellák (0x802DBC - 0x802DC8) és init-idejű pointer cache-ek (0x801F14 - 0x801F24)
EXT_SRAM_D0x8FE000 - 0x8FFFFF8 KBNagy diagnosztikai mérőpont puffer: 3063 db measurement point, 98.2% _mp suffix

Az _mp névvégződés a vizsgált leíróban mérőpontokat jelöl. Sok ilyen változó a belső vezérlési állapot megfigyelési másolata. A név alapján azonban nem szabad minden ilyen objektumot kizárólag diagnosztikai adatnak tekinteni, ezt a kód olvasási és írási referenciái alapján lehet eldönteni.

A CAN perifériák belső puffereit, a szoftveres fogadópuffereket és az esetleges külső memóriát külön kell azonosítani. A címekből önmagukban nem következik külön DPRAM chip. Ghidrában a bizonyított memóriaablakokat érdemes felvenni; a feltételezett tartományokat jelölni kell, nehogy a modell használhatatlan memóriát is valós RAM-nak mutasson.

A flash belső logikai felosztása

A 2 MB-os külső flash nem homogén, több, funkcionálisan elkülönített régióból áll. A felosztás szoftver-verziónként változhat, szóval minden új binárisnál érdemes a kód és a kalibráció határait ellenőrizni. A kalibrációs adatok jellemzően két különálló régióban laknak:

RégióTartalomHozzáférés
0x040000 - 0x052000Skalár kalibrációk, flag-ek, konfigurációr2/r14-relatív (SDA direct)
0x0C0000 - 0x0FFFFFMap-ek, táblák, karakterisztikákPointer table (r15)

A fenti felosztás a vizsgált mintákban hasznos kiindulópont. A konkrét határokat, objektumtípusokat és hivatkozási módokat szoftverenként ellenőrizni kell. Nem minden skalár él az első tartományban, és nem minden térkép címzése vezethető le egyetlen bázisregiszterből.

Az első lépés: Memory Map validáció

A vizsgált DDE6 mentésekben a külső flash a CPU 0x000000 címére van leképezve. A 0x100 címen található induló ugrás hasznos ellenőrzési pont, de önmagában egy értelmes utasítás még nem bizonyítja a teljes fájl leképezését. Ehhez a startup kódot, az abszolút hivatkozásokat és a mentési formátumot is össze kell vetni.

Reset Vector @ 0x00000100
0x100:  48 00 01 82

ba      0x180                 ; Abszolút branch a startup kódra

A példabeli abszolút ugrás az inicializáló kódra mutat. A vizsgált nyers eFlash mentésnél a további referenciákkal együtt ez igazolja a fájloffset és a CPU-cím egyezését. Összevont mentésnél a blokkokat külön kell leképezni.

BTT DDE6 quickflash, a vizsgált 2,5 MiB-os, belső flash-es mentés betöltése: a full backup (internal és external flash egyetlen fájlban) az internal flash-t a 0x200000 file offsettől tárolja. Ez a processzor címterében 0x400000-nak felel meg. Ghidra betöltéskor az external flash-t 0x000000-ra, az internal flash-t 0x400000-ra kell mappolni.
Intel HEX betöltés: egyes intelhex dumpoknál a flash adat nem feltétlenül 0x000000-tól kezdődik. Az E60 535d HEX fájlban például az első adat 0x008000-nál indul, ami 0x8000 byte-os eltolást jelent a WinOLS nyers binárishoz képest. Ghidrában az Intel HEX loader automatikusan a helyes címekre rakja az adatokat, de WinOLS-ben WinOLS cím = CPU cím - 0x8000 korrekció kell.

A startup: Register poisoning és bázisregiszter inicializáció

A reset vector utáni kód először egy register poisoning mintát hajt végre: az összes általános célú regisztert 0x7F7F7F7F értékkel tölti fel:

Register Poisoning - Debug előinicializálás
lis     r1, 0x7f7f
addi    r1, r1, 0x7f7f
or      r2, r1, r1
...                              ; r3-r31 szintén 0x7F7F7F7F
or      r31, r1, r1

Ez egy debug jellegű előinicializálás: ha bármelyik regiszter használat előtt nem kap tényleges értéket, a 0x7F7F7F7F minta azonnal szemet szúr a memória dumpban. A tényleges bázisregiszter beállítás ezután következik a flash alkalmazáskódban:

CPU Init - Bázisregiszterek
lis     r11, 0x80
addi    r1, r11, 0x0         ; r1 = 0x00800000 - EXT_SRAM_C (stack, downward growing)

lis     r13, 0x80
subi    r13, r13, 0x10       ; r13 = 0x007FFFF0 - EXT_SRAM_B (SDA bázis, a vizsgált DDE6 mintában)

lis     r2, 0x5
subi    r2, r2, 0x7F10       ; r2 = 0x000480F0 - TOC/SDA2 Base (flash konstansok)

lis     r14, 0x4
addi    r14, r14, 0x6454     ; r14 = 0x00046454 - SDA Base (flash konstansok)

Dual Variant architektúra

A vizsgált DDE6 szoftverekben két variáns kiválasztására utaló startup logika található. Az r2 és r14 végleges értékét az indulási út határozza meg. A második út létezése nem jelenti azt, hogy a hozzá tartozó teljes adathalmaz minden binárisban jelen van.

A variánsok bázisregiszterei és adatai szoftverfüggők. Variánsflag átírása előtt az összes érintett struktúrát és hardverfeltételt igazolni kell. Egy meglévő feltételes ág önmagában nem tesz használhatóvá egy hiányzó adathalmazt.

INFO: a vizsgált mintában a két kódút megvan, de a B variáns adatai hiányoznak. A 730d DDE626 szoftverben a kód mindkét r2/r14 párt ismeri (A: r2 = 0x480F0, r14 = 0x4653C, B: r2 = 0xF80F0, r14 = 0xF443C), de a B variáns r2-relatív struktúrái nem léteznek: a pointer tábla referencia helyén ([r2 + 0x7C48] = 0xFFD38) FF áll, a 0xF0000 - 0xF913F tartományt pedig az A variáns térkép blokkjai foglalják. A gyakorlatban tehát csak az A futhat. Az r2 értéke bitre megegyezik a fenti példával, az r14 szoftver-verziónként eltér.

Pointer relokáció a variánsváltáskor

A vizsgált indulási szekvenciában az r2/r14 beállítása mellett RAM-ban tárolt pointerek relokációja is szerepel. Az alábbi hét pointeres példa egy konkrét szoftverből származik, a darabszám és a számítás nem általános EDC16 konstans.

Pointer relokáció - Számítási logika
r12 = 0x007FE100                ; fix referencia pont (RAM vége közelében)

; Variant A:
r11 = 0x3E98C                  ; fix offset

; Variant B:
r11 = lwz(r14 - 0x7740) + 0x3E98C  ; flash-ből olvasott delta + fix offset

delta = r12 - r11

; Irány 0 (undo):  new_ptr = delta + old_ptr
; Irány 1 (apply): new_ptr = old_ptr - delta

A négyszintű kalibráció-elérési modell

Az elemzéshez négy címzési mintát különböztetek meg: r15 pointertábla, közvetlen SDA-elérés, regiszterértékek továbbvitele és abszolút vagy mirror-címzés. Ez elemzési felosztás, nem négy egymásra épülő Bosch hardverszint.

SzintMechanizmusPélda
1 - r15 Pointer táblalwz rXX, offset(r15) - pointer tábla entry, kalibrációs blokk base[r15 + 0x5CC] = 0x0F5078
2 - Közvetlen SDAlwz rXX, offset(r2) vagy (r14) - közvetlenül kalibrációs adat[r2 + 0x7034] = 0x04F124
3 - Propagációaddi / mr / or - bázisregiszter továbbvitel függvényen belüladdi r3, r31, 0x4FC = 0x0F5574
4 - Flash mirrorlis rXX, imm ; load rYY, offset(rXX) - 0x01000000+ tükörcímlis r11, 0x10c ; lhz r11, 0x3eea(r11) = 0x0C3EEA

A szintek nem hierarchikusak és nem zárják ki egymást: egy függvényen belül is vegyesen megjelenhet a pointer tábla elérés, a közvetlen SDA hozzáférés és a flash mirror minta. A firmware ezeket párhuzamosan, hibrid módon használja.

Szint 1: Az r15 pointer tábla

A vizsgált DDE6 kódban az r15 a kalibrációs pointertábla báziscímét tartja. A setter egy feltételes ágban a default vagy az alternatív pointert választja ki. Az alábbi nevek elemzéskor adott, szemléltető nevek, nem minden firmware-ből visszanyert gyári szimbólumok.

r15 inicializáció - CAL_SetActiveDataset
; Kritikus szekció belépés (interrupt disable):
bl      FUN_0001f590          ; OSEK TaskEnter

; Instance selector ellenőrzés:
lbz     r12, CAL_PageSelect     ; CCP kalibrációs page (0 = flash, 1 = RAM)
cmpwi   r12, 0x1
bne     else
lbz     r12, CAL_RamCopyState   ; RAM working page állapot
cmpwi   r12, 0x4             ; 4 = RAM másolat kész, váltás engedélyezve
bne     else

; RAM working page (allokált RAM másolat):
lwz     r31, CAL_ActivePtr_Alt  ; RAM pointer tábla
b       done

else:
; Flash page (default, flash pointer tábla):
lwz     r31, CAL_ActivePtr_Default  ; flash tábla cím

done:
lwz     r12, PTR_TABLE_REF(r2) ; flash pointer tábla referencia cím
addi    r15, r31, 0x0       ; r15 = kiválasztott instance
subf    r12, r12, r31      ; offset = r15 - referencia cím
stw     r12, CAL_ActiveOffset  ; offset mentése

; Kritikus szekció kilépés:
b       FUN_0001f5b8          ; OSEK TaskExit (tail call)

A vizsgált setter kritikus szekcióban frissíti az r15-öt és a hozzá tartozó offsetet. Ez a pointerfrissítést védi, de nem frissíti automatikusan az összes RAM-cache-t és nem tesz atomi műveletté egy teljes motorvezérlési ciklust. A hívási konvenciót és a megszakításkezelést portonként ellenőrizni kell.

A vizsgált kódban a CAL init és a CCP page-kezelés is használja a settert. A PageSelect=1 és RamCopyState=4 együtt az alternatív pointert választja, más esetben a default pointert. A mapswitch nem feltételezheti, hogy az alternatív ág soha nem lehet aktív: a default bank kiválasztásához a vizsgált settert r3=0 paraméterrel hívjuk.

A CAL init bootkor két flash tábla közül választ: az r2-relatív referencia táblát (éles eset), vagy egy második táblát, amely ugyanazokra a blokkokra a 0x01000000-s ablakon keresztül mutat (ETK/emulációs eset, lásd Szint 4). A választott címet a CAL_ActivePtr_Default-ba írja, majd page = 0 paraméterrel meghívja a settert.

Az r15 a vizsgált DDE6 rendszerben dedikált kalibrációs bázisregiszterként működik. Új szoftverben az összes írását, a taszk- és megszakítási kontextus kezelését is ellenőrizni kell. A közvetlen r15-elérések a váltás után az új táblát használják, a korábban más regiszterbe vagy RAM-ba mentett pointerek ettől még megmaradhatnak.

Konkrét címek a 730d DDE626 (EDC16CP35) szoftverből:

ElemCím
CAL_SetActiveDataset (setter)0x11E918, r15 írás: 0x11E964
Kritikus szekció enter / exit0x1F488 / 0x1F4B0
CAL init0x11E994 (init lista 0x43614, 8. elem)
CCP SET_CAL_PAGE hívások0x124A38 / 0x124A44
CAL_PageSelect / CAL_RamCopyStater13 + 0x2DCD / r13 + 0x2DCC
CAL_ActivePtr_Default / Altr13 + 0x2DD0 / r13 + 0x2DD4
CAL_ActiveOffset (csak írva)r13 + 0x2DD8
Referencia tábla (éles)[r2 + 0x7C48] = 0x512F0
ETK tábla[r2 + 0x7C4C] = 0x50C10
Entry szám[r2 + 0x7C44] = 0x1B8 (440)

A Pointer Tábla

A pointer tábla egy sűrű, 4 bájtos bejegyzésekből álló tömb a flash-ben. Minden entry formátuma:

[ 32 bites pointer a kalibrációs blokkra ]

A bejegyzések a vizsgált táblákban 32 bites pointerek. A felső bájtot külön bizonyíték nélkül nem nevezem dataset ID-nak. A tábla báziscíme, hossza és esetleges címfordítása szoftverfüggő. A megtalálásához a setter r2-relatív referenciája és az inicializáló kód ad támpontot.

A tábla után egy sentinel szó áll, az utolsó blokk végcíme (a 730d-n 0x519D0 = 0xF9138). A Bosch címfordító rutin, amely a CCP memóriaelérésnél a referencia címet az aktív táblára fordítja, a blokk végét a referencia tábla következő entry-jéből veszi, a blokk kezdetét pedig az r15 táblából. Ebből következik, hogy egy klón táblának ugyanabban a sorrendben, ugyanannyi entry-vel kell követnie a referenciát.

Az A2L szimbólum-import után a pointer tábla valódi modulneveket kap. Minden entry egy-egy Bosch szoftver-modul kalibrációs blokkjára mutat, a Bosch prefix konvenció pedig elárulja, mire való: ExeMon a végrehajtás monitor, OvRMon a fordulatszám monitor, FrmMng a CAN frame manager, AirCtl a levegő szabályozás, CoEng a motor koordinátor, InjCrv a befecskendezés, CrCtl a tempomat. A kisebb modulok csak egy-két konstanst tartalmaznak, a nagyobbak (InjCrv, FrmMng, AirCtl) viszont több ezer byte-nyi térképet és karakterisztikát.

Pointer tábla A2L modul nevekkel Ghidrában

A pointer tábla Ghidrában, A2L szimbólum-import után: minden 4 byte-os entry egy Bosch kalibrációs modul nevét és flash címét tartalmazza.

Szint 2: Közvetlen SDA hozzáférés (r2/r14-relatív)

Az r2 és r14 regiszterek az SDA2 és SDA bázisként szolgálnak. Az SDA2 címzési tartomány (r2 plusz/mínusz 0x8000) tipikusan a 0x040000 - 0x052000 kalibrációs régiót fedi le, ahol a skalár konstansok, flag-ek és a pointer tábla bázisa is megtalálható:

Közvetlen SDA hozzáférés - r2-relatív
lwz     r12, 0x7034(r2)      ; r2 + 0x7034 = kalibrációs adat
lhz     r3, -0x1a6e(r2)      ; r2 - 0x1A6E = skalár konstans

A nagy kalibrációs térképek (0x0C0000 - 0x0FFFFF) kívül esnek az r2 címzési hatótávolságán. Ott a pointer tábla (Szint 1) az elsődleges út, míg az r2/r14-relatív hozzáférés inkább a skalár konfigurációkat szolgálja ki.

Szint 4: Flash mirror abszolút hozzáférés

A vizsgált szoftverben a 0x01000000 - 0x011FFFFF címablakra mutató referenciák is szerepelnek. A flash-alias és az emulációs ablak működését az EBI konfigurációjával és a hardverdetektáló kóddal kell összevetni. Ez nem minden EDC16-ra automatikusan érvényes memóriatérkép.

Flash mirror hozzáférés - lis + load minta
lis     r11, 0x10c            ; r11 = 0x010C0000
lhz     r11, 0x3eea(r11)     ; [0x010C3EEA] = flash tükörkép, valós cím: 0x0C3EEA
sth     r11, 0x77e(r13)      ; eredmény a RAM-ba

Ghidrában a 0x01000000 bázison egy 2MB-os overlay memory block létrehozásával a piros (unresolved) referenciák feloldódnak.

A 730d DDE626 szoftverben ez közvetlenül látszik. A CAL init előtt futó detektáló rutin (0x464F48) egy hardver bitet, két kalibrációs byte-ot, valamint a flash és a RAM oldali azonosítót veti össze. Ha emulációs hardvert talál, a referencia tábla helyett egy második pointer táblát (0x50C10) választ, amely ugyanazt a 440 blokkot a 0x010Cxxxx címeken éri el. Éles ECU-n ez az ág nem aktiválódik, a mirror ablak ilyenkor egyszerűen a flash-t tükrözi.

A vizsgált 730d mintában a feltárt fix mirror-hivatkozások a 0x0C03D0 - 0x0C3A37 tartományra mutatnak, míg a későbbi blokkoknál az r15-alapú hozzáférés jellemző. A teljes lefedettség kimondásához a közvetett és az initkor eltárolt referenciákat is ellenőrizni kell.

A kalibrációs rendszer működés közben

Eddig azt néztük meg, hogyan van felhúzva maga az infrastruktúra: milyen úton-módon jut el a kód a kalibrációs adatokig. Most jöjjön két konkrét modul, amin keresztül látszik, hogy a firmware ténylegesen mihez kezd ezekkel az adatokkal, amikor a motort kell vezérelni.

Eset 1: Main Torque Limiter

A fő nyomaték limiter dolga egyszerű: a fordulatszám és a variant coding alapján megmondja, mennyi nyomatékot szabad kiadni. Egyetlen pointer tábla bejegyzésből dolgozik, semmi több.

Main TQ Limiter Ghidra listing

A Main Torque Limiter wrapper függvény Ghidrában, az annotátor által feloldott pointer referenciákkal.

Main Torque Limiter
lwz     r31, 0x268(r15)        ; PTR[0x268] = kalibrációs blokk (408 byte)
lha     r3, -0x6844(r13)       ; r3 = RPM (X tengely)
lbz     r4, -0x7793(r13)       ; r4 = Variant Coding (Y tengely)
addi    r5, r31, 0xa           ; r5 = map header
subi    r6, r13, 0x4216        ; r6 = interpolátor cache (RAM)
bl      FUN_004541cc             ; 2D INTERPOLÁCIÓ
Main TQ Limiter map WinOLS

A Main Torque Limiter 21x8 térkép a WinOLS-ben. X tengely: variant coding (0-7), Y tengely: RPM (600-5060), Z: nyomaték Nm-ben.

A 2D map struktúrája a flash-ben:

OffsetTípusTartalom
+0x00int16X_size = 21 (RPM tengelypont darabszám)
+0x02int16Y_size = 8 (variant coding tengelypont darabszám)
+0x04int16[21]RPM töréspontok: 600, 800, 1000, ... 5060
+0x2Eint16[8]Variant coding: 0, 1, 2, ... 7
+0x3Eint16[168]Nyomaték értékek (21 x 8, Nm)

A 2D interpolátor bilineáris interpolációt csinál 16.16 fixpontos aritmetikával, cached tengely pozícióval. A "cached" itt azt jelenti, hogy az interpolátor megjegyzi, hol járt legutóbb a tengelyen, és a következő híváskor onnan indul. Ha az RPM az előző hívás óta alig változott, ami motornál elég gyakori helyzet, a szegmens megkeresése szinte azonnal megvan.

2D interpolátor Ghidra listing

A 2D interpolátor függvény eleje: X_size és Y_size olvasás, majd az X tengely keresés indítása.

Az interpoláció után van még egy érdekes kis részlet, egy korrekciós ág:

Korrekciós ág - Enable byte ellenőrzés
lbz     r11, 0x1(r31)         ; korrekciós enable flag
cmpwi   r11, 0x0
beq     skip                  ; ha 0: nincs korrekció

; korrekciós szorzás (a vizsgált kalibrációban inaktív, enable byte = 0):
lha     r11, -0x687e(r13)    ; runtime szorzó
mullw   r3, r12, r11        ; result x factor
li      r4, 0x2000           ; 8192 = Q13 fixpont (1.0x)
bl      FUN_0045380c          ; r3 = (result x factor) / 8192, szaturált

skip:
sth     r3, -0x720e(r13)     ; output = Main TQ Limiter eredmény

Ez jól mutatja a Bosch fejlesztési filozófiáját: a kódban ott van egy runtime korrekciós szorzó, de a konkrét kalibrációban ki van kapcsolva. A Bosch modulárisan fejleszt, a funkciókat pedig kapcsolókkal engedi vagy tiltja. Ami az egyik OEM kalibrációban aktív, a másikban simán ki lehet kapcsolva, a kód viszont mindig ott marad, ha esetleg kell.

Eset 2: Boost Pressure Control - Töltőnyomás szabályozás

A boost modul egy tekintélyes méretű függvény, és egyetlen pointer tábla bejegyzésből szedi ki az összes adatát: PTR[0x558], ami egy 4492 bájtos kalibrációs blokkra mutat. Ezen a blokkon belül több mint 20 térkép és konstans van.

Boost module map selection Ghidra

A boost modul map-választó logikája: egy bit-teszt dönti el, hogy a base vagy az alternatív boost target map-et olvassa.

Base boost target kiválasztás
lwz     r31, 0x558(r15)        ; PTR[0x558] = boost kalibráció (4492 byte blokk)
lha     r3, -0x6844(r13)       ; r3 = RPM
lha     r4, -0x6df0(r13)       ; r4 = befecskendezési mennyiség (mg/cyc)

lbz     r12, runtime_flag      ; runtime flag byte
rlwinm. r12, r12, 0x1f, 0x1f, 0x1f  ; bit 1 teszt
beq     use_base              ; bit 1 = 0: base map

; bit 1 = 1: alternatív boost map
addi    r5, r31, 0x568        ; alt boost target
b       call_interp

use_base:
addi    r5, r31, 0x9fe        ; base boost target

call_interp:
bl      FUN_004541cc          ; 2D INTERPOLÁCIÓ, r3 = boost target [mbar]
Base boost target map WinOLS

A base boost target 16x16 térkép a WinOLS-ben. X tengely: befecskendezés (mg/cyc, 0.01 faktor), Y tengely: RPM, Z: kívánt töltőnyomás mbar-ban.

A base boost target után a kód gyors egymásutánban két DPF regenerációhoz kötődő boost map-et is beolvas:

DPF regen boost maps Ghidra

A DPF regeneráció phase 1 és phase 2 boost map hívások egymás után.

DPF Regen boost map-ek - ugyanazon pointer tábla bejegyzésből
; DPF Regen Phase 1 boost target:
addi    r5, r31, 0x572        ; regen phase 1 boost
bl      FUN_004541cc          ; 2D INTERPOLÁCIÓ

; DPF Regen Phase 2 boost target:
addi    r5, r31, 0x7b6        ; regen phase 2 boost
bl      FUN_004541cc          ; 2D INTERPOLÁCIÓ

Tehát három boost target fekszik párhuzamosan kiolvasva (base/alt, regen phase 1, regen phase 2), és a modul későbbi logikája dönti el, melyiket küldi tovább célértékként a szabályozóra.

A boost blokk barometrikus korrekciói

A boost blokkon belül négy olyan térkép is van, ami a légköri nyomást használja Y tengelyként (RPM x baro):

Boost limit vs atmospheric pressure WinOLS

A boost limit vs légköri nyomás térkép a WinOLS-ben: az egyetlen ténylegesen kalibrált barometrikus térkép ebben a szoftverben.

Négyből viszont csak egy aktív. Háromféle korrekciós mechanizmus van beépítve a kódba (additív, multiplikatív, és egy abszolút limit), de ebben a BMW kalibrációban a BMW kalibrátorok csak a limit táblát kapcsolták be. Szokásos Bosch mintázat, mindent beletesznek a szoftverbe, aztán az OEM kalibrátor eldönti, melyikkel akar dolgozni.

A boost blokk teljes térképe

Egyetlen pointer tábla entry mögött 22+ kalibrációs objektum rejtőzik:

OffsetFunkció
+0x3CBaro korrekció (additív, inaktív)
+0xE0Baro korrekció (multiplikatív, semleges)
+0x1841D karakterisztika
+0x19E2D map
+0x202, +0x224, +0x23E1D karakterisztikák
+0x25C, +0x3B0, +0x5042D map-ek (mg/cyc x RPM)
+0x568Alt boost target
+0x572DPF regen phase 1 boost
+0x7B6DPF regen phase 2 boost
+0x9FEBase boost target
+0xC42Baro korrekció (additív, inaktív)
+0xE86Boost limit vs légköri nyomás
+0x10CE2D map (RPM x mg/cyc)
+0x1134, +0x113832-bit kompárátorok
+0x1144Rate limiter / ramp
+0x1150, +0x1154Threshold konstans + 1D karakterisztika

Automatizáció: A Ghidra Annotáló Script

Miután a manuális nyomkövetéssel megértettük a rendszert, egy Ghidra Python script segítségével az egészet automatizálhatjuk. A script mind a négy szinten dolgozik egyszerre:

4 level script Ghidra

A Ghidra annotáló script eredménye.

SzintMit csinál
L1: r15 pointer táblaMinden lwz rX, offset(r15) mellé beírja a feloldott pointert és méretet
L2: r2/r14 SDA directMinden r2/r14-relatív load/store mellé kiírja a kalibrációs címet
L3: Regiszter propagációaddi, mr, or másolásokat követ, A2L symbol lookup
L4: lis + load mirrorlis rX, imm ; load rY, offset(rX) feloldja a mirror címet

Az annotáló script konfigurációjában meg kell adni a pointertábla címét, a bázisregisztereket és az érintett címtartományokat. A felismert minták több vizsgált szoftverben újrahasznosíthatók, de eltérő fordítói minták vagy memóriaelrendezés esetén a felismerési logikát is módosítani kell.

A script háromféle módban tud futni, attól függően, hogy milyen kalibrációs információ van a kezünk ügyében:

Egy vizsgált mintán a hybrid futtatás körülbelül 17800 annotációt adott 1700 függvényben, mintegy 3870 azonosított függvény mellett. Ezek az adott elemzés statisztikái, önmagukban nem bizonyítják a teljes ECU-család azonosságát.

A kódbázis regiszter-statisztikájából is szépen kilátszik a multi-regiszter architektúra:

RegiszterFunkciók számaMegjegyzés
r31190Leggyakoribb (hagyományos base regiszter)
r12178Scratch regiszter, közvetlen pointer felhasználás
r3085Második leggyakoribb dedikált base
r2948Pl. CoEng (DPF nyomatékkorlátozás) modul
r2829Pl. Rail Pressure Control modul
r27-r2438Ritkábban használt base regiszterek

DAMOS integráció

Ahol a DAMOS (a Bosch kalibrációs adatbázisa) rendelkezésre áll, ott a pointer címek helyére beírhatók az igazi funkcionális nevek:

DAMOS annotáció példa - Légmennyiségmérő diagnosztika
lwz     r12, 0x80(r15)       PTR[0x80], AFSCD_DebPlHiSetyDrftDef_C
addi    r4, r12, 0x27e      calib: AFSCD_swtSensInstVal_C
li      r3, 0x15
bl      FUN_0007df84

Térképstruktúra és testreszabás

Eddig arról volt szó, hogyan találjuk meg a kalibrációs adatokat. De mit kezdjünk velük, ha egyszer megvannak? A Bosch 2D interpolátor a térkép méretét dinamikusan olvassa a headerből, és ez elképesztően hasznos tulajdonság: azt jelenti, hogy a térképeket bővíthetjük anélkül, hogy a kódhoz hozzá kellene nyúlnunk.

A 2D térkép bináris felépítése

Az alábbi, 16 bites tengelyeket és értékeket használó térképformátumnál a vizsgált interpolátor a fejlécből olvassa ki a két méretet. Más objektumok és interpolátorrutinok eltérő formátumot, adattípust vagy skálázást használhatnak.

2D Map bináris struktúra (Bosch II header)
+0x00:  int16    ; X_size (pl. 0x0010 = 16 pont)
+0x02:  int16    ; Y_size (pl. 0x0010 = 16 pont)
+0x04:  int16[X] ; X tengely töréspontok (pl. RPM: 500, 750, 1000, ...)
+0x04+X*2: int16[Y] ; Y tengely töréspontok (pl. Nm: 0, 20, 40, ...)
+0x04+X*2+Y*2: int16[X*Y] ; Z értékek (interpolált kimeneti adat)

Ennél a formátumnál a méret 4 + X*2 + Y*2 + X*Y*2 bájt. A vizsgált interpolátor a fejlécből számolja az adatok helyét. Bővítés előtt a többi hivatkozást, a cache formátumát és az indexek megengedett tartományát is ellenőrizni kell.

Az interpolátor működése

A 2D interpolátor (FUN_00457794 és társai) lépésről lépésre kb. ezt csinálja:

2D interpolátor - Működési elv
; 1. Header olvasás:
lhz     r11, 0x0(r5)          ; X_size = header[0]
lhz     r12, 0x2(r5)          ; Y_size = header[1]

; 2. X tengely keresés (cached):
;    Lineáris keresés a töréspontok között, az előző hívás
;    indexéből indulva (gyors, ha az RPM alig változott)

; 3. Y tengely keresés (cached):
;    Ugyanaz, a Y_size alapján számított offset-ről

; 4. Bilineáris interpoláció:
;    16.16 fixpontos szorzás a 4 szomszédos Z értékből
;    Eredmény: int16 a hívó regiszterbe (r3)

A cached tengely keresés lényege, hogy az interpolátor eltárolja az előző hívás X és Y szegmens indexét egy RAM-ban lévő cache struktúrában (amit r6-ban kap paraméterként). Ha a bemenet alig változott - márpedig motornál ez a tipikus, az RPM és a terhelés nem szokott ciklusról ciklusra ugrálni - a keresés 1-2 összehasonlítás után már meg is van.

Térképek bővítése

Ha az adott interpolátor és minden hívó támogatja a nagyobb méretet, a fejléc és az adatok módosításával bővíthető a térkép. A 16x16 és 32x16 méret közötti különbség:

16x16 (eredeti)32x16 (bővített)
Header4 byte4 byte
X tengely32 byte64 byte
Y tengely32 byte32 byte
Z adat512 byte1024 byte
Összesen580 byte1124 byte

A példában 1124 - 580 = 544 bájt többlethely szükséges. Ez nem fér el 50-200 bájt kitöltésben. Csak igazoltan használaton kívüli terület vehető igénybe: FF vagy 00 bájtok, illetve a következő látható térkép távolsága önmagukban nem bizonyítanak szabad helyet.

Ha helyben nincs elég igazolt terület, az objektum áthelyezhető, de az összes hivatkozását és a checksumokat frissíteni kell. Egy 4 bájtos addi helyére nem fér be közvetlenül két 4 bájtos lis/ori utasítás. Ehhez ellenőrzött kódátalakítás vagy külön kódrészre ugrás szükséges, a regiszterek és a folytatás megőrzésével.

Gyakorlati példa: Nyomaték/Mennyiség átváltás bővítése

A bemutatott Nm/IQ térkép nyomaték és befecskendezési mennyiség között teremt kapcsolatot. A tartomány bővítése csak akkor indokolt, ha a célkalibráció ezt igényli. Az új tartományba extrapolált értékek számítási kiindulópontok, motorüzemi mérés nélkül nem tekinthetők validált kalibrációnak.

32x16 Nm-IQ conversion map

A kibővített 32x16 Nm-IQ átváltó térkép: az X tengely 0-tól 1400 Nm-ig, 32 töréspont. Az eredeti 16 pontos tengely dupla felbontásra bővítve, a 650 Nm feletti tartomány extrapolált értékekkel feltöltve.

A bővítés menete:

LépésMit csinálunk
1. Padding ellenőrzésMegnézzük, mennyi szabad hely van a térkép vége és a következő objektum headere között
2. Maximum méret kalkulációA szabad hely alapján: (hely - 2 - Y) / (1 + Y) = max X_size, ahol a hely a térkép jelenlegi mérete plusz a padding, 16 bites értékekben
3. Header átírásX_size módosítása 16-ról 32-re (egyetlen szó a flash-ben)
4. X tengely bővítésÚj töréspontok hozzáadása (pl. 650, 700, 750, ... 1400 Nm)
5. Z adat kitöltésA meglévő adatokból lineáris extrapoláció az új tartományra

A következő induláskor az interpolátor a 32-es X_size-t olvassa ki a headerből, és 32 töréspontot keres az X tengelyen. A kód nem változott, csak az adat.

INFO: A helyigény mellett az interpolátor és az összes hivatkozás korlátait is ellenőrizni kell. Egy 624 értéknyi (16 bites) teljes hely mellett, 16 pontos Y tengellyel, a max X méret 35. Ebben a számpéldában a 32x16 méret belefér. Ez nem bizonyítja, hogy a konkrét binárisban ekkora szabad terület rendelkezésre áll.

Kivétel: EDC16C31 DDE5 - Az MPC5xx világ

A fenti DDE6 példák pointertáblás címzésétől eltérően az itt vizsgált MPC555-alapú DDE5 mintában közvetlen kalibrációs hivatkozások találhatók. Ez konkrét binárisra vonatkozó megfigyelés; az ECU családneve alapján másik szoftverre nem vihető át automatikusan.

MPC555: Más hardver, más világ

Az MPC555 az MPC500 családba tartozik, és az MPC562-höz képest komolyan eltér a memória-architektúrája:

JellemzőVizsgált DDE6, belső flash-es példaEDC16C31 MPC555 (DDE5)
ProcesszorMPC563, a belső flash-es példábanMPC555
Internal Flash0x400000 - 0x47FFFF (512 KiB, belső flash-es változat)0x000000 - 0x06FFFF (448 KB)
External Flash0x000000 - 0x1FFFFF (2 MB)0x800000 - 0x8FFFFF (1 MB)
Internal SRAM0x3F8000 - 0x3FFFFF (32 KB CALRAM)SRAM A: 0x3F9800 - 0x3FBFFF (10 KB)
SRAM B: 0x3FC000 - 0x3FFFFF (16 KB)
External SRAM0x6F8000 - 0x8FFFFF (több chip)0x400000 - 0x401FFF (~8 KB)
r130x007FFFF0 (EXT_SRAM_B)0x004017F0 (EXT_SRAM)
Kalibráció címzésr15 pointer táblaKözvetlen (direkt) címzés

Direkt kalibrációs címzés - r15 pointer tábla nélkül

A vizsgált MPC555-mintában nem azonosítottam a fenti DDE6-féle r15 pointertáblás rendszert. A bemutatott kód közvetlenül hivatkozik az adott kalibráció címére:

MPC563 (r15 pointer tábla) vs MPC555 (direkt címzés)
; MPC563 EDC16CP35 DDE6 
; Két lépcsős indirekció:
lwz     r31, 0x268(r15)      ; 1. pointer tábla - blokk cím
addi    r5, r31, 0xa         ; 2. blokkon belüli offset
bl      interpolate_2D

; MPC555 EDC16C31 DDE5
; Közvetlen flash hivatkozás:
lis     r5, 0x8C              ; r5 = 0x008C0000 (ext flash kalibrációs terület)
addi    r5, r5, 0x3FE4       ; r5 = 0x008C3FE4 (konkrét térkép cím)
bl      interpolate_2D

A példabeli kalibrációs hivatkozások lis + addi vagy lis + ori utasításokkal képzett címeket használnak. A DDE6 setteres megoldását ebbe a mintába nem lehet változtatás nélkül átvenni.

Ez az egyszerűbb felépítés a statikus elemzésnek kifejezetten kedvez (Ghidrában azonnal látszik, hova mutat egy hivatkozás), viszont a kalibrációs terület módosítását nehezíti: ha áthelyezünk egy térképet, az összes kódreferenciát meg kell keresni és átírni. Az MPC562 r15 pointer táblás megoldásánál ehhez képest egyetlen pointer entry módosítása elég.

INFO: Az MPC555 DDE5 bináris két részből áll a flash fájlban: az internal flash (0x000000 - 0x06FFFF, 448 KB) és az external flash (file offset 0x070000-tól, CPU cím 0x800000 - 0x8FFFFF, 1 MB). Ghidrába külön memory block-ként kell betölteni őket, az external flash-t 0x800000 báziscímre mappolva.

Ghidra r13 konfigurálás MPC555-höz

Az MPC555-nél az r13 = 0x004017F0, és a signed 16-bit offset tartomány (+-0x8000) mindkét SRAM tartományt lefedi: a belső SRAM-ot negatív offset-tel (pl. r13 - 0x4D08 = 0x3FCAE8, ez SRAM B-be esik), a külső SRAM-ot meg pozitív offset-tel. Ghidrában a regiszter beállításnál erre kell figyelni: az assume r13 = 0x4017F0 a helyes érték, nem a 0x7FFFF0, amit az MPC562-nél használunk.

Mapswitch, a bevált működés és az ellenőrzés határai

A mapswitch külön kalibrációs bankok közötti választást tesz lehetővé. A vizsgált DDE6 megoldásban a klónozott blokkokhoz külön pointertáblák tartoznak, és a váltás a gyári kalibrációs setteren keresztül történik.

Mit vált át az r15?

Az r15 módosítása az ezen keresztül elért blokkokat irányítja át. A közvetlen címzésű adatok, az initkor RAM-ba másolt értékek és a már eltárolt pointerek nem váltanak át automatikusan. A "teljes kalibráció" lefedettségét ezekkel együtt kell vizsgálni. Egy teljes bank másolata nem bizonyítja, hogy minden objektuma futás közben is arról olvasódik.

A klónnak a referencia tábla sorrendjét, darabszámát és lezáró bejegyzését is követnie kell. A stock bank deltája nulla. Az új helyen lévő blokkok pointereit a saját bankjukra kell relokálni, és a címeket használat előtt ellenőrizni kell.

Váltás a gyári setterrel

A vizsgált setterben az r3=0xFF megtartja az előző page-választást. Ez nem garantálja a default ág használatát. A default pointerre épülő mapswitchnél r3=0 választja ki a megfelelő ágat. Ez a paraméterjelentés az adott setterből származik, új szoftverben ellenőrizni kell.

Szemléltető hívás, ellenőrzött klónpointerrel
cal_select:
    stwu    r1, -0x10(r1)
    mflr    r0
    stw     r0, 0x14(r1)
    ; r4 = előzőleg ellenőrzött kalibrációs pointertábla
    stw     r4, CAL_ActivePtr_Default(r13)
    li      r3, 0
    bl      CAL_SetActiveDataset
    lwz     r0, 0x14(r1)
    mtlr    r0
    addi    r1, r1, 0x10
    blr

A példa nem teljes patch. A hívó környezet, a stack-frame, az élő regiszterek és a hívott rutin mentési helyei együtt ellenőrzendők. Korábbi tesztben a hívott rutin LR-mentése felülírt egy rossz helyre mentett regisztert. A gyári setter használata ezt a hívóoldali hibát nem oldja meg.

Init-cache és korábbi hibák

Az induláskor eltárolt értékek és pointerek külön vizsgálatot igényelnek. Ha egy bank ezek forrását megváltoztatja, a RAM-ban maradhat a korábbi érték. Az elemzés során talált cache-ek jelenléte azonban önmagában nem bizonyítja, hogy egy adott korábbi CAN-hibát vagy hibás kijelzést ezek okoztak.

A korábbi hibakeresésekben stack- és regisztermentési hiba, hibás klónellenőrzés, valamint nem összetartozó flash-részek is szerepet kaptak. A tüneteket ezért reprodukálható teszthez és konkrét kódhibához kell kötni. Nem helyes minden hibát az r15-váltás vagy egy feltételezett cache-probléma következményének nevezni.

Futásidejű váltásnál az érintett cache-ek és a közvetlen hivatkozások kezelését kell megoldani. Bootkori kiválasztásnál a bankot még az érintett inicializálók előtt kell beállítani. Utóbbi esetben a mentett választás helyét, visszaolvasását és tartományellenőrzését is igazolni kell.

Gyári kezelőszervek és fordulatszámmérős visszajelzés

A menüs változatnál fék és friss tempomat "+" nyitja a választót. Ezután "+" és "-" léptet, a timeout véglegesíti a választást. A fordulatszámmérő a slot számát jelzi, miközben a motorvezérlés tényleges fordulatszámjelét nem módosítjuk.

A vizsgált E70 szoftver fontos eltérést mutatott: a másik változatból átvett feldolgozott tempomatjel nulla maradt, miközben a gyári tempomat működött. A fogadott karjel az első és a második nyomáspontnál külön kódot tartalmazott. A gyári jelleíró, a dekóder és két élő RAM-minta alapján azonosított bemenettel a javított mapswitch működését az autós teszt visszaigazolta.

A két nyomáspont nem azonos a rövid és hosszú nyomással. Ugyanazon irány első és második nyomáspontja közötti átmenet nem számíthat új lenyomásnak. Ez az E70/E71 portoknál hasznos kiindulás, de a konkrét címeket és a jelutat minden szoftverben ellenőrizni kell; külön E71 hardvertesztet ez az E70-mérés nem helyettesít.

RAM-foglalás, mentések és ellenőrzések

A Ghidra "??" jelölése, az XREF hiánya vagy egy nullás RAM-minta nem bizonyít szabad memóriát. A gyári RAM-poolból, a leíró megfelelő módosításával leválasztott állapotterület jobban ellenőrizhető. Az allokáció határait és a tényleges terhelést így is vizsgálni kell.

A négyhengeres, iFlash nélküli változatoknál a teljes eFlash és EEPROM szükséges. A belső flash-es hathengereseknél az iFlash is része az összetartozó készletnek. Egy csak részben frissített ECU megtévesztő teszteredményt adhat. A módosított szegmensek checksumját ellenőrizni kell, az új bankok gyári checksum-lefedettségét pedig külön fel kell tárni.

Az emuláció ellenőrzi a végrehajtott kódutakat, de a mesterségesen beadott bemenet nem igazolja az autó tényleges jelét. Az élő RAM-minta és az autós teszt ezért külön bizonyíték. A buszhibák, a jelkimaradás, a hidegindítás és a bankok közötti átmenetek külön tesztesetek.

Egyetlen kimeneti érték felülírása

Egy RAM-ban tárolt köztes vagy kimeneti érték felülírása egyes célokra használható, de nem helyettesíti a teljes kalibrációs bankváltást. Nem állítható általánosan, hogy az értéket semmilyen monitor nem figyeli, vagy hogy minden motor- és váltóvédelem változatlanul érvényesül. Ehhez az írás helyét, a további feldolgozást és az összes érintett ellenőrzést fel kell tárni.

Tapasztalat és újrafelhasználhatóság

A vizsgált DDE6 szoftverek közös mintái jelentősen gyorsítják a következő portot. A konkrét hardver, a címek, a kalibrációs lefedettség és a kezelőszervek jelútja azonban újra ellenőrizendő. A belső flash nélküli négyhengeres és a külön iFlash-t használó hathengeres változatot a mentés és a kód betöltése során is meg kell különböztetni.

A működő megoldást a statikus elemzés, az emuláció és a valós ECU-n végzett teszt együtt támasztja alá. A bizonyított eredményeket külön kezelem a feltételezésektől. Az egyedi megoldás egy autóhoz kötött; a kötés technikai részletei nem részei ennek a cikknek.

Hardveres források

NXP MPC561/MPC563 referencia kézikönyv, MPC562/MPC564 változatokkal
NXP MPC500 memóriaáttekintés