Add KDL command "mapping"

* VMTranslationMap:
  - Add DebugPrintMappingInfo(): Given a virtual address it is supposed
    to print the paging structure information for that address. To be
    implemented by derived classes.
  - Add DebugGetReverseMappingInfo(): Given a physical addresss it is
    supposed to find all virtual addresses mapped to it. To be
    implemented by derived classes.
* X86VMTranslationMapPAE: Implement the new methods
  DebugPrintMappingInfo() and DebugGetReverseMappingInfo().
* Add KDL command "mapping". It supports both virtual address lookups
  and reverse lookups.
This commit is contained in:
Ingo Weinhold 2013-12-05 05:07:34 +01:00
parent 75c19f7043
commit 7b83ce1142
5 changed files with 348 additions and 0 deletions

View File

@ -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();

View File

@ -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
{

View File

@ -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; }

View File

@ -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

View File

@ -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\" ] <address> [ <team ID> ]\n"
"Prints low-level page mapping information for a given address. If\n"
"neither \"-r\" nor \"-p\" are specified, <address> is a virtual\n"
"address that is looked up in the translation map of the current\n"
"team, respectively the team specified by ID <team ID>. If \"-r\" is\n"
"specified, <address> is a physical address that is searched in the\n"
"translation map of all teams, respectively the team specified by ID\n"
" <team ID>. If \"-p\" is specified, <address> 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();