Reverse Engineering Bosch ME9/MED9: Komplex Multimap, Kétcsatornás CAN Telemetria és NVRAM Integráció
A modern motorvezérlők (ECU-k) szoftverének módosítása hagyományosan a kalibrációs térképek (töltőnyomás, előgyújtás, üzemanyag) átírására korlátozódik. Amikor azonban egy többprogramos "Multimap" rendszert és egy különálló Fuel memóriát építünk be, a gyári architektúra határait kell feszegetnünk.
Hogyan váltson programot a sofőr fizikai kapcsolók utólagos beszerelése nélkül? Hogyan adjunk azonnali vizuális visszajelzést a kiválasztott funkciókról? És hogyan menthetjük el ezeket az állapotokat úgy, hogy az ECU áramtalanítás után is emlékezzen rájuk? Ebben a cikkben bemutatjuk, hogyan lehet a PowerPC alapú Bosch vezérlők gyári kódját assembly szinten megpatchelni.
A "Stolen Instruction" Hook: A Műszerfal Kétcsatornás Eltérítése
Az ECU belső memóriájában a járműsebesség (km/h) és a fordulatszám (RPM) folyamatosan frissül, mielőtt a rendszer kiküldené a CAN-buszra a műszeregység (Cluster) felé. Ha térképváltáskor vizuális visszajelzést szeretnénk, be kell avatkoznunk ebbe az adatfolyamba. A legszebb az egészben, hogy a két mutatót két teljesen független csatornaként használhatjuk!
A megoldás az úgynevezett "Stolen Instruction" (ellopott utasítás) metódus. Megkeressük a gyári kódban azokat a pontokat, ahol ezek az értékek bekerülnek a CAN buffer regisztereibe. Ezt a két gyári utasítást egy-egy feltétel nélküli ugrással (b) lecseréljük a saját rutinunkra.
A Rendszer Lelke: Kijelzés és Állapottárolás egy lépésben
Amikor a sofőr álló motornál (biztonsági feltétel: B_stend) a megfelelő pedálkombinációkkal aktiválja a térképváltókat, a rutinunk egy szinkronizált kettős műveletet hajt végre mindkét csatornán:
1. Vizuális visszajelzés generálása:
- A Sebességmérő (km/h) mint Fuel Switcher: A rutin egy fix értéket tölt a CAN regiszterbe (pl. 95 km/h = normál benzin, 85 km/h = E85 aktív).
- A Fordulatszámmérő (RPM) mint Performance Switcher: A teljesítményszinteket az RPM mutató jelzi vissza (pl. 1000 RPM = Alap töltőnyomás, 3000 RPM = ALS bekapcsolva).
2. Globális állapotmentés:
Ezzel a kijelzéssel egyidőben, a rutin az aktuális programok sorszámait elmenti az NVRAM (Non-Volatile RAM) tükör általunk előzetesen kiszemelt, gyárilag üres bájtjaiba (fuel_mapselector és perf_mapselector). Miután a sofőr felengedi a pedálokat, a műszerfal visszatér a normál működéshez, de a RAM tükörben ott maradnak az elmentett állapotok.
A Map Switch valódi működése: Pointer-szintű architektúra manipuláció
Fontos megérteni, hogy egy professzionális map switch nem a térképek tartalmát módosítja futásidőben. Semmilyen Flash írás nem történik! A Bosch ME9 architektúrában a kalibrációs térképek fix Flash címeken találhatók, a futó kód pedig memóriamutatókon (pointereken) keresztül fér hozzájuk.
Normál működés során a kód fixen betölti egy térkép címét, majd átadja a gyári 2D/3D interpolációs rutinnak. Map switch esetén mi egy indirekt címzést hozunk létre – pontosan úgy, mint egy virtuális függvénytábla (vtable) a C programozási nyelvben. A fix pointer-betöltést lecseréljük egy hook-ra, ami dinamikusan, a mi mapselector RAM változónk alapján dönti el, melyik memóriacímet adja át az ECU-nak.
Nézzünk egy példát a gyújtástérkép (KFZW) esetében:
select_ignition_map_ptr: lbz r4, perf_mapselector(r13) ; Kiolvassuk az elmentett állapotot cmpwi r4, 0 beq use_base cmpwi r4, 1 beq use_102 cmpwi r4, 2 beq use_e85 use_base: lis r3, 0x001C ; KFZW_BASE (Gyári: 0x1C7649) ori r3, r3, 0x7649 blr use_102: lis r3, 0x001D ; KFZW_102 (0x1D2000) ori r3, r3, 0x2000 blr use_e85: lis r3, 0x001D ; KFZW_E85 (0x1DA000) ori r3, r3, 0xA000 blr
A Hook tényleges beillesztése a gyári térképolvasó rutinba
A fent bemutatott select_ignition_map_ptr rutin önmagában még nem elég. A kulcs az, hogy megtaláljuk azt a konkrét pontot a gyári firmware-ben, ahol az ECU betölti a térkép báziscímét, és ezt az utasítást lecseréljük a saját hook-unkra.
A Bosch ME9 firmware tipikusan így tölti be egy 3D térkép címét:
lis r3, 0x001C ; Statikus cím betöltése (felső 16 bit) ori r3, r3, 0x7649 ; Statikus cím betöltése (alsó 16 bit) bl interpolate_3d_map ; Gyári interpoláció hívása
Map switch implementáció során ezt a statikus betöltést felülírjuk:
bl select_ignition_map_ptr ; A mi dinamikus címválasztónk bl interpolate_3d_map ; Gyári interpoláció hívása
A különbség kritikus: normál esetben az r3 egy fix cím, a mi esetünkben pedig egy dinamikusan kiválasztott cím. A hook-ot mindig a pointer betöltés és az interpolációs rutin hívása közé kell elhelyezni, különben a rendszer már a régi térképpel számolna.
A teljes adatfolyam lépésről lépésre:
- A sofőr aktiválja a pedálkombinációt.
- A Hook rutin aktiválódik, frissül a
perf_mapselector. - A Checksum frissül, az állapot mentődik az NVRAM mirror-ba.
- A következő ECU ciklusban a gyújtáskalkulátor meghívja a
select_ignition_map_ptrrutint. - A selector visszaadja az új térkép címét, a gyári interpolációs rutin már ebből dolgozik.
- A motor viselkedése azonnal az új kalibráció szerint történik.
Mivel a Flash memória kalibrációs szegmensében előre elhelyeztük az összes alternatív térképet (Base, RON102, E85), runtime alatt semmilyen Flash írás vagy memóriamódosítás nem történik.
A Map Switcher kizárólag azt határozza meg, hogy az ECU melyik előre definiált Flash címet használja a kalibrációs adatok forrásaként. Ez garantálja a determinisztikus működést, a valós idejű válaszidőt, és megőrzi a Flash memória élettartamát.
Architektúrális kitérő: Ugrások és a Verem (Stack) védelme
Amikor a fenti példában szereplő bl (Branch and Link) utasítással szubrutinokat hívunk meg, rendkívül óvatosnak kell lennünk a regiszterekkel.
A bl utasítás felülírja a Link Register (LR) tartalmát a visszatérési címmel. Ha ezt az LR regisztert nem mentjük ki előtte a verembe (Stack), a rutinunk sosem fog tudni visszatérni az eredeti hívóhoz, és az ECU azonnal összeomlik.
Íme egy gyakorlati példa arra, hogyan kell kinéznie egy jól megírt szubrutinnak (például egy Checksum kalkulátornak), ami tiszteletben tartja a PowerPC EABI veremkezelési szabályait:
eeprom_block2_checksum: ; --- Stack Frame Létrehozása --- stwu r1, -0x20(r1) ; Veremmutató (Stack Pointer) lefelé mozgatása mfspr r0, LR ; Link Register tartalmának kimentése r0-ba stw r0, 0x24(r1) ; LR mentése a verembe ; --- Itt történik a kód érdemi része (pl. Checksum Számolás) --- ... ; --- Stack Frame Lebontása és Visszatérés --- lwz r0, 0x24(r1) ; LR visszatöltése a veremből r0-ba mtspr LR, r0 ; r0 visszatöltése a Link Registerbe addi r1, r1, 0x20 ; Veremmutató visszaállítása blr ; Visszatérés a hívóhoz
Az NVRAM Tükör és a Checksum
A bemutatott fuel_mapselector és perf_mapselector változók perzisztens tárolása létfontosságú. Ha sima RAM területre mentenénk, a gyújtás levétele után az adat elveszne. A közvetlen EEPROM írás menet közben pedig túlságosan lassú, és blokkolná a CPU-t.
Ezt oldja meg a Bosch NVRAM Tükrözése. Indításkor a rendszer a teljes EEPROM tartalmat egy gyors RAM blokkba másolja, majd leállításkor csak a módosult blokkokat írja vissza. Mivel mi a Map Switcher rutinnal ezt a RAM tükröt írjuk át, a leállítási fázisban a gyári rendszer automatikusan és biztonságosan beleégeti a módosított állapotainkat az EEPROM-ba.
Van azonban egy kritikus csapda: a Checksum.
Ha a mi kódunk átírja a bájtunkat a RAM tükörben, de nem frissíti a blokk végén található ellenőrzőösszeget, az ECU adatsérülést detektál, és visszaállítja a blokkot a Flash-ben tárolt alapértékekre. Ennek elkerülése érdekében az érték módosításakor azonnal frissítenünk kell a checksumot.
A Map Switcher így nem módosítja a motorvezérlő működését, hanem a gyári architektúra által biztosított indirekt címzési mechanizmust használja ki, lehetővé téve több, egymástól teljesen független kalibrációs profil valós idejű, biztonságos és perzisztens kiválasztását.
← VISSZA A LOGOKHOZ