1/*
2 * Copyright 2010, Ithamar R. Adema, ithamar.adema@team-embedded.nl
3 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
5 * Distributed under the terms of the MIT License.
6 *
7 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
8 * Distributed under the terms of the NewOS License.
9 */
10
11
12#include "paging/32bit/ARMVMTranslationMap32Bit.h"
13
14#include <stdlib.h>
15#include <string.h>
16
17#include <int.h>
18#include <thread.h>
19#include <slab/Slab.h>
20#include <smp.h>
21#include <util/AutoLock.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/ARMPagingMethod32Bit.h"
29#include "paging/32bit/ARMPagingStructures32Bit.h"
30#include "paging/arm_physical_page_mapper.h"
31
32
33//#define TRACE_ARM_VM_TRANSLATION_MAP_32_BIT
34#ifdef TRACE_ARM_VM_TRANSLATION_MAP_32_BIT
35#	define TRACE(x...) dprintf(x)
36#else
37#	define TRACE(x...) ;
38#endif
39
40
41ARMVMTranslationMap32Bit::ARMVMTranslationMap32Bit()
42	:
43	fPagingStructures(NULL)
44{
45}
46
47
48ARMVMTranslationMap32Bit::~ARMVMTranslationMap32Bit()
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] & ARM_PDE_TYPE_MASK) != 0) {
61				addr_t address = fPagingStructures->pgdir_virt[i]
62					& ARM_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
77ARMVMTranslationMap32Bit::Init(bool kernel)
78{
79	TRACE("ARMVMTranslationMap32Bit::Init()\n");
80
81	ARMVMTranslationMap::Init(kernel);
82
83	fPagingStructures = new(std::nothrow) ARMPagingStructures32Bit;
84	if (fPagingStructures == NULL)
85		return B_NO_MEMORY;
86
87	ARMPagingMethod32Bit* method = ARMPagingMethod32Bit::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
125ARMVMTranslationMap32Bit::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
140ARMVMTranslationMap32Bit::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] & ARM_PDE_TYPE_MASK) == 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		ARMPagingMethod32Bit::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			ARMPagingStructures32Bit::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] & ARM_PDE_ADDRESS_MASK);
192	index = VADDR_TO_PTENT(va);
193
194	ASSERT_PRINT((pt[index] & ARM_PTE_TYPE_MASK) == 0,
195		"virtual address: %#" B_PRIxADDR ", existing pte: %#" B_PRIx32, va,
196		pt[index]);
197
198	ARMPagingMethod32Bit::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
213ARMVMTranslationMap32Bit::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] & ARM_PDE_TYPE_MASK) == 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] & ARM_PDE_ADDRESS_MASK);
237
238		for (index = VADDR_TO_PTENT(start); (index < 256) && (start < end);
239				index++, start += B_PAGE_SIZE) {
240			if ((pt[index] & ARM_PTE_TYPE_MASK) == 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				= ARMPagingMethod32Bit::ClearPageTableEntryFlags(&pt[index],
249					ARM_PTE_TYPE_MASK);
250			fMapCount--;
251
252			if (true /* (oldEntry & ARM_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
266ARMVMTranslationMap32Bit::DebugMarkRangePresent(addr_t start, addr_t end,
267	bool markPresent)
268{
269#if 0
270	start = ROUNDDOWN(start, B_PAGE_SIZE);
271	if (start >= end)
272		return B_OK;
273
274	page_directory_entry *pd = fPagingStructures->pgdir_virt;
275
276	do {
277		int index = VADDR_TO_PDENT(start);
278		if ((pd[index] & X86_PDE_PRESENT) == 0) {
279			// no page table here, move the start up to access the next page
280			// table
281			start = ROUNDUP(start + 1, kPageTableAlignment);
282			continue;
283		}
284
285		Thread* thread = thread_get_current_thread();
286		ThreadCPUPinner pinner(thread);
287
288		page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt(
289			pd[index] & X86_PDE_ADDRESS_MASK);
290
291		for (index = VADDR_TO_PTENT(start); (index < 1024) && (start < end);
292				index++, start += B_PAGE_SIZE) {
293			if ((pt[index] & X86_PTE_PRESENT) == 0) {
294				if (!markPresent)
295					continue;
296
297				X86PagingMethod32Bit::SetPageTableEntryFlags(&pt[index],
298					X86_PTE_PRESENT);
299			} else {
300				if (markPresent)
301					continue;
302
303				page_table_entry oldEntry
304					= X86PagingMethod32Bit::ClearPageTableEntryFlags(&pt[index],
305						X86_PTE_PRESENT);
306
307				if ((oldEntry & X86_PTE_ACCESSED) != 0) {
308					// Note, that we only need to invalidate the address, if the
309					// accessed flags was set, since only then the entry could
310					// have been in any TLB.
311					InvalidatePage(start);
312				}
313			}
314		}
315	} while (start != 0 && start < end);
316#endif
317	return B_OK;
318}
319
320
321/*!	Caller must have locked the cache of the page to be unmapped.
322	This object shouldn't be locked.
323*/
324status_t
325ARMVMTranslationMap32Bit::UnmapPage(VMArea* area, addr_t address,
326	bool updatePageQueue)
327{
328	ASSERT(address % B_PAGE_SIZE == 0);
329
330	page_directory_entry* pd = fPagingStructures->pgdir_virt;
331
332	TRACE("ARMVMTranslationMap32Bit::UnmapPage(%#" B_PRIxADDR ")\n", address);
333
334	RecursiveLocker locker(fLock);
335
336	int index = VADDR_TO_PDENT(address);
337	if ((pd[index] & ARM_PDE_TYPE_MASK) == 0)
338		return B_ENTRY_NOT_FOUND;
339
340	ThreadCPUPinner pinner(thread_get_current_thread());
341
342	page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt(
343		pd[index] & ARM_PDE_ADDRESS_MASK);
344
345	index = VADDR_TO_PTENT(address);
346	page_table_entry oldEntry = ARMPagingMethod32Bit::ClearPageTableEntry(
347		&pt[index]);
348
349	pinner.Unlock();
350
351	if ((oldEntry & ARM_PTE_TYPE_MASK) == 0) {
352		// page mapping not valid
353		return B_ENTRY_NOT_FOUND;
354	}
355
356	fMapCount--;
357
358
359	if (true /*(oldEntry & ARM_PTE_ACCESSED) != 0*/) { // XXX IRA
360		// Note, that we only need to invalidate the address, if the
361		// accessed flags was set, since only then the entry could have been
362		// in any TLB.
363		InvalidatePage(address);
364		Flush();
365
366		// NOTE: Between clearing the page table entry and Flush() other
367		// processors (actually even this processor with another thread of the
368		// same team) could still access the page in question via their cached
369		// entry. We can obviously lose a modified flag in this case, with the
370		// effect that the page looks unmodified (and might thus be recycled),
371		// but is actually modified.
372		// In most cases this is harmless, but for vm_remove_all_page_mappings()
373		// this is actually a problem.
374		// Interestingly FreeBSD seems to ignore this problem as well
375		// (cf. pmap_remove_all()), unless I've missed something.
376	}
377
378	locker.Detach();
379		// PageUnmapped() will unlock for us
380
381	PageUnmapped(area, (oldEntry & ARM_PTE_ADDRESS_MASK) / B_PAGE_SIZE,
382		true /*(oldEntry & ARM_PTE_ACCESSED) != 0*/, true /*(oldEntry & ARM_PTE_DIRTY) != 0*/,
383		updatePageQueue);
384
385	return B_OK;
386}
387
388
389void
390ARMVMTranslationMap32Bit::UnmapPages(VMArea* area, addr_t base, size_t size,
391	bool updatePageQueue)
392{
393	if (size == 0)
394		return;
395
396	addr_t start = base;
397	addr_t end = base + size - 1;
398
399	TRACE("ARMVMTranslationMap32Bit::UnmapPages(%p, %#" B_PRIxADDR ", %#"
400		B_PRIxADDR ")\n", area, start, end);
401
402	page_directory_entry* pd = fPagingStructures->pgdir_virt;
403
404	VMAreaMappings queue;
405
406	RecursiveLocker locker(fLock);
407
408	do {
409		int index = VADDR_TO_PDENT(start);
410		if ((pd[index] & ARM_PDE_TYPE_MASK) == 0) {
411			// no page table here, move the start up to access the next page
412			// table
413			start = ROUNDUP(start + 1, kPageTableAlignment);
414			continue;
415		}
416
417		Thread* thread = thread_get_current_thread();
418		ThreadCPUPinner pinner(thread);
419
420		page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt(
421			pd[index] & ARM_PDE_ADDRESS_MASK);
422
423		for (index = VADDR_TO_PTENT(start); (index < 256) && (start < end);
424				index++, start += B_PAGE_SIZE) {
425			page_table_entry oldEntry
426				= ARMPagingMethod32Bit::ClearPageTableEntry(&pt[index]);
427			if ((oldEntry & ARM_PTE_TYPE_MASK) == 0)
428				continue;
429
430			fMapCount--;
431
432			if (true /*(oldEntry & ARM_PTE_ACCESSED) != 0*/) { // XXX IRA
433				// Note, that we only need to invalidate the address, if the
434				// accessed flags was set, since only then the entry could have
435				// been in any TLB.
436				InvalidatePage(start);
437			}
438
439			if (area->cache_type != CACHE_TYPE_DEVICE) {
440				// get the page
441				vm_page* page = vm_lookup_page(
442					(oldEntry & ARM_PTE_ADDRESS_MASK) / B_PAGE_SIZE);
443				ASSERT(page != NULL);
444
445				DEBUG_PAGE_ACCESS_START(page);
446
447				// transfer the accessed/dirty flags to the page
448				if (/*(oldEntry & ARM_PTE_ACCESSED) != 0*/ true) // XXX IRA
449					page->accessed = true;
450				if (/*(oldEntry & ARM_PTE_DIRTY) != 0 */ true)
451					page->modified = true;
452
453				// remove the mapping object/decrement the wired_count of the
454				// page
455				if (area->wiring == B_NO_LOCK) {
456					vm_page_mapping* mapping = NULL;
457					vm_page_mappings::Iterator iterator
458						= page->mappings.GetIterator();
459					while ((mapping = iterator.Next()) != NULL) {
460						if (mapping->area == area)
461							break;
462					}
463
464					ASSERT(mapping != NULL);
465
466					area->mappings.Remove(mapping);
467					page->mappings.Remove(mapping);
468					queue.Add(mapping);
469				} else
470					page->DecrementWiredCount();
471
472				if (!page->IsMapped()) {
473					atomic_add(&gMappedPagesCount, -1);
474
475					if (updatePageQueue) {
476						if (page->Cache()->temporary)
477							vm_page_set_state(page, PAGE_STATE_INACTIVE);
478						else if (page->modified)
479							vm_page_set_state(page, PAGE_STATE_MODIFIED);
480						else
481							vm_page_set_state(page, PAGE_STATE_CACHED);
482					}
483				}
484
485				DEBUG_PAGE_ACCESS_END(page);
486			}
487		}
488
489		Flush();
490			// flush explicitly, since we directly use the lock
491	} while (start != 0 && start < end);
492
493	// TODO: As in UnmapPage() we can lose page dirty flags here. ATM it's not
494	// really critical here, as in all cases this method is used, the unmapped
495	// area range is unmapped for good (resized/cut) and the pages will likely
496	// be freed.
497
498	locker.Unlock();
499
500	// free removed mappings
501	bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
502	uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
503		| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
504	while (vm_page_mapping* mapping = queue.RemoveHead())
505		object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
506}
507
508
509void
510ARMVMTranslationMap32Bit::UnmapArea(VMArea* area, bool deletingAddressSpace,
511	bool ignoreTopCachePageFlags)
512{
513	if (area->cache_type == CACHE_TYPE_DEVICE || area->wiring != B_NO_LOCK) {
514		ARMVMTranslationMap32Bit::UnmapPages(area, area->Base(), area->Size(),
515			true);
516		return;
517	}
518
519	bool unmapPages = !deletingAddressSpace || !ignoreTopCachePageFlags;
520
521	page_directory_entry* pd = fPagingStructures->pgdir_virt;
522
523	RecursiveLocker locker(fLock);
524
525	VMAreaMappings mappings;
526	mappings.MoveFrom(&area->mappings);
527
528	for (VMAreaMappings::Iterator it = mappings.GetIterator();
529			vm_page_mapping* mapping = it.Next();) {
530		vm_page* page = mapping->page;
531		page->mappings.Remove(mapping);
532
533		VMCache* cache = page->Cache();
534
535		bool pageFullyUnmapped = false;
536		if (!page->IsMapped()) {
537			atomic_add(&gMappedPagesCount, -1);
538			pageFullyUnmapped = true;
539		}
540
541		if (unmapPages || cache != area->cache) {
542			addr_t address = area->Base()
543				+ ((page->cache_offset * B_PAGE_SIZE) - area->cache_offset);
544
545			int index = VADDR_TO_PDENT(address);
546			if ((pd[index] & ARM_PDE_TYPE_MASK) == 0) {
547				panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
548					"has no page dir entry", page, area, address);
549				continue;
550			}
551
552			ThreadCPUPinner pinner(thread_get_current_thread());
553
554			page_table_entry* pt
555				= (page_table_entry*)fPageMapper->GetPageTableAt(
556					pd[index] & ARM_PDE_ADDRESS_MASK);
557			page_table_entry oldEntry
558				= ARMPagingMethod32Bit::ClearPageTableEntry(
559					&pt[VADDR_TO_PTENT(address)]);
560
561			pinner.Unlock();
562
563			if ((oldEntry & ARM_PTE_TYPE_MASK) == 0) {
564				panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
565					"has no page table entry", page, area, address);
566				continue;
567			}
568
569			// transfer the accessed/dirty flags to the page and invalidate
570			// the mapping, if necessary
571			if (true /*(oldEntry & ARM_PTE_ACCESSED) != 0*/) { // XXX IRA
572				page->accessed = true;
573
574				if (!deletingAddressSpace)
575					InvalidatePage(address);
576			}
577
578			if (true /*(oldEntry & ARM_PTE_DIRTY) != 0*/)
579				page->modified = true;
580
581			if (pageFullyUnmapped) {
582				DEBUG_PAGE_ACCESS_START(page);
583
584				if (cache->temporary)
585					vm_page_set_state(page, PAGE_STATE_INACTIVE);
586				else if (page->modified)
587					vm_page_set_state(page, PAGE_STATE_MODIFIED);
588				else
589					vm_page_set_state(page, PAGE_STATE_CACHED);
590
591				DEBUG_PAGE_ACCESS_END(page);
592			}
593		}
594
595		fMapCount--;
596	}
597
598	Flush();
599		// flush explicitely, since we directly use the lock
600
601	locker.Unlock();
602
603	bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
604	uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
605		| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
606	while (vm_page_mapping* mapping = mappings.RemoveHead())
607		object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
608}
609
610
611status_t
612ARMVMTranslationMap32Bit::Query(addr_t va, phys_addr_t *_physical,
613	uint32 *_flags)
614{
615	// default the flags to not present
616	*_flags = 0;
617	*_physical = 0;
618
619	int index = VADDR_TO_PDENT(va);
620	page_directory_entry *pd = fPagingStructures->pgdir_virt;
621	if ((pd[index] & ARM_PDE_TYPE_MASK) == 0) {
622		// no pagetable here
623		return B_OK;
624	}
625
626	Thread* thread = thread_get_current_thread();
627	ThreadCPUPinner pinner(thread);
628
629	page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt(
630		pd[index] & ARM_PDE_ADDRESS_MASK);
631	page_table_entry entry = pt[VADDR_TO_PTENT(va)];
632
633	if ((entry & ARM_PTE_TYPE_MASK) != 0)
634		*_physical = (entry & ARM_PTE_ADDRESS_MASK) | VADDR_TO_PGOFF(va);
635
636#if 0 //IRA
637	// read in the page state flags
638	if ((entry & X86_PTE_USER) != 0) {
639		*_flags |= ((entry & X86_PTE_WRITABLE) != 0 ? B_WRITE_AREA : 0)
640			| B_READ_AREA;
641	}
642
643	*_flags |= ((entry & X86_PTE_WRITABLE) != 0 ? B_KERNEL_WRITE_AREA : 0)
644		| B_KERNEL_READ_AREA
645		| ((entry & ARM_PTE_DIRTY) != 0 ? PAGE_MODIFIED : 0)
646		| ((entry & ARM_PTE_ACCESSED) != 0 ? PAGE_ACCESSED : 0)
647		| ((entry & ARM_PTE_PRESENT) != 0 ? PAGE_PRESENT : 0);
648#else
649	*_flags = B_KERNEL_WRITE_AREA | B_KERNEL_READ_AREA;
650	if (*_physical != 0)
651		*_flags |= PAGE_PRESENT;
652#endif
653	pinner.Unlock();
654
655	TRACE("query_tmap: returning pa 0x%lx for va 0x%lx\n", *_physical, va);
656
657	return B_OK;
658}
659
660
661status_t
662ARMVMTranslationMap32Bit::QueryInterrupt(addr_t va, phys_addr_t *_physical,
663	uint32 *_flags)
664{
665	*_flags = 0;
666	*_physical = 0;
667
668	int index = VADDR_TO_PDENT(va);
669	page_directory_entry* pd = fPagingStructures->pgdir_virt;
670	if ((pd[index] & ARM_PDE_TYPE_MASK) == 0) {
671		// no pagetable here
672		return B_OK;
673	}
674
675	// map page table entry
676	page_table_entry* pt = (page_table_entry*)ARMPagingMethod32Bit::Method()
677		->PhysicalPageMapper()->InterruptGetPageTableAt(
678			pd[index] & ARM_PDE_ADDRESS_MASK);
679	page_table_entry entry = pt[VADDR_TO_PTENT(va)];
680
681	if ((entry & ARM_PTE_TYPE_MASK) != 0)
682		*_physical = (entry & ARM_PTE_ADDRESS_MASK) | VADDR_TO_PGOFF(va);
683
684#if 0
685	// read in the page state flags
686	if ((entry & X86_PTE_USER) != 0) {
687		*_flags |= ((entry & X86_PTE_WRITABLE) != 0 ? B_WRITE_AREA : 0)
688			| B_READ_AREA;
689	}
690
691	*_flags |= ((entry & X86_PTE_WRITABLE) != 0 ? B_KERNEL_WRITE_AREA : 0)
692		| B_KERNEL_READ_AREA
693		| ((entry & X86_PTE_DIRTY) != 0 ? PAGE_MODIFIED : 0)
694		| ((entry & X86_PTE_ACCESSED) != 0 ? PAGE_ACCESSED : 0)
695		| ((entry & X86_PTE_PRESENT) != 0 ? PAGE_PRESENT : 0);
696#else
697	*_flags = B_KERNEL_WRITE_AREA | B_KERNEL_READ_AREA;
698	if (*_physical != 0)
699		*_flags |= PAGE_PRESENT;
700#endif
701	return B_OK;
702}
703
704
705status_t
706ARMVMTranslationMap32Bit::Protect(addr_t start, addr_t end, uint32 attributes,
707	uint32 memoryType)
708{
709	start = ROUNDDOWN(start, B_PAGE_SIZE);
710	if (start >= end)
711		return B_OK;
712
713	TRACE("protect_tmap: pages 0x%lx to 0x%lx, attributes %lx\n", start, end,
714		attributes);
715#if 0 //IRA
716	// compute protection flags
717	uint32 newProtectionFlags = 0;
718	if ((attributes & B_USER_PROTECTION) != 0) {
719		newProtectionFlags = ARM_PTE_USER;
720		if ((attributes & B_WRITE_AREA) != 0)
721			newProtectionFlags |= ARM_PTE_WRITABLE;
722	} else if ((attributes & B_KERNEL_WRITE_AREA) != 0)
723		newProtectionFlags = ARM_PTE_WRITABLE;
724
725	page_directory_entry *pd = fPagingStructures->pgdir_virt;
726
727	do {
728		int index = VADDR_TO_PDENT(start);
729		if ((pd[index] & ARM_PDE_TYPE_MASK) == 0) {
730			// no page table here, move the start up to access the next page
731			// table
732			start = ROUNDUP(start + 1, kPageTableAlignment);
733			continue;
734		}
735
736		Thread* thread = thread_get_current_thread();
737		ThreadCPUPinner pinner(thread);
738
739		page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt(
740			pd[index] & ARM_PDE_ADDRESS_MASK);
741
742		for (index = VADDR_TO_PTENT(start); index < 256 && start < end;
743				index++, start += B_PAGE_SIZE) {
744			page_table_entry entry = pt[index];
745			if ((entry & ARM_PTE_PRESENT) == 0) {
746				// page mapping not valid
747				continue;
748			}
749
750			TRACE("protect_tmap: protect page 0x%lx\n", start);
751
752			// set the new protection flags -- we want to do that atomically,
753			// without changing the accessed or dirty flag
754			page_table_entry oldEntry;
755			while (true) {
756				oldEntry = ARMPagingMethod32Bit::TestAndSetPageTableEntry(
757					&pt[index],
758					(entry & ~(ARM_PTE_PROTECTION_MASK
759							| ARM_PTE_MEMORY_TYPE_MASK))
760						| newProtectionFlags
761						| ARMPagingMethod32Bit::MemoryTypeToPageTableEntryFlags(
762							memoryType),
763					entry);
764				if (oldEntry == entry)
765					break;
766				entry = oldEntry;
767			}
768
769			if ((oldEntry & ARM_PTE_ACCESSED) != 0) {
770				// Note, that we only need to invalidate the address, if the
771				// accessed flag was set, since only then the entry could have
772				// been in any TLB.
773				InvalidatePage(start);
774			}
775		}
776	} while (start != 0 && start < end);
777#endif
778	return B_OK;
779}
780
781
782status_t
783ARMVMTranslationMap32Bit::ClearFlags(addr_t va, uint32 flags)
784{
785	int index = VADDR_TO_PDENT(va);
786	page_directory_entry* pd = fPagingStructures->pgdir_virt;
787	if ((pd[index] & ARM_PDE_TYPE_MASK) == 0) {
788		// no pagetable here
789		return B_OK;
790	}
791#if 0 //IRA
792	uint32 flagsToClear = ((flags & PAGE_MODIFIED) ? X86_PTE_DIRTY : 0)
793		| ((flags & PAGE_ACCESSED) ? X86_PTE_ACCESSED : 0);
794#else
795	uint32 flagsToClear = 0;
796#endif
797	Thread* thread = thread_get_current_thread();
798	ThreadCPUPinner pinner(thread);
799
800	page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt(
801		pd[index] & ARM_PDE_ADDRESS_MASK);
802	index = VADDR_TO_PTENT(va);
803
804	// clear out the flags we've been requested to clear
805	page_table_entry oldEntry
806		= ARMPagingMethod32Bit::ClearPageTableEntryFlags(&pt[index],
807			flagsToClear);
808
809	pinner.Unlock();
810
811	//XXX IRA if ((oldEntry & flagsToClear) != 0)
812		InvalidatePage(va);
813
814	return B_OK;
815}
816
817
818bool
819ARMVMTranslationMap32Bit::ClearAccessedAndModified(VMArea* area, addr_t address,
820	bool unmapIfUnaccessed, bool& _modified)
821{
822	ASSERT(address % B_PAGE_SIZE == 0);
823
824	page_directory_entry* pd = fPagingStructures->pgdir_virt;
825
826	TRACE("ARMVMTranslationMap32Bit::ClearAccessedAndModified(%#" B_PRIxADDR
827		")\n", address);
828
829	RecursiveLocker locker(fLock);
830
831	int index = VADDR_TO_PDENT(address);
832	if ((pd[index] & ARM_PDE_TYPE_MASK) == 0)
833		return false;
834
835	ThreadCPUPinner pinner(thread_get_current_thread());
836
837	page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt(
838		pd[index] & ARM_PDE_ADDRESS_MASK);
839
840	index = VADDR_TO_PTENT(address);
841
842	// perform the deed
843	page_table_entry oldEntry;
844
845	if (unmapIfUnaccessed) {
846		while (true) {
847			oldEntry = pt[index];
848			if ((oldEntry & ARM_PTE_TYPE_MASK) == 0) {
849				// page mapping not valid
850				return false;
851			}
852#if 0 //IRA
853			if (oldEntry & ARM_PTE_ACCESSED) {
854				// page was accessed -- just clear the flags
855				oldEntry = ARMPagingMethod32Bit::ClearPageTableEntryFlags(
856					&pt[index], ARM_PTE_ACCESSED | ARM_PTE_DIRTY);
857				break;
858			}
859#endif
860			// page hasn't been accessed -- unmap it
861			if (ARMPagingMethod32Bit::TestAndSetPageTableEntry(&pt[index], 0,
862					oldEntry) == oldEntry) {
863				break;
864			}
865
866			// something changed -- check again
867		}
868	} else {
869#if 0 //IRA
870		oldEntry = ARMPagingMethod32Bit::ClearPageTableEntryFlags(&pt[index],
871			ARM_PTE_ACCESSED | ARM_PTE_DIRTY);
872#else
873		oldEntry = pt[index];
874#endif
875	}
876
877	pinner.Unlock();
878
879	_modified = true /* (oldEntry & X86_PTE_DIRTY) != 0 */; // XXX IRA
880
881	if (true /*(oldEntry & ARM_PTE_ACCESSED) != 0*/) {
882		// Note, that we only need to invalidate the address, if the
883		// accessed flags was set, since only then the entry could have been
884		// in any TLB.
885		InvalidatePage(address);
886
887		Flush();
888
889		return true;
890	}
891
892	if (!unmapIfUnaccessed)
893		return false;
894
895	// We have unmapped the address. Do the "high level" stuff.
896
897	fMapCount--;
898
899	locker.Detach();
900		// UnaccessedPageUnmapped() will unlock for us
901
902	UnaccessedPageUnmapped(area,
903		(oldEntry & ARM_PTE_ADDRESS_MASK) / B_PAGE_SIZE);
904
905	return false;
906}
907
908
909ARMPagingStructures*
910ARMVMTranslationMap32Bit::PagingStructures() const
911{
912	return fPagingStructures;
913}
914