1/*
2 * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
4 * Distributed under the terms of the MIT License.
5 *
6 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
7 * Distributed under the terms of the NewOS License.
8 */
9
10
11#include "paging/040/M68KVMTranslationMap040.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/queue.h>
22#include <vm/vm_page.h>
23#include <vm/vm_priv.h>
24#include <vm/VMAddressSpace.h>
25#include <vm/VMCache.h>
26
27#include "paging/040/M68KPagingMethod040.h"
28#include "paging/040/M68KPagingStructures040.h"
29#include "paging/m68k_physical_page_mapper.h"
30
31
32#define TRACE_M68K_VM_TRANSLATION_MAP_040
33#ifdef TRACE_M68K_VM_TRANSLATION_MAP_040
34#	define TRACE(x...) dprintf(x)
35#else
36#	define TRACE(x...) ;
37#endif
38
39
40M68KVMTranslationMap040::M68KVMTranslationMap040()
41	:
42	fPagingStructures(NULL)
43{
44}
45
46
47M68KVMTranslationMap040::~M68KVMTranslationMap040()
48{
49	if (fPagingStructures == NULL)
50		return;
51
52	if (fPageMapper != NULL)
53		fPageMapper->Delete();
54
55	if (fPagingStructures->pgroot_virt != NULL) {
56		page_root_entry *pgroot_virt = fPagingStructures->pgroot_virt;
57
58		// cycle through and free all of the user space pgdirs & pgtables
59		// since the size of tables don't match B_PAGE_SIZE,
60		// we alloc several at once, based on modulos,
61		// we make sure they are either all in the tree or none.
62		for (uint32 i = VADDR_TO_PRENT(USER_BASE);
63				i <= VADDR_TO_PRENT(USER_BASE + (USER_SIZE - 1)); i++) {
64			addr_t pgdir_pn;
65			page_directory_entry *pgdir;
66			vm_page *dirpage;
67
68			if (PRE_TYPE(pgroot_virt[i]) == DT_INVALID)
69				continue;
70			if (PRE_TYPE(pgroot_virt[i]) != DT_ROOT) {
71				panic("rtdir[%ld]: buggy descriptor type", i);
72				return;
73			}
74			// XXX:suboptimal (done 8 times)
75			pgdir_pn = PRE_TO_PN(pgroot_virt[i]);
76			dirpage = vm_lookup_page(pgdir_pn);
77			pgdir = &(((page_directory_entry *)dirpage)[i%NUM_DIRTBL_PER_PAGE]);
78
79			for (uint32 j = 0; j <= NUM_DIRENT_PER_TBL;
80					j+=NUM_PAGETBL_PER_PAGE) {
81				addr_t pgtbl_pn;
82				page_table_entry *pgtbl;
83				vm_page *page;
84				if (PDE_TYPE(pgdir[j]) == DT_INVALID)
85					continue;
86				if (PDE_TYPE(pgdir[j]) != DT_DIR) {
87					panic("pgroot[%ld][%ld]: buggy descriptor type", i, j);
88					return;
89				}
90				pgtbl_pn = PDE_TO_PN(pgdir[j]);
91				page = vm_lookup_page(pgtbl_pn);
92				pgtbl = (page_table_entry *)page;
93
94				if (!page) {
95					panic("destroy_tmap: didn't find pgtable page\n");
96					return;
97				}
98				DEBUG_PAGE_ACCESS_START(page);
99				vm_page_set_state(page, PAGE_STATE_FREE);
100			}
101			if (((i + 1) % NUM_DIRTBL_PER_PAGE) == 0) {
102				DEBUG_PAGE_ACCESS_END(dirpage);
103				vm_page_set_state(dirpage, PAGE_STATE_FREE);
104			}
105		}
106
107
108
109#if 0
110//X86
111		for (uint32 i = VADDR_TO_PDENT(USER_BASE);
112				i <= VADDR_TO_PDENT(USER_BASE + (USER_SIZE - 1)); i++) {
113			if ((fPagingStructures->pgdir_virt[i] & M68K_PDE_PRESENT) != 0) {
114				addr_t address = fPagingStructures->pgdir_virt[i]
115					& M68K_PDE_ADDRESS_MASK;
116				vm_page* page = vm_lookup_page(address / B_PAGE_SIZE);
117				if (!page)
118					panic("destroy_tmap: didn't find pgtable page\n");
119				DEBUG_PAGE_ACCESS_START(page);
120				vm_page_set_state(page, PAGE_STATE_FREE);
121			}
122		}
123#endif
124	}
125
126	fPagingStructures->RemoveReference();
127}
128
129
130status_t
131M68KVMTranslationMap040::Init(bool kernel)
132{
133	TRACE("M68KVMTranslationMap040::Init()\n");
134
135	M68KVMTranslationMap::Init(kernel);
136
137	fPagingStructures = new(std::nothrow) M68KPagingStructures040;
138	if (fPagingStructures == NULL)
139		return B_NO_MEMORY;
140
141	M68KPagingMethod040* method = M68KPagingMethod040::Method();
142
143	if (!kernel) {
144		// user
145		// allocate a physical page mapper
146		status_t error = method->PhysicalPageMapper()
147			->CreateTranslationMapPhysicalPageMapper(&fPageMapper);
148		if (error != B_OK)
149			return error;
150
151		// allocate the page root
152		page_root_entry* virtualPageRoot = (page_root_entry*)memalign(
153			SIZ_ROOTTBL, SIZ_ROOTTBL);
154		if (virtualPageRoot == NULL)
155			return B_NO_MEMORY;
156
157		// look up the page directory's physical address
158		phys_addr_t physicalPageRoot;
159		vm_get_page_mapping(VMAddressSpace::KernelID(),
160			(addr_t)virtualPageRoot, &physicalPageRoot);
161
162		fPagingStructures->Init(virtualPageRoot, physicalPageRoot,
163			method->KernelVirtualPageRoot());
164	} else {
165		// kernel
166		// get the physical page mapper
167		fPageMapper = method->KernelPhysicalPageMapper();
168
169		// we already know the kernel pgdir mapping
170		fPagingStructures->Init(method->KernelVirtualPageRoot(),
171			method->KernelPhysicalPageRoot(), NULL);
172	}
173
174	return B_OK;
175}
176
177
178size_t
179M68KVMTranslationMap040::MaxPagesNeededToMap(addr_t start, addr_t end) const
180{
181	size_t need;
182	size_t pgdirs;
183
184	// If start == 0, the actual base address is not yet known to the caller and
185	// we shall assume the worst case.
186	if (start == 0) {
187		// offset the range so it has the worst possible alignment
188#warning M68K: FIXME?
189		start = 1023 * B_PAGE_SIZE;
190		end += 1023 * B_PAGE_SIZE;
191	}
192
193	pgdirs = VADDR_TO_PRENT(end) + 1 - VADDR_TO_PRENT(start);
194	// how much for page directories
195	need = (pgdirs + NUM_DIRTBL_PER_PAGE - 1) / NUM_DIRTBL_PER_PAGE;
196	// and page tables themselves
197	need = ((pgdirs * NUM_DIRENT_PER_TBL) + NUM_PAGETBL_PER_PAGE - 1) / NUM_PAGETBL_PER_PAGE;
198
199	// better rounding when only 1 pgdir
200	// XXX: do better for other cases
201	if (pgdirs == 1) {
202		need = 1;
203		need += (VADDR_TO_PDENT(end) + 1 - VADDR_TO_PDENT(start) + NUM_PAGETBL_PER_PAGE - 1) / NUM_PAGETBL_PER_PAGE;
204	}
205
206	return need;
207}
208
209
210status_t
211M68KVMTranslationMap040::Map(addr_t va, phys_addr_t pa, uint32 attributes,
212	uint32 memoryType, vm_page_reservation* reservation)
213{
214	TRACE("M68KVMTranslationMap040::Map: entry pa 0x%lx va 0x%lx\n", pa, va);
215
216/*
217	dprintf("pgdir at 0x%x\n", pgdir);
218	dprintf("index is %d\n", va / B_PAGE_SIZE / 1024);
219	dprintf("final at 0x%x\n", &pgdir[va / B_PAGE_SIZE / 1024]);
220	dprintf("value is 0x%x\n", *(int *)&pgdir[va / B_PAGE_SIZE / 1024]);
221	dprintf("present bit is %d\n", pgdir[va / B_PAGE_SIZE / 1024].present);
222	dprintf("addr is %d\n", pgdir[va / B_PAGE_SIZE / 1024].addr);
223*/
224	page_root_entry *pr = fPagingStructures->pgroot_virt;
225	page_directory_entry *pd;
226	page_table_entry *pt;
227	addr_t pd_pg, pt_pg;
228	uint32 rindex, dindex, pindex;
229
230
231	// check to see if a page directory exists for this range
232	rindex = VADDR_TO_PRENT(va);
233	if (PRE_TYPE(pr[rindex]) != DT_ROOT) {
234		phys_addr_t pgdir;
235		vm_page *page;
236		uint32 i;
237
238		// we need to allocate a pgdir group
239		page = vm_page_allocate_page(reservation,
240			PAGE_STATE_WIRED | VM_PAGE_ALLOC_CLEAR);
241
242		DEBUG_PAGE_ACCESS_END(page);
243
244		pgdir = (phys_addr_t)page->physical_page_number * B_PAGE_SIZE;
245
246		TRACE("::Map: asked for free page for pgdir. 0x%lx\n", pgdir);
247
248		// for each pgdir on the allocated page:
249		for (i = 0; i < NUM_DIRTBL_PER_PAGE; i++) {
250			uint32 aindex = rindex & ~(NUM_DIRTBL_PER_PAGE-1); /* aligned */
251			page_root_entry *apr = &pr[aindex + i];
252
253			// put in the pgroot
254			M68KPagingMethod040::PutPageDirInPageRoot(apr, pgdir, attributes
255				| ((attributes & B_USER_PROTECTION) != 0
256						? B_WRITE_AREA : B_KERNEL_WRITE_AREA));
257
258			// update any other page roots, if it maps kernel space
259			//XXX: suboptimal, should batch them
260			if ((aindex+i) >= FIRST_KERNEL_PGDIR_ENT && (aindex+i)
261					< (FIRST_KERNEL_PGDIR_ENT + NUM_KERNEL_PGDIR_ENTS))
262				M68KPagingStructures040::UpdateAllPageDirs((aindex+i),
263					pr[aindex+i]);
264
265			pgdir += SIZ_DIRTBL;
266		}
267		fMapCount++;
268	}
269	// now, fill in the pentry
270	//XXX: is this required?
271	Thread* thread = thread_get_current_thread();
272	ThreadCPUPinner pinner(thread);
273
274	pd = (page_directory_entry*)MapperGetPageTableAt(
275		PRE_TO_PA(pr[rindex]));
276
277	//pinner.Unlock();
278
279	// we want the table at rindex, not at rindex%(tbl/page)
280	//pd += (rindex % NUM_DIRTBL_PER_PAGE) * NUM_DIRENT_PER_TBL;
281
282	// check to see if a page table exists for this range
283	dindex = VADDR_TO_PDENT(va);
284	if (PDE_TYPE(pd[dindex]) != DT_DIR) {
285		phys_addr_t pgtable;
286		vm_page *page;
287		uint32 i;
288
289		// we need to allocate a pgtable group
290		page = vm_page_allocate_page(reservation,
291			PAGE_STATE_WIRED | VM_PAGE_ALLOC_CLEAR);
292
293		DEBUG_PAGE_ACCESS_END(page);
294
295		pgtable = (phys_addr_t)page->physical_page_number * B_PAGE_SIZE;
296
297		TRACE("::Map: asked for free page for pgtable. 0x%lx\n", pgtable);
298
299		// for each pgtable on the allocated page:
300		for (i = 0; i < NUM_PAGETBL_PER_PAGE; i++) {
301			uint32 aindex = dindex & ~(NUM_PAGETBL_PER_PAGE-1); /* aligned */
302			page_directory_entry *apd = &pd[aindex + i];
303
304			// put in the pgdir
305			M68KPagingMethod040::PutPageTableInPageDir(apd, pgtable, attributes
306				| ((attributes & B_USER_PROTECTION) != 0
307						? B_WRITE_AREA : B_KERNEL_WRITE_AREA));
308
309			// no need to update other page directories for kernel space;
310			// the root-level already point to us.
311
312			pgtable += SIZ_PAGETBL;
313		}
314
315#warning M68K: really mean map_count++ ??
316		fMapCount++;
317	}
318
319	// now, fill in the pentry
320	//ThreadCPUPinner pinner(thread);
321
322	pt = (page_table_entry*)MapperGetPageTableAt(PDE_TO_PA(pd[dindex]));
323	// we want the table at rindex, not at rindex%(tbl/page)
324	//pt += (dindex % NUM_PAGETBL_PER_PAGE) * NUM_PAGEENT_PER_TBL;
325
326	pindex = VADDR_TO_PTENT(va);
327
328	ASSERT_PRINT((PTE_TYPE(pt[pindex]) != DT_INVALID) == 0,
329		"virtual address: %#" B_PRIxADDR ", existing pte: %#" B_PRIx32, va,
330		pt[pindex]);
331
332	M68KPagingMethod040::PutPageTableEntryInTable(&pt[pindex], pa, attributes,
333		memoryType, fIsKernelMap);
334
335	pinner.Unlock();
336
337	// Note: We don't need to invalidate the TLB for this address, as previously
338	// the entry was not present and the TLB doesn't cache those entries.
339
340	fMapCount++;
341
342	return B_OK;
343}
344
345
346status_t
347M68KVMTranslationMap040::Unmap(addr_t start, addr_t end)
348{
349	start = ROUNDDOWN(start, B_PAGE_SIZE);
350	if (start >= end)
351		return B_OK;
352
353	TRACE("M68KVMTranslationMap040::Unmap: asked to free pages 0x%lx to 0x%lx\n", start, end);
354
355	page_root_entry *pr = fPagingStructures->pgroot_virt;
356	page_directory_entry *pd;
357	page_table_entry *pt;
358	int index;
359
360	do {
361		index = VADDR_TO_PRENT(start);
362		if (PRE_TYPE(pr[index]) != DT_ROOT) {
363			// no pagedir here, move the start up to access the next page
364			// dir group
365			start = ROUNDUP(start + 1, kPageDirAlignment);
366			continue;
367		}
368
369		Thread* thread = thread_get_current_thread();
370		ThreadCPUPinner pinner(thread);
371
372		pd = (page_directory_entry*)MapperGetPageTableAt(
373			PRE_TO_PA(pr[index]));
374		// we want the table at rindex, not at rindex%(tbl/page)
375		//pd += (index % NUM_DIRTBL_PER_PAGE) * NUM_DIRENT_PER_TBL;
376
377
378		index = VADDR_TO_PDENT(start);
379		if (PDE_TYPE(pd[index]) != DT_DIR) {
380			// no pagedir here, move the start up to access the next page
381			// table group
382			start = ROUNDUP(start + 1, kPageTableAlignment);
383			continue;
384		}
385
386		pt = (page_table_entry*)MapperGetPageTableAt(
387			PDE_TO_PA(pd[index]));
388		// we want the table at rindex, not at rindex%(tbl/page)
389		//pt += (index % NUM_PAGETBL_PER_PAGE) * NUM_PAGEENT_PER_TBL;
390
391		for (index = VADDR_TO_PTENT(start);
392				(index < NUM_PAGEENT_PER_TBL) && (start < end);
393				index++, start += B_PAGE_SIZE) {
394			if (PTE_TYPE(pt[index]) != DT_PAGE
395				&& PTE_TYPE(pt[index]) != DT_INDIRECT) {
396				// page mapping not valid
397				continue;
398			}
399
400			TRACE("::Unmap: removing page 0x%lx\n", start);
401
402			page_table_entry oldEntry
403				= M68KPagingMethod040::ClearPageTableEntry(&pt[index]);
404			fMapCount--;
405
406			if ((oldEntry & M68K_PTE_ACCESSED) != 0) {
407				// Note, that we only need to invalidate the address, if the
408				// accessed flags was set, since only then the entry could have
409				// been in any TLB.
410				InvalidatePage(start);
411			}
412		}
413	} while (start != 0 && start < end);
414
415	return B_OK;
416}
417
418
419/*!	Caller must have locked the cache of the page to be unmapped.
420	This object shouldn't be locked.
421*/
422status_t
423M68KVMTranslationMap040::UnmapPage(VMArea* area, addr_t address,
424	bool updatePageQueue)
425{
426	ASSERT(address % B_PAGE_SIZE == 0);
427
428	page_root_entry* pr = fPagingStructures->pgroot_virt;
429
430	TRACE("M68KVMTranslationMap040::UnmapPage(%#" B_PRIxADDR ")\n", address);
431
432	RecursiveLocker locker(fLock);
433
434	int index;
435
436	index = VADDR_TO_PRENT(address);
437	if (PRE_TYPE(pr[index]) == DT_ROOT)
438		return B_ENTRY_NOT_FOUND;
439
440	ThreadCPUPinner pinner(thread_get_current_thread());
441
442	page_table_entry* pd = (page_table_entry*)MapperGetPageTableAt(
443		pr[index] & M68K_PRE_ADDRESS_MASK);
444
445	index = VADDR_TO_PDENT(address);
446	if (PDE_TYPE(pd[index]) == DT_DIR)
447		return B_ENTRY_NOT_FOUND;
448
449	page_table_entry* pt = (page_table_entry*)MapperGetPageTableAt(
450		pd[index] & M68K_PDE_ADDRESS_MASK);
451
452	index = VADDR_TO_PTENT(address);
453	if (PTE_TYPE(pt[index]) == DT_INDIRECT) {
454		phys_addr_t indirectAddress = PIE_TO_TA(pt[index]);
455		pt = (page_table_entry*)MapperGetPageTableAt(
456			PIE_TO_TA(pt[index]), true);
457		index = 0; // single descriptor
458	}
459
460	page_table_entry oldEntry = M68KPagingMethod040::ClearPageTableEntry(
461		&pt[index]);
462
463	pinner.Unlock();
464
465	if (PTE_TYPE(oldEntry) != DT_PAGE) {
466		// page mapping not valid
467		return B_ENTRY_NOT_FOUND;
468	}
469
470	fMapCount--;
471
472	if ((oldEntry & M68K_PTE_ACCESSED) != 0) {
473		// Note, that we only need to invalidate the address, if the
474		// accessed flags was set, since only then the entry could have been
475		// in any TLB.
476		InvalidatePage(address);
477		Flush();
478
479		// NOTE: Between clearing the page table entry and Flush() other
480		// processors (actually even this processor with another thread of the
481		// same team) could still access the page in question via their cached
482		// entry. We can obviously lose a modified flag in this case, with the
483		// effect that the page looks unmodified (and might thus be recycled),
484		// but is actually modified.
485		// In most cases this is harmless, but for vm_remove_all_page_mappings()
486		// this is actually a problem.
487		// Interestingly FreeBSD seems to ignore this problem as well
488		// (cf. pmap_remove_all()), unless I've missed something.
489	}
490
491	locker.Detach();
492		// PageUnmapped() will unlock for us
493
494	PageUnmapped(area, (oldEntry & M68K_PTE_ADDRESS_MASK) / B_PAGE_SIZE,
495		(oldEntry & M68K_PTE_ACCESSED) != 0, (oldEntry & M68K_PTE_DIRTY) != 0,
496		updatePageQueue);
497
498	return B_OK;
499}
500
501
502void
503M68KVMTranslationMap040::UnmapPages(VMArea* area, addr_t base, size_t size,
504	bool updatePageQueue)
505{
506	int index;
507
508	if (size == 0)
509		return;
510
511	addr_t start = base;
512	addr_t end = base + size - 1;
513
514	TRACE("M68KVMTranslationMap040::UnmapPages(%p, %#" B_PRIxADDR ", %#"
515		B_PRIxADDR ")\n", area, start, end);
516
517	page_root_entry* pr = fPagingStructures->pgroot_virt;
518
519	VMAreaMappings queue;
520
521	RecursiveLocker locker(fLock);
522
523	do {
524		index = VADDR_TO_PRENT(start);
525		if (PRE_TYPE(pr[index]) != DT_ROOT) {
526			// no page table here, move the start up to access the next page
527			// table
528			start = ROUNDUP(start + 1, kPageDirAlignment);
529			continue;
530		}
531
532		Thread* thread = thread_get_current_thread();
533		ThreadCPUPinner pinner(thread);
534
535		page_table_entry* pd = (page_directory_entry*)MapperGetPageTableAt(
536			pr[index] & M68K_PRE_ADDRESS_MASK);
537
538		index = VADDR_TO_PDENT(start);
539		if (PDE_TYPE(pd[index]) != DT_DIR) {
540			// no page table here, move the start up to access the next page
541			// table
542			start = ROUNDUP(start + 1, kPageTableAlignment);
543			continue;
544		}
545
546		page_table_entry* pt = (page_table_entry*)MapperGetPageTableAt(
547			pd[index] & M68K_PDE_ADDRESS_MASK);
548
549		for (index = VADDR_TO_PTENT(start); (index < 1024) && (start < end);
550				index++, start += B_PAGE_SIZE) {
551			page_table_entry *e = &pt[index];
552			// fetch indirect descriptor
553			//XXX:clear the indirect descriptor too??
554			if (PTE_TYPE(pt[index]) == DT_INDIRECT) {
555				phys_addr_t indirectAddress = PIE_TO_TA(pt[index]);
556				e = (page_table_entry*)MapperGetPageTableAt(
557					PIE_TO_TA(pt[index]));
558			}
559
560			page_table_entry oldEntry
561				= M68KPagingMethod040::ClearPageTableEntry(e);
562			if (PTE_TYPE(oldEntry) != DT_PAGE)
563				continue;
564
565			fMapCount--;
566
567			if ((oldEntry & M68K_PTE_ACCESSED) != 0) {
568				// Note, that we only need to invalidate the address, if the
569				// accessed flags was set, since only then the entry could have
570				// been in any TLB.
571				InvalidatePage(start);
572			}
573
574			if (area->cache_type != CACHE_TYPE_DEVICE) {
575				// get the page
576				vm_page* page = vm_lookup_page(
577					(oldEntry & M68K_PTE_ADDRESS_MASK) / B_PAGE_SIZE);
578				ASSERT(page != NULL);
579
580				DEBUG_PAGE_ACCESS_START(page);
581
582				// transfer the accessed/dirty flags to the page
583				if ((oldEntry & M68K_PTE_ACCESSED) != 0)
584					page->accessed = true;
585				if ((oldEntry & M68K_PTE_DIRTY) != 0)
586					page->modified = true;
587
588				// remove the mapping object/decrement the wired_count of the
589				// page
590				if (area->wiring == B_NO_LOCK) {
591					vm_page_mapping* mapping = NULL;
592					vm_page_mappings::Iterator iterator
593						= page->mappings.GetIterator();
594					while ((mapping = iterator.Next()) != NULL) {
595						if (mapping->area == area)
596							break;
597					}
598
599					ASSERT(mapping != NULL);
600
601					area->mappings.Remove(mapping);
602					page->mappings.Remove(mapping);
603					queue.Add(mapping);
604				} else
605					page->DecrementWiredCount();
606
607				if (!page->IsMapped()) {
608					atomic_add(&gMappedPagesCount, -1);
609
610					if (updatePageQueue) {
611						if (page->Cache()->temporary)
612							vm_page_set_state(page, PAGE_STATE_INACTIVE);
613						else if (page->modified)
614							vm_page_set_state(page, PAGE_STATE_MODIFIED);
615						else
616							vm_page_set_state(page, PAGE_STATE_CACHED);
617					}
618				}
619
620				DEBUG_PAGE_ACCESS_END(page);
621			}
622		}
623
624		Flush();
625			// flush explicitly, since we directly use the lock
626	} while (start != 0 && start < end);
627
628	// TODO: As in UnmapPage() we can lose page dirty flags here. ATM it's not
629	// really critical here, as in all cases this method is used, the unmapped
630	// area range is unmapped for good (resized/cut) and the pages will likely
631	// be freed.
632
633	locker.Unlock();
634
635	// free removed mappings
636	bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
637	uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
638		| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
639	while (vm_page_mapping* mapping = queue.RemoveHead())
640		object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
641}
642
643
644void
645M68KVMTranslationMap040::UnmapArea(VMArea* area, bool deletingAddressSpace,
646	bool ignoreTopCachePageFlags)
647{
648	if (area->cache_type == CACHE_TYPE_DEVICE || area->wiring != B_NO_LOCK) {
649		M68KVMTranslationMap040::UnmapPages(area, area->Base(), area->Size(),
650			true);
651		return;
652	}
653
654	bool unmapPages = !deletingAddressSpace || !ignoreTopCachePageFlags;
655
656	page_root_entry* pr = fPagingStructures->pgroot_virt;
657
658	RecursiveLocker locker(fLock);
659
660	VMAreaMappings mappings;
661	mappings.MoveFrom(&area->mappings);
662
663	for (VMAreaMappings::Iterator it = mappings.GetIterator();
664			vm_page_mapping* mapping = it.Next();) {
665		vm_page* page = mapping->page;
666		page->mappings.Remove(mapping);
667
668		VMCache* cache = page->Cache();
669
670		bool pageFullyUnmapped = false;
671		if (!page->IsMapped()) {
672			atomic_add(&gMappedPagesCount, -1);
673			pageFullyUnmapped = true;
674		}
675
676		if (unmapPages || cache != area->cache) {
677			addr_t address = area->Base()
678				+ ((page->cache_offset * B_PAGE_SIZE) - area->cache_offset);
679
680			int index;
681			index = VADDR_TO_PRENT(address);
682			if (PRE_TYPE(pr[index]) != DT_ROOT) {
683				panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
684					"has no page root entry", page, area, address);
685				continue;
686			}
687
688			ThreadCPUPinner pinner(thread_get_current_thread());
689
690			page_directory_entry* pd
691				= (page_directory_entry*)MapperGetPageTableAt(
692					pr[index] & M68K_PRE_ADDRESS_MASK);
693
694			index = VADDR_TO_PDENT(address);
695			if (PDE_TYPE(pr[index]) != DT_DIR) {
696				panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
697					"has no page dir entry", page, area, address);
698				continue;
699			}
700
701			page_table_entry* pt
702				= (page_table_entry*)MapperGetPageTableAt(
703					pd[index] & M68K_PDE_ADDRESS_MASK);
704
705			//XXX:M68K: DT_INDIRECT here?
706
707			page_table_entry oldEntry
708				= M68KPagingMethod040::ClearPageTableEntry(
709					&pt[VADDR_TO_PTENT(address)]);
710
711			pinner.Unlock();
712
713			if (PTE_TYPE(oldEntry) != DT_PAGE) {
714				panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
715					"has no page table entry", page, area, address);
716				continue;
717			}
718
719			// transfer the accessed/dirty flags to the page and invalidate
720			// the mapping, if necessary
721			if ((oldEntry & M68K_PTE_ACCESSED) != 0) {
722				page->accessed = true;
723
724				if (!deletingAddressSpace)
725					InvalidatePage(address);
726			}
727
728			if ((oldEntry & M68K_PTE_DIRTY) != 0)
729				page->modified = true;
730
731			if (pageFullyUnmapped) {
732				DEBUG_PAGE_ACCESS_START(page);
733
734				if (cache->temporary)
735					vm_page_set_state(page, PAGE_STATE_INACTIVE);
736				else if (page->modified)
737					vm_page_set_state(page, PAGE_STATE_MODIFIED);
738				else
739					vm_page_set_state(page, PAGE_STATE_CACHED);
740
741				DEBUG_PAGE_ACCESS_END(page);
742			}
743		}
744
745		fMapCount--;
746	}
747
748	Flush();
749		// flush explicitely, since we directly use the lock
750
751	locker.Unlock();
752
753	bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
754	uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
755		| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
756	while (vm_page_mapping* mapping = mappings.RemoveHead())
757		object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
758}
759
760
761status_t
762M68KVMTranslationMap040::Query(addr_t va, phys_addr_t *_physical,
763	uint32 *_flags)
764{
765	// default the flags to not present
766	*_flags = 0;
767	*_physical = 0;
768	TRACE("040::Query(0x%lx,)\n", va);
769
770	int index = VADDR_TO_PRENT(va);
771	page_root_entry *pr = fPagingStructures->pgroot_virt;
772	if (PRE_TYPE(pr[index]) != DT_ROOT) {
773		// no pagetable here
774		return B_OK;
775	}
776
777	Thread* thread = thread_get_current_thread();
778	ThreadCPUPinner pinner(thread);
779
780	page_directory_entry* pd = (page_directory_entry*)MapperGetPageTableAt(
781		pr[index] & M68K_PDE_ADDRESS_MASK);
782
783	index = VADDR_TO_PDENT(va);
784	if (PDE_TYPE(pd[index]) != DT_DIR) {
785		// no pagetable here
786		return B_OK;
787	}
788
789	page_table_entry* pt = (page_table_entry*)MapperGetPageTableAt(
790		pd[index] & M68K_PDE_ADDRESS_MASK);
791
792	index = VADDR_TO_PTENT(va);
793	if (PTE_TYPE(pt[index]) == DT_INDIRECT) {
794		pt = (page_table_entry*)MapperGetPageTableAt(
795			pt[index] & M68K_PIE_ADDRESS_MASK);
796		index = 0;
797	}
798
799	page_table_entry entry = pt[index];
800
801	*_physical = entry & M68K_PTE_ADDRESS_MASK;
802
803	// read in the page state flags
804	if ((entry & M68K_PTE_SUPERVISOR) == 0) {
805		*_flags |= ((entry & M68K_PTE_READONLY) == 0 ? B_WRITE_AREA : 0)
806			| B_READ_AREA;
807	}
808
809	*_flags |= ((entry & M68K_PTE_READONLY) == 0 ? B_KERNEL_WRITE_AREA : 0)
810		| B_KERNEL_READ_AREA
811		| ((entry & M68K_PTE_DIRTY) != 0 ? PAGE_MODIFIED : 0)
812		| ((entry & M68K_PTE_ACCESSED) != 0 ? PAGE_ACCESSED : 0)
813		| ((PTE_TYPE(entry) == DT_PAGE) ? PAGE_PRESENT : 0);
814
815	pinner.Unlock();
816
817	TRACE("query_tmap: returning pa 0x%lx for va 0x%lx\n", *_physical, va);
818
819	return B_OK;
820}
821
822
823status_t
824M68KVMTranslationMap040::QueryInterrupt(addr_t va, phys_addr_t *_physical,
825	uint32 *_flags)
826{
827	*_flags = 0;
828	*_physical = 0;
829	TRACE("040::QueryInterrupt(0x%lx,)\n", va);
830
831	int index = VADDR_TO_PRENT(va);
832	page_root_entry* pr = fPagingStructures->pgroot_virt;
833	if (PRE_TYPE(pr[index]) != DT_ROOT) {
834		// no pagetable here
835		return B_OK;
836	}
837
838	// map page table entry
839	phys_addr_t ppr = pr[index] & M68K_PRE_ADDRESS_MASK;
840	page_directory_entry* pd = (page_directory_entry*)((char *)
841		M68KPagingMethod040::Method()->PhysicalPageMapper()
842		->InterruptGetPageTableAt(ppr & ~(B_PAGE_SIZE-1))
843		+ (ppr % B_PAGE_SIZE));
844
845	index = VADDR_TO_PDENT(va);
846	if (PDE_TYPE(pd[index]) != DT_DIR) {
847		// no pagetable here
848		return B_OK;
849	}
850
851	phys_addr_t ppd = pd[index] & M68K_PDE_ADDRESS_MASK;
852	page_table_entry* pt = (page_table_entry*)((char *)
853		M68KPagingMethod040::Method()->PhysicalPageMapper()
854		->InterruptGetPageTableAt(ppd & ~(B_PAGE_SIZE-1))
855		+ (ppd % B_PAGE_SIZE));
856
857	index = VADDR_TO_PTENT(va);
858	if (PTE_TYPE(pt[index]) == DT_INDIRECT) {
859		phys_addr_t ppt = pt[index] & M68K_PIE_ADDRESS_MASK;
860		pt = (page_table_entry*)((char *)
861			M68KPagingMethod040::Method()->PhysicalPageMapper()
862			->InterruptGetPageTableAt(ppt & ~(B_PAGE_SIZE-1))
863			+ (ppt % B_PAGE_SIZE));
864		index = 0;
865	}
866
867	page_table_entry entry = pt[index];
868
869	*_physical = entry & M68K_PTE_ADDRESS_MASK;
870
871	// read in the page state flags
872	if ((entry & M68K_PTE_SUPERVISOR) == 0) {
873		*_flags |= ((entry & M68K_PTE_READONLY) == 0 ? B_WRITE_AREA : 0)
874			| B_READ_AREA;
875	}
876
877	*_flags |= ((entry & M68K_PTE_READONLY) == 0 ? B_KERNEL_WRITE_AREA : 0)
878		| B_KERNEL_READ_AREA
879		| ((entry & M68K_PTE_DIRTY) != 0 ? PAGE_MODIFIED : 0)
880		| ((entry & M68K_PTE_ACCESSED) != 0 ? PAGE_ACCESSED : 0)
881		| ((PTE_TYPE(entry) == DT_PAGE) ? PAGE_PRESENT : 0);
882
883	return B_OK;
884}
885
886
887status_t
888M68KVMTranslationMap040::Protect(addr_t start, addr_t end, uint32 attributes,
889	uint32 memoryType)
890{
891	start = ROUNDDOWN(start, B_PAGE_SIZE);
892	if (start >= end)
893		return B_OK;
894
895	TRACE("protect_tmap: pages 0x%lx to 0x%lx, attributes %lx\n", start, end,
896		attributes);
897
898	return ENOSYS;
899#if 0
900	// compute protection flags
901	uint32 newProtectionFlags = 0;
902	if ((attributes & B_USER_PROTECTION) != 0) {
903		newProtectionFlags = M68K_PTE_USER;
904		if ((attributes & B_WRITE_AREA) != 0)
905			newProtectionFlags |= M68K_PTE_WRITABLE;
906	} else if ((attributes & B_KERNEL_WRITE_AREA) != 0)
907		newProtectionFlags = M68K_PTE_WRITABLE;
908
909	page_directory_entry *pd = fPagingStructures->pgdir_virt;
910
911	do {
912		int index = VADDR_TO_PDENT(start);
913		if ((pd[index] & M68K_PDE_PRESENT) == 0) {
914			// no page table here, move the start up to access the next page
915			// table
916			start = ROUNDUP(start + 1, kPageTableAlignment);
917			continue;
918		}
919
920		struct thread* thread = thread_get_current_thread();
921		ThreadCPUPinner pinner(thread);
922
923		page_table_entry* pt = (page_table_entry*)MapperGetPageTableAt(
924			pd[index] & M68K_PDE_ADDRESS_MASK);
925
926		for (index = VADDR_TO_PTENT(start); index < 1024 && start < end;
927				index++, start += B_PAGE_SIZE) {
928			page_table_entry entry = pt[index];
929			if ((entry & M68K_PTE_PRESENT) == 0) {
930				// page mapping not valid
931				continue;
932			}
933
934			TRACE("protect_tmap: protect page 0x%lx\n", start);
935
936			// set the new protection flags -- we want to do that atomically,
937			// without changing the accessed or dirty flag
938			page_table_entry oldEntry;
939			while (true) {
940				oldEntry = M68KPagingMethod040::TestAndSetPageTableEntry(
941					&pt[index],
942					(entry & ~(M68K_PTE_PROTECTION_MASK
943							| M68K_PTE_MEMORY_TYPE_MASK))
944						| newProtectionFlags
945						| M68KPagingMethod040::MemoryTypeToPageTableEntryFlags(
946							memoryType),
947					entry);
948				if (oldEntry == entry)
949					break;
950				entry = oldEntry;
951			}
952
953			if ((oldEntry & M68K_PTE_ACCESSED) != 0) {
954				// Note, that we only need to invalidate the address, if the
955				// accessed flag was set, since only then the entry could have
956				// been in any TLB.
957				InvalidatePage(start);
958			}
959		}
960	} while (start != 0 && start < end);
961	return B_OK;
962#endif
963}
964
965
966status_t
967M68KVMTranslationMap040::ClearFlags(addr_t va, uint32 flags)
968{
969	return ENOSYS;
970#if 0
971	int index = VADDR_TO_PDENT(va);
972	page_directory_entry* pd = fPagingStructures->pgdir_virt;
973	if ((pd[index] & M68K_PDE_PRESENT) == 0) {
974		// no pagetable here
975		return B_OK;
976	}
977
978	uint32 flagsToClear = ((flags & PAGE_MODIFIED) ? M68K_PTE_DIRTY : 0)
979		| ((flags & PAGE_ACCESSED) ? M68K_PTE_ACCESSED : 0);
980
981	struct thread* thread = thread_get_current_thread();
982	ThreadCPUPinner pinner(thread);
983
984	page_table_entry* pt = (page_table_entry*)MapperGetPageTableAt(
985		pd[index] & M68K_PDE_ADDRESS_MASK);
986	index = VADDR_TO_PTENT(va);
987
988	// clear out the flags we've been requested to clear
989	page_table_entry oldEntry
990		= M68KPagingMethod040::ClearPageTableEntryFlags(&pt[index],
991			flagsToClear);
992
993	pinner.Unlock();
994
995	if ((oldEntry & flagsToClear) != 0)
996		InvalidatePage(va);
997
998	return B_OK;
999#endif
1000}
1001
1002
1003bool
1004M68KVMTranslationMap040::ClearAccessedAndModified(VMArea* area, addr_t address,
1005	bool unmapIfUnaccessed, bool& _modified)
1006{
1007	ASSERT(address % B_PAGE_SIZE == 0);
1008
1009	page_root_entry* pr = fPagingStructures->pgroot_virt;
1010
1011	TRACE("M68KVMTranslationMap040::ClearAccessedAndModified(%#" B_PRIxADDR
1012		")\n", address);
1013
1014#if 0
1015	RecursiveLocker locker(fLock);
1016
1017	int index = VADDR_TO_PDENT(address);
1018	if ((pd[index] & M68K_PDE_PRESENT) == 0)
1019		return false;
1020
1021	ThreadCPUPinner pinner(thread_get_current_thread());
1022
1023	page_table_entry* pt = (page_table_entry*)MapperGetPageTableAt(
1024		pd[index] & M68K_PDE_ADDRESS_MASK);
1025
1026	index = VADDR_TO_PTENT(address);
1027
1028	// perform the deed
1029	page_table_entry oldEntry;
1030
1031	if (unmapIfUnaccessed) {
1032		while (true) {
1033			oldEntry = pt[index];
1034			if ((oldEntry & M68K_PTE_PRESENT) == 0) {
1035				// page mapping not valid
1036				return false;
1037			}
1038
1039			if (oldEntry & M68K_PTE_ACCESSED) {
1040				// page was accessed -- just clear the flags
1041				oldEntry = M68KPagingMethod040::ClearPageTableEntryFlags(
1042					&pt[index], M68K_PTE_ACCESSED | M68K_PTE_DIRTY);
1043				break;
1044			}
1045
1046			// page hasn't been accessed -- unmap it
1047			if (M68KPagingMethod040::TestAndSetPageTableEntry(&pt[index], 0,
1048					oldEntry) == oldEntry) {
1049				break;
1050			}
1051
1052			// something changed -- check again
1053		}
1054	} else {
1055		oldEntry = M68KPagingMethod040::ClearPageTableEntryFlags(&pt[index],
1056			M68K_PTE_ACCESSED | M68K_PTE_DIRTY);
1057	}
1058
1059	pinner.Unlock();
1060
1061	_modified = (oldEntry & M68K_PTE_DIRTY) != 0;
1062
1063	if ((oldEntry & M68K_PTE_ACCESSED) != 0) {
1064		// Note, that we only need to invalidate the address, if the
1065		// accessed flags was set, since only then the entry could have been
1066		// in any TLB.
1067		InvalidatePage(address);
1068
1069		Flush();
1070
1071		return true;
1072	}
1073
1074	if (!unmapIfUnaccessed)
1075		return false;
1076
1077	// We have unmapped the address. Do the "high level" stuff.
1078
1079	fMapCount--;
1080
1081	locker.Detach();
1082		// UnaccessedPageUnmapped() will unlock for us
1083
1084	UnaccessedPageUnmapped(area,
1085		(oldEntry & M68K_PTE_ADDRESS_MASK) / B_PAGE_SIZE);
1086
1087#endif
1088	return false;
1089}
1090
1091
1092M68KPagingStructures*
1093M68KVMTranslationMap040::PagingStructures() const
1094{
1095	return fPagingStructures;
1096}
1097
1098
1099inline void *
1100M68KVMTranslationMap040::MapperGetPageTableAt(phys_addr_t physicalAddress,
1101	bool indirect)
1102{
1103	// M68K fits several page tables in a single page...
1104	uint32 offset = physicalAddress % B_PAGE_SIZE;
1105	ASSERT((indirect && (offset % 4) == 0) || (offset % SIZ_ROOTTBL) == 0);
1106	physicalAddress &= ~(B_PAGE_SIZE-1);
1107	void *va = fPageMapper->GetPageTableAt(physicalAddress);
1108	return (void *)((addr_t)va + offset);
1109}
1110
1111
1112