u-boot:如何从 linux 用户空间访问 'bootcount'?
u-boot: how to access 'bootcount' from linux userspace?
我正在尝试使用 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)。但是在 linux 方面,我找不到任何将 bootcount 公开为 sysfs 节点或设备的驱动程序,作为读取和重置值的一种方式。所以这似乎在(主线)内核中不受支持,但我想知道它是否可能来自用户空间?
参考:
- https://e2e.ti.com/support/embedded/linux/f/354/t/542737
- https://www.linuxquestions.org/questions/programming-9/am335x-read-and-write-some-rtc-registers-4175519888/
编辑:一个可行的实现
这是一个可行的解决方案,但我有一个问题(见下文...)
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));
现在,如果我将 mmap
的内存转换为 uint32_t *
(如下所示)以避免以后的类型转换,我读回的值是不正确的(BOOTCOUNT_MAGIC
不匹配):
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));
当我使用 uint32_t *mem
时,val
不应该保持相同的值吗?
完整来源可以是 found on Github.
Shouldn't val
still hold the same value when I use uint32_t *mem
?
如果您使用了正确的指针算法(您没有使用),那么您会得到正确的结果。
在<pointer> + <scaler>
的C表达式中,<scaler>
被用来表示一个与指针类型大小相同的量(例如sizeof(struct foo)
)。
只有当指针是字节指针时,<scaler>
才表示字节数。
如果我们有一个包含 100 个结构元素的数组 x
,例如
struct foo x[100];
数组名可以作为指针使用
因此,数组的第十个元素的地址可以引用为 &x[9]
或 (x + 9)
。
所以在你的第一个代码示例中
unsigned char *mem ...
mem
声明为字节指针。
所以
中的指针计算
unsigned char *scratch2 = mem + page_offset;
产生您期望的结果,因为 sizeof(unsigned char)
是 1(字节),而 page_offset
以字节数表示。
但是在你的第二个代码示例中
uint32_t *mem ...
mem
声明为指向 4 字节字的指针。
所以
中相同的指针计算
unsigned char *scratch2 = mem + page_offset;
真的等同于
unsigned char *scratch2 = (unsigned char *)mem + (sizeof(uint32_t) * page_offset);
其中 sizeof(uint32_t)
是 4(字节),应用的偏移量是您预期的四倍。
那是因为 page_offset
仍然是一个字节数,但在原来的表达式中被视为指针类型的缩放器计数,它是一个 4 字节的字。
您不能简单地更改指针类型(即从 char
到 int
),并期望指针算法不受影响。
将mem
改成uint32_t *
时,指针计算需要重新编码,将字节偏移量转换为四字节偏移量:
unsigned char *scratch2 = mem + (page_offset / sizeof(uint32_t));
您可以通过使用 printf() 报告 mem
、scratch2
和其他值来确认这些语句(或调试您的代码)指针计算。
我正在尝试使用 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)。但是在 linux 方面,我找不到任何将 bootcount 公开为 sysfs 节点或设备的驱动程序,作为读取和重置值的一种方式。所以这似乎在(主线)内核中不受支持,但我想知道它是否可能来自用户空间?
参考:
- https://e2e.ti.com/support/embedded/linux/f/354/t/542737
- https://www.linuxquestions.org/questions/programming-9/am335x-read-and-write-some-rtc-registers-4175519888/
编辑:一个可行的实现
这是一个可行的解决方案,但我有一个问题(见下文...)
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));
现在,如果我将 mmap
的内存转换为 uint32_t *
(如下所示)以避免以后的类型转换,我读回的值是不正确的(BOOTCOUNT_MAGIC
不匹配):
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));
当我使用 uint32_t *mem
时,val
不应该保持相同的值吗?
完整来源可以是 found on Github.
Shouldn't
val
still hold the same value when I useuint32_t *mem
?
如果您使用了正确的指针算法(您没有使用),那么您会得到正确的结果。
在<pointer> + <scaler>
的C表达式中,<scaler>
被用来表示一个与指针类型大小相同的量(例如sizeof(struct foo)
)。
只有当指针是字节指针时,<scaler>
才表示字节数。
如果我们有一个包含 100 个结构元素的数组 x
,例如
struct foo x[100];
数组名可以作为指针使用
因此,数组的第十个元素的地址可以引用为 &x[9]
或 (x + 9)
。
所以在你的第一个代码示例中
unsigned char *mem ...
mem
声明为字节指针。
所以
unsigned char *scratch2 = mem + page_offset;
产生您期望的结果,因为 sizeof(unsigned char)
是 1(字节),而 page_offset
以字节数表示。
但是在你的第二个代码示例中
uint32_t *mem ...
mem
声明为指向 4 字节字的指针。
所以
unsigned char *scratch2 = mem + page_offset;
真的等同于
unsigned char *scratch2 = (unsigned char *)mem + (sizeof(uint32_t) * page_offset);
其中 sizeof(uint32_t)
是 4(字节),应用的偏移量是您预期的四倍。
那是因为 page_offset
仍然是一个字节数,但在原来的表达式中被视为指针类型的缩放器计数,它是一个 4 字节的字。
您不能简单地更改指针类型(即从 char
到 int
),并期望指针算法不受影响。
将mem
改成uint32_t *
时,指针计算需要重新编码,将字节偏移量转换为四字节偏移量:
unsigned char *scratch2 = mem + (page_offset / sizeof(uint32_t));
您可以通过使用 printf() 报告 mem
、scratch2
和其他值来确认这些语句(或调试您的代码)指针计算。