diff --git a/base/runtime/virtual_memory_netbsd.odin b/base/runtime/virtual_memory_netbsd.odin index 1e6ccbef9..bd24d2e19 100644 --- a/base/runtime/virtual_memory_netbsd.odin +++ b/base/runtime/virtual_memory_netbsd.odin @@ -6,7 +6,6 @@ import "base:intrinsics" VIRTUAL_MEMORY_SUPPORTED :: true SYS_munmap :: uintptr(73) -SYS_mmap :: uintptr(197) SYS_mremap :: uintptr(411) PROT_READ :: 0x01 @@ -44,7 +43,7 @@ _get_superpage_size :: proc "contextless" () -> int { } _allocate_virtual_memory :: proc "contextless" (size: int) -> rawptr { - result, ok := intrinsics.syscall_bsd(SYS_mmap, 0, uintptr(size), PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, ~uintptr(0), 0) + result, ok := __netbsd_sys_mmap(nil, uint(size), PROT_READ|PROT_WRITE, i32(MAP_ANONYMOUS|MAP_PRIVATE), -1, 0, 0) if !ok { return nil } @@ -59,8 +58,11 @@ _allocate_virtual_memory_aligned :: proc "contextless" (size: int, alignment: in // NOTE: Unlike FreeBSD, the NetBSD man pages do not indicate that // `MAP_ALIGNED` can cause this to fail, so we don't try a second time with // manual alignment. - map_aligned_n := intrinsics.count_trailing_zeros(uintptr(alignment)) << MAP_ALIGNMENT_SHIFT - result, ok := intrinsics.syscall_bsd(SYS_mmap, 0, uintptr(size), PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE|map_aligned_n, ~uintptr(0), 0) + map_aligned_n: u32 + if alignment > page_size { + map_aligned_n = u32(intrinsics.count_trailing_zeros(uintptr(alignment)) << MAP_ALIGNMENT_SHIFT) + } + result, ok := __netbsd_sys_mmap(nil, uint(size), PROT_READ|PROT_WRITE, i32(MAP_ANONYMOUS|MAP_PRIVATE|map_aligned_n), -1, 0, 0) if !ok { return nil } @@ -72,19 +74,43 @@ _free_virtual_memory :: proc "contextless" (ptr: rawptr, size: int) { } _resize_virtual_memory :: proc "contextless" (ptr: rawptr, old_size: int, new_size: int, alignment: int) -> rawptr { - if alignment == 0 { - // The user does not care about alignment, which is the simpler case. - result, ok := intrinsics.syscall_bsd(SYS_mremap, uintptr(ptr), uintptr(old_size), uintptr(new_size), 0) - if !ok { - return nil - } - return rawptr(result) - } else { - map_aligned_n := intrinsics.count_trailing_zeros(uintptr(alignment)) << MAP_ALIGNMENT_SHIFT - result, ok := intrinsics.syscall_bsd(SYS_mremap, uintptr(ptr), uintptr(old_size), uintptr(new_size), map_aligned_n) - if !ok { - return nil - } + // NetBSD will not abide by `new_size` and `old_size` not being a multiple + // of the page size when remapping. + old_size_in_pages := old_size / page_size + if old_size % page_size != 0 { + old_size_in_pages += 1 + } + new_size_in_pages := new_size / page_size + if new_size % page_size != 0 { + new_size_in_pages += 1 + } + old_size_rounded := old_size_in_pages * page_size + new_size_rounded := new_size_in_pages * page_size + + flags := uintptr(0) + if alignment > page_size { + flags = intrinsics.count_trailing_zeros(uintptr(alignment)) << MAP_ALIGNMENT_SHIFT + } + + if result, ok := intrinsics.syscall_bsd(SYS_mremap, uintptr(ptr), uintptr(old_size_rounded), uintptr(new_size_rounded), 0, flags); ok { return rawptr(result) } + + // It may not have been possible to extend the old address space. + // Try to allocate a new mapping and copy the old data. + new_ptr: rawptr + if alignment > page_size { + new_ptr = _allocate_virtual_memory_aligned(new_size, alignment) + } else { + new_ptr = _allocate_virtual_memory(new_size) + } + + if new_ptr == nil { + // Memory allocation failed. + return nil + } + + intrinsics.mem_copy_non_overlapping(new_ptr, ptr, min(old_size, new_size)) + _free_virtual_memory(ptr, old_size) + return rawptr(new_ptr) } diff --git a/base/runtime/virtual_memory_netbsd_amd64.asm b/base/runtime/virtual_memory_netbsd_amd64.asm new file mode 100644 index 000000000..10b642f17 --- /dev/null +++ b/base/runtime/virtual_memory_netbsd_amd64.asm @@ -0,0 +1,36 @@ +bits 64 + +%define SYS_mmap 197 + +global __netbsd_sys_mmap + +; This is a workaround for NetBSD's standard mmap syscall needing seven +; arguments (one of which is useless). The seventh must go on the stack, +; and our syscall intrinsics do not currently handle stack-based arguments. +section .text +__netbsd_sys_mmap: + ; These are the arguments for SYS_mmap. + ; + ; addr: void* + ; len: size_t + ; prot: int + ; flags: int + ; fd: int + ; PAD: long (unused) + ; pos: off_t + + ; Move the flags argument into the right register. + mov r10, rcx + + mov rax, SYS_mmap + syscall + + ; Set the DL register to true if the Carry Flag is clear. + ; + ; This is where the 2nd return value will be taken from. + ; + ; This is valid for the System V AMD64 ABI, as 128-bit return + ; values may be stored in RAX:RDX. + setnb dl + + ret diff --git a/base/runtime/virtual_memory_netbsd_amd64.odin b/base/runtime/virtual_memory_netbsd_amd64.odin new file mode 100644 index 000000000..ac6526d98 --- /dev/null +++ b/base/runtime/virtual_memory_netbsd_amd64.odin @@ -0,0 +1,13 @@ +#+private +package runtime + +import "base:intrinsics" + +foreign import netbsd_asm "virtual_memory_netbsd_amd64.asm" + +@(private="file") +c_long :: i32 when size_of(rawptr) == 4 else i64 + +foreign netbsd_asm { + __netbsd_sys_mmap :: proc "c" (addr: rawptr, len: uint, prot: i32, flags: i32, fd: i32, PAD: c_long, pos: i64) -> (rawptr, bool) --- +} diff --git a/base/runtime/virtual_memory_netbsd_arm64.asm b/base/runtime/virtual_memory_netbsd_arm64.asm new file mode 100644 index 000000000..24ed4a997 --- /dev/null +++ b/base/runtime/virtual_memory_netbsd_arm64.asm @@ -0,0 +1,23 @@ +.set SYS_mmap, 197 + +.global __netbsd_sys_mmap + +// This is a workaround for NetBSD's standard mmap syscall needing seven +// arguments (one of which is useless). The seventh must go on the stack, +// and our syscall intrinsics do not currently handle stack-based arguments. +.section .text +__netbsd_sys_mmap: + // These are the arguments for SYS_mmap. + // + // addr: void* + // len: size_t + // prot: int + // flags: int + // fd: int + // PAD: long (unused) + // pos: off_t + + mov x17, SYS_mmap + svc #0 + + cset x1, cc diff --git a/base/runtime/virtual_memory_netbsd_arm64.odin b/base/runtime/virtual_memory_netbsd_arm64.odin new file mode 100644 index 000000000..02fce151a --- /dev/null +++ b/base/runtime/virtual_memory_netbsd_arm64.odin @@ -0,0 +1,13 @@ +#+private +package runtime + +import "base:intrinsics" + +foreign import netbsd_asm "virtual_memory_netbsd_arm64.asm" + +@(private="file") +c_long :: i32 when size_of(rawptr) == 4 else i64 + +foreign netbsd_asm { + __netbsd_sys_mmap :: proc "c" (addr: rawptr, len: uint, prot: i32, flags: i32, fd: i32, PAD: c_long, pos: i64) -> (rawptr, bool) --- +}