I'm currently trying to format a disk to exFAT using a micro controller. My issue is that I need to calculate a checksum that uses the bytes from sector 1 to 11 of the VBR (Volume Boot Region) to store it into sector 12 but my result is incorrect. When the checksum isn't correct, the disk cannot be used by Windows or any other OS that recognizes exFAT since the checksum is verified and a fatal error occures if it's incorrect.
Here's the function that calculates the 32-bit checksum:
uint32_t BootChecksum(char * data, long bytes){
uint32_t checksum = 0;
for (uint32_t i = 0 ; i < bytes ; i++){
if (i == 106 || i == 107 || i == 112)
continue;
checksum = ((checksum << 31) | (checksum >> 1)) + (uint32_t) data[i];
if(checksum == 0xF1924082){
printf("%02X | i = %d", checksum, i);
}
}
return checksum;
}
From what I've been able to read, the function is correct so my guess is that the data that I use are incorrect. I'm simply taking the 11 sectors needed so with 512 bytes per sector it results in an array of 5632 bytes.
I've used a similar function to calculate the checksum of the entry set (a 16 bit checksum) and the result is correct, it really has to be the data but I don't understand what I'm missing there!
Anyone who knows about exFAT can help me out? Thanks!
I suspect it's a problem of operator precedence.
In this page I see the crc definition where checksum
is modified in this way
checksum = (checksum<<31) | (checksum>> 1) + data[i];
that is, if I'm not wrong, equivalent to
checksum = (checksum<<31) | ((checksum>> 1) + data[i]);
because (if I remember well) the plus operator (+
) has a bigger precedence than the bit or operator (|
).
On the contrary, your code is
checksum = ((checksum << 31) | (checksum >> 1)) + (uint32_t) data[i];
that is a total different code because you first apply the bitwise or and next the plus.
I suppose could work with
checksum = (checksum << 31) | ((checksum >> 1) + (uint32_t) data[i]);
p.s.: sorry for my bad English
---EDIT 2016.06.09---
Another problem should be the signedness of data
.
You define data
as a pointer of char
; ntfs.com define data
as an array of const unsigned char
.
The pointer/array difference isn't a problem; the const
part ins't important (but I suggest you to define your data
as const
too); the problem (I suppose) is the conversion to uint32_t
of a char
, if char
is signed with a negative values, instead of a unsigned char
.
An example: suppose that the value of your data[i]
is -1; with (uint32_t) data[i]
, if I remember well, first data[i]
is converted in int(-1)
and next in uint32_t(-1)
. So you get 4294967295.
If your data[i]
is an unsigned char
, instead of -1 data[i]
value is 255; so (uint32_t) data[i]
first convert 255 to int(255)
, that is 255, next to uint32_t(255)
, that remain 255.
Briefly, my suggestion is: change
checksum = (checksum << 31) | ((checksum >> 1) + (uint32_t) data[i]);
in
checksum = (checksum << 31) | ((checksum >> 1) + (uint32_t) (unsigned char) data[i]);
or simply
checksum = (checksum << 31) | ((checksum >> 1) + (unsigned char) data[i]);