1/*
2 * Copyright 2008-2011, 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/32bit/X86VMTranslationMap32Bit.h"
12
13#include <stdlib.h>
14#include <string.h>
15
16#include <int.h>
17#include <thread.h>
18#include <slab/Slab.h>
19#include <smp.h>
20#include <util/AutoLock.h>
21#include <util/ThreadAutoLock.h>
22#include <util/queue.h>
23#include <vm/vm_page.h>
24#include <vm/vm_priv.h>
25#include <vm/VMAddressSpace.h>
26#include <vm/VMCache.h>
27
28#include "paging/32bit/X86PagingMethod32Bit.h"
29#include "paging/32bit/X86PagingStructures32Bit.h"
30#include "paging/x86_physical_page_mapper.h"
31
32
33//#define TRACE_X86_VM_TRANSLATION_MAP_32_BIT
34#ifdef TRACE_X86_VM_TRANSLATION_MAP_32_BIT
35#	define TRACE(x...) dprintf(x)
36#else
37#	define TRACE(x...) ;
38#endif
39
40
41X86VMTranslationMap32Bit::X86VMTranslationMap32Bit()
42	:
43	fPagingStructures(NULL)
44{
45}
46
47
48X86VMTranslationMap32Bit::~X86VMTranslationMap32Bit()
49{
50	if (fPagingStructures == NULL)
51		return;
52
53	if (fPageMapper != NULL)
54		fPageMapper->Delete();
55
56	if (fPagingStructures->pgdir_virt != NULL) {
57		// cycle through and free all of the user space pgtables
58		for (uint32 i = VADDR_TO_PDENT(USER_BASE);
59				i <= VADDR_TO_PDENT(USER_BASE + (USER_SIZE - 1)); i++) {
60			if ((fPagingStructures->pgdir_virt[i] & X86_PDE_PRESENT) != 0) {
61				addr_t address = fPagingStructures->pgdir_virt[i]
62					& X86_PDE_ADDRESS_MASK;
63				vm_page* page = vm_lookup_page(address / B_PAGE_SIZE);
64				if (!page)
65					panic("destroy_tmap: didn't find pgtable page\n");
66				DEBUG_PAGE_ACCESS_START(page);
67				vm_page_set_state(page, PAGE_STATE_FREE);
68			}
69		}
70	}
71
72	fPagingStructures->RemoveReference();
73}
74
75
76status_t
77X86VMTranslationMap32Bit::Init(bool kernel)
78{
79	TRACE("X86VMTranslationMap32Bit::Init()\n");
80
81	X86VMTranslationMap::Init(kernel);
82
83	fPagingStructures = new(std::nothrow) X86PagingStructures32Bit;
84	if (fPagingStructures == NULL)
85		return B_NO_MEMORY;
86
87	X86PagingMethod32Bit* method = X86PagingMethod32Bit::Method();
88
89	if (!kernel) {
90		// user
91		// allocate a physical page mapper
92		status_t error = method->PhysicalPageMapper()
93			->CreateTranslationMapPhysicalPageMapper(&fPageMapper);
94		if (error != B_OK)
95			return error;
96
97		// allocate the page directory
98		page_directory_entry* virtualPageDir = (page_directory_entry*)memalign(
99			B_PAGE_SIZE, B_PAGE_SIZE);
100		if (virtualPageDir == NULL)
101			return B_NO_MEMORY;
102
103		// look up the page directory's physical address
104		phys_addr_t physicalPageDir;
105		vm_get_page_mapping(VMAddressSpace::KernelID(),
106			(addr_t)virtualPageDir, &physicalPageDir);
107
108		fPagingStructures->Init(virtualPageDir, physicalPageDir,
109			method->KernelVirtualPageDirectory());
110	} else {
111		// kernel
112		// get the physical page mapper
113		fPageMapper = method->KernelPhysicalPageMapper();
114
115		// we already know the kernel pgdir mapping
116		fPagingStructures->Init(method->KernelVirtualPageDirectory(),
117			method->KernelPhysicalPageDirectory(), NULL);
118	}
119
120	return B_OK;
121}
122
123
124size_t
125X86VMTranslationMap32Bit::MaxPagesNeededToMap(addr_t start, addr_t end) const
126{
127	// If start == 0, the actual base address is not yet known to the caller and
128	// we shall assume the worst case.
129	if (start == 0) {
130		// offset the range so it has the worst possible alignment
131		start = 1023 * B_PAGE_SIZE;
132		end += 1023 * B_PAGE_SIZE;
133	}
134
135	return VADDR_TO_PDENT(end) + 1 - VADDR_TO_PDENT(start);
136}
137
138
139status_t
140X86VMTranslationMap32Bit::Map(addr_t va, phys_addr_t pa, uint32 attributes,
141	uint32 memoryType, vm_page_reservation* reservation)
142{
143	TRACE("map_tmap: entry pa 0x%lx va 0x%lx\n", pa, va);
144
145/*
146	dprintf("pgdir at 0x%x\n", pgdir);
147	dprintf("index is %d\n", va / B_PAGE_SIZE / 1024);
148	dprintf("final at 0x%x\n", &pgdir[va / B_PAGE_SIZE / 1024]);
149	dprintf("value is 0x%x\n", *(int *)&pgdir[va / B_PAGE_SIZE / 1024]);
150	dprintf("present bit is %d\n", pgdir[va / B_PAGE_SIZE / 1024].present);
151	dprintf("addr is %d\n", pgdir[va / B_PAGE_SIZE / 1024].addr);
152*/
153	page_directory_entry* pd = fPagingStructures->pgdir_virt;
154
155	// check to see if a page table exists for this range
156	uint32 index = VADDR_TO_PDENT(va);
157	if ((pd[index] & X86_PDE_PRESENT) == 0) {
158		phys_addr_t pgtable;
159		vm_page *page;
160
161		// we need to allocate a pgtable
162		page = vm_page_allocate_page(reservation,
163			PAGE_STATE_WIRED | VM_PAGE_ALLOC_CLEAR);
164
165		DEBUG_PAGE_ACCESS_END(page);
166
167		pgtable = (phys_addr_t)page->physical_page_number * B_PAGE_SIZE;
168
169		TRACE("map_tmap: asked for free page for pgtable. 0x%lx\n", pgtable);
170
171		// put it in the pgdir
172		X86PagingMethod32Bit::PutPageTableInPageDir(&pd[index], pgtable,
173			attributes
174				| ((attributes & B_USER_PROTECTION) != 0
175						? B_WRITE_AREA : B_KERNEL_WRITE_AREA));
176
177		// update any other page directories, if it maps kernel space
178		if (index >= FIRST_KERNEL_PGDIR_ENT
179			&& index < (FIRST_KERNEL_PGDIR_ENT + NUM_KERNEL_PGDIR_ENTS)) {
180			X86PagingStructures32Bit::UpdateAllPageDirs(index, pd[index]);
181		}
182
183		fMapCount++;
184	}
185
186	// now, fill in the pentry
187	Thread* thread = thread_get_current_thread();
188	ThreadCPUPinner pinner(thread);
189
190	page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt(
191		pd[index] & X86_PDE_ADDRESS_MASK);
192	index = VADDR_TO_PTENT(va);
193
194	ASSERT_PRINT((pt[index] & X86_PTE_PRESENT) == 0,
195		"virtual address: %#" B_PRIxADDR ", existing pte: %#" B_PRIx32, va,
196		pt[index]);
197
198	X86PagingMethod32Bit::PutPageTableEntryInTable(&pt[index], pa, attributes,
199		memoryType, fIsKernelMap);
200
201	pinner.Unlock();
202
203	// Note: We don't need to invalidate the TLB for this address, as previously
204	// the entry was not present and the TLB doesn't cache those entries.
205
206	fMapCount++;
207
208	return 0;
209}
210
211
212status_t
213X86VMTranslationMap32Bit::Unmap(addr_t start, addr_t end)
214{
215	start = ROUNDDOWN(start, B_PAGE_SIZE);
216	if (start >= end)
217		return B_OK;
218
219	TRACE("unmap_tmap: asked to free pages 0x%lx to 0x%lx\n", start, end);
220
221	page_directory_entry *pd = fPagingStructures->pgdir_virt;
222
223	do {
224		int index = VADDR_TO_PDENT(start);
225		if ((pd[index] & X86_PDE_PRESENT) == 0) {
226			// no page table here, move the start up to access the next page
227			// table
228			start = ROUNDUP(start + 1, kPageTableAlignment);
229			continue;
230		}
231
232		Thread* thread = thread_get_current_thread();
233		ThreadCPUPinner pinner(thread);
234
235		page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt(
236			pd[index] & X86_PDE_ADDRESS_MASK);
237
238		for (index = VADDR_TO_PTENT(start); (index < 1024) && (start < end);
239				index++, start += B_PAGE_SIZE) {
240			if ((pt[index] & X86_PTE_PRESENT) == 0) {
241				// page mapping not valid
242				continue;
243			}
244
245			TRACE("unmap_tmap: removing page 0x%lx\n", start);
246
247			page_table_entry oldEntry
248				= X86PagingMethod32Bit::ClearPageTableEntryFlags(&pt[index],
249					X86_PTE_PRESENT);
250			fMapCount--;
251
252			if ((oldEntry & X86_PTE_ACCESSED) != 0) {
253				// Note, that we only need to invalidate the address, if the
254				// accessed flags was set, since only then the entry could have
255				// been in any TLB.
256				InvalidatePage(start);
257			}
258		}
259	} while (start != 0 && start < end);
260
261	return B_OK;
262}
263
264
265status_t
266X86VMTranslationMap32Bit::DebugMarkRangePresent(addr_t start, addr_t end,
267	bool markPresent)
268{
269	start = ROUNDDOWN(start, B_PAGE_SIZE);
270	if (start >= end)
271		return B_OK;
272
273	page_directory_entry *pd = fPagingStructures->pgdir_virt;
274
275	do {
276		int index = VADDR_TO_PDENT(start);
277		if ((pd[index] & X86_PDE_PRESENT) == 0) {
278			// no page table here, move the start up to access the next page
279			// table
280			start = ROUNDUP(start + 1, kPageTableAlignment);
281			continue;
282		}
283
284		Thread* thread = thread_get_current_thread();
285		ThreadCPUPinner pinner(thread);
286
287		page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt(
288			pd[index] & X86_PDE_ADDRESS_MASK);
289
290		for (index = VADDR_TO_PTENT(start); (index < 1024) && (start < end);
291				index++, start += B_PAGE_SIZE) {
292			if ((pt[index] & X86_PTE_PRESENT) == 0) {
293				if (!markPresent)
294					continue;
295
296				X86PagingMethod32Bit::SetPageTableEntryFlags(&pt[index],
297					X86_PTE_PRESENT);
298			} else {
299				if (markPresent)
300					continue;
301
302				page_table_entry oldEntry
303					= X86PagingMethod32Bit::ClearPageTableEntryFlags(&pt[index],
304						X86_PTE_PRESENT);
305
306				if ((oldEntry & X86_PTE_ACCESSED) != 0) {
307					// Note, that we only need to invalidate the address, if the
308					// accessed flags was set, since only then the entry could
309					// have been in any TLB.
310					InvalidatePage(start);
311				}
312			}
313		}
314	} while (start != 0 && start < end);
315
316	return B_OK;
317}
318
319
320/*!	Caller must have locked the cache of the page to be unmapped.
321	This object shouldn't be locked.
322*/
323status_t
324X86VMTranslationMap32Bit::UnmapPage(VMArea* area, addr_t address,
325	bool updatePageQueue)
326{
327	ASSERT(address % B_PAGE_SIZE == 0);
328
329	page_directory_entry* pd = fPagingStructures->pgdir_virt;
330
331	TRACE("X86VMTranslationMap32Bit::UnmapPage(%#" B_PRIxADDR ")\n", address);
332
333	RecursiveLocker locker(fLock);
334
335	int index = VADDR_TO_PDENT(address);
336	if ((pd[index] & X86_PDE_PRESENT) == 0)
337		return B_ENTRY_NOT_FOUND;
338
339	ThreadCPUPinner pinner(thread_get_current_thread());
340
341	page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt(
342		pd[index] & X86_PDE_ADDRESS_MASK);
343
344	index = VADDR_TO_PTENT(address);
345	page_table_entry oldEntry = X86PagingMethod32Bit::ClearPageTableEntry(
346		&pt[index]);
347
348	pinner.Unlock();
349
350	if ((oldEntry & X86_PTE_PRESENT) == 0) {
351		// page mapping not valid
352		return B_ENTRY_NOT_FOUND;
353	}
354
355	fMapCount--;
356
357	if ((oldEntry & X86_PTE_ACCESSED) != 0) {
358		// Note, that we only need to invalidate the address, if the
359		// accessed flags was set, since only then the entry could have been
360		// in any TLB.
361		InvalidatePage(address);
362		Flush();
363
364		// NOTE: Between clearing the page table entry and Flush() other
365		// processors (actually even this processor with another thread of the
366		// same team) could still access the page in question via their cached
367		// entry. We can obviously lose a modified flag in this case, with the
368		// effect that the page looks unmodified (and might thus be recycled),
369		// but is actually modified.
370		// In most cases this is harmless, but for vm_remove_all_page_mappings()
371		// this is actually a problem.
372		// Interestingly FreeBSD seems to ignore this problem as well
373		// (cf. pmap_remove_all()), unless I've missed something.
374	}
375
376	locker.Detach();
377		// PageUnmapped() will unlock for us
378
379	PageUnmapped(area, (oldEntry & X86_PTE_ADDRESS_MASK) / B_PAGE_SIZE,
380		(oldEntry & X86_PTE_ACCESSED) != 0, (oldEntry & X86_PTE_DIRTY) != 0,
381		updatePageQueue);
382
383	return B_OK;
384}
385
386
387void
388X86VMTranslationMap32Bit::UnmapPages(VMArea* area, addr_t base, size_t size,
389	bool updatePageQueue)
390{
391	if (size == 0)
392		return;
393
394	addr_t start = base;
395	addr_t end = base + size - 1;
396
397	TRACE("X86VMTranslationMap32Bit::UnmapPages(%p, %#" B_PRIxADDR ", %#"
398		B_PRIxADDR ")\n", area, start, end);
399
400	page_directory_entry* pd = fPagingStructures->pgdir_virt;
401
402	VMAreaMappings queue;
403
404	RecursiveLocker locker(fLock);
405
406	do {
407		int index = VADDR_TO_PDENT(start);
408		if ((pd[index] & X86_PDE_PRESENT) == 0) {
409			// no page table here, move the start up to access the next page
410			// table
411			start = ROUNDUP(start + 1, kPageTableAlignment);
412			continue;
413		}
414
415		Thread* thread = thread_get_current_thread();
416		ThreadCPUPinner pinner(thread);
417
418		page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt(
419			pd[index] & X86_PDE_ADDRESS_MASK);
420
421		for (index = VADDR_TO_PTENT(start); (index < 1024) && (start < end);
422				index++, start += B_PAGE_SIZE) {
423			page_table_entry oldEntry
424				= X86PagingMethod32Bit::ClearPageTableEntry(&pt[index]);
425			if ((oldEntry & X86_PTE_PRESENT) == 0)
426				continue;
427
428			fMapCount--;
429
430			if ((oldEntry & X86_PTE_ACCESSED) != 0) {
431				// Note, that we only need to invalidate the address, if the
432				// accessed flags was set, since only then the entry could have
433				// been in any TLB.
434				InvalidatePage(start);
435			}
436
437			if (area->cache_type != CACHE_TYPE_DEVICE) {
438				// get the page
439				vm_page* page = vm_lookup_page(
440					(oldEntry & X86_PTE_ADDRESS_MASK) / B_PAGE_SIZE);
441				ASSERT(page != NULL);
442
443				DEBUG_PAGE_ACCESS_START(page);
444
445				// transfer the accessed/dirty flags to the page
446				if ((oldEntry & X86_PTE_ACCESSED) != 0)
447					page->accessed = true;
448				if ((oldEntry & X86_PTE_DIRTY) != 0)
449					page->modified = true;
450
451				// remove the mapping object/decrement the wired_count of the
452				// page
453				if (area->wiring == B_NO_LOCK) {
454					vm_page_mapping* mapping = NULL;
455					vm_page_mappings::Iterator iterator
456						= page->mappings.GetIterator();
457					while ((mapping = iterator.Next()) != NULL) {
458						if (mapping->area == area)
459							break;
460					}
461
462					ASSERT(mapping != NULL);
463
464					area->mappings.Remove(mapping);
465					page->mappings.Remove(mapping);
466					queue.Add(mapping);
467				} else
468					page->DecrementWiredCount();
469
470				if (!page->IsMapped()) {
471					atomic_add(&gMappedPagesCount, -1);
472
473					if (updatePageQueue) {
474						if (page->Cache()->temporary)
475							vm_page_set_state(page, PAGE_STATE_INACTIVE);
476						else if (page->modified)
477							vm_page_set_state(page, PAGE_STATE_MODIFIED);
478						else
479							vm_page_set_state(page, PAGE_STATE_CACHED);
480					}
481				}
482
483				DEBUG_PAGE_ACCESS_END(page);
484			}
485		}
486
487		Flush();
488			// flush explicitly, since we directly use the lock
489	} while (start != 0 && start < end);
490
491	// TODO: As in UnmapPage() we can lose page dirty flags here. ATM it's not
492	// really critical here, as in all cases this method is used, the unmapped
493	// area range is unmapped for good (resized/cut) and the pages will likely
494	// be freed.
495
496	locker.Unlock();
497
498	// free removed mappings
499	bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
500	uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
501		| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
502	while (vm_page_mapping* mapping = queue.RemoveHead())
503		object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
504}
505
506
507void
508X86VMTranslationMap32Bit::UnmapArea(VMArea* area, bool deletingAddressSpace,
509	bool ignoreTopCachePageFlags)
510{
511	if (area->cache_type == CACHE_TYPE_DEVICE || area->wiring != B_NO_LOCK) {
512		X86VMTranslationMap32Bit::UnmapPages(area, area->Base(), area->Size(),
513			true);
514		return;
515	}
516
517	bool unmapPages = !deletingAddressSpace || !ignoreTopCachePageFlags;
518
519	page_directory_entry* pd = fPagingStructures->pgdir_virt;
520
521	RecursiveLocker locker(fLock);
522
523	VMAreaMappings mappings;
524	mappings.MoveFrom(&area->mappings);
525
526	for (VMAreaMappings::Iterator it = mappings.GetIterator();
527			vm_page_mapping* mapping = it.Next();) {
528		vm_page* page = mapping->page;
529		page->mappings.Remove(mapping);
530
531		VMCache* cache = page->Cache();
532
533		bool pageFullyUnmapped = false;
534		if (!page->IsMapped()) {
535			atomic_add(&gMappedPagesCount, -1);
536			pageFullyUnmapped = true;
537		}
538
539		if (unmapPages || cache != area->cache) {
540			addr_t address = area->Base()
541				+ ((page->cache_offset * B_PAGE_SIZE) - area->cache_offset);
542
543			int index = VADDR_TO_PDENT(address);
544			if ((pd[index] & X86_PDE_PRESENT) == 0) {
545				panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
546					"has no page dir entry", page, area, address);
547				continue;
548			}
549
550			ThreadCPUPinner pinner(thread_get_current_thread());
551
552			page_table_entry* pt
553				= (page_table_entry*)fPageMapper->GetPageTableAt(
554					pd[index] & X86_PDE_ADDRESS_MASK);
555			page_table_entry oldEntry
556				= X86PagingMethod32Bit::ClearPageTableEntry(
557					&pt[VADDR_TO_PTENT(address)]);
558
559			pinner.Unlock();
560
561			if ((oldEntry & X86_PTE_PRESENT) == 0) {
562				panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
563					"has no page table entry", page, area, address);
564				continue;
565			}
566
567			// transfer the accessed/dirty flags to the page and invalidate
568			// the mapping, if necessary
569			if ((oldEntry & X86_PTE_ACCESSED) != 0) {
570				page->accessed = true;
571
572				if (!deletingAddressSpace)
573					InvalidatePage(address);
574			}
575
576			if ((oldEntry & X86_PTE_DIRTY) != 0)
577				page->modified = true;
578
579			if (pageFullyUnmapped) {
580				DEBUG_PAGE_ACCESS_START(page);
581
582				if (cache->temporary)
583					vm_page_set_state(page, PAGE_STATE_INACTIVE);
584				else if (page->modified)
585					vm_page_set_state(page, PAGE_STATE_MODIFIED);
586				else
587					vm_page_set_state(page, PAGE_STATE_CACHED);
588
589				DEBUG_PAGE_ACCESS_END(page);
590			}
591		}
592
593		fMapCount--;
594	}
595
596	Flush();
597		// flush explicitely, since we directly use the lock
598
599	locker.Unlock();
600
601	bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
602	uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
603		| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
604	while (vm_page_mapping* mapping = mappings.RemoveHead())
605		object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
606}
607
608
609status_t
610X86VMTranslationMap32Bit::Query(addr_t va, phys_addr_t *_physical,
611	uint32 *_flags)
612{
613	// default the flags to not present
614	*_flags = 0;
615	*_physical = 0;
616
617	int index = VADDR_TO_PDENT(va);
618	page_directory_entry *pd = fPagingStructures->pgdir_virt;
619	if ((pd[index] & X86_PDE_PRESENT) == 0) {
620		// no pagetable here
621		return B_OK;
622	}
623
624	Thread* thread = thread_get_current_thread();
625	ThreadCPUPinner pinner(thread);
626
627	page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt(
628		pd[index] & X86_PDE_ADDRESS_MASK);
629	page_table_entry entry = pt[VADDR_TO_PTENT(va)];
630
631	*_physical = entry & X86_PDE_ADDRESS_MASK;
632
633	// read in the page state flags
634	if ((entry & X86_PTE_USER) != 0) {
635		*_flags |= ((entry & X86_PTE_WRITABLE) != 0 ? B_WRITE_AREA : 0)
636			| B_READ_AREA;
637	}
638
639	*_flags |= ((entry & X86_PTE_WRITABLE) != 0 ? B_KERNEL_WRITE_AREA : 0)
640		| B_KERNEL_READ_AREA
641		| ((entry & X86_PTE_DIRTY) != 0 ? PAGE_MODIFIED : 0)
642		| ((entry & X86_PTE_ACCESSED) != 0 ? PAGE_ACCESSED : 0)
643		| ((entry & X86_PTE_PRESENT) != 0 ? PAGE_PRESENT : 0);
644
645	pinner.Unlock();
646
647	TRACE("query_tmap: returning pa 0x%lx for va 0x%lx\n", *_physical, va);
648
649	return B_OK;
650}
651
652
653status_t
654X86VMTranslationMap32Bit::QueryInterrupt(addr_t va, phys_addr_t *_physical,
655	uint32 *_flags)
656{
657	*_flags = 0;
658	*_physical = 0;
659
660	int index = VADDR_TO_PDENT(va);
661	page_directory_entry* pd = fPagingStructures->pgdir_virt;
662	if ((pd[index] & X86_PDE_PRESENT) == 0) {
663		// no pagetable here
664		return B_OK;
665	}
666
667	// map page table entry
668	page_table_entry* pt = (page_table_entry*)X86PagingMethod32Bit::Method()
669		->PhysicalPageMapper()->InterruptGetPageTableAt(
670			pd[index] & X86_PDE_ADDRESS_MASK);
671	page_table_entry entry = pt[VADDR_TO_PTENT(va)];
672
673	*_physical = entry & X86_PDE_ADDRESS_MASK;
674
675	// read in the page state flags
676	if ((entry & X86_PTE_USER) != 0) {
677		*_flags |= ((entry & X86_PTE_WRITABLE) != 0 ? B_WRITE_AREA : 0)
678			| B_READ_AREA;
679	}
680
681	*_flags |= ((entry & X86_PTE_WRITABLE) != 0 ? B_KERNEL_WRITE_AREA : 0)
682		| B_KERNEL_READ_AREA
683		| ((entry & X86_PTE_DIRTY) != 0 ? PAGE_MODIFIED : 0)
684		| ((entry & X86_PTE_ACCESSED) != 0 ? PAGE_ACCESSED : 0)
685		| ((entry & X86_PTE_PRESENT) != 0 ? PAGE_PRESENT : 0);
686
687	return B_OK;
688}
689
690
691status_t
692X86VMTranslationMap32Bit::Protect(addr_t start, addr_t end, uint32 attributes,
693	uint32 memoryType)
694{
695	start = ROUNDDOWN(start, B_PAGE_SIZE);
696	if (start >= end)
697		return B_OK;
698
699	TRACE("protect_tmap: pages 0x%lx to 0x%lx, attributes %lx\n", start, end,
700		attributes);
701
702	// compute protection flags
703	uint32 newProtectionFlags = 0;
704	if ((attributes & B_USER_PROTECTION) != 0) {
705		newProtectionFlags = X86_PTE_USER;
706		if ((attributes & B_WRITE_AREA) != 0)
707			newProtectionFlags |= X86_PTE_WRITABLE;
708	} else if ((attributes & B_KERNEL_WRITE_AREA) != 0)
709		newProtectionFlags = X86_PTE_WRITABLE;
710
711	page_directory_entry *pd = fPagingStructures->pgdir_virt;
712
713	do {
714		int index = VADDR_TO_PDENT(start);
715		if ((pd[index] & X86_PDE_PRESENT) == 0) {
716			// no page table here, move the start up to access the next page
717			// table
718			start = ROUNDUP(start + 1, kPageTableAlignment);
719			continue;
720		}
721
722		Thread* thread = thread_get_current_thread();
723		ThreadCPUPinner pinner(thread);
724
725		page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt(
726			pd[index] & X86_PDE_ADDRESS_MASK);
727
728		for (index = VADDR_TO_PTENT(start); index < 1024 && start < end;
729				index++, start += B_PAGE_SIZE) {
730			page_table_entry entry = pt[index];
731			if ((entry & X86_PTE_PRESENT) == 0) {
732				// page mapping not valid
733				continue;
734			}
735
736			TRACE("protect_tmap: protect page 0x%lx\n", start);
737
738			// set the new protection flags -- we want to do that atomically,
739			// without changing the accessed or dirty flag
740			page_table_entry oldEntry;
741			while (true) {
742				oldEntry = X86PagingMethod32Bit::TestAndSetPageTableEntry(
743					&pt[index],
744					(entry & ~(X86_PTE_PROTECTION_MASK
745							| X86_PTE_MEMORY_TYPE_MASK))
746						| newProtectionFlags
747						| X86PagingMethod32Bit::MemoryTypeToPageTableEntryFlags(
748							memoryType),
749					entry);
750				if (oldEntry == entry)
751					break;
752				entry = oldEntry;
753			}
754
755			if ((oldEntry & X86_PTE_ACCESSED) != 0) {
756				// Note, that we only need to invalidate the address, if the
757				// accessed flag was set, since only then the entry could have
758				// been in any TLB.
759				InvalidatePage(start);
760			}
761		}
762	} while (start != 0 && start < end);
763
764	return B_OK;
765}
766
767
768status_t
769X86VMTranslationMap32Bit::ClearFlags(addr_t va, uint32 flags)
770{
771	int index = VADDR_TO_PDENT(va);
772	page_directory_entry* pd = fPagingStructures->pgdir_virt;
773	if ((pd[index] & X86_PDE_PRESENT) == 0) {
774		// no pagetable here
775		return B_OK;
776	}
777
778	uint32 flagsToClear = ((flags & PAGE_MODIFIED) ? X86_PTE_DIRTY : 0)
779		| ((flags & PAGE_ACCESSED) ? X86_PTE_ACCESSED : 0);
780
781	Thread* thread = thread_get_current_thread();
782	ThreadCPUPinner pinner(thread);
783
784	page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt(
785		pd[index] & X86_PDE_ADDRESS_MASK);
786	index = VADDR_TO_PTENT(va);
787
788	// clear out the flags we've been requested to clear
789	page_table_entry oldEntry
790		= X86PagingMethod32Bit::ClearPageTableEntryFlags(&pt[index],
791			flagsToClear);
792
793	pinner.Unlock();
794
795	if ((oldEntry & flagsToClear) != 0)
796		InvalidatePage(va);
797
798	return B_OK;
799}
800
801
802bool
803X86VMTranslationMap32Bit::ClearAccessedAndModified(VMArea* area, addr_t address,
804	bool unmapIfUnaccessed, bool& _modified)
805{
806	ASSERT(address % B_PAGE_SIZE == 0);
807
808	page_directory_entry* pd = fPagingStructures->pgdir_virt;
809
810	TRACE("X86VMTranslationMap32Bit::ClearAccessedAndModified(%#" B_PRIxADDR
811		")\n", address);
812
813	RecursiveLocker locker(fLock);
814
815	int index = VADDR_TO_PDENT(address);
816	if ((pd[index] & X86_PDE_PRESENT) == 0)
817		return false;
818
819	ThreadCPUPinner pinner(thread_get_current_thread());
820
821	page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt(
822		pd[index] & X86_PDE_ADDRESS_MASK);
823
824	index = VADDR_TO_PTENT(address);
825
826	// perform the deed
827	page_table_entry oldEntry;
828
829	if (unmapIfUnaccessed) {
830		while (true) {
831			oldEntry = pt[index];
832			if ((oldEntry & X86_PTE_PRESENT) == 0) {
833				// page mapping not valid
834				return false;
835			}
836
837			if (oldEntry & X86_PTE_ACCESSED) {
838				// page was accessed -- just clear the flags
839				oldEntry = X86PagingMethod32Bit::ClearPageTableEntryFlags(
840					&pt[index], X86_PTE_ACCESSED | X86_PTE_DIRTY);
841				break;
842			}
843
844			// page hasn't been accessed -- unmap it
845			if (X86PagingMethod32Bit::TestAndSetPageTableEntry(&pt[index], 0,
846					oldEntry) == oldEntry) {
847				break;
848			}
849
850			// something changed -- check again
851		}
852	} else {
853		oldEntry = X86PagingMethod32Bit::ClearPageTableEntryFlags(&pt[index],
854			X86_PTE_ACCESSED | X86_PTE_DIRTY);
855	}
856
857	pinner.Unlock();
858
859	_modified = (oldEntry & X86_PTE_DIRTY) != 0;
860
861	if ((oldEntry & X86_PTE_ACCESSED) != 0) {
862		// Note, that we only need to invalidate the address, if the
863		// accessed flags was set, since only then the entry could have been
864		// in any TLB.
865		InvalidatePage(address);
866
867		Flush();
868
869		return true;
870	}
871
872	if (!unmapIfUnaccessed)
873		return false;
874
875	// We have unmapped the address. Do the "high level" stuff.
876
877	fMapCount--;
878
879	locker.Detach();
880		// UnaccessedPageUnmapped() will unlock for us
881
882	UnaccessedPageUnmapped(area,
883		(oldEntry & X86_PTE_ADDRESS_MASK) / B_PAGE_SIZE);
884
885	return false;
886}
887
888
889X86PagingStructures*
890X86VMTranslationMap32Bit::PagingStructures() const
891{
892	return fPagingStructures;
893}
894