MEMS3 Checksum Calculations

Download Link: https://andrewrevill.co.uk/Downloads/MEMSTools.zip

Checksum calculations and documentation of the internal checksum routines in the boot loader of the MEMS3 ECU.

I was recently asked by someone to document how firmware and map checksums are calculated and how the 0x5AA5 verification words work.

I’ve based this writeup on the ECU’s own calculations of what the ECU accepts as being correct in order for it to “sign” the firmware and map with the 0x5AA5 verification words. For reference and as evidence, at the bottom of this document I’ve fully disassembled and documented the two routines (firmware and map) from the boot loader of an NNN000120 Td5 ECU, although these routines are basically the same across all MEMS3 ECUs, but located at different addresses. I’ve documented the logic it uses and given a running commentary on what it’s trying to do at each stage.

CHECKSUM CALCULATION AND VERIFICATION LOGIC

The basic process is:

·         You always write the firmware and map to the ECU with the verification words set to 0xFFFF. This means “unverified”. At run time, the ECU can overwrite logic “1” with logic “0” only, so 0xFFFF can be overwritten later with any other word value.

·         Checksums are calculated in Mapper based on the verification word being 0xFFFF. The ECU also verifies that the checksums are correct where the verification word is 0xFFFF, or that they would be right if the verification word was 0xFFFF where it finds 0x5AA5.

·         If the ECU calculates the checksum and finds it matches the expected value, it then overwrites the 0xFFFF word with 0x5AA5. This means “verified”. Any other word is considered to be invalid.

·         The firmware checksum is calculated and verified as follows:

o   Sum all WORD values in the address range 0x110000/1 to 0x139FFE/F.

o   If the firmware verification word is 0xFFFF

§  Then the result should be 0xAA55

§  Else the checksum is adjusted to what it would be if the verification word was 0xFFFF and the result should then be 0xAA55.

·         The map checksum is calculated and verified as follows:

o   Sum all WORD values in the address range 0x13C000/1 to 0x13CFFE/F.

o   If the map verification word is 0xFFFF

§  Then the result should be 0xA5A5

§  Else the checksum is adjusted to what it would be if the verification word was 0xFFFF and the result should then be 0xA5A5.

Note that the two checksum constants for firmware (0xAA55) and map (0xA5A5) are different.

The ECU will only attempt to load and execute the firmware if it finds that both of the verification words are 0x5AA5. Because they are erased to 0xFFFF before writing and then written as 0xFFFF, if a write is interrupted at any point the ECU will find 0xFFFF and will not attempt to load the firmware, so the ECU will not be bricked. Similarly if the ECU gets the wrong checksum internally, it won’t write the 0x5AA5 verification words and so the ECU will not be bricked by attempting to load a corrupted uploaded firmware. Some tools (I think Nanocom) don’t seem to understand this mechanism and write the files with the verification words preset to 0x5AA5. This means there is no protection. If the write is interrupted or corrupted the ECU will see it as having been checked and verified and will try to load it, bricking the ECU.

The checksum verification routines are called in response to KWP2000 message [SVC_START_ROUTINE_BY_LOCAL_ID, SSV_ROUTINE_VERIFY_WRITE] or [0x31, 0x02] which should be sent to the ECU after the write is complete. Until the ECU processes this message the firmware and map written will remain unverified and the ECU will not attempt to load them.

You can correct the checksum anywhere unused in each block. However in files I’ve seen (and this is the convention followed by Mapper):

·         The firmware checksum is corrected at address 0x139FFC.

·         The map checksum is corrected in the last two bytes of the map according to the declared map length, e.g.

o   If the map length WORD declared at address 0x13C000 is 0x3FD8.

o   The map occupies the address range from 0x13C000 to 0x13C000+0x3FD8-1=0x13FFD7.

o   The last two bytes of the map are at addresses 0x13FFD6 and 0x13FFd7.

o   So the WORD value a 0x13FFD6 is where the checksum is corrected.

·         You should really stick to this convention; fixing the checksum at an arbitrary address in the middle of unused space will cause Mapper to think that the memory up to or beyond that address is in use and not available for patches etc.

There is no checksum covering other areas of the EEPROM address space. There is no overall checksum covering the full 256kB as portions of it (coding areas) are writeable at run time. The exact address ranges covered by each checksum can be verified in the code listings below. They’re the parameters passed to the funBootCalculateChecksumForMemory function which is responsible for actually summing the specified range and I’ve highlighted them for clarity:

·         0x100000-0x10FFFF: Boot loader. Not intended to be writeable other than at manufacture. No checksum mechanism used.

·         0x110000-0x139FFF: Firmware. Checksum as described above.

·         0x13A000-0x13AFFF: VIN Coding. Records can be appended at run time as the ECU is recoded. No checksum mechanism used.

·         0x13B000-0x13BFFF: ZCS Coding. Records can be appended at run time as the ECU is recoded. No checksum mechanism used.

·         0x13C000-0x13FFFF: Map. Checksum as described above.

FIRMWARE CHECKSUM VERIFICATION ROUTINE

This is the firmware checksum calculation and verification routine taken from the boot loader of an NNN000120 Td5 ECU, although these routines are basically the same across all MEMS3 ECUs, but located at different addresses. I’ve documented the logic it uses and given a running commentary on what it’s trying to do at each stage. This routine is called in response to KWP2000 message [SVC_START_ROUTINE_BY_LOCAL_ID, SSV_ROUTINE_VERIFY_WRITE] or [0x31, 0x02] which should be sent to the ECU after the write is complete. Until the ECU processes this message the firmware and map written will remain unverified and the ECU will not attempt to load them.

                             ********************************************************************************

                             *                                   FUNCTION                                   *

                             ********************************************************************************

                             undefined funVerifyFirmwareChecksumAndWriteVerificationW

             undefined         D0b:1          <RETURN>

                             funVerifyFirmwareChecksumAndWriteVerificationWord         XREF[1]:     FUN_0010490e:00104916(c) 

        00104872 20 7c 00        movea.l    #0x110000,A0                                               Firmware start address.

                 11 00 00

        00104878 22 7c 00        movea.l    #0x139fff,A1                                               Firmware end address.

                 13 9f ff

        0010487e 61 00 00 50     bsr.w      funBootCalculateChecksumForMemory                          Calculate checksum for address range.

        00104882 24 7c 00        movea.l    #0x110000,A2

                 11 00 00

        00104888 d5 fc 00        adda.l     #0x410,A2                                                  Firmware 5AA5 verification address.

                 00 04 10

        0010488e 0c 52 ff ff     cmpi.w     #0xFFFF,(A2)=>funBootCalculateChecksumForMemory            Is verification 0xFFFF?

        00104892 66 12           bne.b      funBootVerifyFirmwareChecksum_VerificationNot0xFFFF

        00104894 0c 40 aa 55     cmpi.w     #0xAA55,D0w                                                Verification is 0xFFFF. Is checksum 0xAA55?

        00104898 66 0a           bne.b      funBootVerifyFirmwareChecksum_EndOf0xFFFF                  Branch if checksum not 0xAA55.

        0010489a 08 f8 00        bset.b     0x3,(3=bitFirmwareChecksumVerified).w                      Set bit flag. Firmware checksum verified.

                 03 02 9a

        001048a0 61 00 ff 5a     bsr.w      funWriteFirmwareVerificationWord                           Write firmware verification word 0x5AA5.

                             funBootVerifyFirmwareChecksum_EndOf0xFFFF                 XREF[1]:     00104898(j) 

        001048a4 60 28           bra.b      funBootVerifyFirmwareChecksum_End                          End of 0xFFFF case. Jump to end.

                             funBootVerifyFirmwareChecksum_VerificationNot0xFFFF       XREF[1]:     00104892(j) 

        001048a6 0c 52 5a a5     cmpi.w     #0x5aa5,(A2)=>funBootCalculateChecksumForMemory            Verification is not 0xFFFF. Is verification 0x5AA5?

        001048aa 66 1c           bne.b      funBootVerifyFirmwareChecksum_VerificationNot0x5AA5

        001048ac 04 40 5a a5     subi.w     #0x5aa5,D0w                                                Adjust checksum as if was 0xFFFF. Subtract 0x5AA5.

        001048b0 04 40 00 01     subi.w     #0x1,D0w                                                   Add 0xFFFF.

        001048b4 0c 40 aa 55     cmpi.w     #0xAA55,D0w                                                Is checksum then 0xAA55?

        001048b8 66 0c           bne.b      funBootVerifyFirmwareChecksum_EndOf0x5AA5                  Branch if checksum is not 0xAA55.

        001048ba 08 f8 00        bset.b     0x3,(3=bitFirmwareChecksumVerified).w                      Set bit flag. Firmware checksum verified.

                 03 02 9a

        001048c0 08 f8 00        bset.b     0x1,(1=bitFirmwareVerification0x5AA5).w                    Set bit flag. Firmware verification is 0x5AA5.

                 01 04 14

                             funBootVerifyFirmwareChecksum_EndOf0x5AA5                 XREF[1]:     001048b8(j) 

        001048c6 60 06           bra.b      funBootVerifyFirmwareChecksum_End

                             funBootVerifyFirmwareChecksum_VerificationNot0x5AA5       XREF[1]:     001048aa(j) 

        001048c8 08 f8 00        bset.b     0x2,(2=bitFirmwareVerificationInvalid).w                   Set bit flag. Firmware verification is invalid.

                 02 04 13

                             funBootVerifyFirmwareChecksum_End                         XREF[2]:     001048a4(j), 001048c6(j) 

        001048ce 4e 75           rts

MAP CHECKSUM VERIFICATION ROUTINE

This is the map checksum calculation and verification routine taken from the boot loader of an NNN000120 Td5 ECU, although these routines are basically the same across all MEMS3 ECUs, but located at different addresses. I’ve documented the logic it uses and given a running commentary on what it’s trying to do at each stage. This routine is called in response to KWP2000 message [SVC_START_ROUTINE_BY_LOCAL_ID, SSV_ROUTINE_VERIFY_WRITE] or [0x31, 0x02] which should be sent to the ECU after the write is complete. Until the ECU processes this message the firmware and map written will remain unverified and the ECU will not attempt to load them.

                             ********************************************************************************

                             *                                   FUNCTION                                   *

                             ********************************************************************************

                             undefined funVerifyMapChecksumAndWriteVerificationWord()

             undefined         D0b:1          <RETURN>

                             funVerifyMapChecksumAndWriteVerificationWord              XREF[1]:     FUN_0010490e:0010491a(c) 

        00104814 20 7c 00        movea.l    #0x13c000,A0                                               Map start address.

                 13 c0 00

        0010481a 22 7c 00        movea.l    #0x13ffff,A1                                               Map end address.

                 13 ff ff

        00104820 61 00 00 ae     bsr.w      funBootCalculateChecksumForMemory                          Calculate checksum for address range.

        00104824 24 7c 00        movea.l    #0x13c000,A2

                 13 c0 00

        0010482a d5 fc 00        adda.l     #0x12,A2                                                   Map 5AA5 verification address.

                 00 00 12

        00104830 0c 52 ff ff     cmpi.w     #0xFFFF,(A2)=>labMapSignature5AA5                          Is verification 0xFFFF?

        00104834 66 12           bne.b      funBootVerifyMapChecksum_VerificationNot0xFFFF

        00104836 0c 40 a5 a5     cmpi.w     #0xA5A5,D0w                                                Signature is 0xFFFF. Is checksum 0xA5A5?

        0010483a 66 0a           bne.b      funBootVerifyMapChecksum_EndOf0xFFFF                       Branch if checksum not 0xA5A5.

        0010483c 08 f8 00        bset.b     0x7,(7=bitMapChecksumVerified).w                           Set bit flag. Map checksum verified.

                 07 02 9a

        00104842 61 00 ff a0     bsr.w      funWriteMapVerificationWord                                Write map verification word 0x5AA5.

                             funBootVerifyMapChecksum_EndOf0xFFFF                      XREF[1]:     0010483a(j) 

        00104846 60 28           bra.b      funBootVerifyMapChecksum_End                               End of 0xFFFF case. Jump to end.

                             funBootVerifyMapChecksum_VerificationNot0xFFFF            XREF[1]:     00104834(j) 

        00104848 0c 52 5a a5     cmpi.w     #0x5aa5,(A2)=>labMapSignature5AA5                          Verification is not 0xFFFF. Is verification 0x5AA5?

        0010484c 66 1c           bne.b      funBootVerifyMapChecksum_VerificationNot0x5AA5

        0010484e 04 40 5a a5     subi.w     #0x5aa5,D0w                                                Adjust checksum as if was 0xFFFF. Subtract 0x5AA5.

        00104852 04 40 00 01     subi.w     #0x1,D0w                                                   Add 0xFFFF.

        00104856 0c 40 a5 a5     cmpi.w     #0xA5A5,D0w                                                Is checksum then 0xA5A5?

        0010485a 66 0c           bne.b      funBootVerifyMapChecksum_EndOf0x5AA5                       Branch if checksum is not 0xA5A5.

        0010485c 08 f8 00        bset.b     0x7,(7=bitMapChecksumVerified).w                           Set bit flag. Map checksum verified.

                 07 02 9a

        00104862 08 f8 00        bset.b     0x5,(5=bitMapVerification0x5AA5).w                         Set bit flag. Map verification is 0x5AA5.

                 05 04 10

                             funBootVerifyMapChecksum_EndOf0x5AA5                      XREF[1]:     0010485a(j) 

        00104868 60 06           bra.b      funBootVerifyMapChecksum_End

                             funBootVerifyMapChecksum_VerificationNot0x5AA5            XREF[1]:     0010484c(j) 

        0010486a 08 f8 00        bset.b     0x1,(1=bitMapVerificationInvalid).w                        Set bit flag. Map verification is invalid.

                 01 04 16

                             funBootVerifyMapChecksum_End                              XREF[2]:     00104846(j), 00104868(j) 

        00104870 4e 75           rts