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