xref: /haiku/src/system/kernel/arch/x86/paging/pae/X86VMTranslationMapPAE.cpp (revision caed67a8cba83913b9c21ac2b06ebc6bd1cb3111)
1 /*
2  * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2002-2010, Axel Dörfler, axeld@pinc-software.de.
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/pae/X86VMTranslationMapPAE.h"
12 
13 #include <int.h>
14 #include <slab/Slab.h>
15 #include <thread.h>
16 #include <tracing.h>
17 #include <util/AutoLock.h>
18 #include <util/ThreadAutoLock.h>
19 #include <vm/vm_page.h>
20 #include <vm/VMAddressSpace.h>
21 #include <vm/VMCache.h>
22 
23 #include "paging/pae/X86PagingMethodPAE.h"
24 #include "paging/pae/X86PagingStructuresPAE.h"
25 #include "paging/x86_physical_page_mapper.h"
26 
27 
28 //#define TRACE_X86_VM_TRANSLATION_MAP_PAE
29 #ifdef TRACE_X86_VM_TRANSLATION_MAP_PAE
30 #	define TRACE(x...) dprintf(x)
31 #else
32 #	define TRACE(x...) ;
33 #endif
34 
35 
36 #if B_HAIKU_PHYSICAL_BITS == 64
37 
38 
39 #if TRANSLATION_MAP_TRACING
40 
41 
42 namespace TranslationMapTracing {
43 
44 
45 class TranslationMapTraceEntryBase
46 	: public TRACE_ENTRY_SELECTOR(TRANSLATION_MAP_TRACING_STACK_TRACE) {
47 public:
48 	TranslationMapTraceEntryBase()
49 		:
50 		TraceEntryBase(TRANSLATION_MAP_TRACING_STACK_TRACE, 0, true)
51 	{
52 	}
53 
54 	void PrintPageTableEntry(TraceOutput& out, pae_page_table_entry entry)
55 	{
56 		out.Print("%#" B_PRIx64  " %c%c%c%c%c %s %s %c%c",
57 			entry & X86_PAE_PTE_ADDRESS_MASK,
58 			(entry & X86_PAE_PTE_PRESENT) != 0 ? 'P' : '-',
59 			(entry & X86_PAE_PTE_WRITABLE) != 0 ? 'W' : '-',
60 			(entry & X86_PAE_PTE_USER) != 0 ? 'U' : '-',
61 			(entry & X86_PAE_PTE_NOT_EXECUTABLE) != 0 ? '-' : 'X',
62 			(entry & X86_PAE_PTE_GLOBAL) != 0 ? 'G' : '-',
63 			(entry & X86_PAE_PTE_WRITE_THROUGH) != 0 ? "WT" : "--",
64 			(entry & X86_PAE_PTE_CACHING_DISABLED) != 0 ? "UC" : "--",
65 			(entry & X86_PAE_PTE_ACCESSED) != 0 ? 'A' : '-',
66 			(entry & X86_PAE_PTE_DIRTY) != 0 ? 'D' : '-');
67 	}
68 };
69 
70 
71 class Map : public TranslationMapTraceEntryBase {
72 public:
73 	Map(X86VMTranslationMapPAE* map, addr_t virtualAddress,
74 		pae_page_table_entry entry)
75 		:
76 		TranslationMapTraceEntryBase(),
77 		fMap(map),
78 		fVirtualAddress(virtualAddress),
79 		fEntry(entry)
80 	{
81 		Initialized();
82 	}
83 
84 	virtual void AddDump(TraceOutput& out)
85 	{
86 		out.Print("translation map map: %p: %#" B_PRIxADDR " -> ", fMap,
87 			fVirtualAddress);
88 		PrintPageTableEntry(out, fEntry);
89 	}
90 
91 private:
92 	X86VMTranslationMapPAE*	fMap;
93 	addr_t					fVirtualAddress;
94 	pae_page_table_entry	fEntry;
95 };
96 
97 
98 class Unmap : public TranslationMapTraceEntryBase {
99 public:
100 	Unmap(X86VMTranslationMapPAE* map, addr_t virtualAddress,
101 		pae_page_table_entry entry)
102 		:
103 		TranslationMapTraceEntryBase(),
104 		fMap(map),
105 		fVirtualAddress(virtualAddress),
106 		fEntry(entry)
107 	{
108 		Initialized();
109 	}
110 
111 	virtual void AddDump(TraceOutput& out)
112 	{
113 		out.Print("translation map unmap: %p: %#" B_PRIxADDR
114 			" -> ", fMap, fVirtualAddress);
115 		PrintPageTableEntry(out, fEntry);
116 	}
117 
118 private:
119 	X86VMTranslationMapPAE*	fMap;
120 	addr_t					fVirtualAddress;
121 	pae_page_table_entry	fEntry;
122 };
123 
124 
125 class Protect : public TranslationMapTraceEntryBase {
126 public:
127 	Protect(X86VMTranslationMapPAE* map, addr_t virtualAddress,
128 		pae_page_table_entry oldEntry, pae_page_table_entry newEntry)
129 		:
130 		TranslationMapTraceEntryBase(),
131 		fMap(map),
132 		fVirtualAddress(virtualAddress),
133 		fOldEntry(oldEntry),
134 		fNewEntry(newEntry)
135 	{
136 		Initialized();
137 	}
138 
139 	virtual void AddDump(TraceOutput& out)
140 	{
141 		out.Print("translation map protect: %p: %#" B_PRIxADDR
142 			" -> ", fMap, fVirtualAddress);
143 		PrintPageTableEntry(out, fNewEntry);
144 		out.Print(" (%c%c%c)",
145 			(fOldEntry & X86_PAE_PTE_WRITABLE) != 0 ? 'W' : '-',
146 			(fOldEntry & X86_PAE_PTE_USER) != 0 ? 'U' : '-',
147 			(fOldEntry & X86_PAE_PTE_NOT_EXECUTABLE) != 0 ? '-' : 'X');
148 	}
149 
150 private:
151 	X86VMTranslationMapPAE*	fMap;
152 	addr_t					fVirtualAddress;
153 	pae_page_table_entry	fOldEntry;
154 	pae_page_table_entry	fNewEntry;
155 };
156 
157 
158 class ClearFlags : public TranslationMapTraceEntryBase {
159 public:
160 	ClearFlags(X86VMTranslationMapPAE* map, addr_t virtualAddress,
161 		pae_page_table_entry oldEntry, pae_page_table_entry flagsCleared)
162 		:
163 		TranslationMapTraceEntryBase(),
164 		fMap(map),
165 		fVirtualAddress(virtualAddress),
166 		fOldEntry(oldEntry),
167 		fFlagsCleared(flagsCleared)
168 	{
169 		Initialized();
170 	}
171 
172 	virtual void AddDump(TraceOutput& out)
173 	{
174 		out.Print("translation map clear flags: %p: %#" B_PRIxADDR
175 			" -> ", fMap, fVirtualAddress);
176 		PrintPageTableEntry(out, fOldEntry & ~fFlagsCleared);
177 		out.Print(", cleared %c%c (%c%c)",
178 			(fOldEntry & fFlagsCleared & X86_PAE_PTE_ACCESSED) != 0 ? 'A' : '-',
179 			(fOldEntry & fFlagsCleared & X86_PAE_PTE_DIRTY) != 0 ? 'D' : '-',
180 			(fFlagsCleared & X86_PAE_PTE_ACCESSED) != 0 ? 'A' : '-',
181 			(fFlagsCleared & X86_PAE_PTE_DIRTY) != 0 ? 'D' : '-');
182 	}
183 
184 private:
185 	X86VMTranslationMapPAE*	fMap;
186 	addr_t					fVirtualAddress;
187 	pae_page_table_entry	fOldEntry;
188 	pae_page_table_entry	fFlagsCleared;
189 };
190 
191 
192 class ClearFlagsUnmap : public TranslationMapTraceEntryBase {
193 public:
194 	ClearFlagsUnmap(X86VMTranslationMapPAE* map, addr_t virtualAddress,
195 		pae_page_table_entry entry)
196 		:
197 		TranslationMapTraceEntryBase(),
198 		fMap(map),
199 		fVirtualAddress(virtualAddress),
200 		fEntry(entry)
201 	{
202 		Initialized();
203 	}
204 
205 	virtual void AddDump(TraceOutput& out)
206 	{
207 		out.Print("translation map clear flags unmap: %p: %#" B_PRIxADDR
208 			" -> ", fMap, fVirtualAddress);
209 		PrintPageTableEntry(out, fEntry);
210 	}
211 
212 private:
213 	X86VMTranslationMapPAE*	fMap;
214 	addr_t					fVirtualAddress;
215 	pae_page_table_entry	fEntry;
216 };
217 
218 
219 }	// namespace TranslationMapTracing
220 
221 #	define T(x)	new(std::nothrow) TranslationMapTracing::x
222 
223 #else
224 #	define T(x)
225 #endif	// TRANSLATION_MAP_TRACING
226 
227 
228 
229 X86VMTranslationMapPAE::X86VMTranslationMapPAE()
230 	:
231 	fPagingStructures(NULL)
232 {
233 }
234 
235 
236 X86VMTranslationMapPAE::~X86VMTranslationMapPAE()
237 {
238 	if (fPagingStructures == NULL)
239 		return;
240 
241 	if (fPageMapper != NULL)
242 		fPageMapper->Delete();
243 
244 	// cycle through and free all of the user space page tables
245 
246 	STATIC_ASSERT(KERNEL_BASE == 0x80000000 && KERNEL_SIZE == 0x80000000);
247 		// assuming 1-1 split of the address space
248 
249 	for (uint32 k = 0; k < 2; k++) {
250 		pae_page_directory_entry* pageDir
251 			= fPagingStructures->VirtualPageDirs()[k];
252 		if (pageDir == NULL)
253 			continue;
254 
255 		for (uint32 i = 0; i < kPAEPageDirEntryCount; i++) {
256 			if ((pageDir[i] & X86_PAE_PDE_PRESENT) != 0) {
257 				phys_addr_t address = pageDir[i] & X86_PAE_PDE_ADDRESS_MASK;
258 				vm_page* page = vm_lookup_page(address / B_PAGE_SIZE);
259 				if (page == NULL)
260 					panic("X86VMTranslationMapPAE::~X86VMTranslationMapPAE: "
261 						"didn't find page table page: page address: %#"
262 						B_PRIxPHYSADDR ", virtual base: %#" B_PRIxADDR "\n",
263 						address,
264 						(k * kPAEPageDirEntryCount + i) * kPAEPageTableRange);
265 				DEBUG_PAGE_ACCESS_START(page);
266 				vm_page_set_state(page, PAGE_STATE_FREE);
267 			}
268 		}
269 	}
270 
271 	fPagingStructures->RemoveReference();
272 }
273 
274 
275 status_t
276 X86VMTranslationMapPAE::Init(bool kernel)
277 {
278 	TRACE("X86VMTranslationMapPAE::Init()\n");
279 
280 	X86VMTranslationMap::Init(kernel);
281 
282 	fPagingStructures = new(std::nothrow) X86PagingStructuresPAE;
283 	if (fPagingStructures == NULL)
284 		return B_NO_MEMORY;
285 
286 	X86PagingMethodPAE* method = X86PagingMethodPAE::Method();
287 
288 	if (kernel) {
289 		// kernel
290 		// get the physical page mapper
291 		fPageMapper = method->KernelPhysicalPageMapper();
292 
293 		// we already know the kernel pgdir mapping
294 		fPagingStructures->Init(method->KernelVirtualPageDirPointerTable(),
295 			method->KernelPhysicalPageDirPointerTable(), NULL,
296 			method->KernelVirtualPageDirs(), method->KernelPhysicalPageDirs());
297 	} else {
298 		// user
299 		// allocate a physical page mapper
300 		status_t error = method->PhysicalPageMapper()
301 			->CreateTranslationMapPhysicalPageMapper(&fPageMapper);
302 		if (error != B_OK)
303 			return error;
304 
305 		// The following code assumes that the kernel address space occupies the
306 		// upper half of the virtual address space. This simplifies things a
307 		// lot, since it allows us to just use the upper two page directories
308 		// of the kernel and create two new lower page directories for the
309 		// userland.
310 		STATIC_ASSERT(KERNEL_BASE == 0x80000000 && KERNEL_SIZE == 0x80000000);
311 
312 		// allocate the page directories (both at once)
313 		pae_page_directory_entry* virtualPageDirs[4];
314 		phys_addr_t physicalPageDirs[4];
315 		virtualPageDirs[0] = (pae_page_directory_entry*)memalign(B_PAGE_SIZE,
316 			2 * B_PAGE_SIZE);
317 		if (virtualPageDirs[0] == NULL)
318 			return B_NO_MEMORY;
319 		virtualPageDirs[1] = virtualPageDirs[0] + kPAEPageTableEntryCount;
320 
321 		// clear the userland page directories
322 		memset(virtualPageDirs[0], 0, 2 * B_PAGE_SIZE);
323 
324 		// use the upper two kernel page directories
325 		for (int32 i = 2; i < 4; i++) {
326 			virtualPageDirs[i] = method->KernelVirtualPageDirs()[i];
327 			physicalPageDirs[i] = method->KernelPhysicalPageDirs()[i];
328 		}
329 
330 		// look up the page directories' physical addresses
331 		for (int32 i = 0; i < 2; i++) {
332 			vm_get_page_mapping(VMAddressSpace::KernelID(),
333 				(addr_t)virtualPageDirs[i], &physicalPageDirs[i]);
334 		}
335 
336 		// allocate the PDPT -- needs to have a 32 bit physical address
337 		phys_addr_t physicalPDPT;
338 		void* pdptHandle;
339 		pae_page_directory_pointer_table_entry* pdpt
340 			= (pae_page_directory_pointer_table_entry*)
341 				method->Allocate32BitPage(physicalPDPT, pdptHandle);
342 		if (pdpt == NULL) {
343 			free(virtualPageDirs[0]);
344 			return B_NO_MEMORY;
345 		}
346 
347 		// init the PDPT entries
348 		for (int32 i = 0; i < 4; i++) {
349 			pdpt[i] = (physicalPageDirs[i] & X86_PAE_PDPTE_ADDRESS_MASK)
350 				| X86_PAE_PDPTE_PRESENT;
351 		}
352 
353 		// init the paging structures
354 		fPagingStructures->Init(pdpt, physicalPDPT, pdptHandle, virtualPageDirs,
355 			physicalPageDirs);
356 	}
357 
358 	return B_OK;
359 }
360 
361 
362 size_t
363 X86VMTranslationMapPAE::MaxPagesNeededToMap(addr_t start, addr_t end) const
364 {
365 	// If start == 0, the actual base address is not yet known to the caller and
366 	// we shall assume the worst case.
367 	if (start == 0) {
368 		// offset the range so it has the worst possible alignment
369 		start = kPAEPageTableRange - B_PAGE_SIZE;
370 		end += kPAEPageTableRange - B_PAGE_SIZE;
371 	}
372 
373 	return end / kPAEPageTableRange + 1 - start / kPAEPageTableRange;
374 }
375 
376 
377 status_t
378 X86VMTranslationMapPAE::Map(addr_t virtualAddress, phys_addr_t physicalAddress,
379 	uint32 attributes, uint32 memoryType, vm_page_reservation* reservation)
380 {
381 	TRACE("X86VMTranslationMapPAE::Map(): %#" B_PRIxADDR " -> %#" B_PRIxPHYSADDR
382 		"\n", virtualAddress, physicalAddress);
383 
384 	// check to see if a page table exists for this range
385 	pae_page_directory_entry* pageDirEntry
386 		= X86PagingMethodPAE::PageDirEntryForAddress(
387 			fPagingStructures->VirtualPageDirs(), virtualAddress);
388 	if ((*pageDirEntry & X86_PAE_PDE_PRESENT) == 0) {
389 		// we need to allocate a page table
390 		vm_page *page = vm_page_allocate_page(reservation,
391 			PAGE_STATE_WIRED | VM_PAGE_ALLOC_CLEAR);
392 
393 		DEBUG_PAGE_ACCESS_END(page);
394 
395 		phys_addr_t physicalPageTable
396 			= (phys_addr_t)page->physical_page_number * B_PAGE_SIZE;
397 
398 		TRACE("X86VMTranslationMapPAE::Map(): asked for free page for "
399 			"page table: %#" B_PRIxPHYSADDR "\n", physicalPageTable);
400 
401 		// put it in the page dir
402 		X86PagingMethodPAE::PutPageTableInPageDir(pageDirEntry,
403 			physicalPageTable,
404 			attributes
405 				| ((attributes & B_USER_PROTECTION) != 0
406 						? B_WRITE_AREA : B_KERNEL_WRITE_AREA));
407 
408 		fMapCount++;
409 	}
410 
411 	// now, fill in the page table entry
412 	Thread* thread = thread_get_current_thread();
413 	ThreadCPUPinner pinner(thread);
414 
415 	pae_page_table_entry* pageTable
416 		= (pae_page_table_entry*)fPageMapper->GetPageTableAt(
417 			*pageDirEntry & X86_PAE_PDE_ADDRESS_MASK);
418 	pae_page_table_entry* entry = pageTable
419 		+ virtualAddress / B_PAGE_SIZE % kPAEPageTableEntryCount;
420 
421 	ASSERT_PRINT((*entry & X86_PAE_PTE_PRESENT) == 0,
422 		"virtual address: %#" B_PRIxADDR ", existing pte: %#" B_PRIx64 " @ %p",
423 		virtualAddress, *entry, entry);
424 
425 	X86PagingMethodPAE::PutPageTableEntryInTable(entry, physicalAddress,
426 		attributes, memoryType, fIsKernelMap);
427 
428 	T(Map(this, virtualAddress, *entry));
429 
430 	pinner.Unlock();
431 
432 	// Note: We don't need to invalidate the TLB for this address, as previously
433 	// the entry was not present and the TLB doesn't cache those entries.
434 
435 	fMapCount++;
436 
437 	return 0;
438 }
439 
440 
441 status_t
442 X86VMTranslationMapPAE::Unmap(addr_t start, addr_t end)
443 {
444 	start = ROUNDDOWN(start, B_PAGE_SIZE);
445 	if (start >= end)
446 		return B_OK;
447 
448 	TRACE("X86VMTranslationMapPAE::Unmap(): %#" B_PRIxADDR " - %#" B_PRIxADDR
449 		"\n", start, end);
450 
451 	do {
452 		pae_page_directory_entry* pageDirEntry
453 			= X86PagingMethodPAE::PageDirEntryForAddress(
454 				fPagingStructures->VirtualPageDirs(), start);
455 		if ((*pageDirEntry & X86_PAE_PDE_PRESENT) == 0) {
456 			// no page table here, move the start up to access the next page
457 			// table
458 			start = ROUNDUP(start + 1, kPAEPageTableRange);
459 			continue;
460 		}
461 
462 		Thread* thread = thread_get_current_thread();
463 		ThreadCPUPinner pinner(thread);
464 
465 		pae_page_table_entry* pageTable
466 			= (pae_page_table_entry*)fPageMapper->GetPageTableAt(
467 				*pageDirEntry & X86_PAE_PDE_ADDRESS_MASK);
468 
469 		uint32 index = start / B_PAGE_SIZE % kPAEPageTableEntryCount;
470 		for (; index < kPAEPageTableEntryCount && start < end;
471 				index++, start += B_PAGE_SIZE) {
472 			if ((pageTable[index] & X86_PAE_PTE_PRESENT) == 0) {
473 				// page mapping not valid
474 				continue;
475 			}
476 
477 			TRACE("X86VMTranslationMapPAE::Unmap(): removing page %#"
478 				B_PRIxADDR "\n", start);
479 
480 			pae_page_table_entry oldEntry
481 				= X86PagingMethodPAE::ClearTableEntryFlags(
482 					&pageTable[index], X86_PAE_PTE_PRESENT);
483 
484 			T(Unmap(this, start, oldEntry));
485 
486 			fMapCount--;
487 
488 			if ((oldEntry & X86_PAE_PTE_ACCESSED) != 0) {
489 				// Note, that we only need to invalidate the address, if the
490 				// accessed flags was set, since only then the entry could have
491 				// been in any TLB.
492 				InvalidatePage(start);
493 			}
494 		}
495 	} while (start != 0 && start < end);
496 
497 	return B_OK;
498 }
499 
500 
501 status_t
502 X86VMTranslationMapPAE::DebugMarkRangePresent(addr_t start, addr_t end,
503 	bool markPresent)
504 {
505 	start = ROUNDDOWN(start, B_PAGE_SIZE);
506 	if (start >= end)
507 		return B_OK;
508 
509 	do {
510 		pae_page_directory_entry* pageDirEntry
511 			= X86PagingMethodPAE::PageDirEntryForAddress(
512 				fPagingStructures->VirtualPageDirs(), start);
513 		if ((*pageDirEntry & X86_PAE_PDE_PRESENT) == 0) {
514 			// no page table here, move the start up to access the next page
515 			// table
516 			start = ROUNDUP(start + 1, kPAEPageTableRange);
517 			continue;
518 		}
519 
520 		Thread* thread = thread_get_current_thread();
521 		ThreadCPUPinner pinner(thread);
522 
523 		pae_page_table_entry* pageTable
524 			= (pae_page_table_entry*)fPageMapper->GetPageTableAt(
525 				*pageDirEntry & X86_PAE_PDE_ADDRESS_MASK);
526 
527 		uint32 index = start / B_PAGE_SIZE % kPAEPageTableEntryCount;
528 		for (; index < kPAEPageTableEntryCount && start < end;
529 				index++, start += B_PAGE_SIZE) {
530 
531 			if ((pageTable[index] & X86_PAE_PTE_PRESENT) == 0) {
532 				if (!markPresent)
533 					continue;
534 
535 				X86PagingMethodPAE::SetTableEntryFlags(
536 					&pageTable[index], X86_PAE_PTE_PRESENT);
537 			} else {
538 				if (markPresent)
539 					continue;
540 
541 				pae_page_table_entry oldEntry
542 					= X86PagingMethodPAE::ClearTableEntryFlags(
543 						&pageTable[index], X86_PAE_PTE_PRESENT);
544 
545 				if ((oldEntry & X86_PAE_PTE_ACCESSED) != 0) {
546 					// Note, that we only need to invalidate the address, if the
547 					// accessed flags was set, since only then the entry could
548 					// have been in any TLB.
549 					InvalidatePage(start);
550 				}
551 			}
552 		}
553 	} while (start != 0 && start < end);
554 
555 	return B_OK;
556 }
557 
558 
559 /*!	Caller must have locked the cache of the page to be unmapped.
560 	This object shouldn't be locked.
561 */
562 status_t
563 X86VMTranslationMapPAE::UnmapPage(VMArea* area, addr_t address,
564 	bool updatePageQueue)
565 {
566 	ASSERT(address % B_PAGE_SIZE == 0);
567 
568 	pae_page_directory_entry* pageDirEntry
569 		= X86PagingMethodPAE::PageDirEntryForAddress(
570 			fPagingStructures->VirtualPageDirs(), address);
571 
572 	TRACE("X86VMTranslationMapPAE::UnmapPage(%#" B_PRIxADDR ")\n", address);
573 
574 	RecursiveLocker locker(fLock);
575 
576 	if ((*pageDirEntry & X86_PAE_PDE_PRESENT) == 0)
577 		return B_ENTRY_NOT_FOUND;
578 
579 	ThreadCPUPinner pinner(thread_get_current_thread());
580 
581 	pae_page_table_entry* pageTable
582 		= (pae_page_table_entry*)fPageMapper->GetPageTableAt(
583 			*pageDirEntry & X86_PAE_PDE_ADDRESS_MASK);
584 
585 	pae_page_table_entry oldEntry = X86PagingMethodPAE::ClearTableEntry(
586 		&pageTable[address / B_PAGE_SIZE % kPAEPageTableEntryCount]);
587 
588 	T(Unmap(this, address, oldEntry));
589 
590 	pinner.Unlock();
591 
592 	if ((oldEntry & X86_PAE_PTE_PRESENT) == 0) {
593 		// page mapping not valid
594 		return B_ENTRY_NOT_FOUND;
595 	}
596 
597 	fMapCount--;
598 
599 	if ((oldEntry & X86_PAE_PTE_ACCESSED) != 0) {
600 		// Note, that we only need to invalidate the address, if the
601 		// accessed flags was set, since only then the entry could have been
602 		// in any TLB.
603 		InvalidatePage(address);
604 
605 		Flush();
606 
607 		// NOTE: Between clearing the page table entry and Flush() other
608 		// processors (actually even this processor with another thread of the
609 		// same team) could still access the page in question via their cached
610 		// entry. We can obviously lose a modified flag in this case, with the
611 		// effect that the page looks unmodified (and might thus be recycled),
612 		// but is actually modified.
613 		// In most cases this is harmless, but for vm_remove_all_page_mappings()
614 		// this is actually a problem.
615 		// Interestingly FreeBSD seems to ignore this problem as well
616 		// (cf. pmap_remove_all()), unless I've missed something.
617 	}
618 
619 	locker.Detach();
620 		// PageUnmapped() will unlock for us
621 
622 	PageUnmapped(area, (oldEntry & X86_PAE_PTE_ADDRESS_MASK) / B_PAGE_SIZE,
623 		(oldEntry & X86_PAE_PTE_ACCESSED) != 0,
624 		(oldEntry & X86_PAE_PTE_DIRTY) != 0, updatePageQueue);
625 
626 	return B_OK;
627 }
628 
629 
630 void
631 X86VMTranslationMapPAE::UnmapPages(VMArea* area, addr_t base, size_t size,
632 	bool updatePageQueue)
633 {
634 	if (size == 0)
635 		return;
636 
637 	addr_t start = base;
638 	addr_t end = base + size - 1;
639 
640 	TRACE("X86VMTranslationMapPAE::UnmapPages(%p, %#" B_PRIxADDR ", %#"
641 		B_PRIxADDR ")\n", area, start, end);
642 
643 	VMAreaMappings queue;
644 
645 	RecursiveLocker locker(fLock);
646 
647 	do {
648 		pae_page_directory_entry* pageDirEntry
649 			= X86PagingMethodPAE::PageDirEntryForAddress(
650 				fPagingStructures->VirtualPageDirs(), start);
651 		if ((*pageDirEntry & X86_PAE_PDE_PRESENT) == 0) {
652 			// no page table here, move the start up to access the next page
653 			// table
654 			start = ROUNDUP(start + 1, kPAEPageTableRange);
655 			continue;
656 		}
657 
658 		Thread* thread = thread_get_current_thread();
659 		ThreadCPUPinner pinner(thread);
660 
661 		pae_page_table_entry* pageTable
662 			= (pae_page_table_entry*)fPageMapper->GetPageTableAt(
663 				*pageDirEntry & X86_PAE_PDE_ADDRESS_MASK);
664 
665 		uint32 index = start / B_PAGE_SIZE % kPAEPageTableEntryCount;
666 		for (; index < kPAEPageTableEntryCount && start < end;
667 				index++, start += B_PAGE_SIZE) {
668 			pae_page_table_entry oldEntry
669 				= X86PagingMethodPAE::ClearTableEntry(&pageTable[index]);
670 			if ((oldEntry & X86_PAE_PTE_PRESENT) == 0)
671 				continue;
672 
673 			T(Unmap(this, start, oldEntry));
674 
675 			fMapCount--;
676 
677 			if ((oldEntry & X86_PAE_PTE_ACCESSED) != 0) {
678 				// Note, that we only need to invalidate the address, if the
679 				// accessed flags was set, since only then the entry could have
680 				// been in any TLB.
681 				InvalidatePage(start);
682 			}
683 
684 			if (area->cache_type != CACHE_TYPE_DEVICE) {
685 				// get the page
686 				vm_page* page = vm_lookup_page(
687 					(oldEntry & X86_PAE_PTE_ADDRESS_MASK) / B_PAGE_SIZE);
688 				ASSERT(page != NULL);
689 
690 				DEBUG_PAGE_ACCESS_START(page);
691 
692 				// transfer the accessed/dirty flags to the page
693 				if ((oldEntry & X86_PAE_PTE_ACCESSED) != 0)
694 					page->accessed = true;
695 				if ((oldEntry & X86_PAE_PTE_DIRTY) != 0)
696 					page->modified = true;
697 
698 				// remove the mapping object/decrement the wired_count of the
699 				// page
700 				if (area->wiring == B_NO_LOCK) {
701 					vm_page_mapping* mapping = NULL;
702 					vm_page_mappings::Iterator iterator
703 						= page->mappings.GetIterator();
704 					while ((mapping = iterator.Next()) != NULL) {
705 						if (mapping->area == area)
706 							break;
707 					}
708 
709 					ASSERT(mapping != NULL);
710 
711 					area->mappings.Remove(mapping);
712 					page->mappings.Remove(mapping);
713 					queue.Add(mapping);
714 				} else
715 					page->DecrementWiredCount();
716 
717 				if (!page->IsMapped()) {
718 					atomic_add(&gMappedPagesCount, -1);
719 
720 					if (updatePageQueue) {
721 						if (page->Cache()->temporary)
722 							vm_page_set_state(page, PAGE_STATE_INACTIVE);
723 						else if (page->modified)
724 							vm_page_set_state(page, PAGE_STATE_MODIFIED);
725 						else
726 							vm_page_set_state(page, PAGE_STATE_CACHED);
727 					}
728 				}
729 
730 				DEBUG_PAGE_ACCESS_END(page);
731 			}
732 		}
733 
734 		Flush();
735 			// flush explicitly, since we directly use the lock
736 	} while (start != 0 && start < end);
737 
738 	// TODO: As in UnmapPage() we can lose page dirty flags here. ATM it's not
739 	// really critical here, as in all cases this method is used, the unmapped
740 	// area range is unmapped for good (resized/cut) and the pages will likely
741 	// be freed.
742 
743 	locker.Unlock();
744 
745 	// free removed mappings
746 	bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
747 	uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
748 		| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
749 	while (vm_page_mapping* mapping = queue.RemoveHead())
750 		vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
751 }
752 
753 
754 void
755 X86VMTranslationMapPAE::UnmapArea(VMArea* area, bool deletingAddressSpace,
756 	bool ignoreTopCachePageFlags)
757 {
758 	if (area->cache_type == CACHE_TYPE_DEVICE || area->wiring != B_NO_LOCK) {
759 		X86VMTranslationMapPAE::UnmapPages(area, area->Base(), area->Size(),
760 			true);
761 		return;
762 	}
763 
764 	bool unmapPages = !deletingAddressSpace || !ignoreTopCachePageFlags;
765 
766 	RecursiveLocker locker(fLock);
767 
768 	VMAreaMappings mappings;
769 	mappings.MoveFrom(&area->mappings);
770 
771 	for (VMAreaMappings::Iterator it = mappings.GetIterator();
772 			vm_page_mapping* mapping = it.Next();) {
773 		vm_page* page = mapping->page;
774 		page->mappings.Remove(mapping);
775 
776 		VMCache* cache = page->Cache();
777 
778 		bool pageFullyUnmapped = false;
779 		if (!page->IsMapped()) {
780 			atomic_add(&gMappedPagesCount, -1);
781 			pageFullyUnmapped = true;
782 		}
783 
784 		if (unmapPages || cache != area->cache) {
785 			addr_t address = area->Base()
786 				+ ((page->cache_offset * B_PAGE_SIZE) - area->cache_offset);
787 
788 			pae_page_directory_entry* pageDirEntry
789 				= X86PagingMethodPAE::PageDirEntryForAddress(
790 					fPagingStructures->VirtualPageDirs(), address);
791 			if ((*pageDirEntry & X86_PAE_PDE_PRESENT) == 0) {
792 				panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
793 					"has no page dir entry", page, area, address);
794 				continue;
795 			}
796 
797 			ThreadCPUPinner pinner(thread_get_current_thread());
798 
799 			pae_page_table_entry* pageTable
800 				= (pae_page_table_entry*)fPageMapper->GetPageTableAt(
801 					*pageDirEntry & X86_PAE_PDE_ADDRESS_MASK);
802 			pae_page_table_entry oldEntry
803 				= X86PagingMethodPAE::ClearTableEntry(
804 					&pageTable[address / B_PAGE_SIZE
805 						% kPAEPageTableEntryCount]);
806 
807 			pinner.Unlock();
808 
809 			if ((oldEntry & X86_PAE_PTE_PRESENT) == 0) {
810 				panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
811 					"has no page table entry", page, area, address);
812 				continue;
813 			}
814 
815 			T(Unmap(this, address, oldEntry));
816 
817 			// transfer the accessed/dirty flags to the page and invalidate
818 			// the mapping, if necessary
819 			if ((oldEntry & X86_PAE_PTE_ACCESSED) != 0) {
820 				page->accessed = true;
821 
822 				if (!deletingAddressSpace)
823 					InvalidatePage(address);
824 			}
825 
826 			if ((oldEntry & X86_PAE_PTE_DIRTY) != 0)
827 				page->modified = true;
828 
829 			if (pageFullyUnmapped) {
830 				DEBUG_PAGE_ACCESS_START(page);
831 
832 				if (cache->temporary)
833 					vm_page_set_state(page, PAGE_STATE_INACTIVE);
834 				else if (page->modified)
835 					vm_page_set_state(page, PAGE_STATE_MODIFIED);
836 				else
837 					vm_page_set_state(page, PAGE_STATE_CACHED);
838 
839 				DEBUG_PAGE_ACCESS_END(page);
840 			}
841 		} else {
842 #if TRANSLATION_MAP_TRACING
843 			addr_t address = area->Base()
844 				+ ((page->cache_offset * B_PAGE_SIZE) - area->cache_offset);
845 
846 			ThreadCPUPinner pinner(thread_get_current_thread());
847 
848 			pae_page_directory_entry* pageDirEntry
849 				= X86PagingMethodPAE::PageDirEntryForAddress(
850 					fPagingStructures->VirtualPageDirs(), address);
851 			if ((*pageDirEntry & X86_PAE_PDE_PRESENT) != 0) {
852 				pae_page_table_entry* pageTable
853 					= (pae_page_table_entry*)fPageMapper->GetPageTableAt(
854 						*pageDirEntry & X86_PAE_PDE_ADDRESS_MASK);
855 				pae_page_table_entry oldEntry = pageTable[
856 					address / B_PAGE_SIZE % kPAEPageTableEntryCount];
857 
858 				pinner.Unlock();
859 
860 				if ((oldEntry & X86_PAE_PTE_PRESENT) != 0)
861 					T(Unmap(this, address, oldEntry));
862 			}
863 #endif
864 		}
865 
866 		fMapCount--;
867 	}
868 
869 	Flush();
870 		// flush explicitely, since we directly use the lock
871 
872 	locker.Unlock();
873 
874 	bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
875 	uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
876 		| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
877 	while (vm_page_mapping* mapping = mappings.RemoveHead())
878 		vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
879 }
880 
881 
882 status_t
883 X86VMTranslationMapPAE::Query(addr_t virtualAddress,
884 	phys_addr_t* _physicalAddress, uint32* _flags)
885 {
886 	// default the flags to not present
887 	*_flags = 0;
888 	*_physicalAddress = 0;
889 
890 	// get the page directory entry
891 	pae_page_directory_entry* pageDirEntry
892 		= X86PagingMethodPAE::PageDirEntryForAddress(
893 			fPagingStructures->VirtualPageDirs(), virtualAddress);
894 	if ((*pageDirEntry & X86_PAE_PDE_PRESENT) == 0) {
895 		// no pagetable here
896 		return B_OK;
897 	}
898 
899 	// get the page table entry
900 	Thread* thread = thread_get_current_thread();
901 	ThreadCPUPinner pinner(thread);
902 
903 	pae_page_table_entry* pageTable
904 		= (pae_page_table_entry*)fPageMapper->GetPageTableAt(
905 			*pageDirEntry & X86_PAE_PDE_ADDRESS_MASK);
906 	pae_page_table_entry entry
907 		= pageTable[virtualAddress / B_PAGE_SIZE % kPAEPageTableEntryCount];
908 
909 	pinner.Unlock();
910 
911 	*_physicalAddress = entry & X86_PAE_PTE_ADDRESS_MASK;
912 
913 	// translate the page state flags
914 	if ((entry & X86_PAE_PTE_USER) != 0) {
915 		*_flags |= ((entry & X86_PAE_PTE_WRITABLE) != 0 ? B_WRITE_AREA : 0)
916 			| B_READ_AREA
917 			| ((entry & X86_PAE_PTE_NOT_EXECUTABLE) == 0 ? B_EXECUTE_AREA : 0);
918 	}
919 
920 	*_flags |= ((entry & X86_PAE_PTE_WRITABLE) != 0 ? B_KERNEL_WRITE_AREA : 0)
921 		| B_KERNEL_READ_AREA
922 		| ((entry & X86_PAE_PTE_NOT_EXECUTABLE) == 0
923 			? B_KERNEL_EXECUTE_AREA : 0)
924 		| ((entry & X86_PAE_PTE_DIRTY) != 0 ? PAGE_MODIFIED : 0)
925 		| ((entry & X86_PAE_PTE_ACCESSED) != 0 ? PAGE_ACCESSED : 0)
926 		| ((entry & X86_PAE_PTE_PRESENT) != 0 ? PAGE_PRESENT : 0);
927 
928 	TRACE("X86VMTranslationMapPAE::Query(%#" B_PRIxADDR ") -> %#"
929 		B_PRIxPHYSADDR ":\n", virtualAddress, *_physicalAddress);
930 
931 	return B_OK;
932 }
933 
934 
935 status_t
936 X86VMTranslationMapPAE::QueryInterrupt(addr_t virtualAddress,
937 	phys_addr_t* _physicalAddress, uint32* _flags)
938 {
939 	// default the flags to not present
940 	*_flags = 0;
941 	*_physicalAddress = 0;
942 
943 	// get the page directory entry
944 	pae_page_directory_entry* pageDirEntry
945 		= X86PagingMethodPAE::PageDirEntryForAddress(
946 			fPagingStructures->VirtualPageDirs(), virtualAddress);
947 	if ((*pageDirEntry & X86_PAE_PDE_PRESENT) == 0) {
948 		// no pagetable here
949 		return B_OK;
950 	}
951 
952 	// get the page table entry
953 	pae_page_table_entry* pageTable
954 		= (pae_page_table_entry*)X86PagingMethodPAE::Method()
955 			->PhysicalPageMapper()->InterruptGetPageTableAt(
956 				*pageDirEntry & X86_PAE_PDE_ADDRESS_MASK);
957 	pae_page_table_entry entry
958 		= pageTable[virtualAddress / B_PAGE_SIZE % kPAEPageTableEntryCount];
959 
960 	*_physicalAddress = entry & X86_PAE_PTE_ADDRESS_MASK;
961 
962 	// translate the page state flags
963 	if ((entry & X86_PAE_PTE_USER) != 0) {
964 		*_flags |= ((entry & X86_PAE_PTE_WRITABLE) != 0 ? B_WRITE_AREA : 0)
965 			| B_READ_AREA
966 			| ((entry & X86_PAE_PTE_NOT_EXECUTABLE) == 0 ? B_EXECUTE_AREA : 0);
967 	}
968 
969 	*_flags |= ((entry & X86_PAE_PTE_WRITABLE) != 0 ? B_KERNEL_WRITE_AREA : 0)
970 		| B_KERNEL_READ_AREA
971 		| ((entry & X86_PAE_PTE_NOT_EXECUTABLE) == 0
972 			? B_KERNEL_EXECUTE_AREA : 0)
973 		| ((entry & X86_PAE_PTE_DIRTY) != 0 ? PAGE_MODIFIED : 0)
974 		| ((entry & X86_PAE_PTE_ACCESSED) != 0 ? PAGE_ACCESSED : 0)
975 		| ((entry & X86_PAE_PTE_PRESENT) != 0 ? PAGE_PRESENT : 0);
976 
977 	TRACE("X86VMTranslationMapPAE::Query(%#" B_PRIxADDR ") -> %#"
978 		B_PRIxPHYSADDR ":\n", virtualAddress, *_physicalAddress);
979 
980 	return B_OK;
981 }
982 
983 
984 status_t
985 X86VMTranslationMapPAE::Protect(addr_t start, addr_t end, uint32 attributes,
986 	uint32 memoryType)
987 {
988 	start = ROUNDDOWN(start, B_PAGE_SIZE);
989 	if (start >= end)
990 		return B_OK;
991 
992 	TRACE("X86VMTranslationMapPAE::Protect(): %#" B_PRIxADDR " - %#" B_PRIxADDR
993 		", attributes: %#" B_PRIx32 "\n", start, end, attributes);
994 
995 	// compute protection/memory type flags
996 	uint64 newFlags
997 		= X86PagingMethodPAE::MemoryTypeToPageTableEntryFlags(memoryType);
998 	if ((attributes & B_USER_PROTECTION) != 0) {
999 		newFlags |= X86_PAE_PTE_USER;
1000 		if ((attributes & B_WRITE_AREA) != 0)
1001 			newFlags |= X86_PAE_PTE_WRITABLE;
1002 		if ((attributes & B_EXECUTE_AREA) == 0
1003 			&& x86_check_feature(IA32_FEATURE_AMD_EXT_NX, FEATURE_EXT_AMD)) {
1004 			newFlags |= X86_PAE_PTE_NOT_EXECUTABLE;
1005 		}
1006 	} else if ((attributes & B_KERNEL_WRITE_AREA) != 0)
1007 		newFlags |= X86_PAE_PTE_WRITABLE;
1008 
1009 	do {
1010 		pae_page_directory_entry* pageDirEntry
1011 			= X86PagingMethodPAE::PageDirEntryForAddress(
1012 				fPagingStructures->VirtualPageDirs(), start);
1013 		if ((*pageDirEntry & X86_PAE_PDE_PRESENT) == 0) {
1014 			// no page table here, move the start up to access the next page
1015 			// table
1016 			start = ROUNDUP(start + 1, kPAEPageTableRange);
1017 			continue;
1018 		}
1019 
1020 		Thread* thread = thread_get_current_thread();
1021 		ThreadCPUPinner pinner(thread);
1022 
1023 		pae_page_table_entry* pageTable
1024 			= (pae_page_table_entry*)fPageMapper->GetPageTableAt(
1025 				*pageDirEntry & X86_PAE_PDE_ADDRESS_MASK);
1026 
1027 		uint32 index = start / B_PAGE_SIZE % kPAEPageTableEntryCount;
1028 		for (; index < kPAEPageTableEntryCount && start < end;
1029 				index++, start += B_PAGE_SIZE) {
1030 			pae_page_table_entry entry = pageTable[index];
1031 			if ((pageTable[index] & X86_PAE_PTE_PRESENT) == 0) {
1032 				// page mapping not valid
1033 				continue;
1034 			}
1035 
1036 			TRACE("X86VMTranslationMapPAE::Protect(): protect page %#"
1037 				B_PRIxADDR "\n", start);
1038 
1039 			// set the new protection flags -- we want to do that atomically,
1040 			// without changing the accessed or dirty flag
1041 			pae_page_table_entry oldEntry;
1042 			while (true) {
1043 				oldEntry = X86PagingMethodPAE::TestAndSetTableEntry(
1044 					&pageTable[index],
1045 					(entry & ~(X86_PAE_PTE_PROTECTION_MASK
1046 							| X86_PAE_PTE_MEMORY_TYPE_MASK))
1047 						| newFlags,
1048 					entry);
1049 				if (oldEntry == entry)
1050 					break;
1051 				entry = oldEntry;
1052 			}
1053 
1054 			T(Protect(this, start, entry,
1055 				(entry & ~(X86_PAE_PTE_PROTECTION_MASK
1056 						| X86_PAE_PTE_MEMORY_TYPE_MASK))
1057 					| newFlags));
1058 
1059 			if ((oldEntry & X86_PAE_PTE_ACCESSED) != 0) {
1060 				// Note, that we only need to invalidate the address, if the
1061 				// accessed flag was set, since only then the entry could have been
1062 				// in any TLB.
1063 				InvalidatePage(start);
1064 			}
1065 		}
1066 	} while (start != 0 && start < end);
1067 
1068 	return B_OK;
1069 }
1070 
1071 
1072 status_t
1073 X86VMTranslationMapPAE::ClearFlags(addr_t address, uint32 flags)
1074 {
1075 	pae_page_directory_entry* pageDirEntry
1076 		= X86PagingMethodPAE::PageDirEntryForAddress(
1077 			fPagingStructures->VirtualPageDirs(), address);
1078 	if ((*pageDirEntry & X86_PAE_PDE_PRESENT) == 0) {
1079 		// no pagetable here
1080 		return B_OK;
1081 	}
1082 
1083 	uint64 flagsToClear = ((flags & PAGE_MODIFIED) ? X86_PAE_PTE_DIRTY : 0)
1084 		| ((flags & PAGE_ACCESSED) ? X86_PAE_PTE_ACCESSED : 0);
1085 
1086 	Thread* thread = thread_get_current_thread();
1087 	ThreadCPUPinner pinner(thread);
1088 
1089 	pae_page_table_entry* entry
1090 		= (pae_page_table_entry*)fPageMapper->GetPageTableAt(
1091 				*pageDirEntry & X86_PAE_PDE_ADDRESS_MASK)
1092 			+ address / B_PAGE_SIZE % kPAEPageTableEntryCount;
1093 
1094 	// clear out the flags we've been requested to clear
1095 	pae_page_table_entry oldEntry
1096 		= X86PagingMethodPAE::ClearTableEntryFlags(entry, flagsToClear);
1097 
1098 	pinner.Unlock();
1099 
1100 	T(ClearFlags(this, address, oldEntry, flagsToClear));
1101 
1102 	if ((oldEntry & flagsToClear) != 0)
1103 		InvalidatePage(address);
1104 
1105 	return B_OK;
1106 }
1107 
1108 
1109 bool
1110 X86VMTranslationMapPAE::ClearAccessedAndModified(VMArea* area, addr_t address,
1111 	bool unmapIfUnaccessed, bool& _modified)
1112 {
1113 	ASSERT(address % B_PAGE_SIZE == 0);
1114 
1115 	TRACE("X86VMTranslationMapPAE::ClearAccessedAndModified(%#" B_PRIxADDR
1116 		")\n", address);
1117 
1118 	pae_page_directory_entry* pageDirEntry
1119 		= X86PagingMethodPAE::PageDirEntryForAddress(
1120 			fPagingStructures->VirtualPageDirs(), address);
1121 
1122 	RecursiveLocker locker(fLock);
1123 
1124 	if ((*pageDirEntry & X86_PAE_PDE_PRESENT) == 0)
1125 		return false;
1126 
1127 	ThreadCPUPinner pinner(thread_get_current_thread());
1128 
1129 	pae_page_table_entry* entry
1130 		= (pae_page_table_entry*)fPageMapper->GetPageTableAt(
1131 				*pageDirEntry & X86_PAE_PDE_ADDRESS_MASK)
1132 			+ address / B_PAGE_SIZE % kPAEPageTableEntryCount;
1133 
1134 	// perform the deed
1135 	pae_page_table_entry oldEntry;
1136 
1137 	if (unmapIfUnaccessed) {
1138 		while (true) {
1139 			oldEntry = *entry;
1140 			if ((oldEntry & X86_PAE_PTE_PRESENT) == 0) {
1141 				// page mapping not valid
1142 				return false;
1143 			}
1144 
1145 			if (oldEntry & X86_PAE_PTE_ACCESSED) {
1146 				// page was accessed -- just clear the flags
1147 				oldEntry = X86PagingMethodPAE::ClearTableEntryFlags(entry,
1148 					X86_PAE_PTE_ACCESSED | X86_PAE_PTE_DIRTY);
1149 				T(ClearFlags(this, address, oldEntry,
1150 					X86_PAE_PTE_ACCESSED | X86_PAE_PTE_DIRTY));
1151 				break;
1152 			}
1153 
1154 			// page hasn't been accessed -- unmap it
1155 			if (X86PagingMethodPAE::TestAndSetTableEntry(entry, 0, oldEntry)
1156 					== oldEntry) {
1157 				T(ClearFlagsUnmap(this, address, oldEntry));
1158 				break;
1159 			}
1160 
1161 			// something changed -- check again
1162 		}
1163 	} else {
1164 		oldEntry = X86PagingMethodPAE::ClearTableEntryFlags(entry,
1165 			X86_PAE_PTE_ACCESSED | X86_PAE_PTE_DIRTY);
1166 		T(ClearFlags(this, address, oldEntry,
1167 			X86_PAE_PTE_ACCESSED | X86_PAE_PTE_DIRTY));
1168 	}
1169 
1170 	pinner.Unlock();
1171 
1172 	_modified = (oldEntry & X86_PAE_PTE_DIRTY) != 0;
1173 
1174 	if ((oldEntry & X86_PAE_PTE_ACCESSED) != 0) {
1175 		// Note, that we only need to invalidate the address, if the
1176 		// accessed flags was set, since only then the entry could have been
1177 		// in any TLB.
1178 		InvalidatePage(address);
1179 		Flush();
1180 
1181 		return true;
1182 	}
1183 
1184 	if (!unmapIfUnaccessed)
1185 		return false;
1186 
1187 	// We have unmapped the address. Do the "high level" stuff.
1188 
1189 	fMapCount--;
1190 
1191 	locker.Detach();
1192 		// UnaccessedPageUnmapped() will unlock for us
1193 
1194 	UnaccessedPageUnmapped(area,
1195 		(oldEntry & X86_PAE_PTE_ADDRESS_MASK) / B_PAGE_SIZE);
1196 
1197 	return false;
1198 }
1199 
1200 
1201 void
1202 X86VMTranslationMapPAE::DebugPrintMappingInfo(addr_t virtualAddress)
1203 {
1204 	// get the page directory
1205 	pae_page_directory_entry* const* pdpt
1206 		= fPagingStructures->VirtualPageDirs();
1207 	pae_page_directory_entry* pageDirectory = pdpt[virtualAddress >> 30];
1208 	kprintf("page directory: %p (PDPT[%zu])\n", pageDirectory,
1209 		virtualAddress >> 30);
1210 
1211 	// get the page directory entry
1212 	pae_page_directory_entry* pageDirEntry
1213 		= X86PagingMethodPAE::PageDirEntryForAddress(pdpt, virtualAddress);
1214 	kprintf("page directory entry %zu (%p): %#" B_PRIx64 "\n",
1215 		pageDirEntry - pageDirectory, pageDirEntry, *pageDirEntry);
1216 
1217 	kprintf("  access: ");
1218 	if ((*pageDirEntry & X86_PAE_PDE_PRESENT) != 0)
1219 		kprintf(" present");
1220 	if ((*pageDirEntry & X86_PAE_PDE_WRITABLE) != 0)
1221 		kprintf(" writable");
1222 	if ((*pageDirEntry & X86_PAE_PDE_USER) != 0)
1223 		kprintf(" user");
1224 	if ((*pageDirEntry & X86_PAE_PDE_NOT_EXECUTABLE) == 0)
1225 		kprintf(" executable");
1226 	if ((*pageDirEntry & X86_PAE_PDE_LARGE_PAGE) != 0)
1227 		kprintf(" large");
1228 
1229 	kprintf("\n  caching:");
1230 	if ((*pageDirEntry & X86_PAE_PDE_WRITE_THROUGH) != 0)
1231 		kprintf(" write-through");
1232 	if ((*pageDirEntry & X86_PAE_PDE_CACHING_DISABLED) != 0)
1233 		kprintf(" uncached");
1234 
1235 	kprintf("\n  flags:  ");
1236 	if ((*pageDirEntry & X86_PAE_PDE_ACCESSED) != 0)
1237 		kprintf(" accessed");
1238 	kprintf("\n");
1239 
1240 	if ((*pageDirEntry & X86_PAE_PDE_PRESENT) == 0)
1241 		return;
1242 
1243 	// get the page table entry
1244 	pae_page_table_entry* pageTable
1245 		= (pae_page_table_entry*)X86PagingMethodPAE::Method()
1246 			->PhysicalPageMapper()->InterruptGetPageTableAt(
1247 				*pageDirEntry & X86_PAE_PDE_ADDRESS_MASK);
1248 	kprintf("page table: %#" B_PRIx64 "\n",
1249 		*pageDirEntry & X86_PAE_PDE_ADDRESS_MASK);
1250 	size_t pteIndex = virtualAddress / B_PAGE_SIZE % kPAEPageTableEntryCount;
1251 	pae_page_table_entry entry = pageTable[pteIndex];
1252 	kprintf("page table entry %zu (phys: %#" B_PRIx64 "): %#" B_PRIx64 "\n",
1253 		pteIndex,
1254 		(*pageDirEntry & X86_PAE_PDE_ADDRESS_MASK)
1255 			+ pteIndex * sizeof(pae_page_table_entry),
1256 		entry);
1257 
1258 	kprintf("  access: ");
1259 	if ((entry & X86_PAE_PTE_PRESENT) != 0)
1260 		kprintf(" present");
1261 	if ((entry & X86_PAE_PTE_WRITABLE) != 0)
1262 		kprintf(" writable");
1263 	if ((entry & X86_PAE_PTE_USER) != 0)
1264 		kprintf(" user");
1265 	if ((entry & X86_PAE_PTE_NOT_EXECUTABLE) == 0)
1266 		kprintf(" executable");
1267 	if ((entry & X86_PAE_PTE_GLOBAL) == 0)
1268 		kprintf(" global");
1269 
1270 	kprintf("\n  caching:");
1271 	if ((entry & X86_PAE_PTE_WRITE_THROUGH) != 0)
1272 		kprintf(" write-through");
1273 	if ((entry & X86_PAE_PTE_CACHING_DISABLED) != 0)
1274 		kprintf(" uncached");
1275 	if ((entry & X86_PAE_PTE_PAT) != 0)
1276 		kprintf(" PAT");
1277 
1278 	kprintf("\n  flags:  ");
1279 	if ((entry & X86_PAE_PTE_ACCESSED) != 0)
1280 		kprintf(" accessed");
1281 	if ((entry & X86_PAE_PTE_DIRTY) != 0)
1282 		kprintf(" dirty");
1283 	kprintf("\n");
1284 
1285 	if ((entry & X86_PAE_PTE_PRESENT) != 0) {
1286 		kprintf("  address: %#" B_PRIx64 "\n",
1287 			entry & X86_PAE_PTE_ADDRESS_MASK);
1288 	}
1289 }
1290 
1291 
1292 bool
1293 X86VMTranslationMapPAE::DebugGetReverseMappingInfo(phys_addr_t physicalAddress,
1294 	ReverseMappingInfoCallback& callback)
1295 {
1296 	pae_page_directory_entry* const* pdpt
1297 		= fPagingStructures->VirtualPageDirs();
1298 	for (uint32 pageDirIndex = fIsKernelMap ? 2 : 0;
1299 		pageDirIndex < uint32(fIsKernelMap ? 4 : 2); pageDirIndex++) {
1300 		// iterate through the page directory
1301 		pae_page_directory_entry* pageDirectory = pdpt[pageDirIndex];
1302 		for (uint32 pdeIndex = 0; pdeIndex < kPAEPageDirEntryCount;
1303 			pdeIndex++) {
1304 			pae_page_directory_entry& pageDirEntry = pageDirectory[pdeIndex];
1305 			if ((pageDirEntry & X86_PAE_PDE_ADDRESS_MASK) == 0)
1306 				continue;
1307 
1308 			// get and iterate through the page table
1309 			pae_page_table_entry* pageTable
1310 				= (pae_page_table_entry*)X86PagingMethodPAE::Method()
1311 					->PhysicalPageMapper()->InterruptGetPageTableAt(
1312 						pageDirEntry & X86_PAE_PDE_ADDRESS_MASK);
1313 			for (uint32 pteIndex = 0; pteIndex < kPAEPageTableEntryCount;
1314 				pteIndex++) {
1315 				pae_page_table_entry entry = pageTable[pteIndex];
1316 				if ((entry & X86_PAE_PTE_PRESENT) != 0
1317 					&& (entry & X86_PAE_PTE_ADDRESS_MASK) == physicalAddress) {
1318 					addr_t virtualAddress = pageDirIndex * kPAEPageDirRange
1319 						+ pdeIndex * kPAEPageTableRange
1320 						+ pteIndex * B_PAGE_SIZE;
1321 					if (callback.HandleVirtualAddress(virtualAddress))
1322 						return true;
1323 				}
1324 			}
1325 		}
1326 	}
1327 
1328 	return false;
1329 }
1330 
1331 
1332 X86PagingStructures*
1333 X86VMTranslationMapPAE::PagingStructures() const
1334 {
1335 	return fPagingStructures;
1336 }
1337 
1338 
1339 #endif	// B_HAIKU_PHYSICAL_BITS == 64
1340