I'm trying to use u-boot's "bootcount" feature to detect multiple failed attempts to boot, on a TI am335x device (e.g. beaglebone black.) On TI/ Davinci platform, the bootcount value is stored in the RTC_SCRATCH2
register (source). However on the linux side, I can't find any driver that exposes bootcount as a sysfs node or device, as a means of reading and resetting the value. So it seems this is not supported in the (mainline) kernel, but I'm wondering if it's possible from userspace?
Ref:
Here's the meat of a working solution, however I have one question (see below...)
int fd = open("/dev/mem", O_SYNC | O_RDWR);
unsigned char *mem = mmap(NULL, page_offset + len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, page_base);
if (mem == MAP_FAILED) {
perror("Can't map memory");
return -1;
}
unsigned char *scratch2 = mem + page_offset;
// Read value from SCRATCH2, verify magic number
uint32_t val = *(uint32_t *)scratch2;
//printf("%08" PRIx32 "\n", val);
// low two bytes are the value, high two bytes are magic
if ((val & 0xffff0000) != (BOOTCOUNT_MAGIC & 0xffff0000)) {
fprintf(stderr, "Error: BOOTCOUNT_MAGIC does not match\n");
return -1;
}
printf("%d\n", (uint16_t)(val & 0x0000ffff));
Now, if I cast the mmap
'd memory as a uint32_t *
(shown below,) to avoid avoid later type casts, the value I read back is incorrect (the BOOTCOUNT_MAGIC
doesn't match):
uint32_t *mem = mmap(NULL, page_offset + len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, page_base);
uint32_t *scratch2 = mem + page_offset;
// Read value from SCRATCH2, verify magic number
uint32_t val = *scratch2;
//printf("%08" PRIx32 "\n", val);
// low two bytes are the value, high two bytes are magic
if ((val & 0xffff0000) != (BOOTCOUNT_MAGIC & 0xffff0000)) {
fprintf(stderr, "Error: BOOTCOUNT_MAGIC does not match\n");
return -1;
}
printf("%d\n", (uint16_t)(val & 0x0000ffff));
Shouldn't val
still hold the same value when I use uint32_t *mem
?
Full source can be found on Github.
Shouldn't
val
still hold the same value when I useuint32_t *mem
?
If you had used proper pointer arithmetic (which you did not), then you would have gotten the correct results.
In a C expression of <pointer> + <scaler>
, the <scaler>
is taken to represent a quantity with the same size as the pointer type (e.g. sizeof(struct foo)
).
Only when the pointer is a byte pointer would the <scaler>
represent a byte count.
If we have an array x
consisting of 100 elements of a structure, e.g.
struct foo x[100];
the name of the array can be used as a pointer.
So the address of tenth element of the array can be referenced either as &x[9]
or (x + 9)
.
So in your first example of code
unsigned char *mem ...
mem
is declared as a byte pointer.
So the pointer calculation in
unsigned char *scratch2 = mem + page_offset;
produces the results you expect, since sizeof(unsigned char)
is 1 (byte), and page_offset
is expressed in number of bytes.
However in your second code example
uint32_t *mem ...
mem
is declared as a pointer to a 4-byte word.
So the identical pointer calculation in
unsigned char *scratch2 = mem + page_offset;
is really equivalent to
unsigned char *scratch2 = (unsigned char *)mem + (sizeof(uint32_t) * page_offset);
where sizeof(uint32_t)
is 4 (bytes), and the offset applied is quadruple what you intended.
That's because page_offset
is still a number of bytes, but in the original expression is treated as a scaler count of the pointer type, which is a 4-byte word.
You cannot simply change the pointer type (i.e. from a char
to an int
), and expect the pointer arithmetic to be unaffected.
When you change mem
to type uint32_t *
, the pointer calculation needs to re-coded to convert the byte offset to a quad-byte offset:
unsigned char *scratch2 = mem + (page_offset / sizeof(uint32_t));
You could confirm these statements (or debug your code) by using printf() to report the values of mem
, scratch2
, and other pointer calculations.
User contributions licensed under CC BY-SA 3.0