1. Add mips64_init() implementation, do all necessary machine-specific setup,
which will be called multiple times during initialization.
2. Add the implementation of the vtop command, which is used to convert a
virtual address to a physical address. When entering the crash command line,
the corresponding symbols in the kernel will be read, and at the same time,
the conversion of virtual and real addresses will also be used, so the vtop
command is a prerequisite for entering the crash command line.
3. Add mips64_get_smp_cpus() implementation, get the number of online cpus.
4. Add mips64_get_page_size() implementation, get page size.
The results after applying patch 01~04 are as follows:
...
KERNEL: /boot/vmlinux-4.19.161kexec+
DUMPFILE: /home/tang/vmcore_4.19.161
CPUS: 4
DATE: Mon Jan 25 18:54:14 HKT 2021
UPTIME: (cannot calculate: unknown HZ value)
LOAD AVERAGE: 0.24, 0.21, 0.09
TASKS: 348
NODENAME: bogon
RELEASE: 4.19.161kexec+
VERSION: #15 SMP PREEMPT Mon Jan 25 17:56:16 HKT 2021
MACHINE: mips64 (unknown Mhz)
MEMORY: 0
PANIC: "CPU 3 Unable to handle kernel paging request at virtual address
0000000000000000, epc == ffffffff8085d318, ra == ffffffff8085d308"
PID: 4768
COMMAND: "bash"
TASK: 9800000243bcf200 [THREAD_INFO: 980000024291c000]
CPU: 3
STATE: TASK_RUNNING (PANIC)
crash>
Signed-off-by: Huacai Chen <chenhuacai(a)loongson.cn>
Signed-off-by: Youling Tang <tangyouling(a)loongson.cn>
---
v1 -> v2:
- Remove the use of machdep->get_task_pgd(), which directly obtains pgd
from mm_struct.
mips64.c | 301 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 301 insertions(+)
diff --git a/mips64.c b/mips64.c
index c3eb03c..89de5c3 100644
--- a/mips64.c
+++ b/mips64.c
@@ -17,11 +17,262 @@
#include <elf.h>
#include "defs.h"
+static int mips64_pgd_vtop(ulong *pgd, ulong vaddr,
+ physaddr_t *paddr, int verbose);
+static int mips64_uvtop(struct task_context *tc, ulong vaddr,
+ physaddr_t *paddr, int verbose);
+static int mips64_kvtop(struct task_context *tc, ulong kvaddr,
+ physaddr_t *paddr, int verbose);
+
+/*
+ * 3 Levels paging PAGE_SIZE=16KB
+ * PGD | PMD | PTE | OFFSET |
+ * 11 | 11 | 11 | 14 |
+ */
+/* From arch/mips/include/asm/pgtable{,-64}.h */
+typedef struct { ulong pgd; } pgd_t;
+typedef struct { ulong pmd; } pmd_t;
+typedef struct { ulong pte; } pte_t;
+
+#define PMD_ORDER 0
+#define PTE_ORDER 0
+
+#define PMD_SHIFT (PAGESHIFT() + (PAGESHIFT() + PTE_ORDER - 3))
+#define PMD_SIZE (1UL << PMD_SHIFT)
+#define PMD_MASK (~(PMD_SIZE - 1))
+
+#define PGDIR_SHIFT (PMD_SHIFT + (PAGESHIFT() + PMD_ORDER - 3))
+#define PGDIR_SIZE (1UL << PGDIR_SHIFT)
+#define PGDIR_MASK (~(PGDIR_SIZE - 1))
+
+#define PTRS_PER_PTE (1UL << (PAGESHIFT() - 3))
+#define PTRS_PER_PMD PTRS_PER_PTE
+#define PTRS_PER_PGD PTRS_PER_PTE
+#define USER_PTRS_PER_PGD (0x80000000UL/PGDIR_SIZE)
+
+#define pte_index(addr) (((addr) >> PAGESHIFT()) & (PTRS_PER_PTE - 1))
+#define pmd_index(addr) (((addr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1))
+#define pgd_index(addr) (((addr) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1))
+
+#define MIPS64_CPU_RIXI (1UL << 23) /* CPU has TLB Read/eXec Inhibit */
+
+/* From arch/mips/include/uapi/asm/reg.h */
+#define MIPS64_EF_R0 0
+#define MIPS64_EF_R29 29
+#define MIPS64_EF_R31 31
+#define MIPS64_EF_LO 32
+#define MIPS64_EF_HI 33
+#define MIPS64_EF_CP0_EPC 34
+#define MIPS64_EF_CP0_BADVADDR 35
+#define MIPS64_EF_CP0_STATUS 36
+#define MIPS64_EF_CP0_CAUSE 37
+
+static struct machine_specific mips64_machine_specific = { 0 };
+
+/*
+ * Holds registers during the crash.
+ */
+static struct mips64_register *panic_task_regs;
+
+/*
+ * Virtual to physical memory translation. This function will be called
+ * by both mips64_kvtop and mips64_uvtop.
+ */
+static int
+mips64_pgd_vtop(ulong *pgd, ulong vaddr, physaddr_t *paddr, int verbose)
+{
+ ulong *pgd_ptr, pgd_val;
+ ulong *pmd_ptr, pmd_val;
+ ulong *pte_ptr, pte_val;
+
+ if (verbose) {
+ const char *segment;
+
+ if (vaddr < 0x4000000000000000lu)
+ segment = "xuseg";
+ else if (vaddr < 0x8000000000000000lu)
+ segment = "xsseg";
+ else if (vaddr < 0xc000000000000000lu)
+ segment = "xkphys";
+ else if (vaddr < 0xffffffff80000000lu)
+ segment = "xkseg";
+ else if (vaddr < 0xffffffffa0000000lu)
+ segment = "kseg0";
+ else if (vaddr < 0xffffffffc0000000lu)
+ segment = "kseg1";
+ else if (vaddr < 0xffffffffe0000000lu)
+ segment = "sseg";
+ else
+ segment = "kseg3";
+
+ fprintf(fp, "SEGMENT: %s\n", segment);
+ }
+
+ if (IS_CKPHYS(vaddr) || IS_XKPHYS(vaddr)) {
+ *paddr = VTOP(vaddr);
+ return TRUE;
+ }
+
+ if (verbose)
+ fprintf(fp, "PAGE DIRECTORY: %016lx\n", (ulong)pgd);
+
+ pgd_ptr = pgd + pgd_index(vaddr);
+ FILL_PGD(PAGEBASE(pgd), KVADDR, PAGESIZE());
+ pgd_val = ULONG(machdep->pgd + PAGEOFFSET(pgd_ptr));
+ if (verbose)
+ fprintf(fp, " PGD: %16lx => %16lx\n", (ulong)pgd_ptr, pgd_val);
+ if (!pgd_val)
+ goto no_page;
+
+ pmd_ptr = (ulong *)(VTOP(pgd_val) + sizeof(pmd_t) * pmd_index(vaddr));
+ FILL_PMD(PAGEBASE(pmd_ptr), PHYSADDR, PAGESIZE());
+ pmd_val = ULONG(machdep->pmd + PAGEOFFSET(pmd_ptr));
+ if (verbose)
+ fprintf(fp, " PMD: %016lx => %016lx\n", (ulong)pmd_ptr, pmd_val);
+ if (!pmd_val)
+ goto no_page;
+
+ pte_ptr = (ulong *)(VTOP(pmd_val) + sizeof(pte_t) * pte_index(vaddr));
+ FILL_PTBL(PAGEBASE(pte_ptr), PHYSADDR, PAGESIZE());
+ pte_val = ULONG(machdep->ptbl + PAGEOFFSET(pte_ptr));
+ if (verbose)
+ fprintf(fp, " PTE: %016lx => %016lx\n", (ulong)pte_ptr, pte_val);
+ if (!pte_val)
+ goto no_page;
+
+ return TRUE;
+no_page:
+ fprintf(fp, "invalid\n");
+ return FALSE;
+}
+
+/* Translates a user virtual address to its physical address. cmd_vtop() sets
+ * the verbose flag so that the pte translation gets displayed; all other
+ * callers quietly accept the translation.
+ */
+static int
+mips64_uvtop(struct task_context *tc, ulong vaddr, physaddr_t *paddr, int verbose)
+{
+ ulong mm, active_mm;
+ ulong *pgd;
+
+ if (!tc)
+ error(FATAL, "current context invalid\n");
+
+ *paddr = 0;
+
+ if (is_kernel_thread(tc->task) && IS_KVADDR(vaddr)) {
+ readmem(tc->task + OFFSET(task_struct_active_mm),
+ KVADDR, &active_mm, sizeof(void *),
+ "task active_mm contents", FAULT_ON_ERROR);
+
+ if (!active_mm)
+ error(FATAL,
+ "no active_mm for this kernel thread\n");
+
+ readmem(active_mm + OFFSET(mm_struct_pgd),
+ KVADDR, &pgd, sizeof(long),
+ "mm_struct pgd", FAULT_ON_ERROR);
+ } else {
+ if ((mm = task_mm(tc->task, TRUE)))
+ pgd = ULONG_PTR(tt->mm_struct + OFFSET(mm_struct_pgd));
+ else
+ readmem(tc->mm_struct + OFFSET(mm_struct_pgd),
+ KVADDR, &pgd, sizeof(long), "mm_struct pgd",
+ FAULT_ON_ERROR);
+ }
+
+ return mips64_pgd_vtop(pgd, vaddr, paddr, verbose);;
+}
+
+/* Translates a user virtual address to its physical address. cmd_vtop() sets
+ * the verbose flag so that the pte translation gets displayed; all other
+ * callers quietly accept the translation.
+ */
+static int
+mips64_kvtop(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose)
+{
+ if (!IS_KVADDR(kvaddr))
+ return FALSE;
+
+ if (!verbose) {
+ if (IS_CKPHYS(kvaddr) || IS_XKPHYS(kvaddr)) {
+ *paddr = VTOP(kvaddr);
+ return TRUE;
+ }
+ }
+
+ return mips64_pgd_vtop((ulong *)vt->kernel_pgd[0], kvaddr, paddr,
+ verbose);
+}
+
+/*
+ * Accept or reject a symbol from the kernel namelist.
+ */
+static int
+mips64_verify_symbol(const char *name, ulong value, char type)
+{
+ return TRUE;
+}
+
+/*
+ * Override smp_num_cpus if possible and necessary.
+ */
+static int
+mips64_get_smp_cpus(void)
+{
+ return (get_cpus_online() > 0) ? get_cpus_online() : kt->cpus;
+}
+
+static ulong
+mips64_get_page_size(void)
+{
+ return memory_page_size();
+}
+
+/*
+ * Determine where vmalloc'd memory starts.
+ */
+static ulong
+mips64_vmalloc_start(void)
+{
+ return 0;
+}
+
+static ulong
+mips64_processor_speed(void)
+{
+ return 0;
+}
+
+/*
+ * Checks whether given task is valid task address.
+ */
+static int
+mips64_is_task_addr(ulong task)
+{
+ if (tt->flags & THREAD_INFO)
+ return IS_KVADDR(task);
+
+ return (IS_KVADDR(task) && ALIGNED_STACK_OFFSET(task) == 0);
+}
+
void
mips64_dump_machdep_table(ulong arg)
{
}
+static void
+pt_level_alloc(char **lvl, char *name)
+{
+ size_t sz = PAGESIZE();
+ void *pointer = malloc(sz);
+
+ if (!pointer)
+ error(FATAL, name);
+ *lvl = pointer;
+}
+
/*
* Do all necessary machine-specific setup here. This is called several
* times during initialization.
@@ -33,6 +284,56 @@ mips64_init(int when)
case SETUP_ENV:
machdep->process_elf_notes = process_elf64_notes;
break;
+
+ case PRE_SYMTAB:
+ machdep->verify_symbol = mips64_verify_symbol;
+ machdep->machspec = &mips64_machine_specific;
+ if (pc->flags & KERNEL_DEBUG_QUERY)
+ return;
+ machdep->last_pgd_read = 0;
+ machdep->last_pmd_read = 0;
+ machdep->last_ptbl_read = 0;
+ machdep->verify_paddr = generic_verify_paddr;
+ machdep->ptrs_per_pgd = PTRS_PER_PGD;
+ break;
+
+ case PRE_GDB:
+ machdep->pagesize = mips64_get_page_size();
+ machdep->pageshift = ffs(machdep->pagesize) - 1;
+ machdep->pageoffset = machdep->pagesize - 1;
+ machdep->pagemask = ~((ulonglong)machdep->pageoffset);
+ if (machdep->pagesize >= 16384)
+ machdep->stacksize = machdep->pagesize;
+ else
+ machdep->stacksize = machdep->pagesize * 2;
+
+ pt_level_alloc(&machdep->pgd, "cannot malloc pgd space.");
+ pt_level_alloc(&machdep->pmd, "cannot malloc pmd space.");
+ pt_level_alloc(&machdep->ptbl, "cannot malloc ptbl space.");
+ machdep->kvbase = 0x8000000000000000lu;
+ machdep->identity_map_base = machdep->kvbase;
+ machdep->is_kvaddr = generic_is_kvaddr;
+ machdep->is_uvaddr = generic_is_uvaddr;
+ machdep->uvtop = mips64_uvtop;
+ machdep->kvtop = mips64_kvtop;
+ machdep->vmalloc_start = mips64_vmalloc_start;
+ machdep->processor_speed = mips64_processor_speed;
+ machdep->get_stackbase = generic_get_stackbase;
+ machdep->get_stacktop = generic_get_stacktop;
+ machdep->memory_size = generic_memory_size;
+ machdep->is_task_addr = mips64_is_task_addr;
+ machdep->get_smp_cpus = mips64_get_smp_cpus;
+ machdep->value_to_symbol = generic_machdep_value_to_symbol;
+ machdep->init_kernel_pgd = NULL;
+ break;
+
+ case POST_GDB:
+ machdep->section_size_bits = _SECTION_SIZE_BITS;
+ machdep->max_physmem_bits = _MAX_PHYSMEM_BITS;
+ break;
+
+ case POST_VM:
+ break;
}
}
--
2.1.0