Read and write mileage from IPC (EEPROM)

This forum is for Ford specific instrument panels like Convers+
User avatar
Ursadon
Posts: 52
Joined: 10 Mar 2019, 19:23

Re: Read and write mileage from IPC (EEPROM)

Post by Ursadon » 12 Jul 2019, 12:52

Yes, i cracked it too :)

As i suggested before:
32 D1 - is a mileage in km, calculated with using XOR-table
- it is xor-based (crc-based) algorithm.

Resulting value is 5 times of divided odometer (lsb 12 bits + 4 bits of crc. Polynome looks like x^3+1) + inverted odo msb (4 bits) + 0x0f + remainder (7 bits + crc-1(x+1 polynome, aka "parity"))

Thank God that we have IDA Pro :)
1.png
CRC calculation function
1.png (8.88 KiB) Viewed 136 times
2.png
CRC table
2.png (7.97 KiB) Viewed 136 times
ipc.jpg
odometer.png

Go4IT
Posts: 238
Joined: 08 Feb 2019, 12:25

Re: Read and write mileage from IPC (EEPROM)

Post by Go4IT » 13 Jul 2019, 15:17

Yes, you're right. Even the algo you formed is more compact than mine, but it is exactly the same. Reverse engineered from IPC firmware.
There is another one at 0x778 as 4 bytes. Coded with parity and CRC, multiplied by 10, giving two numbers behind comma. This seems the one shown in the TEST modus of the IPC. I only miss the one for the trip odo. It must be there also. Just for completeness ;-)

BTW: Did you manage to write the EEPROM via CAN now?

User avatar
Ursadon
Posts: 52
Joined: 10 Mar 2019, 19:23

Re: Read and write mileage from IPC (EEPROM)

Post by Ursadon » 13 Jul 2019, 15:38

Go4IT wrote:
13 Jul 2019, 15:17
Yes, you're right. Even the algo you formed is more compact than mine, but it is exactly the same. Reverse engineered from IPC firmware.
There is another one at 0x778 as 4 bytes. Coded with parity and CRC, multiplied by 10, giving two numbers behind comma. This seems the one shown in the TEST modus of the IPC. I only miss the one for the trip odo. It must be there also. Just for completeness ;-)

BTW: Did you manage to write the EEPROM via CAN now?
Thx for 0x778, i forgot about this value :)
I have a proof-of-concept eeprom write algo.
Since i2c mapped to 0x6000000, it easy to write to it using Secondary Bootloader (*-AA.vbf). I think, UCDS using same technique to correct odometer value.

Go4IT
Posts: 238
Joined: 08 Feb 2019, 12:25

Re: Read and write mileage from IPC (EEPROM)

Post by Go4IT » 13 Jul 2019, 17:53

Ups, sorry, typo, i mean 0x788 not 0x778 !

Go4IT
Posts: 238
Joined: 08 Feb 2019, 12:25

Re: Read and write mileage from IPC (EEPROM)

Post by Go4IT » 13 Jul 2019, 19:48

At least it is very simple to calc the ODO from the bytes. Just take the two first bytes at offset 0x774 as a word, shift this by 4 bits to right (=divide by 0x10) and multiply it by 5 and 0x80 and store this value. Then take the last byte of each of the 4-byte groups, logical and it with 0x7F and add 0x01 to the value. Add all those values to the previous stored value. Then divide that by 5 and add 1 and you got your KM value.
Example: 0x774 79 D9 FF 26 79 D9 FF A2 79 D9 FF 23 79 D9 FF A4 79 D9 FF 25

Code: Select all

KM_HIGH = (0x79D9 / 0x10) * 5 * 0x80 = 0x13 0880
KM_LOW = 0x27 + 0x23 + 0x24 + 0x25 + 0x26 = 0xB9
KM = (0x130880 + 0xB9) / 5 + 1 = 0x3CEA6 (=249.510km)

User avatar
Ursadon
Posts: 52
Joined: 10 Mar 2019, 19:23

Re: Read and write mileage from IPC (EEPROM)

Post by Ursadon » 14 Jul 2019, 02:44

Go4IT wrote:
13 Jul 2019, 19:48
At least it is very simple to calc the ODO from the bytes. Just take the two first bytes at offset 0x774 as a word, shift this by 4 bits to right (=divide by 0x10) and multiply it by 5 and 0x80 and store this value. Then take the last byte of each of the 4-byte groups, logical and it with 0x7F and add 0x01 to the value. Add all those values to the previous stored value. Then divide that by 5 and add 1 and you got your KM value.
Example: 0x774 79 D9 FF 26 79 D9 FF A2 79 D9 FF 23 79 D9 FF A4 79 D9 FF 25

Code: Select all

KM_HIGH = (0x79D9 / 0x10) * 5 * 0x80 = 0x13 0880
KM_LOW = 0x27 + 0x23 + 0x24 + 0x25 + 0x26 = 0xB9
KM = (0x130880 + 0xB9) / 5 + 1 = 0x3CEA6 (=249.510km)
You forgot to prepend KM_HIGH with inverted high halfbyte of 79D9FF26.
Otherwise the max km value is 524288+640

Go4IT
Posts: 238
Joined: 08 Feb 2019, 12:25

Re: Read and write mileage from IPC (EEPROM)

Post by Go4IT » 14 Jul 2019, 08:19

Here is a C example off how to decode mileage from bytes:

Code: Select all

BYTE g_ubMileage[20];

// CRC Table. 16 Bytes found in 1MB Flash ("7M2T-14C026-AG") at 0x4B064
BYTE g_ubCrc8Bit[16] =
{
    0x0, 0xD, 0x7, 0xA,
    0xE, 0x3, 0x9, 0x4,
    0x1, 0xC, 0x6, 0xB,
    0xF, 0x2, 0x8, 0x5,
};

// return word from mileage data entry (five entries, four bytes each) in Big Endian
WORD get_mileage_entry(int idx, int iWord)
{
    if(idx < 0 || idx > 4)
        return 0xFFFF;

    if(iWord != 0 && iWord != 1)
        return 0xFFFF;

    return (g_ubMileage[(idx * 4) + (iWord * 2) + 0] << 8) |
           (g_ubMileage[(idx * 4) + (iWord * 2) + 1] << 0);
}

// calculate 4-bit CRC of a 16-bit word
BYTE calc_crc_4bit(WORD w)
{
    BYTE ubCrc = 0xF;   
    int i;

    for(i = 0; i < 4; i++)
    {
        ubCrc = g_ubCrc8Bit[ubCrc] ^ (w & 0xF);
        w = (w >> 4);
    }

    return ubCrc;
}

// get parity bit of a byte (even/odd)
BYTE get_parity(BYTE ub)
{
    BYTE ubParity = 0;

    while(ub != 0)
    {
        ubParity++;
        ub &= (ub - ubParity);
    }

    return ubParity & 0x01;
}

// get MSB mileage bits, check for valid CRC
WORD get_mileage_entry_and_check_crc(int idx)
{
    if(idx < 0 || idx > 4)
        return 0xFFFF;

    WORD w1 = get_mileage_entry(idx, 0);
    WORD w2 = get_mileage_entry(idx, 1);

    BYTE ub = (w2 >> 8); // MSB of word
    WORD w = (w1 >> 4) | (((~ub) & 0xF0) << 8);
    BYTE ubCrc = calc_crc_4bit(w);

    if(ubCrc == (w1 & 0x0F)) // CRC valid ?
        return w;
    else
        return 0xFFFF;
}

// get LSB mileage bits, check for valid parity
WORD get_mileage_entry_and_check_parity(int idx)
{
    if(idx < 0 || idx > 4)
        return 0xFFFF;

    WORD w1 = get_mileage_entry(idx, 0);
    WORD w2 = get_mileage_entry(idx, 1);

    BYTE ub = (w2 & 0xFF); // LSB of word

    if(get_parity(ub)) // odd parity ?
        return (ub & 0x7F) + 1;
    else
        return 0;
}

void calc_mileage(BYTE *ubMileage)
{
    // copy data into buffer
    memcpy(g_ubMileage, ubMileage, sizeof(g_ubMileage));

    // calculate sum of each all five entries
    DWORD dw1sum = 0;
    WORD w2sum = 0;

    int i;
    for(i = 0; i < 5; i++)
    {
        WORD w1 = get_mileage_entry_and_check_crc(i);    // MSB of mileage
        WORD w2 = get_mileage_entry_and_check_parity(i); // LSB of mileage
        dw1sum += w1;
        w2sum += w2;
    }

    // combine MSB and LSB
    DWORD dw = (dw1sum << 7) + w2sum;

    // final calulation, 200 meter resolution
    dw = (dw / 5) + 1;
    return(dw);
}

// try it...
void main()
{
  // Test data of 249.510 km (offset 0x774)
  BYTE testdata[20] = 
  {
    0x79, 0xD9, 0xFF, 0x26,
    0x79, 0xD9, 0xFF, 0xA2,
    0x79, 0xD9, 0xFF, 0x23,
    0x79, 0xD9, 0xFF, 0xA4,
    0x79, 0xD9, 0xFF, 0x25,
  };
  WORD km = calc_mileage(testdata);
  printf("Mileage is %d km\n", km);
}

Go4IT
Posts: 238
Joined: 08 Feb 2019, 12:25

Re: Read and write mileage from IPC (EEPROM)

Post by Go4IT » 14 Jul 2019, 08:26

Ursadon wrote:
14 Jul 2019, 02:44
You forgot to prepend KM_HIGH with inverted high halfbyte of 79D9FF26.
Otherwise the max km value is 524288+640
Yes, it was a known simplyfication, just to demonstrate how the algo works :D
The beauty of this design is that EEPROM data changes for the lower bits of the milage are distributed over 5 memory locations in turn. So by writing the milage every 200 meter will result in a theoretical max. of 1 million kilometers.

Go4IT
Posts: 238
Joined: 08 Feb 2019, 12:25

Re: Read and write mileage from IPC (EEPROM)

Post by Go4IT » 14 Jul 2019, 09:15

I try to figure out how to do the reverse, calc bytes from odo (Pseudo-Code):

Code: Select all

function calc_crc(DWORD) {
  ...
}

DWORD km = 0x3CEA6;  // =249.510 km
DWORD dw = (km - 1) * 5  // =0x130939
DWORD dw1sum = dw / 0x80  // =0x2612 (equals dw >> 7)
WORD w2sum = (dw - dw1sum) & 0x7F // =0x28
WORD dw1crc = calc_crc(dw1sum)
WORD msb = (dw1crc * 0x100) | dw1sum
WORD lsb[5] = ...

Post Reply