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