BMW EDC16: kalibrációs címzés és mapswitch négy- és hathengeres motoroknál
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.
| Blokk | Cím | Méret | Tartalom |
|---|---|---|---|
Külső Flash | 0x000000 - 0x1FFFFF | 2 MB | Alkalmazás kód + kalibrációs terület |
Belső Flash | 0x400000 - 0x47FFFF | 512 KiB | Bosch 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áltozat | Programtartalom | Kiinduló mentés |
|---|---|---|
| Négyhengeres DDE6, külön iFlash nélkül | Program és kalibráció az eFlash-ben | EEPROM és teljes eFlash |
| Hathengeres DDE6, belső flash-es kivitel | Összetartozó iFlash és eFlash | EEPROM, 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ány | Méret | Funkció (A2L elemzés alapján) |
|---|---|---|---|
| INT_CALRAM (on-chip) | 0x3F9800 - 0x3FFFFF | 26 KB | Belső futási adatok, a pontos felhasználás kódellenőrzést igényel |
| CS0 - External Flash | 0x000000 - 0x1FFFFF | 2 MB | Applikációs kód + Kalibrációs terület |
| EXT_SRAM_A | 0x6F8000 - 0x6F87FF | 2 KB | Mérőpont puffer (kis, kiemelt): 99.2% _mp suffix (Messpunkt), ~128 db kritikus measurement point |
| EXT_SRAM_B | 0x7F8000 - 0x7FFFFF | 32 KB | Fő alkalmazói RAM (SDA, r13-relatív): 2469 futási változó, state machines, FrmMng, InjCrv, Lambda... |
| EXT_SRAM_C | 0x800000 - 0x807FFF | 32 KB | Stack + 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_D | 0x8FE000 - 0x8FFFFF | 8 KB | Nagy 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ó | Tartalom | Hozzáférés |
|---|---|---|
0x040000 - 0x052000 | Skalár kalibrációk, flag-ek, konfiguráció | r2/r14-relatív (SDA direct) |
0x0C0000 - 0x0FFFFF | Map-ek, táblák, karakterisztikák | Pointer 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.
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.
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.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:
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:
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.
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.
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.
| Szint | Mechanizmus | Példa |
|---|---|---|
| 1 - r15 Pointer tábla | lwz rXX, offset(r15) - pointer tábla entry, kalibrációs blokk base | [r15 + 0x5CC] = 0x0F5078 |
| 2 - Közvetlen SDA | lwz 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ül | addi r3, r31, 0x4FC = 0x0F5574 |
| 4 - Flash mirror | lis rXX, imm ; load rYY, offset(rXX) - 0x01000000+ tükörcím | lis 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.
; 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:
| Elem | Cím |
|---|---|
| CAL_SetActiveDataset (setter) | 0x11E918, r15 írás: 0x11E964 |
| Kritikus szekció enter / exit | 0x1F488 / 0x1F4B0 |
| CAL init | 0x11E994 (init lista 0x43614, 8. elem) |
| CCP SET_CAL_PAGE hívások | 0x124A38 / 0x124A44 |
| CAL_PageSelect / CAL_RamCopyState | r13 + 0x2DCD / r13 + 0x2DCC |
| CAL_ActivePtr_Default / Alt | r13 + 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:
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.
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ó:
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.
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.
A Main Torque Limiter wrapper függvény Ghidrában, az annotátor által feloldott pointer referenciákkal.
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Ó
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:
| Offset | Típus | Tartalom |
|---|---|---|
+0x00 | int16 | X_size = 21 (RPM tengelypont darabszám) |
+0x02 | int16 | Y_size = 8 (variant coding tengelypont darabszám) |
+0x04 | int16[21] | RPM töréspontok: 600, 800, 1000, ... 5060 |
+0x2E | int16[8] | Variant coding: 0, 1, 2, ... 7 |
+0x3E | int16[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.
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:
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.
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.
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]
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:
A DPF regeneráció phase 1 és phase 2 boost map hívások egymás után.
; 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):
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:
| Offset | Funkció |
|---|---|
+0x3C | Baro korrekció (additív, inaktív) |
+0xE0 | Baro korrekció (multiplikatív, semleges) |
+0x184 | 1D karakterisztika |
+0x19E | 2D map |
+0x202, +0x224, +0x23E | 1D karakterisztikák |
+0x25C, +0x3B0, +0x504 | 2D map-ek (mg/cyc x RPM) |
+0x568 | Alt boost target |
+0x572 | DPF regen phase 1 boost |
+0x7B6 | DPF regen phase 2 boost |
+0x9FE | Base boost target |
+0xC42 | Baro korrekció (additív, inaktív) |
+0xE86 | Boost limit vs légköri nyomás |
+0x10CE | 2D map (RPM x mg/cyc) |
+0x1134, +0x1138 | 32-bit kompárátorok |
+0x1144 | Rate limiter / ramp |
+0x1150, +0x1154 | Threshold 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:
A Ghidra annotáló script eredménye.
| Szint | Mit csinál |
|---|---|
| L1: r15 pointer tábla | Minden lwz rX, offset(r15) mellé beírja a feloldott pointert és méretet |
| L2: r2/r14 SDA direct | Minden 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 mirror | lis 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:
- r15 POINTER TABLE: csak pointer tábla
- DIRECT SDA: csak r2/r14-relatív
- HYBRID: minden szint egyszerre
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:
| Regiszter | Funkciók száma | Megjegyzés |
|---|---|---|
r31 | 190 | Leggyakoribb (hagyományos base regiszter) |
r12 | 178 | Scratch regiszter, közvetlen pointer felhasználás |
r30 | 85 | Második leggyakoribb dedikált base |
r29 | 48 | Pl. CoEng (DPF nyomatékkorlátozás) modul |
r28 | 29 | Pl. Rail Pressure Control modul |
r27-r24 | 38 | Ritká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:
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.
+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:
; 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) | |
|---|---|---|
| Header | 4 byte | 4 byte |
| X tengely | 32 byte | 64 byte |
| Y tengely | 32 byte | 32 byte |
| Z adat | 512 byte | 1024 byte |
| Összesen | 580 byte | 1124 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.
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és | Mit csinálunk |
|---|---|
| 1. Padding ellenőrzés | Megné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ás | X_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és | A 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.
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élda | EDC16C31 MPC555 (DDE5) |
|---|---|---|
| Processzor | MPC563, a belső flash-es példában | MPC555 |
| Internal Flash | 0x400000 - 0x47FFFF (512 KiB, belső flash-es változat) | 0x000000 - 0x06FFFF (448 KB) |
| External Flash | 0x000000 - 0x1FFFFF (2 MB) | 0x800000 - 0x8FFFFF (1 MB) |
| Internal SRAM | 0x3F8000 - 0x3FFFFF (32 KB CALRAM) | SRAM A: 0x3F9800 - 0x3FBFFF (10 KB) SRAM B: 0x3FC000 - 0x3FFFFF (16 KB) |
| External SRAM | 0x6F8000 - 0x8FFFFF (több chip) | 0x400000 - 0x401FFF (~8 KB) |
| r13 | 0x007FFFF0 (EXT_SRAM_B) | 0x004017F0 (EXT_SRAM) |
| Kalibráció címzés | r15 pointer tábla | Kö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 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.
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.
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
blrA 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