Skip to content

Commit 5972354

Browse files
widgetiiclaude
andauthored
tools: fall back to PAGE_SIZE mmap when /dev/mem rejects 64 KiB window (#172)
mem_reg() always mmap()s a 64 KiB window so consecutive register reads in the same SoC block hit a cache. On kernels built with CONFIG_IO_STRICT_DEVMEM=y (the kernel default since v4.6, and what the OpenIPC hi3516cv6xx defconfig currently ships) any /dev/mem mapping whose range overlaps a page already claimed by a driver via request_mem_region() is rejected with EPERM, even for root. On Hi3516CV608 the very first probe touches SCSYSID at 0x11020EE0. The 64 KiB window [0x11020000..0x1102FFFF] covers 0x11029000, claimed by the PWM driver, so mmap fails and ipctool aborts before printing the chip name -- even though the SYSCTRL page itself is unclaimed and readable. When EPERM is observed, retry the mmap with a single PAGE_SIZE window aligned to the requested address. That unblocks chip-ID, HPM, DDR PHY and any other register block whose own page isn't kernel-claimed. Pages that *are* claimed (CRG/clocks, I2C, SPI, GPIO, UART) still need the kernel-side companion fix in the OpenIPC firmware defconfig. Cache invalidation is rewritten to track the actual loaded window range instead of comparing against a fixed 64 KiB offset, so the fallback path's smaller window still benefits from the read-side cache for adjacent registers in the same page. Verified on OpenIPC hi3516cv6xx (kernel 5.10.221, CONFIG_STRICT_DEVMEM and CONFIG_IO_STRICT_DEVMEM both =y): before the patch ipctool exited with "read_mem_reg mmap error: Operation not permitted (1)"; after, it correctly identifies model 3516CV608 and emits NOR/RAM/firmware sections of the YAML report. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 56a3696 commit 5972354

1 file changed

Lines changed: 24 additions & 15 deletions

File tree

src/tools.c

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,20 @@ bool mem_reg(uint32_t addr, uint32_t *data, enum REG_OPS op) {
4141
return true;
4242
}
4343

44-
uint32_t offset = addr & 0xffff0000;
45-
uint32_t size = 0xffff;
46-
if (!addr || (loaded_area && offset != loaded_offset)) {
44+
bool in_cache = loaded_area && addr >= loaded_offset &&
45+
addr - loaded_offset < loaded_size;
46+
if (!addr || (loaded_area && !in_cache)) {
4747
int res = munmap(loaded_area, loaded_size);
4848
if (res) {
4949
fprintf(stderr, "read_mem_reg error: %s (%d)\n", strerror(errno),
5050
errno);
5151
}
52+
loaded_area = NULL;
5253
}
5354

5455
if (!addr) {
5556
close(mem_fd);
57+
mem_fd = 0;
5658
return true;
5759
}
5860

@@ -65,16 +67,23 @@ bool mem_reg(uint32_t addr, uint32_t *data, enum REG_OPS op) {
6567
}
6668

6769
volatile char *mapped_area;
68-
if (offset != loaded_offset) {
69-
mapped_area =
70-
mmap(NULL, // Any adddress in our space will do
71-
size, // Map length
72-
PROT_READ |
73-
PROT_WRITE, // Enable reading & writting to mapped memory
74-
MAP_SHARED, // Shared with other processes
75-
mem_fd, // File to map
76-
offset // Offset to base address
77-
);
70+
if (!loaded_area) {
71+
uint32_t offset = addr & 0xffff0000;
72+
uint32_t size = 0xffff;
73+
mapped_area = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED,
74+
mem_fd, offset);
75+
if (mapped_area == MAP_FAILED && errno == EPERM) {
76+
// CONFIG_IO_STRICT_DEVMEM blocks any /dev/mem mmap whose range
77+
// overlaps a driver-claimed page. Retry with a single page so
78+
// the read at least succeeds when our target page itself isn't
79+
// claimed (the 64 KiB window may have caught an unrelated
80+
// sibling). See OpenIPC firmware PR for the kernel-side fix.
81+
uint32_t page = (uint32_t)sysconf(_SC_PAGESIZE);
82+
offset = addr & ~(page - 1);
83+
size = page;
84+
mapped_area = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED,
85+
mem_fd, offset);
86+
}
7887
if (mapped_area == MAP_FAILED) {
7988
fprintf(stderr, "read_mem_reg mmap error: %s (%d)\n",
8089
strerror(errno), errno);
@@ -87,9 +96,9 @@ bool mem_reg(uint32_t addr, uint32_t *data, enum REG_OPS op) {
8796
mapped_area = loaded_area;
8897

8998
if (op == OP_READ)
90-
*data = *(volatile uint32_t *)(mapped_area + (addr - offset));
99+
*data = *(volatile uint32_t *)(mapped_area + (addr - loaded_offset));
91100
else if (op == OP_WRITE)
92-
*(volatile uint32_t *)(mapped_area + (addr - offset)) = *data;
101+
*(volatile uint32_t *)(mapped_area + (addr - loaded_offset)) = *data;
93102

94103
return true;
95104
}

0 commit comments

Comments
 (0)