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