aboutsummaryrefslogtreecommitdiff
path: root/core/mem/virtual/virtual_windows.odin
blob: 1d777af17fe32fd3f57c048556e3e422d07153fc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#+build windows
#+private
package mem_virtual

foreign import Kernel32 "system:Kernel32.lib"

LPSYSTEM_INFO :: ^SYSTEM_INFO
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,
}

MEM_COMMIT      :: 0x00001000
MEM_RESERVE     :: 0x00002000
MEM_RESET       :: 0x00080000
MEM_RESET_UNDO  :: 0x01000000
MEM_LARGE_PAGES :: 0x20000000
MEM_PHYSICAL    :: 0x00400000
MEM_TOP_DOWN    :: 0x00100000
MEM_WRITE_WATCH :: 0x00200000

MEM_DECOMMIT :: 0x00004000
MEM_RELEASE  :: 0x00008000

MEM_COALESCE_PLACEHOLDERS :: 0x00000001
MEM_PRESERVE_PLACEHOLDER  :: 0x00000002

PAGE_EXECUTE           :: 0x10
PAGE_EXECUTE_READ      :: 0x20
PAGE_EXECUTE_READWRITE :: 0x40
PAGE_EXECUTE_WRITECOPY :: 0x80
PAGE_NOACCESS          :: 0x01
PAGE_READONLY          :: 0x02
PAGE_READWRITE         :: 0x04
PAGE_WRITECOPY         :: 0x08
PAGE_TARGETS_INVALID   :: 0x40000000
PAGE_TARGETS_NO_UPDATE :: 0x40000000

SECTION_MAP_WRITE :: 0x0002
SECTION_MAP_READ  :: 0x0004
FILE_MAP_WRITE    :: SECTION_MAP_WRITE
FILE_MAP_READ     :: SECTION_MAP_READ

ERROR_INVALID_ADDRESS :: 487
ERROR_COMMITMENT_LIMIT :: 1455

@(default_calling_convention="system")
foreign Kernel32 {
	GetSystemInfo  :: proc(lpSystemInfo: LPSYSTEM_INFO) ---
	VirtualAlloc   :: proc(lpAddress: rawptr, dwSize: uint, flAllocationType: u32, flProtect: u32) -> rawptr ---
	VirtualFree    :: proc(lpAddress: rawptr, dwSize: uint, dwFreeType: u32) -> b32 ---
	VirtualProtect :: proc(lpAddress: rawptr, dwSize: uint, flNewProtect: u32, lpflOldProtect: ^u32) -> b32 ---
	GetLastError   :: proc() -> u32 ---

	CreateFileMappingW :: proc(
		hFile:                   rawptr,
		lpFileMappingAttributes: rawptr,
		flProtect:               u32,
		dwMaximumSizeHigh:       u32,
		dwMaximumSizeLow:        u32,
		lpName:                  cstring16,
	) -> rawptr ---

	MapViewOfFile :: proc(
		hFileMappingObject:   rawptr,
		dwDesiredAccess:      u32,
		dwFileOffsetHigh:     u32,
		dwFileOffsetLow:      u32,
		dwNumberOfBytesToMap: uint,
	) -> rawptr ---
}

@(no_sanitize_address)
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
	result := VirtualAlloc(nil, size, MEM_RESERVE, PAGE_READWRITE)
	if result == nil {
		err = .Out_Of_Memory
		return
	}
	data = ([^]byte)(result)[:size]
	return
}

@(no_sanitize_address)
_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
	result := VirtualAlloc(data, size, MEM_COMMIT, PAGE_READWRITE)
	if result == nil {
		switch err := GetLastError(); err {
		case 0:
			return .Invalid_Argument
		case ERROR_INVALID_ADDRESS, ERROR_COMMITMENT_LIMIT:
			return .Out_Of_Memory
		}

		return .Out_Of_Memory
	}
	return nil
}

@(no_sanitize_address)
_decommit :: proc "contextless" (data: rawptr, size: uint) {
	VirtualFree(data, size, MEM_DECOMMIT)
}

@(no_sanitize_address)
_release :: proc "contextless" (data: rawptr, size: uint) {
	VirtualFree(data, 0, MEM_RELEASE)
}

@(no_sanitize_address)
_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
	pflags: u32
	pflags = PAGE_NOACCESS
	switch flags {
	case {}:                        pflags = PAGE_NOACCESS
	case {.Read}:                   pflags = PAGE_READONLY
	case {.Read, .Write}:           pflags = PAGE_READWRITE
	case {.Write}:                  pflags = PAGE_WRITECOPY
	case {.Execute}:                pflags = PAGE_EXECUTE
	case {.Execute, .Read}:         pflags = PAGE_EXECUTE_READ
	case {.Execute, .Read, .Write}: pflags = PAGE_EXECUTE_READWRITE
	case {.Execute, .Write}:        pflags = PAGE_EXECUTE_WRITECOPY
	case: 
		return false
	}
	
	
	old_protect: u32
	ok := VirtualProtect(data, size, pflags, &old_protect)
	return bool(ok)
}


@(no_sanitize_address)
_platform_memory_init :: proc "contextless" () {
	sys_info: SYSTEM_INFO
	GetSystemInfo(&sys_info)
	DEFAULT_PAGE_SIZE = max(DEFAULT_PAGE_SIZE, uint(sys_info.dwPageSize))
	
	// is power of two
	assert_contextless(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0)
}


@(no_sanitize_address)
_map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
	page_flags: u32
	if flags == {.Read} {
		page_flags = PAGE_READONLY
	} else if flags == {.Write} {
		page_flags = PAGE_READWRITE
	} else if flags == {.Read, .Write} {
		page_flags = PAGE_READWRITE
	} else {
		page_flags = PAGE_NOACCESS
	}
	maximum_size := transmute([2]u32)size
	handle := CreateFileMappingW(rawptr(fd), nil, page_flags, maximum_size[1], maximum_size[0], nil)
	if handle == nil {
		return nil, .Map_Failure
	}

	desired_access: u32
	if .Read in flags {
		desired_access |= FILE_MAP_READ
	}
	if .Write in flags {
		desired_access |= FILE_MAP_WRITE
	}

	file_data := MapViewOfFile(handle, desired_access, 0, 0, uint(size))
	return ([^]byte)(file_data)[:size], nil
}