mirror of
https://github.com/odin-lang/Odin.git
synced 2026-07-30 17:57:45 -04:00
162 lines
4.6 KiB
Odin
162 lines
4.6 KiB
Odin
#+private
|
|
package runtime
|
|
|
|
import "base:intrinsics"
|
|
|
|
foreign import Kernel32 "system:Kernel32.lib"
|
|
foreign import OneCore "system:OneCore.lib"
|
|
|
|
VIRTUAL_MEMORY_SUPPORTED :: true
|
|
|
|
// NOTE: Windows makes a distinction between page size and allocation
|
|
// granularity. Addresses returned from the allocation procedures are rounded
|
|
// to allocation granularity boundaries, at a minimum, not page sizes.
|
|
|
|
@(default_calling_convention="system")
|
|
foreign Kernel32 {
|
|
GetLastError :: proc() -> u32 ---
|
|
GetSystemInfo :: proc(lpSystemInfo: ^SYSTEM_INFO) ---
|
|
}
|
|
|
|
@(default_calling_convention="system")
|
|
foreign OneCore {
|
|
VirtualAlloc :: proc(lpAddress: rawptr, dwSize: uint, flAllocationType: u32, flProtect: u32) -> rawptr ---
|
|
VirtualAlloc2 :: proc(
|
|
Process: rawptr,
|
|
BaseAddress: rawptr,
|
|
Size: uint,
|
|
AllocationType: u32,
|
|
PageProtection: u32,
|
|
ExtendedParameters: [^]MEM_EXTENDED_PARAMETER,
|
|
ParameterCount: u32) -> rawptr ---
|
|
VirtualFree :: proc(lpAddress: rawptr, dwSize: uint, dwFreeType: u32) -> b32 ---
|
|
}
|
|
|
|
SYSTEM_INFO :: struct {
|
|
using DUMMYUNIONNAME: struct #raw_union {
|
|
dwOemId: u32,
|
|
using DUMMYSTRUCTNAME:struct {
|
|
wProcessorArchitecture: u16,
|
|
wReserved: u16,
|
|
},
|
|
},
|
|
dwPageSize: u32,
|
|
lpMinimumApplicationAddress: rawptr,
|
|
lpMaximumApplicationAddress: rawptr,
|
|
dwActiveProcessorMask: uint,
|
|
dwNumberOfProcessors: u32,
|
|
dwProcessorType: u32,
|
|
dwAllocationGranularity: u32,
|
|
wProcessorLevel: u16,
|
|
wProcessorRevision: u16,
|
|
}
|
|
|
|
MemExtendedParameterAddressRequirements :: 0x01
|
|
MEM_EXTENDED_PARAMETER_TYPE_BITS :: 8
|
|
|
|
MEM_ADDRESS_REQUIREMENTS :: struct {
|
|
LowestStartingAddress: rawptr,
|
|
HighestEndingAddress: rawptr,
|
|
Alignment: uint,
|
|
}
|
|
|
|
MEM_EXTENDED_PARAMETER :: struct {
|
|
using DUMMYSTRUCTNAME: bit_field u64 {
|
|
Type: u64 | MEM_EXTENDED_PARAMETER_TYPE_BITS,
|
|
Reserved: u64 | 64 - MEM_EXTENDED_PARAMETER_TYPE_BITS,
|
|
},
|
|
using DUMMYUNIONNAME: struct #raw_union {
|
|
ULong64: u64,
|
|
Pointer: rawptr,
|
|
Size: uint,
|
|
Handle: rawptr,
|
|
ULong: u32,
|
|
},
|
|
}
|
|
|
|
|
|
MEM_COMMIT :: 0x00001000
|
|
MEM_RESERVE :: 0x00002000
|
|
MEM_RELEASE :: 0x00008000
|
|
|
|
PAGE_READWRITE :: 0x04
|
|
|
|
_allocation_granularity: int
|
|
|
|
_init_virtual_memory :: proc "contextless" () {
|
|
sys_info: SYSTEM_INFO
|
|
GetSystemInfo(&sys_info)
|
|
|
|
page_size = int(sys_info.dwPageSize)
|
|
_allocation_granularity = int(sys_info.dwAllocationGranularity)
|
|
}
|
|
|
|
_get_superpage_size :: proc "contextless" () -> int {
|
|
// TODO: Windows has support for Large Pages, but its usage requires privilege escalation.
|
|
return 0
|
|
}
|
|
|
|
_allocate_virtual_memory :: proc "contextless" (size: int) -> rawptr {
|
|
// `Size` must be a multiple of the page size.
|
|
rounded_size := size
|
|
if rounded_size % page_size != 0 {
|
|
rounded_size = (size / page_size + 1) * page_size
|
|
}
|
|
result := VirtualAlloc(nil, uint(rounded_size), MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE)
|
|
if result == nil {
|
|
return nil
|
|
}
|
|
return rawptr(result)
|
|
}
|
|
|
|
_allocate_virtual_memory_superpage :: proc "contextless" () -> rawptr {
|
|
return nil
|
|
}
|
|
|
|
_allocate_virtual_memory_aligned :: proc "contextless" (size: int, alignment: int) -> rawptr {
|
|
if alignment <= _allocation_granularity {
|
|
// The alignment is less than or equal to the allocation granularity,
|
|
// which means it will automatically be aligned and any request for
|
|
// alignment less than the allocation granularity will result in
|
|
// ERROR_INVALID_PARAMETER.
|
|
return _allocate_virtual_memory(size)
|
|
}
|
|
addr_req := MEM_ADDRESS_REQUIREMENTS{
|
|
LowestStartingAddress = nil,
|
|
HighestEndingAddress = nil,
|
|
Alignment = uint(alignment),
|
|
}
|
|
param := MEM_EXTENDED_PARAMETER{
|
|
Type = MemExtendedParameterAddressRequirements,
|
|
Pointer = &addr_req,
|
|
}
|
|
// `Size` must be a multiple of the page size.
|
|
rounded_size := size
|
|
if rounded_size % page_size != 0 {
|
|
rounded_size = (size / page_size + 1) * page_size
|
|
}
|
|
result := VirtualAlloc2(nil, nil, uint(rounded_size), MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE, ¶m, 1)
|
|
if result == nil {
|
|
return nil
|
|
}
|
|
return rawptr(result)
|
|
}
|
|
|
|
_free_virtual_memory :: proc "contextless" (ptr: rawptr, size: int) {
|
|
VirtualFree(ptr, 0, MEM_RELEASE)
|
|
}
|
|
|
|
_resize_virtual_memory :: proc "contextless" (ptr: rawptr, old_size: int, new_size: int, alignment: int) -> rawptr {
|
|
// There is no system support for resizing addresses returned by VirtualAlloc.
|
|
// All we can do is request a new address, copy the data, and free the old.
|
|
result: rawptr = ---
|
|
if alignment == 0 {
|
|
result = _allocate_virtual_memory(new_size)
|
|
} else {
|
|
result = _allocate_virtual_memory_aligned(new_size, alignment)
|
|
}
|
|
intrinsics.mem_copy_non_overlapping(result, ptr, min(new_size, old_size))
|
|
VirtualFree(ptr, 0, MEM_RELEASE)
|
|
return result
|
|
}
|