xref: /haiku/src/system/kernel/arch/x86/arch_vm.cpp (revision 2d2212bd711f09275aca556bf4d097484e037c25)
1393fceb5SAxel Dörfler /*
2393fceb5SAxel Dörfler  * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de.
3393fceb5SAxel Dörfler  * Distributed under the terms of the MIT License.
4393fceb5SAxel Dörfler  *
5393fceb5SAxel Dörfler  * Copyright 2001, Travis Geiselbrecht. All rights reserved.
6393fceb5SAxel Dörfler  * Distributed under the terms of the NewOS License.
7393fceb5SAxel Dörfler  */
8393fceb5SAxel Dörfler 
9393fceb5SAxel Dörfler 
10393fceb5SAxel Dörfler #include <KernelExport.h>
11393fceb5SAxel Dörfler #include <smp.h>
12393fceb5SAxel Dörfler #include <util/AutoLock.h>
13393fceb5SAxel Dörfler #include <vm.h>
14393fceb5SAxel Dörfler #include <vm_page.h>
15393fceb5SAxel Dörfler #include <vm_priv.h>
16393fceb5SAxel Dörfler 
17393fceb5SAxel Dörfler #include <arch/vm.h>
18393fceb5SAxel Dörfler #include <arch/int.h>
19393fceb5SAxel Dörfler #include <arch/cpu.h>
20393fceb5SAxel Dörfler 
21393fceb5SAxel Dörfler #include <arch/x86/bios.h>
22393fceb5SAxel Dörfler 
23393fceb5SAxel Dörfler #include <stdlib.h>
24393fceb5SAxel Dörfler #include <string.h>
25393fceb5SAxel Dörfler 
26393fceb5SAxel Dörfler 
27393fceb5SAxel Dörfler //#define TRACE_ARCH_VM
28393fceb5SAxel Dörfler #ifdef TRACE_ARCH_VM
29393fceb5SAxel Dörfler #	define TRACE(x) dprintf x
30393fceb5SAxel Dörfler #else
31393fceb5SAxel Dörfler #	define TRACE(x) ;
32393fceb5SAxel Dörfler #endif
33393fceb5SAxel Dörfler 
34393fceb5SAxel Dörfler 
35393fceb5SAxel Dörfler #define kMaxMemoryTypeRegisters 32
36393fceb5SAxel Dörfler 
37393fceb5SAxel Dörfler void *gDmaAddress;
38393fceb5SAxel Dörfler 
39393fceb5SAxel Dörfler static uint32 sMemoryTypeBitmap;
40393fceb5SAxel Dörfler static int32 sMemoryTypeIDs[kMaxMemoryTypeRegisters];
41393fceb5SAxel Dörfler static uint32 sMemoryTypeRegisterCount;
42393fceb5SAxel Dörfler static spinlock sMemoryTypeLock;
43393fceb5SAxel Dörfler 
44393fceb5SAxel Dörfler 
45393fceb5SAxel Dörfler static int32
46393fceb5SAxel Dörfler allocate_mtrr(void)
47393fceb5SAxel Dörfler {
48393fceb5SAxel Dörfler 	InterruptsSpinLocker _(&sMemoryTypeLock);
49393fceb5SAxel Dörfler 
50393fceb5SAxel Dörfler 	// find free bit
51393fceb5SAxel Dörfler 
52393fceb5SAxel Dörfler 	for (uint32 index = 0; index < sMemoryTypeRegisterCount; index++) {
53393fceb5SAxel Dörfler 		if (sMemoryTypeBitmap & (1UL << index))
54393fceb5SAxel Dörfler 			continue;
55393fceb5SAxel Dörfler 
56393fceb5SAxel Dörfler 		sMemoryTypeBitmap |= 1UL << index;
57393fceb5SAxel Dörfler 		return index;
58393fceb5SAxel Dörfler 	}
59393fceb5SAxel Dörfler 
60393fceb5SAxel Dörfler 	return -1;
61393fceb5SAxel Dörfler }
62393fceb5SAxel Dörfler 
63393fceb5SAxel Dörfler 
64393fceb5SAxel Dörfler static void
65393fceb5SAxel Dörfler free_mtrr(int32 index)
66393fceb5SAxel Dörfler {
67393fceb5SAxel Dörfler 	InterruptsSpinLocker _(&sMemoryTypeLock);
68393fceb5SAxel Dörfler 
69393fceb5SAxel Dörfler 	sMemoryTypeBitmap &= ~(1UL << index);
70393fceb5SAxel Dörfler }
71393fceb5SAxel Dörfler 
72393fceb5SAxel Dörfler 
73393fceb5SAxel Dörfler static uint64
74393fceb5SAxel Dörfler nearest_power(addr_t value)
75393fceb5SAxel Dörfler {
76393fceb5SAxel Dörfler 	uint64 power = 1UL << 12;
77393fceb5SAxel Dörfler 		// 12 bits is the smallest supported alignment/length
78393fceb5SAxel Dörfler 
79393fceb5SAxel Dörfler 	while (value > power)
80393fceb5SAxel Dörfler 		power <<= 1;
81393fceb5SAxel Dörfler 
82393fceb5SAxel Dörfler 	return power;
83393fceb5SAxel Dörfler }
84393fceb5SAxel Dörfler 
85393fceb5SAxel Dörfler 
86393fceb5SAxel Dörfler static status_t
87393fceb5SAxel Dörfler set_memory_type(int32 id, uint64 base, uint64 length, uint32 type)
88393fceb5SAxel Dörfler {
89393fceb5SAxel Dörfler 	int32 index;
90393fceb5SAxel Dörfler 
91393fceb5SAxel Dörfler 	if (type == 0)
92393fceb5SAxel Dörfler 		return B_OK;
93393fceb5SAxel Dörfler 
94*2d2212bdSJérôme Duval 	uint32 newType;
95*2d2212bdSJérôme Duval 
96393fceb5SAxel Dörfler 	switch (type) {
97393fceb5SAxel Dörfler 		case B_MTR_UC:
98*2d2212bdSJérôme Duval 			newType = IA32_MTR_UNCACHED;
99393fceb5SAxel Dörfler 			break;
100393fceb5SAxel Dörfler 		case B_MTR_WC:
101*2d2212bdSJérôme Duval 			newType = IA32_MTR_WRITE_COMBINING;
102393fceb5SAxel Dörfler 			break;
103393fceb5SAxel Dörfler 		case B_MTR_WT:
104*2d2212bdSJérôme Duval 			newType = IA32_MTR_WRITE_THROUGH;
105393fceb5SAxel Dörfler 			break;
106393fceb5SAxel Dörfler 		case B_MTR_WP:
107*2d2212bdSJérôme Duval 			newType = IA32_MTR_WRITE_PROTECTED;
108393fceb5SAxel Dörfler 			break;
109393fceb5SAxel Dörfler 		case B_MTR_WB:
110*2d2212bdSJérôme Duval 			newType = IA32_MTR_WRITE_BACK;
111393fceb5SAxel Dörfler 			break;
112393fceb5SAxel Dörfler 
113393fceb5SAxel Dörfler 		default:
114393fceb5SAxel Dörfler 			return B_BAD_VALUE;
115393fceb5SAxel Dörfler 	}
116393fceb5SAxel Dörfler 
117393fceb5SAxel Dörfler 	if (sMemoryTypeRegisterCount == 0)
118393fceb5SAxel Dörfler 		return B_NOT_SUPPORTED;
119393fceb5SAxel Dörfler 
120393fceb5SAxel Dörfler 	// length must be a power of 2; just round it up to the next value
121*2d2212bdSJérôme Duval 	uint64 newLength = nearest_power(length);
122*2d2212bdSJérôme Duval 
123*2d2212bdSJérôme Duval 	// avoids more than 2GB slots
124*2d2212bdSJérôme Duval 	if (newLength > 0x80000000)
125*2d2212bdSJérôme Duval 		newLength = 0x80000000;
126*2d2212bdSJérôme Duval 
127*2d2212bdSJérôme Duval 	if (newLength + base <= base) {
128393fceb5SAxel Dörfler 		// 4GB overflow
129393fceb5SAxel Dörfler 		return B_BAD_VALUE;
130393fceb5SAxel Dörfler 	}
131393fceb5SAxel Dörfler 
132393fceb5SAxel Dörfler 	// base must be aligned to the length
133*2d2212bdSJérôme Duval 	if (base & (newLength - 1))
134393fceb5SAxel Dörfler 		return B_BAD_VALUE;
135393fceb5SAxel Dörfler 
136393fceb5SAxel Dörfler 	index = allocate_mtrr();
137393fceb5SAxel Dörfler 	if (index < 0)
138393fceb5SAxel Dörfler 		return B_ERROR;
139393fceb5SAxel Dörfler 
140393fceb5SAxel Dörfler 	TRACE(("allocate MTRR slot %ld, base = %Lx, length = %Lx\n", index,
141*2d2212bdSJérôme Duval 		base, newLength));
142393fceb5SAxel Dörfler 
143393fceb5SAxel Dörfler 	sMemoryTypeIDs[index] = id;
144*2d2212bdSJérôme Duval 	x86_set_mtrr(index, base, newLength, newType);
145*2d2212bdSJérôme Duval 
146*2d2212bdSJérôme Duval 	// now handle remaining memory
147*2d2212bdSJérôme Duval 	if (length > newLength) {
148*2d2212bdSJérôme Duval 		// TODO iterate over smaller lengths to avoid the PCI hole after the physical memory.
149*2d2212bdSJérôme Duval 		set_memory_type(id, base + newLength, length - newLength, type);
150*2d2212bdSJérôme Duval 	}
151393fceb5SAxel Dörfler 
152393fceb5SAxel Dörfler 	return B_OK;
153393fceb5SAxel Dörfler }
154393fceb5SAxel Dörfler 
155393fceb5SAxel Dörfler 
156393fceb5SAxel Dörfler //	#pragma mark -
157393fceb5SAxel Dörfler 
158393fceb5SAxel Dörfler 
159393fceb5SAxel Dörfler status_t
160393fceb5SAxel Dörfler arch_vm_init(kernel_args *args)
161393fceb5SAxel Dörfler {
162393fceb5SAxel Dörfler 	TRACE(("arch_vm_init: entry\n"));
163393fceb5SAxel Dörfler 	return 0;
164393fceb5SAxel Dörfler }
165393fceb5SAxel Dörfler 
166393fceb5SAxel Dörfler 
167393fceb5SAxel Dörfler /*!	Marks DMA region as in-use, and maps it into the kernel space */
168393fceb5SAxel Dörfler status_t
169393fceb5SAxel Dörfler arch_vm_init_post_area(kernel_args *args)
170393fceb5SAxel Dörfler {
171393fceb5SAxel Dörfler 	area_id id;
172393fceb5SAxel Dörfler 
173393fceb5SAxel Dörfler 	TRACE(("arch_vm_init_post_area: entry\n"));
174393fceb5SAxel Dörfler 
175393fceb5SAxel Dörfler 	// account for DMA area and mark the pages unusable
176393fceb5SAxel Dörfler 	vm_mark_page_range_inuse(0x0, 0xa0000 / B_PAGE_SIZE);
177393fceb5SAxel Dörfler 
178393fceb5SAxel Dörfler 	// map 0 - 0xa0000 directly
179393fceb5SAxel Dörfler 	id = map_physical_memory("dma_region", (void *)0x0, 0xa0000,
180393fceb5SAxel Dörfler 		B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
181393fceb5SAxel Dörfler 		&gDmaAddress);
182393fceb5SAxel Dörfler 	if (id < 0) {
183393fceb5SAxel Dörfler 		panic("arch_vm_init_post_area: unable to map dma region\n");
184393fceb5SAxel Dörfler 		return B_NO_MEMORY;
185393fceb5SAxel Dörfler 	}
186393fceb5SAxel Dörfler 
187393fceb5SAxel Dörfler 	return bios_init();
188393fceb5SAxel Dörfler }
189393fceb5SAxel Dörfler 
190393fceb5SAxel Dörfler 
191393fceb5SAxel Dörfler /*!	Gets rid of all yet unmapped (and therefore now unused) page tables */
192393fceb5SAxel Dörfler status_t
193393fceb5SAxel Dörfler arch_vm_init_end(kernel_args *args)
194393fceb5SAxel Dörfler {
195393fceb5SAxel Dörfler 	TRACE(("arch_vm_init_endvm: entry\n"));
196393fceb5SAxel Dörfler 
197393fceb5SAxel Dörfler 	// throw away anything in the kernel_args.pgtable[] that's not yet mapped
198393fceb5SAxel Dörfler 	vm_free_unused_boot_loader_range(KERNEL_BASE,
199393fceb5SAxel Dörfler 		0x400000 * args->arch_args.num_pgtables);
200393fceb5SAxel Dörfler 
201393fceb5SAxel Dörfler 	return B_OK;
202393fceb5SAxel Dörfler }
203393fceb5SAxel Dörfler 
204393fceb5SAxel Dörfler 
205393fceb5SAxel Dörfler status_t
206393fceb5SAxel Dörfler arch_vm_init_post_modules(kernel_args *args)
207393fceb5SAxel Dörfler {
208393fceb5SAxel Dörfler //	void *cookie;
209393fceb5SAxel Dörfler 
210393fceb5SAxel Dörfler 	// the x86 CPU modules are now accessible
211393fceb5SAxel Dörfler 
212393fceb5SAxel Dörfler 	sMemoryTypeRegisterCount = x86_count_mtrrs();
213393fceb5SAxel Dörfler 	if (sMemoryTypeRegisterCount == 0)
214393fceb5SAxel Dörfler 		return B_OK;
215393fceb5SAxel Dörfler 
216393fceb5SAxel Dörfler 	// not very likely, but play safe here
217393fceb5SAxel Dörfler 	if (sMemoryTypeRegisterCount > kMaxMemoryTypeRegisters)
218393fceb5SAxel Dörfler 		sMemoryTypeRegisterCount = kMaxMemoryTypeRegisters;
219393fceb5SAxel Dörfler 
220393fceb5SAxel Dörfler 	// init memory type ID table
221393fceb5SAxel Dörfler 
222393fceb5SAxel Dörfler 	for (uint32 i = 0; i < sMemoryTypeRegisterCount; i++) {
223393fceb5SAxel Dörfler 		sMemoryTypeIDs[i] = -1;
224393fceb5SAxel Dörfler 	}
225393fceb5SAxel Dörfler 
226393fceb5SAxel Dörfler 	// set the physical memory ranges to write-back mode
227393fceb5SAxel Dörfler 
228393fceb5SAxel Dörfler 	for (uint32 i = 0; i < args->num_physical_memory_ranges; i++) {
229393fceb5SAxel Dörfler 		set_memory_type(-1, args->physical_memory_range[i].start,
230393fceb5SAxel Dörfler 			args->physical_memory_range[i].size, B_MTR_WB);
231393fceb5SAxel Dörfler 	}
232393fceb5SAxel Dörfler 
233393fceb5SAxel Dörfler 	return B_OK;
234393fceb5SAxel Dörfler }
235393fceb5SAxel Dörfler 
236393fceb5SAxel Dörfler 
237393fceb5SAxel Dörfler void
238393fceb5SAxel Dörfler arch_vm_aspace_swap(vm_address_space *aspace)
239393fceb5SAxel Dörfler {
240393fceb5SAxel Dörfler 	i386_swap_pgdir((addr_t)i386_translation_map_get_pgdir(
241393fceb5SAxel Dörfler 		&aspace->translation_map));
242393fceb5SAxel Dörfler }
243393fceb5SAxel Dörfler 
244393fceb5SAxel Dörfler 
245393fceb5SAxel Dörfler bool
246393fceb5SAxel Dörfler arch_vm_supports_protection(uint32 protection)
247393fceb5SAxel Dörfler {
248393fceb5SAxel Dörfler 	// x86 always has the same read/write properties for userland and the
249393fceb5SAxel Dörfler 	// kernel.
250393fceb5SAxel Dörfler 	// That's why we do not support user-read/kernel-write access. While the
251393fceb5SAxel Dörfler 	// other way around is not supported either, we don't care in this case
252393fceb5SAxel Dörfler 	// and give the kernel full access.
253393fceb5SAxel Dörfler 	if ((protection & (B_READ_AREA | B_WRITE_AREA)) == B_READ_AREA
254393fceb5SAxel Dörfler 		&& protection & B_KERNEL_WRITE_AREA)
255393fceb5SAxel Dörfler 		return false;
256393fceb5SAxel Dörfler 
257393fceb5SAxel Dörfler 	return true;
258393fceb5SAxel Dörfler }
259393fceb5SAxel Dörfler 
260393fceb5SAxel Dörfler 
261393fceb5SAxel Dörfler void
262393fceb5SAxel Dörfler arch_vm_unset_memory_type(struct vm_area *area)
263393fceb5SAxel Dörfler {
264393fceb5SAxel Dörfler 	uint32 index;
265393fceb5SAxel Dörfler 
266393fceb5SAxel Dörfler 	if (area->memory_type == 0)
267393fceb5SAxel Dörfler 		return;
268393fceb5SAxel Dörfler 
269393fceb5SAxel Dörfler 	// find index for area ID
270393fceb5SAxel Dörfler 
271393fceb5SAxel Dörfler 	for (index = 0; index < sMemoryTypeRegisterCount; index++) {
272393fceb5SAxel Dörfler 		if (sMemoryTypeIDs[index] == area->id) {
273393fceb5SAxel Dörfler 			x86_set_mtrr(index, 0, 0, 0);
274393fceb5SAxel Dörfler 
275393fceb5SAxel Dörfler 			sMemoryTypeIDs[index] = -1;
276393fceb5SAxel Dörfler 			free_mtrr(index);
277393fceb5SAxel Dörfler 			break;
278393fceb5SAxel Dörfler 		}
279393fceb5SAxel Dörfler 	}
280393fceb5SAxel Dörfler }
281393fceb5SAxel Dörfler 
282393fceb5SAxel Dörfler 
283393fceb5SAxel Dörfler status_t
284393fceb5SAxel Dörfler arch_vm_set_memory_type(struct vm_area *area, addr_t physicalBase,
285393fceb5SAxel Dörfler 	uint32 type)
286393fceb5SAxel Dörfler {
287393fceb5SAxel Dörfler 	area->memory_type = type >> MEMORY_TYPE_SHIFT;
288393fceb5SAxel Dörfler 	return set_memory_type(area->id, physicalBase, area->size, type);
289393fceb5SAxel Dörfler }
290