xref: /haiku/src/system/kernel/arch/ppc/paging/classic/PPCPagingMethodClassic.cpp (revision 5c1f231967bbf06af56728b86ad70f266c99f64d)
1 /*
2  * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
4  * Distributed under the terms of the MIT License.
5  *
6  * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
7  * Distributed under the terms of the NewOS License.
8  */
9 
10 
11 #include "paging/classic/PPCPagingMethodClassic.h"
12 
13 #include <stdlib.h>
14 #include <string.h>
15 
16 #include <AutoDeleter.h>
17 
18 #include <arch/cpu.h>
19 #include <arch_mmu.h>
20 #include <arch_system_info.h>
21 #include <boot/kernel_args.h>
22 #include <int.h>
23 #include <thread.h>
24 #include <vm/vm.h>
25 #include <vm/VMAddressSpace.h>
26 
27 #include "paging/classic/PPCPagingStructuresClassic.h"
28 #include "paging/classic/PPCVMTranslationMapClassic.h"
29 #include "generic_vm_physical_page_mapper.h"
30 #include "generic_vm_physical_page_ops.h"
31 #include "GenericVMPhysicalPageMapper.h"
32 
33 
34 //#define TRACE_PPC_PAGING_METHOD_CLASSIC
35 #ifdef TRACE_PPC_PAGING_METHOD_CLASSIC
36 #	define TRACE(x...) dprintf(x)
37 #else
38 #	define TRACE(x...) ;
39 #endif
40 
41 // 64 MB of iospace
42 #define IOSPACE_SIZE (64*1024*1024)
43 // We only have small (4 KB) pages. The only reason for choosing greater chunk
44 // size is to keep the waste of memory limited, since the generic page mapper
45 // allocates structures per physical/virtual chunk.
46 // TODO: Implement a page mapper more suitable for small pages!
47 #define IOSPACE_CHUNK_SIZE (16 * B_PAGE_SIZE)
48 
49 static addr_t sIOSpaceBase;
50 
51 
52 static status_t
map_iospace_chunk(addr_t va,phys_addr_t pa,uint32 flags)53 map_iospace_chunk(addr_t va, phys_addr_t pa, uint32 flags)
54 {
55 	pa &= ~(B_PAGE_SIZE - 1); // make sure it's page aligned
56 	va &= ~(B_PAGE_SIZE - 1); // make sure it's page aligned
57 	if (va < sIOSpaceBase || va >= (sIOSpaceBase + IOSPACE_SIZE))
58 		panic("map_iospace_chunk: passed invalid va 0x%lx\n", va);
59 
60 	// map the pages
61 	return ppc_map_address_range(va, pa, IOSPACE_CHUNK_SIZE);
62 }
63 
64 
65 // #pragma mark - PPCPagingMethodClassic
66 
67 
PPCPagingMethodClassic()68 PPCPagingMethodClassic::PPCPagingMethodClassic()
69 /*
70 	:
71 	fPageHole(NULL),
72 	fPageHolePageDir(NULL),
73 	fKernelPhysicalPageDirectory(0),
74 	fKernelVirtualPageDirectory(NULL),
75 	fPhysicalPageMapper(NULL),
76 	fKernelPhysicalPageMapper(NULL)
77 */
78 {
79 }
80 
81 
~PPCPagingMethodClassic()82 PPCPagingMethodClassic::~PPCPagingMethodClassic()
83 {
84 }
85 
86 
87 status_t
Init(kernel_args * args,VMPhysicalPageMapper ** _physicalPageMapper)88 PPCPagingMethodClassic::Init(kernel_args* args,
89 	VMPhysicalPageMapper** _physicalPageMapper)
90 {
91 	TRACE("PPCPagingMethodClassic::Init(): entry\n");
92 
93 	fPageTable = (page_table_entry_group *)args->arch_args.page_table.start;
94 	fPageTableSize = args->arch_args.page_table.size;
95 	fPageTableHashMask = fPageTableSize / sizeof(page_table_entry_group) - 1;
96 
97 	// init physical page mapper
98 	status_t error = generic_vm_physical_page_mapper_init(args,
99 		map_iospace_chunk, &sIOSpaceBase, IOSPACE_SIZE, IOSPACE_CHUNK_SIZE);
100 	if (error != B_OK)
101 		return error;
102 
103 	new(&fPhysicalPageMapper) GenericVMPhysicalPageMapper;
104 
105 	*_physicalPageMapper = &fPhysicalPageMapper;
106 	return B_OK;
107 
108 #if 0//X86
109 	fKernelPhysicalPageDirectory = args->arch_args.phys_pgdir;
110 	fKernelVirtualPageDirectory = (page_directory_entry*)(addr_t)
111 		args->arch_args.vir_pgdir;
112 
113 #ifdef TRACE_PPC_PAGING_METHOD_CLASSIC
114 	TRACE("page hole: %p, page dir: %p\n", fPageHole, fPageHolePageDir);
115 	TRACE("page dir: %p (physical: %#" B_PRIx32 ")\n",
116 		fKernelVirtualPageDirectory, fKernelPhysicalPageDirectory);
117 #endif
118 
119 	PPCPagingStructuresClassic::StaticInit();
120 
121 	// create the initial pool for the physical page mapper
122 	PhysicalPageSlotPool* pool
123 		= new(&PhysicalPageSlotPool::sInitialPhysicalPagePool)
124 			PhysicalPageSlotPool;
125 	status_t error = pool->InitInitial(args);
126 	if (error != B_OK) {
127 		panic("PPCPagingMethodClassic::Init(): Failed to create initial pool "
128 			"for physical page mapper!");
129 		return error;
130 	}
131 
132 	// create physical page mapper
133 	large_memory_physical_page_ops_init(args, pool, fPhysicalPageMapper,
134 		fKernelPhysicalPageMapper);
135 		// TODO: Select the best page mapper!
136 
137 	// enable global page feature if available
138 	if (x86_check_feature(IA32_FEATURE_PGE, FEATURE_COMMON)) {
139 		// this prevents kernel pages from being flushed from TLB on
140 		// context-switch
141 		x86_write_cr4(x86_read_cr4() | IA32_CR4_GLOBAL_PAGES);
142 	}
143 
144 	TRACE("PPCPagingMethodClassic::Init(): done\n");
145 
146 	*_physicalPageMapper = fPhysicalPageMapper;
147 	return B_OK;
148 #endif
149 }
150 
151 
152 status_t
InitPostArea(kernel_args * args)153 PPCPagingMethodClassic::InitPostArea(kernel_args* args)
154 {
155 
156 	// If the page table doesn't lie within the kernel address space, we
157 	// remap it.
158 	if (!IS_KERNEL_ADDRESS(fPageTable)) {
159 		addr_t newAddress = (addr_t)fPageTable;
160 		status_t error = ppc_remap_address_range(&newAddress, fPageTableSize,
161 			false);
162 		if (error != B_OK) {
163 			panic("arch_vm_translation_map_init_post_area(): Failed to remap "
164 				"the page table!");
165 			return error;
166 		}
167 
168 		// set the new page table address
169 		addr_t oldVirtualBase = (addr_t)(fPageTable);
170 		fPageTable = (page_table_entry_group*)newAddress;
171 
172 		// unmap the old pages
173 		ppc_unmap_address_range(oldVirtualBase, fPageTableSize);
174 
175 // TODO: We should probably map the page table via BAT. It is relatively large,
176 // and due to being a hash table the access patterns might look sporadic, which
177 // certainly isn't to the liking of the TLB.
178 	}
179 
180 	// create an area to cover the page table
181 	fPageTableArea = create_area("page_table", (void **)&fPageTable, B_EXACT_ADDRESS,
182 		fPageTableSize, B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
183 
184 	// init physical page mapper
185 	status_t error = generic_vm_physical_page_mapper_init_post_area(args);
186 	if (error != B_OK)
187 		return error;
188 
189 	return B_OK;
190 
191 #if 0//X86
192 	// now that the vm is initialized, create an area that represents
193 	// the page hole
194 	void *temp;
195 	status_t error;
196 	area_id area;
197 
198 	// unmap the page hole hack we were using before
199 	fKernelVirtualPageDirectory[1023] = 0;
200 	fPageHolePageDir = NULL;
201 	fPageHole = NULL;
202 
203 	temp = (void*)fKernelVirtualPageDirectory;
204 	area = create_area("kernel_pgdir", &temp, B_EXACT_ADDRESS, B_PAGE_SIZE,
205 		B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
206 	if (area < B_OK)
207 		return area;
208 
209 	error = PhysicalPageSlotPool::sInitialPhysicalPagePool
210 		.InitInitialPostArea(args);
211 	if (error != B_OK)
212 		return error;
213 
214 	return B_OK;
215 #endif//X86
216 }
217 
218 
219 status_t
CreateTranslationMap(bool kernel,VMTranslationMap ** _map)220 PPCPagingMethodClassic::CreateTranslationMap(bool kernel, VMTranslationMap** _map)
221 {
222 	PPCVMTranslationMapClassic* map = new(std::nothrow) PPCVMTranslationMapClassic;
223 	if (map == NULL)
224 		return B_NO_MEMORY;
225 
226 	status_t error = map->Init(kernel);
227 	if (error != B_OK) {
228 		delete map;
229 		return error;
230 	}
231 
232 	*_map = map;
233 	return B_OK;
234 }
235 
236 
237 status_t
MapEarly(kernel_args * args,addr_t virtualAddress,phys_addr_t physicalAddress,uint8 attributes,page_num_t (* get_free_page)(kernel_args *))238 PPCPagingMethodClassic::MapEarly(kernel_args* args, addr_t virtualAddress,
239 	phys_addr_t physicalAddress, uint8 attributes,
240 	page_num_t (*get_free_page)(kernel_args*))
241 {
242 	uint32 virtualSegmentID = get_sr((void *)virtualAddress) & 0xffffff;
243 
244 	uint32 hash = page_table_entry::PrimaryHash(virtualSegmentID, (uint32)virtualAddress);
245 	page_table_entry_group *group = &fPageTable[hash & fPageTableHashMask];
246 
247 	for (int32 i = 0; i < 8; i++) {
248 		// 8 entries in a group
249 		if (group->entry[i].valid)
250 			continue;
251 
252 		FillPageTableEntry(&group->entry[i], virtualSegmentID,
253 			virtualAddress, physicalAddress, PTE_READ_WRITE, 0, false);
254 		return B_OK;
255 	}
256 
257 	hash = page_table_entry::SecondaryHash(hash);
258 	group = &fPageTable[hash & fPageTableHashMask];
259 
260 	for (int32 i = 0; i < 8; i++) {
261 		if (group->entry[i].valid)
262 			continue;
263 
264 		FillPageTableEntry(&group->entry[i], virtualSegmentID,
265 			virtualAddress, physicalAddress, PTE_READ_WRITE, 0, true);
266 		return B_OK;
267 	}
268 
269 	return B_ERROR;
270 }
271 
272 
273 bool
IsKernelPageAccessible(addr_t virtualAddress,uint32 protection)274 PPCPagingMethodClassic::IsKernelPageAccessible(addr_t virtualAddress,
275 	uint32 protection)
276 {
277 	// TODO:factor out to baseclass
278 	VMAddressSpace *addressSpace = VMAddressSpace::Kernel();
279 
280 //XXX:
281 //	PPCVMTranslationMap* map = static_cast<PPCVMTranslationMap*>(
282 //		addressSpace->TranslationMap());
283 //	VMTranslationMap* map = addressSpace->TranslationMap();
284 	PPCVMTranslationMapClassic* map = static_cast<PPCVMTranslationMapClassic*>(
285 		addressSpace->TranslationMap());
286 
287 	phys_addr_t physicalAddress;
288 	uint32 flags;
289 	if (map->Query(virtualAddress, &physicalAddress, &flags) != B_OK)
290 		return false;
291 
292 	if ((flags & PAGE_PRESENT) == 0)
293 		return false;
294 
295 	// present means kernel-readable, so check for writable
296 	return (protection & B_KERNEL_WRITE_AREA) == 0
297 		|| (flags & B_KERNEL_WRITE_AREA) != 0;
298 }
299 
300 
301 void
FillPageTableEntry(page_table_entry * entry,uint32 virtualSegmentID,addr_t virtualAddress,phys_addr_t physicalAddress,uint8 protection,uint32 memoryType,bool secondaryHash)302 PPCPagingMethodClassic::FillPageTableEntry(page_table_entry *entry,
303 	uint32 virtualSegmentID, addr_t virtualAddress, phys_addr_t physicalAddress,
304 	uint8 protection, uint32 memoryType, bool secondaryHash)
305 {
306 	// lower 32 bit - set at once
307 	entry->physical_page_number = physicalAddress / B_PAGE_SIZE;
308 	entry->_reserved0 = 0;
309 	entry->referenced = false;
310 	entry->changed = false;
311 	entry->write_through = (memoryType == B_UNCACHED_MEMORY) || (memoryType == B_WRITE_THROUGH_MEMORY);
312 	entry->caching_inhibited = (memoryType == B_UNCACHED_MEMORY);
313 	entry->memory_coherent = false;
314 	entry->guarded = false;
315 	entry->_reserved1 = 0;
316 	entry->page_protection = protection & 0x3;
317 	eieio();
318 		// we need to make sure that the lower 32 bit were
319 		// already written when the entry becomes valid
320 
321 	// upper 32 bit
322 	entry->virtual_segment_id = virtualSegmentID;
323 	entry->secondary_hash = secondaryHash;
324 	entry->abbr_page_index = (virtualAddress >> 22) & 0x3f;
325 	entry->valid = true;
326 
327 	ppc_sync();
328 }
329 
330 
331 #if 0//X86
332 /*static*/ void
333 PPCPagingMethodClassic::PutPageTableInPageDir(page_directory_entry* entry,
334 	phys_addr_t pgtablePhysical, uint32 attributes)
335 {
336 	*entry = (pgtablePhysical & PPC_PDE_ADDRESS_MASK)
337 		| PPC_PDE_PRESENT
338 		| PPC_PDE_WRITABLE
339 		| PPC_PDE_USER;
340 		// TODO: we ignore the attributes of the page table - for compatibility
341 		// with BeOS we allow having user accessible areas in the kernel address
342 		// space. This is currently being used by some drivers, mainly for the
343 		// frame buffer. Our current real time data implementation makes use of
344 		// this fact, too.
345 		// We might want to get rid of this possibility one day, especially if
346 		// we intend to port it to a platform that does not support this.
347 }
348 
349 
350 /*static*/ void
351 PPCPagingMethodClassic::PutPageTableEntryInTable(page_table_entry* entry,
352 	phys_addr_t physicalAddress, uint32 attributes, uint32 memoryType,
353 	bool globalPage)
354 {
355 	page_table_entry page = (physicalAddress & PPC_PTE_ADDRESS_MASK)
356 		| PPC_PTE_PRESENT | (globalPage ? PPC_PTE_GLOBAL : 0)
357 		| MemoryTypeToPageTableEntryFlags(memoryType);
358 
359 	// if the page is user accessible, it's automatically
360 	// accessible in kernel space, too (but with the same
361 	// protection)
362 	if ((attributes & B_USER_PROTECTION) != 0) {
363 		page |= PPC_PTE_USER;
364 		if ((attributes & B_WRITE_AREA) != 0)
365 			page |= PPC_PTE_WRITABLE;
366 	} else if ((attributes & B_KERNEL_WRITE_AREA) != 0)
367 		page |= PPC_PTE_WRITABLE;
368 
369 	// put it in the page table
370 	*(volatile page_table_entry*)entry = page;
371 }
372 
373 
374 /*static*/ void
375 PPCPagingMethodClassic::_EarlyPreparePageTables(page_table_entry* pageTables,
376 	addr_t address, size_t size)
377 {
378 	memset(pageTables, 0, B_PAGE_SIZE * (size / (B_PAGE_SIZE * 1024)));
379 
380 	// put the array of pgtables directly into the kernel pagedir
381 	// these will be wired and kept mapped into virtual space to be easy to get
382 	// to
383 	{
384 		addr_t virtualTable = (addr_t)pageTables;
385 
386 		page_directory_entry* pageHolePageDir
387 			= PPCPagingMethodClassic::Method()->PageHolePageDir();
388 
389 		for (size_t i = 0; i < (size / (B_PAGE_SIZE * 1024));
390 				i++, virtualTable += B_PAGE_SIZE) {
391 			phys_addr_t physicalTable = 0;
392 			_EarlyQuery(virtualTable, &physicalTable);
393 			page_directory_entry* entry = &pageHolePageDir[
394 				(address / (B_PAGE_SIZE * 1024)) + i];
395 			PutPageTableInPageDir(entry, physicalTable,
396 				B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
397 		}
398 	}
399 }
400 
401 
402 //! TODO: currently assumes this translation map is active
403 /*static*/ status_t
404 PPCPagingMethodClassic::_EarlyQuery(addr_t virtualAddress,
405 	phys_addr_t *_physicalAddress)
406 {
407 	PPCPagingMethodClassic* method = PPCPagingMethodClassic::Method();
408 	int index = VADDR_TO_PDENT(virtualAddress);
409 	if ((method->PageHolePageDir()[index] & PPC_PDE_PRESENT) == 0) {
410 		// no pagetable here
411 		return B_ERROR;
412 	}
413 
414 	page_table_entry* entry = method->PageHole() + virtualAddress / B_PAGE_SIZE;
415 	if ((*entry & PPC_PTE_PRESENT) == 0) {
416 		// page mapping not valid
417 		return B_ERROR;
418 	}
419 
420 	*_physicalAddress = *entry & PPC_PTE_ADDRESS_MASK;
421 	return B_OK;
422 }
423 #endif
424