diff --git a/headers/private/kernel/vm/VMTranslationMap.h b/headers/private/kernel/vm/VMTranslationMap.h index d0ad63dcc9..cffde94794 100644 --- a/headers/private/kernel/vm/VMTranslationMap.h +++ b/headers/private/kernel/vm/VMTranslationMap.h @@ -20,6 +20,9 @@ struct vm_page_reservation; struct VMTranslationMap { + struct ReverseMappingInfoCallback; + +public: VMTranslationMap(); virtual ~VMTranslationMap(); @@ -72,6 +75,12 @@ struct VMTranslationMap { virtual void Flush() = 0; + // backends for KDL commands + virtual void DebugPrintMappingInfo(addr_t virtualAddress); + virtual bool DebugGetReverseMappingInfo( + phys_addr_t physicalAddress, + ReverseMappingInfoCallback& callback); + protected: void PageUnmapped(VMArea* area, page_num_t pageNumber, bool accessed, @@ -85,6 +94,13 @@ protected: }; +struct VMTranslationMap::ReverseMappingInfoCallback { + virtual ~ReverseMappingInfoCallback(); + + virtual bool HandleVirtualAddress(addr_t virtualAddress) = 0; +}; + + struct VMPhysicalPageMapper { VMPhysicalPageMapper(); virtual ~VMPhysicalPageMapper(); diff --git a/src/system/kernel/arch/x86/paging/pae/X86VMTranslationMapPAE.cpp b/src/system/kernel/arch/x86/paging/pae/X86VMTranslationMapPAE.cpp index c9e7b27a93..ce63b91ed4 100644 --- a/src/system/kernel/arch/x86/paging/pae/X86VMTranslationMapPAE.cpp +++ b/src/system/kernel/arch/x86/paging/pae/X86VMTranslationMapPAE.cpp @@ -1091,6 +1091,137 @@ X86VMTranslationMapPAE::ClearAccessedAndModified(VMArea* area, addr_t address, } +void +X86VMTranslationMapPAE::DebugPrintMappingInfo(addr_t virtualAddress) +{ + // get the page directory + pae_page_directory_entry* const* pdpt + = fPagingStructures->VirtualPageDirs(); + pae_page_directory_entry* pageDirectory = pdpt[virtualAddress >> 30]; + kprintf("page directory: %p (PDPT[%zu])\n", pageDirectory, + virtualAddress >> 30); + + // get the page directory entry + pae_page_directory_entry* pageDirEntry + = X86PagingMethodPAE::PageDirEntryForAddress(pdpt, virtualAddress); + kprintf("page directory entry %zu (%p): %#" B_PRIx64 "\n", + pageDirEntry - pageDirectory, pageDirEntry, *pageDirEntry); + + kprintf(" access: "); + if ((*pageDirEntry & X86_PAE_PDE_PRESENT) != 0) + kprintf(" present"); + if ((*pageDirEntry & X86_PAE_PDE_WRITABLE) != 0) + kprintf(" writable"); + if ((*pageDirEntry & X86_PAE_PDE_USER) != 0) + kprintf(" user"); + if ((*pageDirEntry & X86_PAE_PDE_NOT_EXECUTABLE) == 0) + kprintf(" executable"); + if ((*pageDirEntry & X86_PAE_PDE_LARGE_PAGE) != 0) + kprintf(" large"); + + kprintf("\n caching:"); + if ((*pageDirEntry & X86_PAE_PDE_WRITE_THROUGH) != 0) + kprintf(" write-through"); + if ((*pageDirEntry & X86_PAE_PDE_CACHING_DISABLED) != 0) + kprintf(" uncached"); + + kprintf("\n flags: "); + if ((*pageDirEntry & X86_PAE_PDE_ACCESSED) != 0) + kprintf(" accessed"); + kprintf("\n"); + + if ((*pageDirEntry & X86_PAE_PDE_PRESENT) == 0) + return; + + // get the page table entry + pae_page_table_entry* pageTable + = (pae_page_table_entry*)X86PagingMethodPAE::Method() + ->PhysicalPageMapper()->InterruptGetPageTableAt( + *pageDirEntry & X86_PAE_PDE_ADDRESS_MASK); + kprintf("page table: %#" B_PRIx64 "\n", + *pageDirEntry & X86_PAE_PDE_ADDRESS_MASK); + size_t pteIndex = virtualAddress / B_PAGE_SIZE % kPAEPageTableEntryCount; + pae_page_table_entry entry = pageTable[pteIndex]; + kprintf("page table entry %zu (phys: %#" B_PRIx64 "): %#" B_PRIx64 "\n", + pteIndex, + (*pageDirEntry & X86_PAE_PDE_ADDRESS_MASK) + + pteIndex * sizeof(pae_page_table_entry), + entry); + + kprintf(" access: "); + if ((entry & X86_PAE_PTE_PRESENT) != 0) + kprintf(" present"); + if ((entry & X86_PAE_PTE_WRITABLE) != 0) + kprintf(" writable"); + if ((entry & X86_PAE_PTE_USER) != 0) + kprintf(" user"); + if ((entry & X86_PAE_PTE_NOT_EXECUTABLE) == 0) + kprintf(" executable"); + if ((entry & X86_PAE_PTE_GLOBAL) == 0) + kprintf(" global"); + + kprintf("\n caching:"); + if ((entry & X86_PAE_PTE_WRITE_THROUGH) != 0) + kprintf(" write-through"); + if ((entry & X86_PAE_PTE_CACHING_DISABLED) != 0) + kprintf(" uncached"); + if ((entry & X86_PAE_PTE_PAT) != 0) + kprintf(" PAT"); + + kprintf("\n flags: "); + if ((entry & X86_PAE_PTE_ACCESSED) != 0) + kprintf(" accessed"); + if ((entry & X86_PAE_PTE_DIRTY) != 0) + kprintf(" dirty"); + kprintf("\n"); + + if ((entry & X86_PAE_PTE_PRESENT) != 0) { + kprintf(" address: %#" B_PRIx64 "\n", + entry & X86_PAE_PTE_ADDRESS_MASK); + } +} + + +bool +X86VMTranslationMapPAE::DebugGetReverseMappingInfo(phys_addr_t physicalAddress, + ReverseMappingInfoCallback& callback) +{ + pae_page_directory_entry* const* pdpt + = fPagingStructures->VirtualPageDirs(); + for (uint32 pageDirIndex = fIsKernelMap ? 2 : 0; + pageDirIndex < (fIsKernelMap ? 4 : 2); pageDirIndex++) { + // iterate through the page directory + pae_page_directory_entry* pageDirectory = pdpt[pageDirIndex]; + for (uint32 pdeIndex = 0; pdeIndex < kPAEPageDirEntryCount; + pdeIndex++) { + pae_page_directory_entry& pageDirEntry = pageDirectory[pdeIndex]; + if ((pageDirEntry & X86_PAE_PDE_ADDRESS_MASK) == 0) + continue; + + // get and iterate through the page table + pae_page_table_entry* pageTable + = (pae_page_table_entry*)X86PagingMethodPAE::Method() + ->PhysicalPageMapper()->InterruptGetPageTableAt( + pageDirEntry & X86_PAE_PDE_ADDRESS_MASK); + for (uint32 pteIndex = 0; pteIndex < kPAEPageTableEntryCount; + pteIndex++) { + pae_page_table_entry entry = pageTable[pteIndex]; + if ((entry & X86_PAE_PTE_PRESENT) != 0 + && (entry & X86_PAE_PTE_ADDRESS_MASK) == physicalAddress) { + addr_t virtualAddress = pageDirIndex * kPAEPageDirRange + + pdeIndex * kPAEPageTableRange + + pteIndex * B_PAGE_SIZE; + if (callback.HandleVirtualAddress(virtualAddress)) + return true; + } + } + } + } + + return false; +} + + X86PagingStructures* X86VMTranslationMapPAE::PagingStructures() const { diff --git a/src/system/kernel/arch/x86/paging/pae/X86VMTranslationMapPAE.h b/src/system/kernel/arch/x86/paging/pae/X86VMTranslationMapPAE.h index 59bd5ff96b..2873fc144c 100644 --- a/src/system/kernel/arch/x86/paging/pae/X86VMTranslationMapPAE.h +++ b/src/system/kernel/arch/x86/paging/pae/X86VMTranslationMapPAE.h @@ -59,6 +59,11 @@ struct X86VMTranslationMapPAE : X86VMTranslationMap { bool unmapIfUnaccessed, bool& _modified); + virtual void DebugPrintMappingInfo(addr_t virtualAddress); + virtual bool DebugGetReverseMappingInfo( + phys_addr_t physicalAddress, + ReverseMappingInfoCallback& callback); + virtual X86PagingStructures* PagingStructures() const; inline X86PagingStructuresPAE* PagingStructuresPAE() const { return fPagingStructures; } diff --git a/src/system/kernel/vm/VMTranslationMap.cpp b/src/system/kernel/vm/VMTranslationMap.cpp index dafbaedc69..ee937db3a3 100644 --- a/src/system/kernel/vm/VMTranslationMap.cpp +++ b/src/system/kernel/vm/VMTranslationMap.cpp @@ -116,6 +116,39 @@ VMTranslationMap::UnmapArea(VMArea* area, bool deletingAddressSpace, } +/*! Print mapping information for a virtual address. + The method navigates the paging structures and prints all relevant + information on the way. + The method is invoked from a KDL command. The default implementation is a + no-op. + \param virtualAddress The virtual address to look up. +*/ +void +VMTranslationMap::DebugPrintMappingInfo(addr_t virtualAddress) +{ +} + + +/*! Find virtual addresses mapped to the given physical address. + For each virtual address the method finds, it invokes the callback object's + HandleVirtualAddress() method. When that method returns \c true, the search + is terminated and \c true is returned. + The method is invoked from a KDL command. The default implementation is a + no-op. + \param physicalAddress The physical address to search for. + \param callback Callback object to be notified of each found virtual + address. + \return \c true, if for a found virtual address the callback's + HandleVirtualAddress() returned \c true, \c false otherwise. +*/ +bool +VMTranslationMap::DebugGetReverseMappingInfo(phys_addr_t physicalAddress, + ReverseMappingInfoCallback& callback) +{ + return false; +} + + /*! Called by UnmapPage() after performing the architecture specific part. Looks up the page, updates its flags, removes the page-area mapping, and requeues the page, if necessary. @@ -227,6 +260,14 @@ VMTranslationMap::UnaccessedPageUnmapped(VMArea* area, page_num_t pageNumber) } +// #pragma mark - ReverseMappingInfoCallback + + +VMTranslationMap::ReverseMappingInfoCallback::~ReverseMappingInfoCallback() +{ +} + + // #pragma mark - VMPhysicalPageMapper diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index c9ebda44ef..0d2dea13b6 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -3410,6 +3410,147 @@ dump_available_memory(int argc, char** argv) } +static int +dump_mapping_info(int argc, char** argv) +{ + bool reverseLookup = false; + bool pageLookup = false; + + int argi = 1; + for (; argi < argc && argv[argi][0] == '-'; argi++) { + const char* arg = argv[argi]; + if (strcmp(arg, "-r") == 0) { + reverseLookup = true; + } else if (strcmp(arg, "-p") == 0) { + reverseLookup = true; + pageLookup = true; + } else { + print_debugger_command_usage(argv[0]); + return 0; + } + } + + // We need at least one argument, the address. Optionally a team ID can be + // specified. + if (argi >= argc || argi + 1 < argc) { + print_debugger_command_usage(argv[0]); + return 0; + } + + uint64 addressValue; + if (!evaluate_debug_expression(argv[argi++], &addressValue, false)) + return 0; + + uint64 teamID = B_CURRENT_TEAM; + bool teamSpecified = argi < argc; + if (teamSpecified) { + if (!evaluate_debug_expression(argv[argi++], &teamID, false)) + return 0; + } + + if (reverseLookup) { + phys_addr_t physicalAddress; + if (pageLookup) { + vm_page* page = (vm_page*)(addr_t)addressValue; + physicalAddress = page->physical_page_number * B_PAGE_SIZE; + } else { + physicalAddress = (phys_addr_t)addressValue; + physicalAddress -= physicalAddress % B_PAGE_SIZE; + } + + kprintf(" Team Virtual Address Area\n"); + kprintf("--------------------------------------\n"); + + struct Callback : VMTranslationMap::ReverseMappingInfoCallback { + Callback() + : + fAddressSpace(NULL) + { + } + + void SetAddressSpace(VMAddressSpace* addressSpace) + { + fAddressSpace = addressSpace; + } + + virtual bool HandleVirtualAddress(addr_t virtualAddress) + { + kprintf("%8" B_PRId32 " %#18" B_PRIxADDR, fAddressSpace->ID(), + virtualAddress); + if (VMArea* area = fAddressSpace->LookupArea(virtualAddress)) + kprintf(" %8" B_PRId32 " %s\n", area->id, area->name); + else + kprintf("\n"); + return false; + } + + private: + VMAddressSpace* fAddressSpace; + } callback; + + if (teamSpecified) { + // team specified -- get its address space + VMAddressSpace* addressSpace; + if (teamID == B_CURRENT_TEAM) { + Thread* thread = debug_get_debugged_thread(); + if (thread == NULL || thread->team == NULL) { + kprintf("Failed to get team!\n"); + return 0; + } + + addressSpace = thread->team->address_space; + } else + addressSpace = VMAddressSpace::DebugGet(teamID); + + if (addressSpace == NULL) { + kprintf("Failed to get address space!\n"); + return 0; + } + + callback.SetAddressSpace(addressSpace); + addressSpace->TranslationMap()->DebugGetReverseMappingInfo( + physicalAddress, callback); + } else { + // no team specified -- iterate through all address spaces + for (VMAddressSpace* addressSpace = VMAddressSpace::DebugFirst(); + addressSpace != NULL; + addressSpace = VMAddressSpace::DebugNext(addressSpace)) { + callback.SetAddressSpace(addressSpace); + addressSpace->TranslationMap()->DebugGetReverseMappingInfo( + physicalAddress, callback); + } + } + } else { + // get the address space + addr_t virtualAddress = (addr_t)addressValue; + virtualAddress -= virtualAddress % B_PAGE_SIZE; + VMAddressSpace* addressSpace; + if (IS_KERNEL_ADDRESS(virtualAddress)) { + addressSpace = VMAddressSpace::Kernel(); + } else if (!teamSpecified || teamID == B_CURRENT_TEAM) { + Thread* thread = debug_get_debugged_thread(); + if (thread == NULL || thread->team == NULL) { + kprintf("Failed to get team!\n"); + return 0; + } + + addressSpace = thread->team->address_space; + } else + addressSpace = VMAddressSpace::DebugGet(teamID); + + if (addressSpace == NULL) { + kprintf("Failed to get address space!\n"); + return 0; + } + + // let the translation map implementation do the job + addressSpace->TranslationMap()->DebugPrintMappingInfo(virtualAddress); + } + + return 0; +} + + /*! Deletes all areas and reserved regions in the given address space. The caller must ensure that none of the areas has any wired ranges. @@ -3935,6 +4076,20 @@ vm_init(kernel_args* args) add_debugger_command("db", &display_mem, "dump memory bytes (8-bit)"); add_debugger_command("string", &display_mem, "dump strings"); + add_debugger_command_etc("mapping", &dump_mapping_info, + "Print address mapping information", + "[ \"-r\" | \"-p\" ]
[ ]\n" + "Prints low-level page mapping information for a given address. If\n" + "neither \"-r\" nor \"-p\" are specified,
is a virtual\n" + "address that is looked up in the translation map of the current\n" + "team, respectively the team specified by ID . If \"-r\" is\n" + "specified,
is a physical address that is searched in the\n" + "translation map of all teams, respectively the team specified by ID\n" + " . If \"-p\" is specified,
is the address of a\n" + "vm_page structure. The behavior is equivalent to specifying \"-r\"\n" + "with the physical address of that page.\n", + 0); + TRACE(("vm_init: exit\n")); vm_cache_init_post_heap();