// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2016, Rashmica Gupta, IBM Corp. * * This traverses the kernel pagetables and dumps the * information about the used sections of memory to * /sys/kernel/debug/kernel_pagetables. * * Derived from the arm64 implementation: * Copyright (c) 2014, The Linux Foundation, Laura Abbott. * (C) Copyright 2008 Intel Corporation, Arjan van de Ven.
*/ #include <linux/debugfs.h> #include <linux/fs.h> #include <linux/hugetlb.h> #include <linux/io.h> #include <linux/mm.h> #include <linux/highmem.h> #include <linux/ptdump.h> #include <linux/sched.h> #include <linux/seq_file.h> #include <asm/fixmap.h> #include <linux/const.h> #include <linux/kasan.h> #include <asm/page.h> #include <asm/hugetlb.h>
#include <mm/mmu_decl.h>
#include"ptdump.h"
/* * To visualise what is happening, * * - PTRS_PER_P** = how many entries there are in the corresponding P** * - P**_SHIFT = how many bits of the address we use to index into the * corresponding P** * - P**_SIZE is how much memory we can access through the table - not the * size of the table itself. * P**={PGD, PUD, PMD, PTE} * * * Each entry of the PGD points to a PUD. Each entry of a PUD points to a * PMD. Each entry of a PMD points to a PTE. And every PTE entry points to * a page. * * In the case where there are only 3 levels, the PUD is folded into the * PGD: every PUD has only one entry which points to the PMD. * * The page dumper groups page table entries of the same type into a single * description. It uses pg_state to track the range information while * iterating over the PTE entries. When the continuity is broken it then * dumps out a description of the range - ie PTEs that are virtually contiguous * with the same PTE flags are chunked together. This is to make it clear how * different areas of the kernel virtual memory are used. *
*/ struct pg_state { struct ptdump_state ptdump; struct seq_file *seq; conststruct addr_marker *marker; unsignedlong start_address; unsignedlong start_pa; int level;
u64 current_flags; bool check_wx; unsignedlong wx_pages;
};
/* Work out what appropriate unit to use */ while (!(size & 1023) && unit[1]) {
size >>= 10;
unit++;
}
pt_dump_seq_printf(m, "%9lu%c ", size, *unit);
}
for (i = 0; i < num; i++, flag++) { constchar *s = NULL;
u64 val;
/* flag not defined so don't check it */ if (flag->mask == 0) continue; /* Some 'flags' are actually values */ if (flag->is_val) {
val = pte & flag->val; if (flag->shift)
val = val >> flag->shift;
pt_dump_seq_printf(st->seq, " %s:%llx", flag->set, val);
} else { if ((pte & flag->mask) == flag->val)
s = flag->set; else
s = flag->clear; if (s)
pt_dump_seq_printf(st->seq, " %s", s);
}
st->current_flags &= ~flag->mask;
} if (st->current_flags != 0)
pt_dump_seq_printf(st->seq, " unknown flags:%llx", st->current_flags);
}
/* At first no level is set */ if (st->level == -1) {
pt_dump_seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
note_page_update_state(st, addr, level, val); /* * Dump the section of virtual memory when: * - the PTE flags from one entry to the next differs. * - we change levels in the tree. * - the address is in a different section of memory and is thus * used for a different purpose, regardless of the flags.
*/
} elseif (flag != st->current_flags || level != st->level ||
addr >= st->marker[1].start_address) {
/* Check the PTE flags */ if (st->current_flags) {
note_prot_wx(st, addr);
dump_addr(st, addr);
/* Dump all the flags */ if (pg_level[st->level].flag)
dump_flag_info(st, pg_level[st->level].flag,
st->current_flags,
pg_level[st->level].num);
pt_dump_seq_putc(st->seq, '\n');
}
/* * Address indicates we have passed the end of the * current section of virtual memory
*/
note_page_update_state(st, addr, level, val);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.