Tag Archives: reflash

DPF Light Patch

This is meant for programmers or at least folks who understand coding in general. Here I am going to show how I implemented the DPF light patch, part of Diesel ECU Patch v1, on my former (Euro 4) car.
DPF Light Patch - Active Regeneration In Progress


Actual source code, updated to C++14:


// Copyright SubaruDieselCrew (2011-2017)   https://subdiesel.wordpress.com

#include <array>
#include <chrono>
//#include <bitset>
#include "sh.h"
#include "JZ2F401A.h"

using namespace std::chrono_literals;

/**
 * @brief DPF light flashing modes (stock)
 *
 */
enum class DPFLightMode {
    off = 0,
    /**
     * @brief soot-high warning aka vehicle speed request
     *
     */
    on_steady = 1,
    /**
     * @brief error
     *
     * (multiple causes: compulsory regeneration required, oil dilution critical, ash overfill, DPF limp-home mode;
     * see https://subdiesel.wordpress.com/2011/03/21/dpf-light/ )
     */
    flashing = 2,
};

/**
 * @brief time resolution (= CAN frame ID 0x600 interval)
 *
 */
const constexpr auto interval {50ms};
/**
 * @brief stock period for flashing mode is 800 ms,
 *        does not have to match stock here
 *
 */
const constexpr auto dpfLightPeriod {800ms};
/**
 * @brief defines DPF light output over time when active regeneration is on
 *
 */
const constexpr std::array<bool, dpfLightPeriod / interval> dpfLightCustomPattern
{
    1, 1, 0, 0,  1, 1, 0, 0,
    0, 0, 0, 0,  0, 0, 0, 0
};
// tested alternative: bitset; storage-efficient but much more lookup code
// const std::bitset<dpfLightPeriod / interval> bits {"1100110000000000"};

/**
 * @brief Implement custom flashing mode.
 *
 * Called every 50 ms (CAN-ID 0x600 interval)
 * from patched stock function "calcDpfLight".
 * Standard error flashing mode already handled by untouched
 * stock subroutine portion and this case this function won't get called.
 */
void calc_DPFLight_continue()
{
    // needed as original functionality has been overwritten for hook instructions
    if (DPFLightMode(*DPFLightModeEnum_b) == DPFLightMode::on_steady)
    {
        *DPFLight_bool = true;
        return;
    }

    // at this point DPFLightMode == DPFLightMode::off
    if (!*DPF_Regeneration_bool_SSM)
    {
        *DPFLight_bool = false;
        return;
    }

    // at this point active DPF regeneration is ON, do custom flashing
    // reusing DPF light counter var is safe
    auto counter = *DPFLightCounter_b;
    if (++counter >= dpfLightCustomPattern.size())
        counter = 0;
    *DPFLightCounter_b = counter;
    *DPFLight_bool = dpfLightCustomPattern.at(counter);
}



// Copyright SubaruDieselCrew (2011-2017)   https://subdiesel.wordpress.com
/*
	For stock ROM:
	Model	2009/2010 Impreza Turbo Diesel 2.0 6MT EDM 110 kW / 150 PS
	ROMID	6644D87207
	CID		JZ2F401A
	CVN		F5AD7142 FB841734
	PAK		22611AP283
*/

#ifndef JZ2F401A_H
#define JZ2F401A_H

#include "diesel_rom.h"

// RAM vars
static auto const DPFLight_bool = reinterpret_cast<volatile bool*>(0xFFFF9C1E);
static auto const DPFLightModeEnum_b = reinterpret_cast<volatile int8_t*>(0xFFFF9C1F);
static auto const DPFLightCounter_b = reinterpret_cast<volatile uint8_t*>(0xFFFF9C53);
static auto const DPF_Regeneration_bool_SSM = reinterpret_cast<volatile bool*>(0xFFFFB222);
…


Disassembly made by objdump:


       c:	91 26       	mov.w	0x5c,r1	! 9c1f
       e:	61 10       	mov.b	@r1,r1
      10:	60 1c       	extu.b	r1,r0
      12:	88 01       	cmp/eq	#1,r0
      14:	89 14       	bt	0x40
      16:	91 22       	mov.w	0x5e,r1	! b222
      18:	61 10       	mov.b	@r1,r1
      1a:	62 1c       	extu.b	r1,r2
      1c:	22 28       	tst	r2,r2
      1e:	8d 0f       	bt.s	0x40
      20:	e2 0f       	mov	#15,r2
      22:	91 1d       	mov.w	0x60,r1	! 9c53
      24:	61 10       	mov.b	@r1,r1
      26:	71 01       	add	#1,r1
      28:	61 1c       	extu.b	r1,r1
      2a:	31 26       	cmp/hi	r2,r1
      2c:	8d 0c       	bt.s	0x48
      2e:	60 13       	mov	r1,r0
      30:	92 16       	mov.w	0x60,r2	! 9c53
      32:	22 10       	mov.b	r1,@r2
      34:	d1 0b       	mov.l	0x64,r1	! 945a8
      36:	02 1c       	mov.b	@(r0,r1),r2
      38:	91 13       	mov.w	0x62,r1	! 9c1e
      3a:	21 20       	mov.b	r2,@r1
      3c:	00 0b       	rts	
      3e:	00 09       	nop	
      40:	92 0f       	mov.w	0x62,r2	! 9c1e
      42:	22 10       	mov.b	r1,@r2
      44:	00 0b       	rts	
      46:	00 09       	nop	
      48:	92 0a       	mov.w	0x60,r2	! 9c53
      4a:	e1 00       	mov	#0,r1
      4c:	e0 00       	mov	#0,r0
      4e:	22 10       	mov.b	r1,@r2
      50:	d1 04       	mov.l	0x64,r1	! 945a8
      52:	02 1c       	mov.b	@(r0,r1),r2
      54:	91 05       	mov.w	0x62,r1	! 9c1e
      56:	21 20       	mov.b	r2,@r1
      58:	00 0b       	rts	
      5a:	00 09       	nop	
      5c:	9c 1f       	mov.w	0x9e,r12	! 600c
      5e:	b2 22       	bsr	0x4a6
      60:	9c 53       	mov.w	0x10a,r12	! e000
      62:	9c 1e       	mov.w	0xa2,r12	! e0ff
      64:	00 09       	nop	
      66:	45 a8       	.word 0x45a8

     5a8:	01 01       	.word 0x0101
     5aa:	00 00       	.word 0x0000
     5ac:	01 01       	.word 0x0101
     5ae:	00 00       	.word 0x0000
     5b0:	00 00       	.word 0x0000
     5b2:	00 00       	.word 0x0000
     5b4:	00 00       	.word 0x0000
     5b6:	00 00       	.word 0x0000

As you can tell there is not much code required. Much more work, orders of magnitude (!), is necessary to reverse-engineer the related stock ROM portions in the first place, defining functions, disassembling machine instructions, naming local and global variables etc.

Usually, ROM and RAM addresses depend on the actual ROM version used. Above definitions work for outdated CID JZ2F401A (dated 2009-Sep). Apart from ROM specific variable addresses the same code will work for all known Euro 4/5/6 models.

Resultant binary data is meant to be inserted into a free unused ROM region. On Renesas SH microprocessors (i.e. SH7058S) free ROM space is rather easy to find – just look for big chunks of continuous FF-bytes. This is because those chips erase bytes to value 0xFF. Others, e.g. Infineon TriCore series, erase their internal flash ROM to zeroes instead.

To actually make use of the added logic, I had to modify (patch) a few bytes in the original calc_DPF_light subroutine, so that after doing some of its work it will call my own function, knowing its start address (0x9400C). Usually there is no free space in between stock functions, therefore we have to apply clever patching tricks to make room for a few new instructions and/or divert execution flow.

Finally, after carefully verifying the changes applied to the stock ROM, you have to correct checksums. Flashing software usually does this anyway, perhaps asking first. Checksum correction and verification is actually very easy to do for such Denso firmware.

Providing SDC-modified ROMs is possible, however will not be free due to amount of labour involved. Contact us if you’re interested.

Updates

  • 2017-10: update code, syntax-highlighted disassembly, C++17
  • 2017-04: minor update, actual disassembly
  • 2016-11: updated source to C++14
  • 2016-10: updated source to C++11 with Doxygen documentation
  • 2016-01: added disassembly
Advertisements

Why and when cycling ignition matters

Turning ignition off triggers the ECU’s software shutdown procedure.

This includes among other things:

  • Saving important variables (microcontroller RAM) into the extra data (EEPROM) chip – necessary to cope with potential power loss (disconnected battery).
  • Actuator testing (throttle, EGR valve)

Only after finishing shutdown the software cuts off the ECU’s own main power supply via relay. The resulting relay click sound was audible in my car, shutdown took around 15 seconds or so. I also tested this relay using own kernel software, confirming the I/O port number.

Here’s a problem I noticed with my Impreza Diesel during a dealership service where I had an oil change and software update done:

  1. Oil dilution reset – without ignition cycle
  2. ECU software update

Checking oil dilution parameter afterwards revealed that it actually had not been reset!

I am almost sure, the reason is that for the software update the ECU software switches into a very special mode. In this mode, it does not know anything about an engine, it only knows and responds to a few commands to transfer new software into the chip. Apparently it did not save variables (including the zeroed oil dilution amount) into EEPROM before switching modes.

At ignition-on, the software goes into normal mode, loading saved values from EEPROM into the chip’s RAM, ready to run the engine.

Conclusion

Either cycle ignition or do any maintenance/adjustment procedures after a software update.

In any case, verifying parameters after a such operations is always a good idea – takes only a minute whereas a failed procedure can cause the car owner troubles e.g. another dealership visit or much worse (DPF light, limp mode).

Diesel ECU Patch v1

Finally, now that we are able to reflash diesel ECUs it makes sense to do some changes – or improvements as we think of it.
Couple of things already implemented and working perfectly:

  • Added SSM2 getter function Engine Load, using standard definition 0x000007, x*100/255 [%] – unused in stock diesel ROMs. As engine load value is being calculated internally anyway, also available as OBDII Mode 1 PID 0x04, it makes sense to provide this in SSM2 protocol, too. Very easy to do actually, just a single function pointer change – 4 bytes.
  • Added SSM2 getter function Gear, standard def 0x00004A, x+1 [-], unused in stock ROMs. Tiny function required because of +1 offset.

Btw, as with all SSM2 functions, the physical layer does not matter – any SSM2 function gets called via both serial (Euro4) and CAN connection. Diesel ECUs typically have SSM2 virtual address space 0x000000-0x00034F. That’s 848 function pointers, plus same amount in SSM2-write vector table. You could hook up a lot non-standard things as there are plenty of unused addresses…

  • Modified SSM2 init capability bits accordingly. That way above additional params do show up automatically on ALL SSM2 capable software (RomRaider, FreeSSM, SSM3, DashDAQ ?, …).
  • DPF light active regeneration flashing mode. Applies to closed-type DPF models only. Stock behavior (steady on and error flashing) is untouched, we managed to append own additional code. New code part checks if any stock DPF light mode is currently active. If not, it checks DPF regeneration flag, and only if true it performs this new flashing mode. It’s particularly useful at low engine speed – warning driver that additional turbo lag can occur. Remember, engine management reduces boost and manifold air pressure – sometimes a lot – when active regen is in progress.
    Update: got very positive feedback on this one. Even on a bright sunny day looking through the windshield, that blinking light on dashboard gets noticed immediatly. Whole thing is only a screen page of easy C++. Kudos to GCC, v4.6.x generated code is awesome – highly efficient.

Right now this is all internal, experimental, Linux-centric stuff. Availability of our services to the public will be discussed. It meant many months of full-time work to get this far, call for open source collaboration (less work for everyone) did not work…

Bootloader Stuff

Fact: Because of ROM differences diesel ECUs need a special bootloader!
Popular EcuFlash software (commercial, USA) won’t work, I think no one ever has reported success on a diesel with it. There does not seem to be active EcuFlash development for some time either.

So we are trying to build our own bootloader, open source – yeah!

It’s a pitty, after so many (gasoline) years, there does not seem to be an open bootloader (+ PC reflash software) for modern Subaru ECUs. Naturally I would expect most code to be identical.

Just a couple of hints and details:
All diesel ROMs seen so far expect bootloader based on RAM address 0xFFFF4000.
If you do everything required – challenge response, crypto etc. – the ROM code will accept your bootloader upload via CAN messages, decrypting and storing it at 0xFFFF4000+. There’s also a checksum to match. Then via CPU instruction “jmp FFFF4000” the bootloader gets called and takes over entirely.
A few gasoline ROMs I looked into needed 0xFFFF3000 so that’s one major difference to comply with.

Microcontroller architecture is Renesas (former Hitachi) SuperH, “SH” for short. It’s a 32 bit RISC chip, same as gasoline.
A must-have download from www.renesas.com :
Look for “SH-2E SH7059 F-ZTATTM SH7058S F-ZTATTM Hardware Manual” on http://www.renesas.eu/products/mpumcu/superh/Documentation.jsp
(PDF, around 1000 pages, very technical, not Subaru specific of course)
The standard method is called “User Program Mode”. Other modes (useful for ECU recovery) I don’t care much about yet.

Couple things already tested working:

  • Whole process of uploading bootloader via CAN. We’ve got easy-to-maintain C# code for everything needed (challenge-response, encryption, decryption, checksum).
  • CAN communication (needs more testing, very important – if you can’t communicate with the bootloader during reflash you’re screwed…)
  • Downloading on-chip subroutines (doing the actual erase and reprogramming, copied from chip into RAM, so these we don’t have to write but saves only a small part of the code required)
  • ROM access and building CAN messages (I’d like to add some compression algorithm to get even more speed)

Anyone wants to join our crew, somewhat being part of open source history?
Obviously we need extra ECUs for testing dangerous things (reflashing).
Could we raise donations to buy ourselves ECUs? Used diesel ECUs are hard to find. Any thoughts?

Update 2011/07: CAN-reflash collaboration possibilities timed out as we completed most of the work ourselves by now. Unfortunately, no one cared till very recently. Due to huge amount of work required, open-sourcing our solution is rather unlikely, without any sponsorship at least. Thanks for understanding!

Reflash Counter

Updated 2011-03-18: 100-limit, see last paragraph

Just like their petrol counterparts, BoxerDiesel ECUs also have a flash counter at ROM address 0xFFB00 & 0xFFB02 (2x uint16, big endian). The latter one is a logical complement for backup and/or to level out a possible checksum.
Example: “00 01 FF FE” means one reflash has been made.
Counter value probably starts at zero (“00 00 FF FF”) out of factory, showing re-flashes so to speak.
Maximum value is decimal 65,534 (“FF FE 00 01”), at which point it stays the same, not overflowing back to zero.
In standard dealer flash procedure, the actual incrementing is done by the bootloader.
Besides those four bytes, no further data seems to be saved in this special area (0xFFB00, length 128 bytes). Probably a good thing – not to overdo things like other manufacturers tend to do.
When flashing this particular block, the standard bootloader ignores the upload data (supposed to be FFs).
For downloading ROM it depends on the used bootloader whether it returns the actual bytes from there. Standard bootloader just reports fake bytes (FFs) in order to match upload and pass the flash verification test.

Regarding reflash counter there’s an artificial limit of 100 reflashes. Reaching this value, dealer flash software will likely refuse to work. Both ECUs (gasoline and diesel) as well as TCUs seem to be affected, chip types SH7055 and SH7058(S) at least.