xref: /haiku/src/system/kernel/arch/x86/arch_vm.cpp (revision e7c8829c5d8e5d34a2a1e111f1c06aceff256013)
1 /*
2  * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de.
3  * Copyright 2008, Jérôme Duval.
4  * Distributed under the terms of the MIT License.
5  *
6  * Copyright 2001, Travis Geiselbrecht. All rights reserved.
7  * Distributed under the terms of the NewOS License.
8  */
9 
10 
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include <KernelExport.h>
15 
16 #include <smp.h>
17 #include <util/AutoLock.h>
18 #include <vm.h>
19 #include <vm_address_space.h>
20 #include <vm_page.h>
21 #include <vm_priv.h>
22 
23 #include <arch/vm.h>
24 #include <arch/int.h>
25 #include <arch/cpu.h>
26 
27 #include <arch/x86/bios.h>
28 
29 #include "x86_paging.h"
30 
31 
32 //#define TRACE_ARCH_VM
33 #ifdef TRACE_ARCH_VM
34 #	define TRACE(x) dprintf x
35 #else
36 #	define TRACE(x) ;
37 #endif
38 
39 #define TRACE_MTRR_ARCH_VM
40 #ifdef TRACE_MTRR_ARCH_VM
41 #	define TRACE_MTRR(x...) dprintf(x)
42 #else
43 #	define TRACE_MTRR(x...)
44 #endif
45 
46 
47 #define kMaxMemoryTypeRegisters 32
48 
49 void *gDmaAddress;
50 
51 static uint32 sMemoryTypeBitmap;
52 static int32 sMemoryTypeIDs[kMaxMemoryTypeRegisters];
53 static uint32 sMemoryTypeRegisterCount;
54 static spinlock sMemoryTypeLock;
55 
56 
57 static int32
58 allocate_mtrr(void)
59 {
60 	InterruptsSpinLocker _(&sMemoryTypeLock);
61 
62 	// find free bit
63 
64 	for (uint32 index = 0; index < sMemoryTypeRegisterCount; index++) {
65 		if (sMemoryTypeBitmap & (1UL << index))
66 			continue;
67 
68 		sMemoryTypeBitmap |= 1UL << index;
69 		return index;
70 	}
71 
72 	return -1;
73 }
74 
75 
76 static void
77 free_mtrr(int32 index)
78 {
79 	InterruptsSpinLocker _(&sMemoryTypeLock);
80 
81 	sMemoryTypeBitmap &= ~(1UL << index);
82 }
83 
84 
85 #if 0
86 /*!
87  	Checks if the provided range overlaps an existing mtrr range
88  	If it actually extends an existing range, extendedIndex is filled
89 */
90 static bool
91 is_memory_overlapping(uint64 base, uint64 length, int32 *extendedIndex)
92 {
93 	*extendedIndex = -1;
94 	for (uint32 index = 0; index < sMemoryTypeRegisterCount; index++) {
95 		if (sMemoryTypeBitmap & (1UL << index)) {
96 			uint64 b,l;
97 			uint8 t;
98 			x86_get_mtrr(index, &b, &l, &t);
99 
100 			// check first for write combining extensions
101 			if (base <= b
102 				&& (base + length) >= (b + l)
103 				&& t == IA32_MTR_WRITE_COMBINING) {
104 				*extendedIndex = index;
105 				return true;
106 			}
107 			if ((base >= b && base < (b + l))
108 				|| ((base + length) > b
109 					&& (base + length) <= (b + l)))
110 				return true;
111 		}
112 	}
113 	return false;
114 }
115 #endif	// 0
116 
117 
118 static uint64
119 nearest_power(uint64 value)
120 {
121 	uint64 power = 1UL << 12;
122 		// 12 bits is the smallest supported alignment/length
123 
124 	while (value > power)
125 		power <<= 1;
126 
127 	return power;
128 }
129 
130 
131 static void
132 nearest_powers(uint64 value, uint64 *lower, uint64 *upper)
133 {
134 	uint64 power = 1UL << 12;
135 	*lower = power;
136 		// 12 bits is the smallest supported alignment/length
137 
138 	while (value >= power) {
139 		*lower = power;
140 		power <<= 1;
141 	}
142 
143 	*upper = power;
144 }
145 
146 
147 static status_t
148 set_memory_type(int32 id, uint64 base, uint64 length, uint32 type)
149 {
150 	int32 index = -1;
151 
152 	if (type == 0)
153 		return B_OK;
154 
155 	switch (type) {
156 		case B_MTR_UC:
157 			type = IA32_MTR_UNCACHED;
158 			break;
159 		case B_MTR_WC:
160 			type = IA32_MTR_WRITE_COMBINING;
161 			break;
162 		case B_MTR_WT:
163 			type = IA32_MTR_WRITE_THROUGH;
164 			break;
165 		case B_MTR_WP:
166 			type = IA32_MTR_WRITE_PROTECTED;
167 			break;
168 		case B_MTR_WB:
169 			type = IA32_MTR_WRITE_BACK;
170 			break;
171 		default:
172 			return B_BAD_VALUE;
173 	}
174 
175 	if (sMemoryTypeRegisterCount == 0)
176 		return B_NOT_SUPPORTED;
177 
178 #if 0
179 	// check if it overlaps
180 	if (type == IA32_MTR_WRITE_COMBINING
181 		&& is_memory_overlapping(base, length, &index)) {
182 		if (index < 0) {
183 			dprintf("allocate MTRR failed, it overlaps an existing MTRR slot\n");
184 			return B_BAD_VALUE;
185 		}
186 		// we replace an existing write-combining mtrr with a bigger one at the index position
187 	}
188 #endif
189 
190 	// length must be a power of 2; just round it up to the next value
191 	length = nearest_power(length);
192 
193 	if (length + base <= base) {
194 		// 4GB overflow
195 		return B_BAD_VALUE;
196 	}
197 
198 	// base must be aligned to the length
199 	if (base & (length - 1))
200 		return B_BAD_VALUE;
201 
202 	if (index < 0)
203 		index = allocate_mtrr();
204 	if (index < 0)
205 		return B_ERROR;
206 
207 	TRACE_MTRR("allocate MTRR slot %ld, base = %Lx, length = %Lx, type=0x%lx\n",
208 		index, base, length, type);
209 
210 	sMemoryTypeIDs[index] = id;
211 	x86_set_mtrr(index, base, length, type);
212 
213 	return B_OK;
214 }
215 
216 
217 #define MTRR_MAX_SOLUTIONS 	5	// usually MTRR count is eight, keep a few for other needs
218 #define MTRR_MIN_SIZE 		0x80000	// 512 KB
219 static int64 sSolutions[MTRR_MAX_SOLUTIONS];
220 static int32 sSolutionCount;
221 static int64 sPropositions[MTRR_MAX_SOLUTIONS];
222 
223 
224 /*!	Find the nearest powers of two for a value, save current iteration,
225   	then make recursives calls for the remaining values.
226   	It uses at most MTRR_MAX_SOLUTIONS levels of recursion because
227   	only that count of MTRR registers are available to map the memory.
228 */
229 static void
230 find_nearest(uint64 value, int iteration)
231 {
232 	int i;
233 	uint64 down, up;
234 	TRACE_MTRR("find_nearest %Lx %d\n", value, iteration);
235 	if (iteration > (MTRR_MAX_SOLUTIONS - 1) || (iteration + 1) >= sSolutionCount) {
236 		if (sSolutionCount > MTRR_MAX_SOLUTIONS) {
237 			// no solutions yet, save something
238 			for (i=0; i<iteration; i++)
239 				sSolutions[i] = sPropositions[i];
240 			sSolutionCount = iteration;
241 		}
242 		return;
243 	}
244 	nearest_powers(value, &down, &up);
245 	sPropositions[iteration] = down;
246 	if (value - down < MTRR_MIN_SIZE) {
247 		for (i=0; i<=iteration; i++)
248 			sSolutions[i] = sPropositions[i];
249 		sSolutionCount = iteration + 1;
250 		return;
251 	}
252 	find_nearest(value - down, iteration + 1);
253 	sPropositions[iteration] = -up;
254 	if (up - value < MTRR_MIN_SIZE) {
255 		for (i=0; i<=iteration; i++)
256 			sSolutions[i] = sPropositions[i];
257 		sSolutionCount = iteration + 1;
258 		return;
259 	}
260 	find_nearest(up - value, iteration + 1);
261 }
262 
263 
264 /*!	Set up MTRR to map the memory to write-back using uncached if necessary */
265 static void
266 set_memory_write_back(int32 id, uint64 base, uint64 length)
267 {
268 	status_t err;
269 	TRACE_MTRR("set_memory_write_back base %Lx length %Lx\n", base, length);
270 	sSolutionCount = MTRR_MAX_SOLUTIONS + 1;
271 	find_nearest(length, 0);
272 
273 #ifdef TRACE_MTRR
274 	dprintf("solutions: ");
275 	for (int i=0; i<sSolutionCount; i++) {
276                 dprintf("0x%Lx ", sSolutions[i]);
277         }
278         dprintf("\n");
279 #endif
280 
281 	bool nextDown = false;
282 	for (int i = 0; i < sSolutionCount; i++) {
283 		if (sSolutions[i] < 0) {
284 			if (nextDown)
285 				base += sSolutions[i];
286 			err = set_memory_type(id, base, -sSolutions[i], nextDown ? B_MTR_UC : B_MTR_WB);
287 			if (err != B_OK) {
288 				dprintf("set_memory_type returned %s (0x%lx)\n", strerror(err), err);
289 			}
290 			if (!nextDown)
291 				base -= sSolutions[i];
292 			nextDown = !nextDown;
293 		} else {
294 			if (nextDown)
295 				base -= sSolutions[i];
296 			err = set_memory_type(id, base, sSolutions[i], nextDown ? B_MTR_UC : B_MTR_WB);
297 			if (err != B_OK) {
298 				dprintf("set_memory_type returned %s (0x%lx)\n", strerror(err), err);
299 			}
300 			if (!nextDown)
301 				base += sSolutions[i];
302 		}
303 	}
304 }
305 
306 
307 //	#pragma mark -
308 
309 
310 status_t
311 arch_vm_init(kernel_args *args)
312 {
313 	TRACE(("arch_vm_init: entry\n"));
314 	return 0;
315 }
316 
317 
318 /*!	Marks DMA region as in-use, and maps it into the kernel space */
319 status_t
320 arch_vm_init_post_area(kernel_args *args)
321 {
322 	area_id id;
323 
324 	TRACE(("arch_vm_init_post_area: entry\n"));
325 
326 	// account for DMA area and mark the pages unusable
327 	vm_mark_page_range_inuse(0x0, 0xa0000 / B_PAGE_SIZE);
328 
329 	// map 0 - 0xa0000 directly
330 	id = map_physical_memory("dma_region", (void *)0x0, 0xa0000,
331 		B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
332 		&gDmaAddress);
333 	if (id < 0) {
334 		panic("arch_vm_init_post_area: unable to map dma region\n");
335 		return B_NO_MEMORY;
336 	}
337 
338 	return bios_init();
339 }
340 
341 
342 /*!	Gets rid of all yet unmapped (and therefore now unused) page tables */
343 status_t
344 arch_vm_init_end(kernel_args *args)
345 {
346 	TRACE(("arch_vm_init_endvm: entry\n"));
347 
348 	// throw away anything in the kernel_args.pgtable[] that's not yet mapped
349 	vm_free_unused_boot_loader_range(KERNEL_BASE,
350 		0x400000 * args->arch_args.num_pgtables);
351 
352 	return B_OK;
353 }
354 
355 
356 status_t
357 arch_vm_init_post_modules(kernel_args *args)
358 {
359 //	void *cookie;
360 
361 	// the x86 CPU modules are now accessible
362 
363 	sMemoryTypeRegisterCount = x86_count_mtrrs();
364 	if (sMemoryTypeRegisterCount == 0)
365 		return B_OK;
366 
367 	// not very likely, but play safe here
368 	if (sMemoryTypeRegisterCount > kMaxMemoryTypeRegisters)
369 		sMemoryTypeRegisterCount = kMaxMemoryTypeRegisters;
370 
371 	// init memory type ID table
372 
373 	for (uint32 i = 0; i < sMemoryTypeRegisterCount; i++) {
374 		sMemoryTypeIDs[i] = -1;
375 	}
376 
377 	// set the physical memory ranges to write-back mode
378 
379 	for (uint32 i = 0; i < args->num_physical_memory_ranges; i++) {
380 		set_memory_write_back(-1, args->physical_memory_range[i].start,
381 			args->physical_memory_range[i].size);
382 	}
383 
384 	return B_OK;
385 }
386 
387 
388 void
389 arch_vm_aspace_swap(struct vm_address_space *from, struct vm_address_space *to)
390 {
391 	// This functions is only invoked when a userland thread is in the process
392 	// of dying. It switches to the kernel team and does whatever cleanup is
393 	// necessary (in case it is the team's main thread, it will delete the
394 	// team).
395 	// It is however not necessary to change the page directory. Userland team's
396 	// page directories include all kernel mappings as well. Furthermore our
397 	// arch specific translation map data objects are ref-counted, so they won't
398 	// go away as long as they are still used on any CPU.
399 }
400 
401 
402 bool
403 arch_vm_supports_protection(uint32 protection)
404 {
405 	// x86 always has the same read/write properties for userland and the
406 	// kernel.
407 	// That's why we do not support user-read/kernel-write access. While the
408 	// other way around is not supported either, we don't care in this case
409 	// and give the kernel full access.
410 	if ((protection & (B_READ_AREA | B_WRITE_AREA)) == B_READ_AREA
411 		&& protection & B_KERNEL_WRITE_AREA)
412 		return false;
413 
414 	return true;
415 }
416 
417 
418 void
419 arch_vm_unset_memory_type(struct vm_area *area)
420 {
421 	uint32 index;
422 
423 	if (area->memory_type == 0)
424 		return;
425 
426 	// find index for area ID
427 
428 	for (index = 0; index < sMemoryTypeRegisterCount; index++) {
429 		if (sMemoryTypeIDs[index] == area->id) {
430 			x86_set_mtrr(index, 0, 0, 0);
431 
432 			sMemoryTypeIDs[index] = -1;
433 			free_mtrr(index);
434 			break;
435 		}
436 	}
437 }
438 
439 
440 status_t
441 arch_vm_set_memory_type(struct vm_area *area, addr_t physicalBase,
442 	uint32 type)
443 {
444 	area->memory_type = type >> MEMORY_TYPE_SHIFT;
445 	return set_memory_type(area->id, physicalBase, area->size, type);
446 }
447