status_t M68KVMTranslationMap040::ClearFlags(addr_t va, uint32 flags) { return ENOSYS; #if 0 int index = VADDR_TO_PDENT(va); page_directory_entry* pd = fPagingStructures->pgdir_virt; if ((pd[index] & M68K_PDE_PRESENT) == 0) { // no pagetable here return B_OK; } uint32 flagsToClear = ((flags & PAGE_MODIFIED) ? M68K_PTE_DIRTY : 0) | ((flags & PAGE_ACCESSED) ? M68K_PTE_ACCESSED : 0); struct thread* thread = thread_get_current_thread(); ThreadCPUPinner pinner(thread); page_table_entry* pt = (page_table_entry*)MapperGetPageTableAt( pd[index] & M68K_PDE_ADDRESS_MASK); index = VADDR_TO_PTENT(va); // clear out the flags we've been requested to clear page_table_entry oldEntry = M68KPagingMethod040::ClearPageTableEntryFlags(&pt[index], flagsToClear); pinner.Unlock(); if ((oldEntry & flagsToClear) != 0) InvalidatePage(va); return B_OK; #endif }
status_t ARMVMTranslationMap32Bit::ClearFlags(addr_t va, uint32 flags) { int index = VADDR_TO_PDENT(va); page_directory_entry* pd = fPagingStructures->pgdir_virt; if ((pd[index] & ARM_PDE_TYPE_MASK) == 0) { // no pagetable here return B_OK; } #if 0 //IRA uint32 flagsToClear = ((flags & PAGE_MODIFIED) ? ARM_PTE_DIRTY : 0) | ((flags & PAGE_ACCESSED) ? ARM_PTE_ACCESSED : 0); #else uint32 flagsToClear = 0; #endif Thread* thread = thread_get_current_thread(); ThreadCPUPinner pinner(thread); page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt( pd[index] & ARM_PDE_ADDRESS_MASK); index = VADDR_TO_PTENT(va); // clear out the flags we've been requested to clear page_table_entry oldEntry = ARMPagingMethod32Bit::ClearPageTableEntryFlags(&pt[index], flagsToClear); pinner.Unlock(); //XXX IRA if ((oldEntry & flagsToClear) != 0) InvalidatePage(va); return B_OK; }
status_t X86VMTranslationMap64Bit::ClearFlags(addr_t address, uint32 flags) { TRACE("X86VMTranslationMap64Bit::ClearFlags(%#" B_PRIxADDR ", %#" B_PRIx32 ")\n", address, flags); ThreadCPUPinner pinner(thread_get_current_thread()); uint64* entry = X86PagingMethod64Bit::PageTableEntryForAddress( fPagingStructures->VirtualPML4(), address, fIsKernelMap, false, NULL, fPageMapper, fMapCount); if (entry == NULL) return B_OK; uint64 flagsToClear = ((flags & PAGE_MODIFIED) ? X86_64_PTE_DIRTY : 0) | ((flags & PAGE_ACCESSED) ? X86_64_PTE_ACCESSED : 0); uint64 oldEntry = X86PagingMethod64Bit::ClearTableEntryFlags(entry, flagsToClear); if ((oldEntry & flagsToClear) != 0) InvalidatePage(address); return B_OK; }
status_t X86VMTranslationMap64Bit::UnmapPage(VMArea* area, addr_t address, bool updatePageQueue) { ASSERT(address % B_PAGE_SIZE == 0); TRACE("X86VMTranslationMap64Bit::UnmapPage(%#" B_PRIxADDR ")\n", address); ThreadCPUPinner pinner(thread_get_current_thread()); // Look up the page table for the virtual address. uint64* entry = X86PagingMethod64Bit::PageTableEntryForAddress( fPagingStructures->VirtualPML4(), address, fIsKernelMap, false, NULL, fPageMapper, fMapCount); if (entry == NULL) return B_ENTRY_NOT_FOUND; RecursiveLocker locker(fLock); uint64 oldEntry = X86PagingMethod64Bit::ClearTableEntry(entry); pinner.Unlock(); if ((oldEntry & X86_64_PTE_PRESENT) == 0) return B_ENTRY_NOT_FOUND; fMapCount--; if ((oldEntry & X86_64_PTE_ACCESSED) != 0) { // Note, that we only need to invalidate the address, if the // accessed flags was set, since only then the entry could have been // in any TLB. InvalidatePage(address); Flush(); // NOTE: Between clearing the page table entry and Flush() other // processors (actually even this processor with another thread of the // same team) could still access the page in question via their cached // entry. We can obviously lose a modified flag in this case, with the // effect that the page looks unmodified (and might thus be recycled), // but is actually modified. // In most cases this is harmless, but for vm_remove_all_page_mappings() // this is actually a problem. // Interestingly FreeBSD seems to ignore this problem as well // (cf. pmap_remove_all()), unless I've missed something. } locker.Detach(); // PageUnmapped() will unlock for us PageUnmapped(area, (oldEntry & X86_64_PTE_ADDRESS_MASK) / B_PAGE_SIZE, (oldEntry & X86_64_PTE_ACCESSED) != 0, (oldEntry & X86_64_PTE_DIRTY) != 0, updatePageQueue); return B_OK; }
status_t ARMVMTranslationMap32Bit::DebugMarkRangePresent(addr_t start, addr_t end, bool markPresent) { #if 0 start = ROUNDDOWN(start, B_PAGE_SIZE); if (start >= end) return B_OK; page_directory_entry *pd = fPagingStructures->pgdir_virt; do { int index = VADDR_TO_PDENT(start); if ((pd[index] & X86_PDE_PRESENT) == 0) { // no page table here, move the start up to access the next page // table start = ROUNDUP(start + 1, kPageTableAlignment); continue; } Thread* thread = thread_get_current_thread(); ThreadCPUPinner pinner(thread); page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt( pd[index] & X86_PDE_ADDRESS_MASK); for (index = VADDR_TO_PTENT(start); (index < 1024) && (start < end); index++, start += B_PAGE_SIZE) { if ((pt[index] & X86_PTE_PRESENT) == 0) { if (!markPresent) continue; X86PagingMethod32Bit::SetPageTableEntryFlags(&pt[index], X86_PTE_PRESENT); } else { if (markPresent) continue; page_table_entry oldEntry = X86PagingMethod32Bit::ClearPageTableEntryFlags(&pt[index], X86_PTE_PRESENT); if ((oldEntry & X86_PTE_ACCESSED) != 0) { // Note, that we only need to invalidate the address, if the // accessed flags was set, since only then the entry could // have been in any TLB. InvalidatePage(start); } } } } while (start != 0 && start < end); #endif return B_OK; }
status_t ARMVMTranslationMap32Bit::Unmap(addr_t start, addr_t end) { start = ROUNDDOWN(start, B_PAGE_SIZE); if (start >= end) return B_OK; TRACE("unmap_tmap: asked to free pages 0x%lx to 0x%lx\n", start, end); page_directory_entry *pd = fPagingStructures->pgdir_virt; do { int index = VADDR_TO_PDENT(start); if ((pd[index] & ARM_PDE_TYPE_MASK) == 0) { // no page table here, move the start up to access the next page // table start = ROUNDUP(start + 1, kPageTableAlignment); continue; } Thread* thread = thread_get_current_thread(); ThreadCPUPinner pinner(thread); page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt( pd[index] & ARM_PDE_ADDRESS_MASK); for (index = VADDR_TO_PTENT(start); (index < 1024) && (start < end); index++, start += B_PAGE_SIZE) { if ((pt[index] & ARM_PTE_TYPE_MASK) == 0) { // page mapping not valid continue; } TRACE("unmap_tmap: removing page 0x%lx\n", start); page_table_entry oldEntry = ARMPagingMethod32Bit::ClearPageTableEntryFlags(&pt[index], ARM_PTE_TYPE_MASK); fMapCount--; if (true /* (oldEntry & ARM_PTE_ACCESSED) != 0*/) { // Note, that we only need to invalidate the address, if the // accessed flags was set, since only then the entry could have // been in any TLB. InvalidatePage(start); } } } while (start != 0 && start < end); return B_OK; }
status_t X86VMTranslationMap64Bit::DebugMarkRangePresent(addr_t start, addr_t end, bool markPresent) { start = ROUNDDOWN(start, B_PAGE_SIZE); if (start >= end) return B_OK; TRACE("X86VMTranslationMap64Bit::DebugMarkRangePresent(%#" B_PRIxADDR ", %#" B_PRIxADDR ")\n", start, end); ThreadCPUPinner pinner(thread_get_current_thread()); do { uint64* pageTable = X86PagingMethod64Bit::PageTableForAddress( fPagingStructures->VirtualPML4(), start, fIsKernelMap, false, NULL, fPageMapper, fMapCount); if (pageTable == NULL) { // Move on to the next page table. start = ROUNDUP(start + 1, k64BitPageTableRange); continue; } for (uint32 index = start / B_PAGE_SIZE % k64BitTableEntryCount; index < k64BitTableEntryCount && start < end; index++, start += B_PAGE_SIZE) { if ((pageTable[index] & X86_64_PTE_PRESENT) == 0) { if (!markPresent) continue; X86PagingMethod64Bit::SetTableEntryFlags(&pageTable[index], X86_64_PTE_PRESENT); } else { if (markPresent) continue; uint64 oldEntry = X86PagingMethod64Bit::ClearTableEntryFlags( &pageTable[index], X86_64_PTE_PRESENT); if ((oldEntry & X86_64_PTE_ACCESSED) != 0) { // Note, that we only need to invalidate the address, if the // accessed flags was set, since only then the entry could // have been in any TLB. InvalidatePage(start); } } } } while (start != 0 && start < end); return B_OK; }
status_t ARMVMTranslationMap32Bit::Protect(addr_t start, addr_t end, uint32 attributes, uint32 memoryType) { start = ROUNDDOWN(start, B_PAGE_SIZE); if (start >= end) return B_OK; TRACE("protect_tmap: pages 0x%lx to 0x%lx, attributes %lx\n", start, end, attributes); #if 0 //IRA // compute protection flags uint32 newProtectionFlags = 0; if ((attributes & B_USER_PROTECTION) != 0) { newProtectionFlags = ARM_PTE_USER; if ((attributes & B_WRITE_AREA) != 0) newProtectionFlags |= ARM_PTE_WRITABLE; } else if ((attributes & B_KERNEL_WRITE_AREA) != 0) newProtectionFlags = ARM_PTE_WRITABLE; page_directory_entry *pd = fPagingStructures->pgdir_virt; do { int index = VADDR_TO_PDENT(start); if ((pd[index] & ARM_PDE_TYPE_MASK) == 0) { // no page table here, move the start up to access the next page // table start = ROUNDUP(start + 1, kPageTableAlignment); continue; } Thread* thread = thread_get_current_thread(); ThreadCPUPinner pinner(thread); page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt( pd[index] & ARM_PDE_ADDRESS_MASK); for (index = VADDR_TO_PTENT(start); index < 1024 && start < end; index++, start += B_PAGE_SIZE) { page_table_entry entry = pt[index]; if ((entry & ARM_PTE_PRESENT) == 0) { // page mapping not valid continue; } TRACE("protect_tmap: protect page 0x%lx\n", start); // set the new protection flags -- we want to do that atomically, // without changing the accessed or dirty flag page_table_entry oldEntry; while (true) { oldEntry = ARMPagingMethod32Bit::TestAndSetPageTableEntry( &pt[index], (entry & ~(ARM_PTE_PROTECTION_MASK | ARM_PTE_MEMORY_TYPE_MASK)) | newProtectionFlags | ARMPagingMethod32Bit::MemoryTypeToPageTableEntryFlags( memoryType), entry); if (oldEntry == entry) break; entry = oldEntry; } if ((oldEntry & ARM_PTE_ACCESSED) != 0) { // Note, that we only need to invalidate the address, if the // accessed flag was set, since only then the entry could have // been in any TLB. InvalidatePage(start); } } } while (start != 0 && start < end); #endif return B_OK; }
void ARMVMTranslationMap32Bit::UnmapArea(VMArea* area, bool deletingAddressSpace, bool ignoreTopCachePageFlags) { if (area->cache_type == CACHE_TYPE_DEVICE || area->wiring != B_NO_LOCK) { ARMVMTranslationMap32Bit::UnmapPages(area, area->Base(), area->Size(), true); return; } bool unmapPages = !deletingAddressSpace || !ignoreTopCachePageFlags; page_directory_entry* pd = fPagingStructures->pgdir_virt; RecursiveLocker locker(fLock); VMAreaMappings mappings; mappings.MoveFrom(&area->mappings); for (VMAreaMappings::Iterator it = mappings.GetIterator(); vm_page_mapping* mapping = it.Next();) { vm_page* page = mapping->page; page->mappings.Remove(mapping); VMCache* cache = page->Cache(); bool pageFullyUnmapped = false; if (!page->IsMapped()) { atomic_add(&gMappedPagesCount, -1); pageFullyUnmapped = true; } if (unmapPages || cache != area->cache) { addr_t address = area->Base() + ((page->cache_offset * B_PAGE_SIZE) - area->cache_offset); int index = VADDR_TO_PDENT(address); if ((pd[index] & ARM_PDE_TYPE_MASK) == 0) { panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but " "has no page dir entry", page, area, address); continue; } ThreadCPUPinner pinner(thread_get_current_thread()); page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt( pd[index] & ARM_PDE_ADDRESS_MASK); page_table_entry oldEntry = ARMPagingMethod32Bit::ClearPageTableEntry( &pt[VADDR_TO_PTENT(address)]); pinner.Unlock(); if ((oldEntry & ARM_PTE_TYPE_MASK) == 0) { panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but " "has no page table entry", page, area, address); continue; } // transfer the accessed/dirty flags to the page and invalidate // the mapping, if necessary if (true /*(oldEntry & ARM_PTE_ACCESSED) != 0*/) { // XXX IRA page->accessed = true; if (!deletingAddressSpace) InvalidatePage(address); } if (true /*(oldEntry & ARM_PTE_DIRTY) != 0*/) page->modified = true; if (pageFullyUnmapped) { DEBUG_PAGE_ACCESS_START(page); if (cache->temporary) vm_page_set_state(page, PAGE_STATE_INACTIVE); else if (page->modified) vm_page_set_state(page, PAGE_STATE_MODIFIED); else vm_page_set_state(page, PAGE_STATE_CACHED); DEBUG_PAGE_ACCESS_END(page); } } fMapCount--; } Flush(); // flush explicitely, since we directly use the lock locker.Unlock(); bool isKernelSpace = area->address_space == VMAddressSpace::Kernel(); uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY | (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0); while (vm_page_mapping* mapping = mappings.RemoveHead()) object_cache_free(gPageMappingsObjectCache, mapping, freeFlags); }
void ARMVMTranslationMap32Bit::UnmapPages(VMArea* area, addr_t base, size_t size, bool updatePageQueue) { if (size == 0) return; addr_t start = base; addr_t end = base + size - 1; TRACE("ARMVMTranslationMap32Bit::UnmapPages(%p, %#" B_PRIxADDR ", %#" B_PRIxADDR ")\n", area, start, end); page_directory_entry* pd = fPagingStructures->pgdir_virt; VMAreaMappings queue; RecursiveLocker locker(fLock); do { int index = VADDR_TO_PDENT(start); if ((pd[index] & ARM_PDE_TYPE_MASK) == 0) { // no page table here, move the start up to access the next page // table start = ROUNDUP(start + 1, kPageTableAlignment); continue; } Thread* thread = thread_get_current_thread(); ThreadCPUPinner pinner(thread); page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt( pd[index] & ARM_PDE_ADDRESS_MASK); for (index = VADDR_TO_PTENT(start); (index < 1024) && (start < end); index++, start += B_PAGE_SIZE) { page_table_entry oldEntry = ARMPagingMethod32Bit::ClearPageTableEntry(&pt[index]); if ((oldEntry & ARM_PTE_TYPE_MASK) == 0) continue; fMapCount--; if (true /*(oldEntry & ARM_PTE_ACCESSED) != 0*/) { // XXX IRA // Note, that we only need to invalidate the address, if the // accessed flags was set, since only then the entry could have // been in any TLB. InvalidatePage(start); } if (area->cache_type != CACHE_TYPE_DEVICE) { // get the page vm_page* page = vm_lookup_page( (oldEntry & ARM_PTE_ADDRESS_MASK) / B_PAGE_SIZE); ASSERT(page != NULL); DEBUG_PAGE_ACCESS_START(page); // transfer the accessed/dirty flags to the page if (/*(oldEntry & ARM_PTE_ACCESSED) != 0*/ true) // XXX IRA page->accessed = true; if (/*(oldEntry & ARM_PTE_DIRTY) != 0 */ true) page->modified = true; // remove the mapping object/decrement the wired_count of the // page if (area->wiring == B_NO_LOCK) { vm_page_mapping* mapping = NULL; vm_page_mappings::Iterator iterator = page->mappings.GetIterator(); while ((mapping = iterator.Next()) != NULL) { if (mapping->area == area) break; } ASSERT(mapping != NULL); area->mappings.Remove(mapping); page->mappings.Remove(mapping); queue.Add(mapping); } else page->DecrementWiredCount(); if (!page->IsMapped()) { atomic_add(&gMappedPagesCount, -1); if (updatePageQueue) { if (page->Cache()->temporary) vm_page_set_state(page, PAGE_STATE_INACTIVE); else if (page->modified) vm_page_set_state(page, PAGE_STATE_MODIFIED); else vm_page_set_state(page, PAGE_STATE_CACHED); } } DEBUG_PAGE_ACCESS_END(page); } } Flush(); // flush explicitly, since we directly use the lock } while (start != 0 && start < end); // TODO: As in UnmapPage() we can lose page dirty flags here. ATM it's not // really critical here, as in all cases this method is used, the unmapped // area range is unmapped for good (resized/cut) and the pages will likely // be freed. locker.Unlock(); // free removed mappings bool isKernelSpace = area->address_space == VMAddressSpace::Kernel(); uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY | (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0); while (vm_page_mapping* mapping = queue.RemoveHead()) object_cache_free(gPageMappingsObjectCache, mapping, freeFlags); }
/*! Caller must have locked the cache of the page to be unmapped. This object shouldn't be locked. */ status_t ARMVMTranslationMap32Bit::UnmapPage(VMArea* area, addr_t address, bool updatePageQueue) { ASSERT(address % B_PAGE_SIZE == 0); page_directory_entry* pd = fPagingStructures->pgdir_virt; TRACE("ARMVMTranslationMap32Bit::UnmapPage(%#" B_PRIxADDR ")\n", address); RecursiveLocker locker(fLock); int index = VADDR_TO_PDENT(address); if ((pd[index] & ARM_PDE_TYPE_MASK) == 0) return B_ENTRY_NOT_FOUND; ThreadCPUPinner pinner(thread_get_current_thread()); page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt( pd[index] & ARM_PDE_ADDRESS_MASK); index = VADDR_TO_PTENT(address); page_table_entry oldEntry = ARMPagingMethod32Bit::ClearPageTableEntry( &pt[index]); pinner.Unlock(); if ((oldEntry & ARM_PTE_TYPE_MASK) == 0) { // page mapping not valid return B_ENTRY_NOT_FOUND; } fMapCount--; if (true /*(oldEntry & ARM_PTE_ACCESSED) != 0*/) { // XXX IRA // Note, that we only need to invalidate the address, if the // accessed flags was set, since only then the entry could have been // in any TLB. InvalidatePage(address); Flush(); // NOTE: Between clearing the page table entry and Flush() other // processors (actually even this processor with another thread of the // same team) could still access the page in question via their cached // entry. We can obviously lose a modified flag in this case, with the // effect that the page looks unmodified (and might thus be recycled), // but is actually modified. // In most cases this is harmless, but for vm_remove_all_page_mappings() // this is actually a problem. // Interestingly FreeBSD seems to ignore this problem as well // (cf. pmap_remove_all()), unless I've missed something. } locker.Detach(); // PageUnmapped() will unlock for us PageUnmapped(area, (oldEntry & ARM_PTE_ADDRESS_MASK) / B_PAGE_SIZE, true /*(oldEntry & ARM_PTE_ACCESSED) != 0*/, true /*(oldEntry & ARM_PTE_DIRTY) != 0*/, updatePageQueue); return B_OK; }
bool X86VMTranslationMap64Bit::ClearAccessedAndModified(VMArea* area, addr_t address, bool unmapIfUnaccessed, bool& _modified) { ASSERT(address % B_PAGE_SIZE == 0); TRACE("X86VMTranslationMap64Bit::ClearAccessedAndModified(%#" B_PRIxADDR ")\n", address); RecursiveLocker locker(fLock); ThreadCPUPinner pinner(thread_get_current_thread()); uint64* entry = X86PagingMethod64Bit::PageTableEntryForAddress( fPagingStructures->VirtualPML4(), address, fIsKernelMap, false, NULL, fPageMapper, fMapCount); if (entry == NULL) return false; uint64 oldEntry; if (unmapIfUnaccessed) { while (true) { oldEntry = *entry; if ((oldEntry & X86_64_PTE_PRESENT) == 0) { // page mapping not valid return false; } if (oldEntry & X86_64_PTE_ACCESSED) { // page was accessed -- just clear the flags oldEntry = X86PagingMethod64Bit::ClearTableEntryFlags(entry, X86_64_PTE_ACCESSED | X86_64_PTE_DIRTY); break; } // page hasn't been accessed -- unmap it if (X86PagingMethod64Bit::TestAndSetTableEntry(entry, 0, oldEntry) == oldEntry) { break; } // something changed -- check again } } else { oldEntry = X86PagingMethod64Bit::ClearTableEntryFlags(entry, X86_64_PTE_ACCESSED | X86_64_PTE_DIRTY); } pinner.Unlock(); _modified = (oldEntry & X86_64_PTE_DIRTY) != 0; if ((oldEntry & X86_64_PTE_ACCESSED) != 0) { // Note, that we only need to invalidate the address, if the // accessed flags was set, since only then the entry could have been // in any TLB. InvalidatePage(address); Flush(); return true; } if (!unmapIfUnaccessed) return false; // We have unmapped the address. Do the "high level" stuff. fMapCount--; locker.Detach(); // UnaccessedPageUnmapped() will unlock for us UnaccessedPageUnmapped(area, (oldEntry & X86_64_PTE_ADDRESS_MASK) / B_PAGE_SIZE); return false; }
status_t X86VMTranslationMap64Bit::Protect(addr_t start, addr_t end, uint32 attributes, uint32 memoryType) { start = ROUNDDOWN(start, B_PAGE_SIZE); if (start >= end) return B_OK; TRACE("X86VMTranslationMap64Bit::Protect(%#" B_PRIxADDR ", %#" B_PRIxADDR ", %#" B_PRIx32 ")\n", start, end, attributes); // compute protection flags uint64 newProtectionFlags = 0; if ((attributes & B_USER_PROTECTION) != 0) { newProtectionFlags = X86_64_PTE_USER; if ((attributes & B_WRITE_AREA) != 0) newProtectionFlags |= X86_64_PTE_WRITABLE; } else if ((attributes & B_KERNEL_WRITE_AREA) != 0) newProtectionFlags = X86_64_PTE_WRITABLE; ThreadCPUPinner pinner(thread_get_current_thread()); do { uint64* pageTable = X86PagingMethod64Bit::PageTableForAddress( fPagingStructures->VirtualPML4(), start, fIsKernelMap, false, NULL, fPageMapper, fMapCount); if (pageTable == NULL) { // Move on to the next page table. start = ROUNDUP(start + 1, k64BitPageTableRange); continue; } for (uint32 index = start / B_PAGE_SIZE % k64BitTableEntryCount; index < k64BitTableEntryCount && start < end; index++, start += B_PAGE_SIZE) { uint64 entry = pageTable[index]; if ((entry & X86_64_PTE_PRESENT) == 0) continue; TRACE("X86VMTranslationMap64Bit::Protect(): protect page %#" B_PRIxADDR "\n", start); // set the new protection flags -- we want to do that atomically, // without changing the accessed or dirty flag uint64 oldEntry; while (true) { oldEntry = X86PagingMethod64Bit::TestAndSetTableEntry( &pageTable[index], (entry & ~(X86_64_PTE_PROTECTION_MASK | X86_64_PTE_MEMORY_TYPE_MASK)) | newProtectionFlags | X86PagingMethod64Bit::MemoryTypeToPageTableEntryFlags( memoryType), entry); if (oldEntry == entry) break; entry = oldEntry; } if ((oldEntry & X86_64_PTE_ACCESSED) != 0) { // Note, that we only need to invalidate the address, if the // accessed flag was set, since only then the entry could have // been in any TLB. InvalidatePage(start); } } } while (start != 0 && start < end); return B_OK; }
void X86VMTranslationMap64Bit::UnmapArea(VMArea* area, bool deletingAddressSpace, bool ignoreTopCachePageFlags) { TRACE("X86VMTranslationMap64Bit::UnmapArea(%p)\n", area); if (area->cache_type == CACHE_TYPE_DEVICE || area->wiring != B_NO_LOCK) { X86VMTranslationMap64Bit::UnmapPages(area, area->Base(), area->Size(), true); return; } bool unmapPages = !deletingAddressSpace || !ignoreTopCachePageFlags; RecursiveLocker locker(fLock); ThreadCPUPinner pinner(thread_get_current_thread()); VMAreaMappings mappings; mappings.MoveFrom(&area->mappings); for (VMAreaMappings::Iterator it = mappings.GetIterator(); vm_page_mapping* mapping = it.Next();) { vm_page* page = mapping->page; page->mappings.Remove(mapping); VMCache* cache = page->Cache(); bool pageFullyUnmapped = false; if (!page->IsMapped()) { atomic_add(&gMappedPagesCount, -1); pageFullyUnmapped = true; } if (unmapPages || cache != area->cache) { addr_t address = area->Base() + ((page->cache_offset * B_PAGE_SIZE) - area->cache_offset); uint64* entry = X86PagingMethod64Bit::PageTableEntryForAddress( fPagingStructures->VirtualPML4(), address, fIsKernelMap, false, NULL, fPageMapper, fMapCount); if (entry == NULL) { panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but " "has no page table", page, area, address); continue; } uint64 oldEntry = X86PagingMethod64Bit::ClearTableEntry(entry); if ((oldEntry & X86_64_PTE_PRESENT) == 0) { panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but " "has no page table entry", page, area, address); continue; } // transfer the accessed/dirty flags to the page and invalidate // the mapping, if necessary if ((oldEntry & X86_64_PTE_ACCESSED) != 0) { page->accessed = true; if (!deletingAddressSpace) InvalidatePage(address); } if ((oldEntry & X86_64_PTE_DIRTY) != 0) page->modified = true; if (pageFullyUnmapped) { DEBUG_PAGE_ACCESS_START(page); if (cache->temporary) vm_page_set_state(page, PAGE_STATE_INACTIVE); else if (page->modified) vm_page_set_state(page, PAGE_STATE_MODIFIED); else vm_page_set_state(page, PAGE_STATE_CACHED); DEBUG_PAGE_ACCESS_END(page); } } fMapCount--; } Flush(); // flush explicitely, since we directly use the lock locker.Unlock(); bool isKernelSpace = area->address_space == VMAddressSpace::Kernel(); uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY | (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0); while (vm_page_mapping* mapping = mappings.RemoveHead()) object_cache_free(gPageMappingsObjectCache, mapping, freeFlags); }
void X86VMTranslationMap64Bit::UnmapPages(VMArea* area, addr_t base, size_t size, bool updatePageQueue) { if (size == 0) return; addr_t start = base; addr_t end = base + size - 1; TRACE("X86VMTranslationMap64Bit::UnmapPages(%p, %#" B_PRIxADDR ", %#" B_PRIxADDR ")\n", area, start, end); VMAreaMappings queue; RecursiveLocker locker(fLock); ThreadCPUPinner pinner(thread_get_current_thread()); do { uint64* pageTable = X86PagingMethod64Bit::PageTableForAddress( fPagingStructures->VirtualPML4(), start, fIsKernelMap, false, NULL, fPageMapper, fMapCount); if (pageTable == NULL) { // Move on to the next page table. start = ROUNDUP(start + 1, k64BitPageTableRange); continue; } for (uint32 index = start / B_PAGE_SIZE % k64BitTableEntryCount; index < k64BitTableEntryCount && start < end; index++, start += B_PAGE_SIZE) { uint64 oldEntry = X86PagingMethod64Bit::ClearTableEntry( &pageTable[index]); if ((oldEntry & X86_64_PTE_PRESENT) == 0) continue; fMapCount--; if ((oldEntry & X86_64_PTE_ACCESSED) != 0) { // Note, that we only need to invalidate the address, if the // accessed flags was set, since only then the entry could have // been in any TLB. InvalidatePage(start); } if (area->cache_type != CACHE_TYPE_DEVICE) { // get the page vm_page* page = vm_lookup_page( (oldEntry & X86_64_PTE_ADDRESS_MASK) / B_PAGE_SIZE); ASSERT(page != NULL); DEBUG_PAGE_ACCESS_START(page); // transfer the accessed/dirty flags to the page if ((oldEntry & X86_64_PTE_ACCESSED) != 0) page->accessed = true; if ((oldEntry & X86_64_PTE_DIRTY) != 0) page->modified = true; // remove the mapping object/decrement the wired_count of the // page if (area->wiring == B_NO_LOCK) { vm_page_mapping* mapping = NULL; vm_page_mappings::Iterator iterator = page->mappings.GetIterator(); while ((mapping = iterator.Next()) != NULL) { if (mapping->area == area) break; } ASSERT(mapping != NULL); area->mappings.Remove(mapping); page->mappings.Remove(mapping); queue.Add(mapping); } else page->DecrementWiredCount(); if (!page->IsMapped()) { atomic_add(&gMappedPagesCount, -1); if (updatePageQueue) { if (page->Cache()->temporary) vm_page_set_state(page, PAGE_STATE_INACTIVE); else if (page->modified) vm_page_set_state(page, PAGE_STATE_MODIFIED); else vm_page_set_state(page, PAGE_STATE_CACHED); } } DEBUG_PAGE_ACCESS_END(page); } } Flush(); // flush explicitly, since we directly use the lock } while (start != 0 && start < end); // TODO: As in UnmapPage() we can lose page dirty flags here. ATM it's not // really critical here, as in all cases this method is used, the unmapped // area range is unmapped for good (resized/cut) and the pages will likely // be freed. locker.Unlock(); // free removed mappings bool isKernelSpace = area->address_space == VMAddressSpace::Kernel(); uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY | (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0); while (vm_page_mapping* mapping = queue.RemoveHead()) object_cache_free(gPageMappingsObjectCache, mapping, freeFlags); }
void CDocWindow::SetupPageView(bool bSetScrollbars) { #define OUTSIDE_PADDING_X 10 #define OUTSIDE_PADDING_Y 10 if (!m_hWnd) return; if (!m_pClientDC) return; if (m_PageRect.IsRectEmpty()) return; // Suspend any selection tracking m_Select.SuspendTracking(); // Clear the old page from the offscreen DC m_pClientDC->Clear(); // Invalidate the old page rect (m_PageViewRect) before we change it InvalidatePage(); // Now figure out how much window area we have for the document display CRect WndRect; GetClientRect(&WndRect); // Subtract the outside padding WndRect.bottom -= m_ShadowSize.cx; WndRect.right -= m_ShadowSize.cy; WndRect.InflateRect(-OUTSIDE_PADDING_X, -OUTSIDE_PADDING_Y); // Convert to Document coordinates m_pClientDC->GetDeviceMatrix().Inverse().Transform(WndRect); int xVisible = WndRect.Width(); int yVisible = WndRect.Height(); // Fit the extended page into the available window space int dxPage = m_PageRect.Width(); int dyPage = m_PageRect.Height(); if (!dxPage) dxPage = 1; if (!dyPage) dyPage = 1; double fxPageScale = (double)xVisible / dxPage; double fyPageScale = (double)yVisible / dyPage; double fFitInWindowScale = min(fxPageScale, fyPageScale); if (!fFitInWindowScale) fFitInWindowScale = 1.0; // Default the scroll position to the center of the current page int xPos = -1; int yPos = -1; // Special setting to indicate a zoom into the center of the SELECTED OBJECT if (m_fZoomScale == -1.0 && m_Select.GetSelected()) { CRect DestRect = m_Select.GetSelected()->GetDestRectTransformed(); int dxObject = DestRect.Width(); int dyObject = DestRect.Height(); if (!dxObject) dxObject = 1; if (!dyObject) dyObject = 1; double fxObjectScale = (double)xVisible / dxObject; double fyObjectScale = (double)yVisible / dyObject; double fFitScaleObject = min(fxObjectScale, fyObjectScale); m_fZoomScale = fFitScaleObject / fFitInWindowScale; m_iZoom = dtoi(m_fZoomScale - 1.0); // Set the (x,y) position to be the center of the object xPos = (DestRect.left + DestRect.right) / 2; yPos = (DestRect.top + DestRect.bottom) / 2; } // Special setting to indicate a zoom into the width of the FULL PAGE if (m_fZoomScale <= 0) { m_fZoomScale = fxPageScale / fFitInWindowScale; m_iZoom = dtoi(m_fZoomScale - 1.0); } // Compute the scale and adjust the visible area double fScale = fFitInWindowScale * m_fZoomScale; xVisible = dtoi((double)xVisible / fScale); yVisible = dtoi((double)yVisible / fScale); // If the (x,y) position was set to the object center, adjust it by 1/2 the visible area if (xPos >= 0 || yPos >= 0) { xPos -= xVisible / 2; yPos -= yVisible / 2; } // Setup the scrollbars if (bSetScrollbars) SetupScrollbars(xPos, yPos, dxPage, dyPage, xVisible, yVisible, true/*bRedraw*/); // Get the updated position xPos = GetScrollPos(SB_HORZ); yPos = GetScrollPos(SB_VERT); // Calculate a view matrix int xExcess = xVisible - dxPage; if (xExcess < 0) xExcess = 0; int yExcess = yVisible - dyPage; if (yExcess < 0) yExcess = 0; CAGMatrix ViewMatrix; ViewMatrix.Translate(xExcess/2 - xPos, yExcess/2 - yPos); ViewMatrix.Scale(fScale, fScale); ViewMatrix.Translate(WndRect.left, WndRect.top); m_pClientDC->SetViewingMatrix(ViewMatrix); // Compute the new page view rectangle (screen coordinates) CSize PageSize(m_PageRect.Width(), m_PageRect.Height()); m_pClientDC->SetClipToView(PageSize, &m_PageViewRect, true/*bIncludeRawDC*/); // Invalidate the new page rect (m_PageViewRect) now that we've changed it InvalidatePage(); // Resume any selection tracking m_Select.ResumeTracking(); }
bool ARMVMTranslationMap32Bit::ClearAccessedAndModified(VMArea* area, addr_t address, bool unmapIfUnaccessed, bool& _modified) { ASSERT(address % B_PAGE_SIZE == 0); page_directory_entry* pd = fPagingStructures->pgdir_virt; TRACE("ARMVMTranslationMap32Bit::ClearAccessedAndModified(%#" B_PRIxADDR ")\n", address); RecursiveLocker locker(fLock); int index = VADDR_TO_PDENT(address); if ((pd[index] & ARM_PDE_TYPE_MASK) == 0) return false; ThreadCPUPinner pinner(thread_get_current_thread()); page_table_entry* pt = (page_table_entry*)fPageMapper->GetPageTableAt( pd[index] & ARM_PDE_ADDRESS_MASK); index = VADDR_TO_PTENT(address); // perform the deed page_table_entry oldEntry; if (unmapIfUnaccessed) { while (true) { oldEntry = pt[index]; if ((oldEntry & ARM_PTE_TYPE_MASK) == 0) { // page mapping not valid return false; } #if 0 //IRA if (oldEntry & ARM_PTE_ACCESSED) { // page was accessed -- just clear the flags oldEntry = ARMPagingMethod32Bit::ClearPageTableEntryFlags( &pt[index], ARM_PTE_ACCESSED | ARM_PTE_DIRTY); break; } #endif // page hasn't been accessed -- unmap it if (ARMPagingMethod32Bit::TestAndSetPageTableEntry(&pt[index], 0, oldEntry) == oldEntry) { break; } // something changed -- check again } } else { #if 0 //IRA oldEntry = ARMPagingMethod32Bit::ClearPageTableEntryFlags(&pt[index], ARM_PTE_ACCESSED | ARM_PTE_DIRTY); #else oldEntry = pt[index]; #endif } pinner.Unlock(); _modified = true /* (oldEntry & ARM_PTE_DIRTY) != 0 */; // XXX IRA if (true /*(oldEntry & ARM_PTE_ACCESSED) != 0*/) { // Note, that we only need to invalidate the address, if the // accessed flags was set, since only then the entry could have been // in any TLB. InvalidatePage(address); Flush(); return true; } if (!unmapIfUnaccessed) return false; // We have unmapped the address. Do the "high level" stuff. fMapCount--; locker.Detach(); // UnaccessedPageUnmapped() will unlock for us UnaccessedPageUnmapped(area, (oldEntry & ARM_PTE_ADDRESS_MASK) / B_PAGE_SIZE); return false; }
status_t M68KVMTranslationMap040::Unmap(addr_t start, addr_t end) { start = ROUNDDOWN(start, B_PAGE_SIZE); if (start >= end) return B_OK; TRACE("M68KVMTranslationMap040::Unmap: asked to free pages 0x%lx to 0x%lx\n", start, end); page_root_entry *pr = fPagingStructures->pgroot_virt; page_directory_entry *pd; page_table_entry *pt; int index; do { index = VADDR_TO_PRENT(start); if (PRE_TYPE(pr[index]) != DT_ROOT) { // no pagedir here, move the start up to access the next page // dir group start = ROUNDUP(start + 1, kPageDirAlignment); continue; } Thread* thread = thread_get_current_thread(); ThreadCPUPinner pinner(thread); pd = (page_directory_entry*)MapperGetPageTableAt( PRE_TO_PA(pr[index])); // we want the table at rindex, not at rindex%(tbl/page) //pd += (index % NUM_DIRTBL_PER_PAGE) * NUM_DIRENT_PER_TBL; index = VADDR_TO_PDENT(start); if (PDE_TYPE(pd[index]) != DT_DIR) { // no pagedir here, move the start up to access the next page // table group start = ROUNDUP(start + 1, kPageTableAlignment); continue; } pt = (page_table_entry*)MapperGetPageTableAt( PDE_TO_PA(pd[index])); // we want the table at rindex, not at rindex%(tbl/page) //pt += (index % NUM_PAGETBL_PER_PAGE) * NUM_PAGEENT_PER_TBL; for (index = VADDR_TO_PTENT(start); (index < NUM_PAGEENT_PER_TBL) && (start < end); index++, start += B_PAGE_SIZE) { if (PTE_TYPE(pt[index]) != DT_PAGE && PTE_TYPE(pt[index]) != DT_INDIRECT) { // page mapping not valid continue; } TRACE("::Unmap: removing page 0x%lx\n", start); page_table_entry oldEntry = M68KPagingMethod040::ClearPageTableEntry(&pt[index]); fMapCount--; if ((oldEntry & M68K_PTE_ACCESSED) != 0) { // Note, that we only need to invalidate the address, if the // accessed flags was set, since only then the entry could have // been in any TLB. InvalidatePage(start); } } } while (start != 0 && start < end); return B_OK; }