Skip to content

Commit 437a3aa

Browse files
committed
RISC-V: detect virtual address space at runtime using hwprobe
The virtual address space on RISC-V is currently detected at build time by parsing /proc/cpuinfo. This works when the binary runs on the same hardware it was built, however running such a binary on a system with a larger address space just causes a segmentation fault. Replace the build time check with a runtime detection using the hwprobe interface (available since Linux 6.11, commit c9b8cd139c1d "riscv: hwprobe export highest virtual userspace address"), with a fallback to parsing /proc/cpuinfo on older kernels.
1 parent d7d90c6 commit 437a3aa

2 files changed

Lines changed: 62 additions & 15 deletions

File tree

CMakeLists.txt

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -475,21 +475,14 @@ else()
475475
# list(APPEND mi_defines MI_WIN_INIT_USE_CRT_TLS=1)
476476
endif()
477477

478-
# Check /proc/cpuinfo for an SV39/48/57 MMU and limit the virtual address bits.
479-
# (for sv39, this will skip the aligned hinting in that case. Issue #939, #949)
480-
if (EXISTS /proc/cpuinfo)
481-
file(STRINGS /proc/cpuinfo mi_sv39_mmu REGEX "^mmu[ \t]+:[ \t]+sv39$")
482-
file(STRINGS /proc/cpuinfo mi_sv48_mmu REGEX "^mmu[ \t]+:[ \t]+sv48$")
483-
file(STRINGS /proc/cpuinfo mi_sv57_mmu REGEX "^mmu[ \t]+:[ \t]+sv57$")
484-
if (mi_sv39_mmu)
485-
MESSAGE( STATUS "Set virtual address bits to 39 (SV39 MMU detected)" )
486-
list(APPEND mi_defines MI_DEFAULT_VIRTUAL_ADDRESS_BITS=39)
487-
elseif (mi_sv48_mmu)
488-
MESSAGE( STATUS "Set virtual address bits to 48 (SV48 MMU detected)" )
489-
list(APPEND mi_defines MI_DEFAULT_VIRTUAL_ADDRESS_BITS=48)
490-
elseif (mi_sv57_mmu)
491-
MESSAGE( STATUS "Set virtual address bits to 57 (SV57 MMU detected)" )
492-
list(APPEND mi_defines MI_DEFAULT_VIRTUAL_ADDRESS_BITS=57)
478+
if(MI_ARCH MATCHES "riscv")
479+
CHECK_INCLUDE_FILES("asm/hwprobe.h;" MI_HAS_ASM_HWPROBEH)
480+
if (MI_HAS_ASM_HWPROBEH)
481+
list(APPEND mi_defines MI_HAS_ASM_HWPROBEH=1)
482+
endif()
483+
CHECK_INCLUDE_FILES("sys/hwprobe.h;" MI_HAS_SYS_HWPROBEH)
484+
if (MI_HAS_SYS_HWPROBEH)
485+
list(APPEND mi_defines MI_HAS_SYS_HWPROBEH=1)
493486
endif()
494487
endif()
495488

src/prim/unix/prim.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ terms of the MIT license. A copy of the license can be found in the file
4141
#else
4242
#include <sys/mman.h>
4343
#endif
44+
#if defined(__riscv) || defined(_M_RISCV)
45+
#if defined(MI_HAS_SYS_HWPROBEH)
46+
#include <sys/hwprobe.h>
47+
#elif defined(MI_HAS_ASM_HWPROBEH)
48+
#include <asm/hwprobe.h>
49+
#endif
50+
#endif
4451
#elif defined(__APPLE__)
4552
#include <AvailabilityMacros.h>
4653
#include <TargetConditionals.h>
@@ -206,6 +213,52 @@ static void unix_detect_physical_memory( size_t page_size, size_t* physical_memo
206213
#endif
207214
}
208215

216+
// Detect the virtual address bits (currently Linux/RISC-V only)
217+
static size_t unix_detect_virtual_address_bits(void)
218+
{
219+
#if defined(__riscv) || defined(_M_RISCV)
220+
#if defined(RISCV_HWPROBE_KEY_HIGHEST_VIRT_ADDRESS)
221+
struct riscv_hwprobe probe = {
222+
.key = RISCV_HWPROBE_KEY_HIGHEST_VIRT_ADDRESS,
223+
};
224+
225+
// Prefer the GNU libc interface if available, as it can also use the VDSO
226+
#if defined(MI_HAS_SYS_HWPROBEH)
227+
if (__riscv_hwprobe(&probe, 1, 0, NULL, 0) == 0) {
228+
#else
229+
if (syscall(__NR_riscv_hwprobe, &probe, 1, 0, NULL, 0) == 0) {
230+
#endif
231+
// If a key is unknown to the kernel, its key field will be cleared to -1.
232+
if (probe.key != -1) {
233+
return MI_SIZE_BITS - mi_clz((uintptr_t)probe.value);
234+
}
235+
}
236+
#endif
237+
238+
// Fallback to checking /proc/cpuinfo for older kernels
239+
const int fd = mi_prim_open("/proc/cpuinfo", O_RDONLY);
240+
if (fd >= 0) {
241+
char buf[2048];
242+
ssize_t nread = mi_prim_read(fd, &buf, sizeof(buf));
243+
mi_prim_close(fd);
244+
if ((nread >= 1) && (nread < (ssize_t)sizeof(buf))) {
245+
if (_mi_strnstr(buf, nread, "sv39")) {
246+
return 39;
247+
}
248+
if (_mi_strnstr(buf, nread, "sv48")) {
249+
return 48;
250+
}
251+
if (_mi_strnstr(buf, nread, "sv57")) {
252+
return 57;
253+
}
254+
}
255+
}
256+
#endif
257+
258+
// default: MI_MAX_VABITS
259+
return MI_MAX_VABITS;
260+
}
261+
209262
void _mi_prim_mem_init( mi_os_mem_config_t* config )
210263
{
211264
long psize = sysconf(_SC_PAGESIZE);
@@ -219,6 +272,7 @@ void _mi_prim_mem_init( mi_os_mem_config_t* config )
219272
config->has_partial_free = true; // mmap can free in parts
220273
config->has_virtual_reserve = true; // todo: check if this true for NetBSD? (for anonymous mmap with PROT_NONE)
221274
config->has_transparent_huge_pages = unix_detect_thp();
275+
config->virtual_address_bits = unix_detect_virtual_address_bits();
222276

223277
// disable transparent huge pages for this process?
224278
#if (defined(__linux__) || defined(__ANDROID__)) && defined(PR_GET_THP_DISABLE)

0 commit comments

Comments
 (0)