u-boot: how to access 'bootcount' from linux userspace?

1

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:

EDIT: a working implementation

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.

c
beagleboneblack
u-boot
asked on Stack Overflow Jul 17, 2018 by thom_nic • edited Jul 20, 2018 by thom_nic

1 Answer

2

Shouldn't val still hold the same value when I use uint32_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.

answered on Stack Overflow Jul 21, 2018 by sawdust • edited Jul 21, 2018 by sawdust

User contributions licensed under CC BY-SA 3.0