/* riscv64.c - core analysis suite
 *
 * Copyright (C) 2022 Alibaba Group Holding Limited.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
#include "defs.h"
#ifdef RISCV64

#include <elf.h>
#include <math.h>

static ulong riscv64_get_page_size(void);
static int riscv64_vtop_3level_4k(ulong *pgd, ulong vaddr,
				   physaddr_t *paddr, int verbose);
static int riscv64_vtop_4level_4k(ulong *pgd, ulong vaddr,
				   physaddr_t *paddr, int verbose);
static int riscv64_vtop_5level_4k(ulong *pgd, ulong vaddr,
				   physaddr_t *paddr, int verbose);
static void riscv64_page_type_init(void);
static int riscv64_is_kvaddr(ulong vaddr);
static int riscv64_is_uvaddr(ulong vaddr, struct task_context *tc);
static int riscv64_uvtop(struct task_context *tc, ulong vaddr,
			  physaddr_t *paddr, int verbose);
static int riscv64_kvtop(struct task_context *tc, ulong kvaddr,
			  physaddr_t *paddr, int verbose);
static void riscv64_cmd_mach(void);
static void riscv64_irq_stack_init(void);
static void riscv64_overflow_stack_init(void);
static void riscv64_stackframe_init(void);
static void riscv64_back_trace_cmd(struct bt_info *bt);
static int riscv64_eframe_search(struct bt_info *bt);
static int riscv64_get_dumpfile_stack_frame(struct bt_info *bt,
					     ulong *nip, ulong *ksp);
static void riscv64_get_stack_frame(struct bt_info *bt, ulong *pcp,
				     ulong *spp);
static int riscv64_get_frame(struct bt_info *bt, ulong *pcp,
			      ulong *spp);
static void riscv64_display_full_frame(struct bt_info *bt,
				        struct riscv64_unwind_frame *current,
				        struct riscv64_unwind_frame *previous);
static int riscv64_translate_pte(ulong, void *, ulonglong);
static int riscv64_init_active_task_regs(void);
static int riscv64_get_crash_notes(void);
static int riscv64_get_elf_notes(void);
static void riscv64_get_va_range(struct machine_specific *ms);
static void riscv64_get_va_bits(struct machine_specific *ms);
static void riscv64_get_struct_page_size(struct machine_specific *ms);
static void riscv64_print_exception_frame(struct bt_info *, ulong , int );
static int riscv64_is_kernel_exception_frame(struct bt_info *, ulong );
static int riscv64_on_irq_stack(int , ulong);
static int riscv64_on_process_stack(struct bt_info *, ulong );
static void riscv64_set_process_stack(struct bt_info *);
static void riscv64_set_irq_stack(struct bt_info *);
static int riscv64_on_overflow_stack(int, ulong);
static void riscv64_set_overflow_stack(struct bt_info *);

#define REG_FMT 	"%016lx"
#define SZ_2G		0x80000000
#define USER_MODE	(0)
#define KERNEL_MODE	(1)

/*
 * Holds registers during the crash.
 */
static struct riscv64_register *panic_task_regs;

/* from arch/riscv/include/asm/stacktrace.h */
struct stackframe {
	ulong fp;
	ulong ra;
};

static struct machine_specific riscv64_machine_specific = {
	._page_present = (1 << 0),
	._page_read = (1 << 1),
	._page_write = (1 << 2),
	._page_exec = (1 << 3),
	._page_user = (1 << 4),
	._page_global = (1 << 5),
	._page_accessed = (1 << 6),
	._page_dirty = (1 << 7),
	._page_soft = (1 << 8),

	.va_bits = 0,
	.struct_page_size = 0,
};

static void
pt_level_alloc(char **lvl, char *name)
{
	size_t sz = PAGESIZE();
	void *pointer = malloc(sz);

	if (!pointer)
		error(FATAL, name);
	*lvl = pointer;
}

static ulong
riscv64_get_page_size(void)
{
	return memory_page_size();
}

static ulong
riscv64_vmalloc_start(void)
{
	return ((ulong)VMALLOC_START);
}

/* Get the size of struct page {} */
static void riscv64_get_struct_page_size(struct machine_specific *ms)
{
	char *string;

	string = pc->read_vmcoreinfo("SIZE(page)");
	if (string) {
		ms->struct_page_size = atol(string);
		free(string);
	}
}

/*
 * "mach" command output.
 */
static void
riscv64_display_machine_stats(void)
{
	int i, pad;
	struct new_utsname *uts;
	char buf[BUFSIZE];
	ulong mhz;

	uts = &kt->utsname;

	fprintf(fp, "		MACHINE TYPE: %s\n", uts->machine);
	fprintf(fp, "		 MEMORY SIZE: %s\n", get_memory_size(buf));
	fprintf(fp, "			CPUS: %d\n", get_cpus_to_display());
	fprintf(fp, "	     PROCESSOR SPEED: ");
	if ((mhz = machdep->processor_speed()))
		fprintf(fp, "%ld Mhz\n", mhz);
	else
		fprintf(fp, "(unknown)\n");
	fprintf(fp, "			  HZ: %d\n", machdep->hz);
	fprintf(fp, "		   PAGE SIZE: %d\n", PAGESIZE());
	fprintf(fp, "	 KERNEL VIRTUAL BASE: %lx\n", machdep->machspec->page_offset);
	fprintf(fp, "	 KERNEL MODULES BASE: %lx\n", machdep->machspec->modules_vaddr);
	fprintf(fp, "	 KERNEL VMALLOC BASE: %lx\n", machdep->machspec->vmalloc_start_addr);
	fprintf(fp, "	 KERNEL VMEMMAP BASE: %lx\n", machdep->machspec->vmemmap_vaddr);
	fprintf(fp, "	   KERNEL STACK SIZE: %ld\n", STACKSIZE());
	if (machdep->machspec->irq_stack_size) {
		fprintf(fp, " IRQ STACK SIZE: %ld\n",
			machdep->machspec->irq_stack_size);
		fprintf(fp, "     IRQ STACKS:\n");
		for (i = 0; i < kt->cpus; i++) {
			pad = (i < 10) ? 3 : (i < 100) ? 2 : (i < 1000) ? 1 : 0;
			fprintf(fp, "%s           CPU %d: %lx\n", space(pad), i,
				machdep->machspec->irq_stacks[i]);
		}
	}
	if (machdep->machspec->overflow_stack_size) {
		fprintf(fp, "OVERFLOW STACK SIZE: %ld\n",
			machdep->machspec->overflow_stack_size);
		fprintf(fp, "    OVERFLOW STACKS:\n");
		for (i = 0; i < kt->cpus; i++) {
			pad = (i < 10) ? 3 : (i < 100) ? 2 : (i < 1000) ? 1 : 0;
			fprintf(fp, "%s           CPU %d: %lx\n", space(pad), i,
				machdep->machspec->overflow_stacks[i]);
		}
	}
}

static void
riscv64_cmd_mach(void)
{
	int c;

	while ((c = getopt(argcnt, args, "cmo")) != EOF) {
		switch (c) {
		case 'c':
		case 'm':
		case 'o':
			option_not_supported(c);
			break;
		default:
			argerrs++;
			break;
		}
	}

	if (argerrs)
		cmd_usage(pc->curcmd, SYNOPSIS);

	riscv64_display_machine_stats();
}

/*
 * Accept or reject a symbol from the kernel namelist.
 */
static int
riscv64_verify_symbol(const char *name, ulong value, char type)
{
	if (CRASHDEBUG(8) && name && strlen(name))
		fprintf(fp, "%08lx %s\n", value, name);

	if (!(machdep->flags & KSYMS_START)) {
		if (STREQ(name, "_text") || STREQ(name, "_stext"))
			machdep->flags |= KSYMS_START;

		return (name && strlen(name) && !STRNEQ(name, "__func__.") &&
			!STRNEQ(name, "__crc_"));
	}

	return TRUE;
}

void
riscv64_dump_machdep_table(ulong arg)
{
	const struct machine_specific *ms = machdep->machspec;
	int others = 0, i = 0;

	fprintf(fp, "              flags: %lx (", machdep->flags);
	if (machdep->flags & KSYMS_START)
		fprintf(fp, "%sKSYMS_START", others++ ? "|" : "");
	if (machdep->flags & IRQ_STACKS)
		fprintf(fp, "%sIRQ_STACKS", others++ ? "|" : "");
	if (machdep->flags & OVERFLOW_STACKS)
		fprintf(fp, "%sOVERFLOW_STACKS", others++ ? "|" : "");
	fprintf(fp, ")\n");

	fprintf(fp, "             kvbase: %lx\n", machdep->kvbase);
	fprintf(fp, "  identity_map_base: %lx\n", machdep->identity_map_base);
	fprintf(fp, "           pagesize: %d\n", machdep->pagesize);
	fprintf(fp, "          pageshift: %d\n", machdep->pageshift);
	fprintf(fp, "           pagemask: %llx\n", machdep->pagemask);
	fprintf(fp, "         pageoffset: %lx\n", machdep->pageoffset);
	fprintf(fp, "        pgdir_shift: %ld\n", machdep->machspec->va_bits - 9);
	fprintf(fp, "       ptrs_per_pgd: %u\n", PTRS_PER_PGD);
	fprintf(fp, "       ptrs_per_pte: %d\n", PTRS_PER_PTE);
	fprintf(fp, "          stacksize: %ld\n", machdep->stacksize);
	fprintf(fp, "                 hz: %d\n", machdep->hz);
	fprintf(fp, "            memsize: %ld (0x%lx)\n",
		machdep->memsize, machdep->memsize);
	fprintf(fp, "               bits: %d\n", machdep->bits);
	fprintf(fp, "         back_trace: riscv64_back_trace_cmd()\n");
	fprintf(fp, "      eframe_search: riscv64_eframe_search()\n");
	fprintf(fp, "    processor_speed: riscv64_processor_speed()\n");
	fprintf(fp, "              uvtop: riscv64_uvtop()\n");
	fprintf(fp, "              kvtop: riscv64_kvtop()\n");
	fprintf(fp, "    get_stack_frame: riscv64_get_stack_frame()\n");
	fprintf(fp, "      get_stackbase: generic_get_stackbase()\n");
	fprintf(fp, "       get_stacktop: generic_get_stacktop()\n");
	fprintf(fp, "      translate_pte: riscv64_translate_pte()\n");
	fprintf(fp, "        memory_size: generic_memory_size()\n");
	fprintf(fp, "      vmalloc_start: riscv64_vmalloc_start()\n");
	fprintf(fp, "       is_task_addr: riscv64_is_task_addr()\n");
	fprintf(fp, "      verify_symbol: riscv64_verify_symbol()\n");
	fprintf(fp, "         dis_filter: generic_dis_filter()\n");
	fprintf(fp, "           dump_irq: generic_dump_irq()\n");
	fprintf(fp, "    show_interrupts: generic_show_interrupts()\n");
	fprintf(fp, "   get_irq_affinity: generic_get_irq_affinity()\n");
	fprintf(fp, "           cmd_mach: riscv64_cmd_mach()\n");
	fprintf(fp, "       get_smp_cpus: riscv64_get_smp_cpus()\n");
	fprintf(fp, "          is_kvaddr: riscv64_is_kvaddr()\n");
	fprintf(fp, "          is_uvaddr: riscv64_is_uvaddr()\n");
	fprintf(fp, "       verify_paddr: generic_verify_paddr()\n");
	fprintf(fp, "    init_kernel_pgd: NULL\n");
	fprintf(fp, "    value_to_symbol: generic_machdep_value_to_symbol()\n");
	fprintf(fp, "  line_number_hooks: NULL\n");
	fprintf(fp, "      last_pgd_read: %lx\n", machdep->last_pgd_read);
	fprintf(fp, "      last_p4d_read: %lx\n", machdep->machspec->last_p4d_read);
	fprintf(fp, "      last_pud_read: %lx\n", machdep->last_pud_read);
	fprintf(fp, "      last_pmd_read: %lx\n", machdep->last_pmd_read);
	fprintf(fp, "     last_ptbl_read: %lx\n", machdep->last_ptbl_read);
	fprintf(fp, "                pgd: %lx\n", (ulong)machdep->pgd);
	fprintf(fp, "                p4d: %lx\n", (ulong)machdep->machspec->p4d);
	fprintf(fp, "                pud: %lx\n", (ulong)machdep->pud);
	fprintf(fp, "                pmd: %lx\n", (ulong)machdep->pmd);
	fprintf(fp, "               ptbl: %lx\n", (ulong)machdep->ptbl);
	fprintf(fp, "  section_size_bits: %ld\n", machdep->section_size_bits);
	fprintf(fp, "   max_physmem_bits: %ld\n", machdep->max_physmem_bits);
	fprintf(fp, "  sections_per_root: %ld\n", machdep->sections_per_root);
	fprintf(fp, "           machspec: %lx\n", (ulong)machdep->machspec);
	if (machdep->flags & IRQ_STACKS) {
		fprintf(fp, "        irq_stack_size: %ld\n", ms->irq_stack_size);
		for (i = 0; i < kt->cpus; i++)
			fprintf(fp, "         irq_stacks[%d]: %lx\n",
				i, ms->irq_stacks[i]);
	} else {
		fprintf(fp, "        irq_stack_size: (unused)\n");
		fprintf(fp, "            irq_stacks: (unused)\n");
	}
	if (machdep->flags & OVERFLOW_STACKS) {
		fprintf(fp, "        overflow_stack_size: %ld\n", ms->overflow_stack_size);
		for (i = 0; i < kt->cpus; i++)
			fprintf(fp, "         overflow_stacks[%d]: %lx\n",
				i, ms->overflow_stacks[i]);
	} else {
		fprintf(fp, "        overflow_stack_size: (unused)\n");
		fprintf(fp, "            overflow_stacks: (unused)\n");
	}
}

static ulong
riscv64_processor_speed(void)
{
	/* TODO: */
	return 0;
}

static unsigned long riscv64_get_kernel_version(void)
{
	char *string;

	if (THIS_KERNEL_VERSION)
		return THIS_KERNEL_VERSION;

	if ((string = pc->read_vmcoreinfo("OSRELEASE"))) {
		parse_kernel_version(string);
		free(string);
	}
	return THIS_KERNEL_VERSION;
}

static void
riscv64_get_phys_ram_base(struct machine_specific *ms)
{
	unsigned long kernel_version = riscv64_get_kernel_version();

	/*
	 * phys_ram_base is defined in Linux kernel since 5.14.
	 */
	if (kernel_version >= LINUX(5,14,0)) {
		char *string;
		if ((string = pc->read_vmcoreinfo("NUMBER(phys_ram_base)"))) {
			ms->phys_base = atol(string);
			free(string);
		} else
			error(FATAL, "cannot read phys_ram_base\n");
	} else
		/*
		 * For qemu rv64 env and hardware platform, default phys base
		 * may different, eg,
		 *	hardware platform: 0x200000
		 *	qemu   rv64   env: 0x80200000
		 *
		 * But we only can set one default value, in this case, qemu
		 * rv64 env may can't work.
		 */
		ms->phys_base = 0x200000;
}

static void riscv64_get_va_bits(struct machine_specific *ms)
{
	unsigned long kernel_version = riscv64_get_kernel_version();

	/*
	 * VA_BITS is defined in Linux kernel since 5.17. So we use the
	 * default va bits 39 when Linux version < 5.17.
	 */
	if (kernel_version >= LINUX(5,17,0)) {
		char *string;
		if ((string = pc->read_vmcoreinfo("NUMBER(VA_BITS)"))) {
			ms->va_bits = atol(string);
			free(string);
		}
	} else
		ms->va_bits = 39;
}

static void riscv64_get_va_range(struct machine_specific *ms)
{
	unsigned long kernel_version = riscv64_get_kernel_version();
	char *string;

	if ((string = pc->read_vmcoreinfo("NUMBER(PAGE_OFFSET)"))) {
		ms->page_offset = htol(string, QUIET, NULL);
		free(string);
	} else
		goto error;

	if ((string = pc->read_vmcoreinfo("NUMBER(VMALLOC_START)"))) {
		ms->vmalloc_start_addr = htol(string, QUIET, NULL);
		free(string);
	} else
		goto error;

	if ((string = pc->read_vmcoreinfo("NUMBER(VMALLOC_END)"))) {
		ms->vmalloc_end = htol(string, QUIET, NULL);
                free(string);
	} else
		goto error;

	if ((string = pc->read_vmcoreinfo("NUMBER(VMEMMAP_START)"))) {
		ms->vmemmap_vaddr = htol(string, QUIET, NULL);
		free(string);
	} else
		goto error;

	if ((string = pc->read_vmcoreinfo("NUMBER(VMEMMAP_END)"))) {
		ms->vmemmap_end = htol(string, QUIET, NULL);
		free(string);
	} else
		goto error;

	if ((string = pc->read_vmcoreinfo("NUMBER(KERNEL_LINK_ADDR)"))) {
		ms->kernel_link_addr = htol(string, QUIET, NULL);
		free(string);
	} else
		goto error;

	if ((kt->flags2 & KASLR) && (kt->flags & RELOC_SET))
		ms->kernel_link_addr += (kt->relocate * -1);

	/*
	 * From Linux 5.13, the kernel mapping is moved to the last 2GB
	 * of the address space, modules use the 2GB memory range right
	 * before the kernel. Before Linux 5.13, modules area is embedded
	 * in vmalloc area.
	 *
	 */
	if (kernel_version >= LINUX(5,13,0)) {
		if ((string = pc->read_vmcoreinfo("NUMBER(MODULES_VADDR)"))) {
			ms->modules_vaddr = htol(string, QUIET, NULL);
			free(string);
		} else
			goto error;

		if ((string = pc->read_vmcoreinfo("NUMBER(MODULES_END)"))) {
			ms->modules_end = htol(string, QUIET, NULL);
			free(string);
		} else
			goto error;
	} else {
		ms->modules_vaddr = ms->vmalloc_start_addr;
		ms->modules_end = ms->vmalloc_end;
	}

	if (CRASHDEBUG(1)) {
		fprintf(fp, "vmemmap	: 0x%lx - 0x%lx\n",
			ms->vmemmap_vaddr, ms->vmemmap_end);
		fprintf(fp, "vmalloc	: 0x%lx - 0x%lx\n",
			ms->vmalloc_start_addr, ms->vmalloc_end);
		fprintf(fp, "mudules	: 0x%lx - 0x%lx\n",
			ms->modules_vaddr, ms->modules_end);
		fprintf(fp, "lowmem	: 0x%lx -\n", ms->page_offset);
		fprintf(fp, "kernel link addr	: 0x%lx\n",
			ms->kernel_link_addr);
	}
	return;
error:
	error(FATAL, "cannot get vm layout\n");
}

static void
riscv64_get_va_kernel_pa_offset(struct machine_specific *ms)
{
	unsigned long kernel_version = riscv64_get_kernel_version();

	/*
	 * Since Linux v6.4 phys_base is not the physical start of the kernel,
	 * trying to use "va_kernel_pa_offset" to determine the offset between
	 * kernel virtual and physical addresses.
	 */
	if (kernel_version >= LINUX(6,4,0)) {
		char *string;
		if ((string = pc->read_vmcoreinfo("NUMBER(va_kernel_pa_offset)"))) {
			ms->va_kernel_pa_offset = htol(string, QUIET, NULL);
			free(string);
		} else
			error(FATAL, "cannot read va_kernel_pa_offset\n");
	}
	else
		ms->va_kernel_pa_offset = ms->kernel_link_addr - ms->phys_base;
}

static int
riscv64_is_kvaddr(ulong vaddr)
{
	if (IS_VMALLOC_ADDR(vaddr))
		return TRUE;

	return (vaddr >= machdep->kvbase);
}

static int
riscv64_is_uvaddr(ulong vaddr, struct task_context *unused)
{
	if (IS_VMALLOC_ADDR(vaddr))
		return FALSE;

	return (vaddr < machdep->kvbase);
}

static int
riscv64_is_task_addr(ulong task)
{
	if (tt->flags & THREAD_INFO)
		return IS_KVADDR(task);

	return (IS_KVADDR(task) && ALIGNED_STACK_OFFSET(task) == 0);
}

static int
riscv64_get_smp_cpus(void)
{
	return (get_cpus_present() > 0) ? get_cpus_present() : kt->cpus;
}

/*
 *  Include both vmalloc'd and module address space as VMALLOC space.
 */
int
riscv64_IS_VMALLOC_ADDR(ulong vaddr)
{
	return ((vaddr >= VMALLOC_START && vaddr <= VMALLOC_END) ||
		(vaddr >= VMEMMAP_VADDR && vaddr <= VMEMMAP_END) ||
		(vaddr >= MODULES_VADDR && vaddr <= MODULES_END));
}

/*
 * Translate a PTE, returning TRUE if the page is present.
 * If a physaddr pointer is passed in, don't print anything.
 */
static int
riscv64_translate_pte(ulong pte, void *physaddr, ulonglong unused)
{
	char ptebuf[BUFSIZE];
	char physbuf[BUFSIZE];
	char buf[BUFSIZE];
	int page_present;
	int len1, len2, others;
	ulong paddr;

	paddr = PTOB(pte >> _PAGE_PFN_SHIFT);
	page_present = !!(pte & _PAGE_PRESENT);

	if (physaddr) {
		*(ulong *)physaddr = paddr;
		return page_present;
	}

	sprintf(ptebuf, "%lx", pte);
	len1 = MAX(strlen(ptebuf), strlen("PTE"));
	fprintf(fp, "%s  ", mkstring(buf, len1, CENTER | LJUST, "PTE"));

	if (!page_present)
		return page_present;

	sprintf(physbuf, "%lx", paddr);
	len2 = MAX(strlen(physbuf), strlen("PHYSICAL"));
	fprintf(fp, "%s  ", mkstring(buf, len2, CENTER | LJUST, "PHYSICAL"));

	fprintf(fp, "FLAGS\n");
	fprintf(fp, "%s  %s  ",
		mkstring(ptebuf, len1, CENTER | RJUST, NULL),
		mkstring(physbuf, len2, CENTER | RJUST, NULL));

	fprintf(fp, "(");
	others = 0;

#define CHECK_PAGE_FLAG(flag)				\
	if ((_PAGE_##flag) && (pte & _PAGE_##flag))	\
		fprintf(fp, "%s" #flag, others++ ? "|" : "")
	if (pte) {
		CHECK_PAGE_FLAG(PRESENT);
		CHECK_PAGE_FLAG(READ);
		CHECK_PAGE_FLAG(WRITE);
		CHECK_PAGE_FLAG(EXEC);
		CHECK_PAGE_FLAG(USER);
		CHECK_PAGE_FLAG(GLOBAL);
		CHECK_PAGE_FLAG(ACCESSED);
		CHECK_PAGE_FLAG(DIRTY);
		CHECK_PAGE_FLAG(SOFT);
	} else {
		fprintf(fp, "no mapping");
	}

	fprintf(fp, ")\n");

	return page_present;
}

static void
riscv64_page_type_init(void)
{
	ulong va_bits = machdep->machspec->va_bits;

	/*
	 * For RISCV64 arch, any level of PTE may be a leaf PTE,
	 * so in addition to 4KiB pages,
	 * Sv39 supports 2 MiB megapages, 1 GiB gigapages;
	 * Sv48 supports 2 MiB megapages, 1 GiB gigapages, 512 GiB terapages;
	 * Sv57 supports 2 MiB megapages, 1 GiB gigapages, 512 GiB terapages, and 256 TiB petapages.
	 *
	 * refs to riscv-privileged spec.
	 *
	 * We just support 4KiB, 2MiB, 1GiB now.
	 */
	switch (machdep->pagesize)
	{
	case 0x1000:		// 4 KiB
		machdep->flags |= (va_bits == 57 ? VM_L5_4K :
				  (va_bits == 48 ? VM_L4_4K : VM_L3_4K));
		break;
	case 0x200000:		// 2 MiB
		/* TODO: */
	case 0x40000000: 	// 1 GiB
		/* TODO: */
	default:
		if (machdep->pagesize)
			error(FATAL, "invalid/unsupported page size: %d\n",
			      machdep->pagesize);
		else
			error(FATAL, "cannot determine page size\n");
	}
}

static int
riscv64_vtop_3level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr, int verbose)
{
	ulong *pgd_ptr, pgd_val;
	ulong pmd_base, pmd_addr, pmd_val;
	ulong pte_base, pte_addr, pte_val, pte_pfn;

	if (verbose)
		fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd);

	/* PGD */
	pgd_ptr = pgd + pgd_index_l3_4k(vaddr);
	FILL_PGD(pgd, KVADDR, PAGESIZE());
	pgd_val = ULONG(machdep->pgd + PAGEOFFSET(pgd_ptr));
	if (verbose)
		fprintf(fp, "  PGD: %lx => %lx\n", (ulong)pgd_ptr, pgd_val);
	if (!pgd_val)
		goto no_page;
	pgd_val &= PTE_PFN_PROT_MASK;
	pmd_base = (pgd_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();

	/* PMD */
	FILL_PMD(PAGEBASE(pmd_base), PHYSADDR, PAGESIZE());
	pmd_addr = pmd_base + sizeof(pmd_t) * pmd_index_l3_4k(vaddr);
	pmd_val = ULONG(machdep->pmd + PAGEOFFSET(pmd_addr));
	if (verbose)
		fprintf(fp, "  PMD: %016lx => %016lx\n", pmd_addr, pmd_val);
	if (!pmd_val)
		goto no_page;
	pmd_val &= PTE_PFN_PROT_MASK;
	pte_base = (pmd_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();

	/* PTE */
	FILL_PTBL(PAGEBASE(pte_base), PHYSADDR, PAGESIZE());
	pte_addr = pte_base + sizeof(pmd_t) * pte_index_l3_4k(vaddr);
	pte_val = ULONG(machdep->ptbl + PAGEOFFSET(pte_addr));
	if (verbose)
		fprintf(fp, "  PTE: %lx => %lx\n", pte_addr, pte_val);
	if (!pte_val)
		goto no_page;
	pte_val &= PTE_PFN_PROT_MASK;
	pte_pfn = pte_val >> _PAGE_PFN_SHIFT;

	if (!(pte_val & _PAGE_PRESENT)) {
		if (verbose) {
			fprintf(fp, "\n");
			riscv64_translate_pte((ulong)pte_val, 0, 0);
		}
		fprintf(fp, " PAGE: %016lx not present\n\n", PAGEBASE(*paddr));
		return FALSE;
	}

	*paddr = PTOB(pte_pfn) + PAGEOFFSET(vaddr);

	if (verbose) {
		fprintf(fp, " PAGE: %016lx\n\n", PAGEBASE(*paddr));
		riscv64_translate_pte(pte_val, 0, 0);
	}

	return TRUE;
no_page:
	fprintf(fp, "invalid\n");
	return FALSE;
}

/*
 * 'bt -f' command output
 * Display all stack data contained in a frame
 */
static void
riscv64_display_full_frame(struct bt_info *bt, struct riscv64_unwind_frame *current,
			  struct riscv64_unwind_frame *previous)
{
	int i, u_idx;
	ulong *up;
	ulong words, addr;
	char buf[BUFSIZE];

	if (previous->sp < current->sp)
		return;

	if (!(INSTACK(previous->sp, bt) && INSTACK(current->sp, bt)))
		return;

	words = (previous->sp - current->sp) / sizeof(ulong) + 1;
	addr = current->sp;
	u_idx = (current->sp - bt->stackbase) / sizeof(ulong);

	for (i = 0; i < words; i++, u_idx++) {
		if (!(i & 1))
			fprintf(fp, "%s    %lx: ", i ? "\n" : "", addr);

		up = (ulong *)(&bt->stackbuf[u_idx*sizeof(ulong)]);
		fprintf(fp, "%s ", format_stack_entry(bt, buf, *up, 0));
		addr += sizeof(ulong);
	}
	fprintf(fp, "\n");
}


/*
 * Gather Overflow stack values.
 */
static void
riscv64_overflow_stack_init(void)
{
	int i;
	struct syment *sp;
	struct gnu_request request, *req;
	struct machine_specific *ms = machdep->machspec;
	req = &request;

	if (symbol_exists("overflow_stack") &&
	    (sp = per_cpu_symbol_search("overflow_stack")) &&
	    get_symbol_type("overflow_stack", NULL, req)) {
		if (CRASHDEBUG(1)) {
			fprintf(fp, "overflow_stack: \n");
			fprintf(fp, "  type: %x, %s\n",
				(int)req->typecode,
				(req->typecode == TYPE_CODE_ARRAY) ?
						"TYPE_CODE_ARRAY" : "other");
			fprintf(fp, "  target_typecode: %x, %s\n",
				(int)req->target_typecode,
				req->target_typecode == TYPE_CODE_INT ?
						"TYPE_CODE_INT" : "other");
			fprintf(fp, "  target_length: %ld\n",
						req->target_length);
			fprintf(fp, "  length: %ld\n", req->length);
		}

		if (!(ms->overflow_stacks = (ulong *)malloc((size_t)(kt->cpus * sizeof(ulong)))))
			error(FATAL, "cannot malloc overflow_stack addresses\n");

		ms->overflow_stack_size = RISCV64_OVERFLOW_STACK_SIZE;
		machdep->flags |= OVERFLOW_STACKS;

		for (i = 0; i < kt->cpus; i++)
			ms->overflow_stacks[i] = kt->__per_cpu_offset[i] + sp->value;
	}
}

/*
 * Gather IRQ stack values.
 */
static void
riscv64_irq_stack_init(void)
{
	int i;
	struct syment *sp;
	struct gnu_request request, *req;
	struct machine_specific *ms = machdep->machspec;
	ulong p, sz;
	req = &request;

	if (symbol_exists("irq_stack_ptr") &&
	    (sp = per_cpu_symbol_search("irq_stack_ptr")) &&
	    get_symbol_type("irq_stack_ptr", NULL, req)) {
		if (CRASHDEBUG(1)) {
			fprintf(fp, "irq_stack_ptr: \n");
			fprintf(fp, "  type: %x, %s\n",
				(int)req->typecode,
				(req->typecode == TYPE_CODE_PTR) ?
						"TYPE_CODE_PTR" : "other");
			fprintf(fp, "  target_typecode: %x, %s\n",
				(int)req->target_typecode,
				req->target_typecode == TYPE_CODE_INT ?
						"TYPE_CODE_INT" : "other");
			fprintf(fp, "  target_length: %ld\n",
						req->target_length);
			fprintf(fp, "  length: %ld\n", req->length);
		}

		if (!(ms->irq_stacks = (ulong *)malloc((size_t)(kt->cpus * sizeof(ulong)))))
			error(FATAL, "cannot malloc irq_stack addresses\n");

		/*
		 * find IRQ_STACK_SIZE (i.e. THREAD_SIZE) via thread_union.stack
		 * or set STACKSIZE() as default.
		 */
		if (MEMBER_EXISTS("thread_union", "stack")) {
			if ((sz = MEMBER_SIZE("thread_union", "stack")) > 0)
				ms->irq_stack_size = sz;
		} else
			ms->irq_stack_size = machdep->stacksize;

		machdep->flags |= IRQ_STACKS;

		for (i = 0; i < kt->cpus; i++) {
			p = kt->__per_cpu_offset[i] + sp->value;
			if (CRASHDEBUG(1))
				fprintf(fp, " IRQ stack pointer[%d] is  %lx\n", i, p);
			readmem(p, KVADDR, &(ms->irq_stacks[i]), sizeof(ulong),
				"IRQ stack pointer", RETURN_ON_ERROR);
		}
	}
}

static int
riscv64_on_irq_stack(int cpu, ulong stkptr)
{
	struct machine_specific *ms = machdep->machspec;
	ulong * stacks = ms->irq_stacks;
	ulong stack_size = ms->irq_stack_size;

	if ((cpu >= kt->cpus) || (stacks == NULL) || !stack_size)
		return FALSE;

	if ((stkptr >= stacks[cpu]) &&
	    (stkptr < (stacks[cpu] + stack_size)))
		return TRUE;

	return FALSE;
}

static int
riscv64_on_overflow_stack(int cpu, ulong stkptr)
{
	struct machine_specific *ms = machdep->machspec;
	ulong * stacks = ms->overflow_stacks;
	ulong stack_size = ms->overflow_stack_size;

	if ((cpu >= kt->cpus) || (stacks == NULL) || !stack_size)
		return FALSE;

	if ((stkptr >= stacks[cpu]) &&
	    (stkptr < (stacks[cpu] + stack_size)))
		return TRUE;

	return FALSE;
}

static int
riscv64_on_process_stack(struct bt_info *bt, ulong stkptr)
{
	ulong stackbase, stacktop;

	stackbase = GET_STACKBASE(bt->task);
	stacktop = GET_STACKTOP(bt->task);

	if ((stkptr >= stackbase) && (stkptr < stacktop))
		return TRUE;

	return FALSE;
}

static void
riscv64_set_irq_stack(struct bt_info *bt)
{
	struct machine_specific *ms = machdep->machspec;

	bt->stackbase = ms->irq_stacks[bt->tc->processor];
	bt->stacktop = bt->stackbase + ms->irq_stack_size;
	alter_stackbuf(bt);
}

static void
riscv64_set_overflow_stack(struct bt_info *bt)
{
	struct machine_specific *ms = machdep->machspec;

	bt->stackbase = ms->overflow_stacks[bt->tc->processor];
	bt->stacktop = bt->stackbase + ms->overflow_stack_size;
	alter_stackbuf(bt);
}

static void
riscv64_set_process_stack(struct bt_info *bt)
{
	bt->stackbase = GET_STACKBASE(bt->task);
	bt->stacktop = GET_STACKTOP(bt->task);
	alter_stackbuf(bt);
}

static void
riscv64_stackframe_init(void)
{
	long task_struct_thread = MEMBER_OFFSET("task_struct", "thread");

	/* from arch/riscv/include/asm/processor.h */
	long thread_reg_ra = MEMBER_OFFSET("thread_struct", "ra");
	long thread_reg_sp = MEMBER_OFFSET("thread_struct", "sp");
	long thread_reg_fp = MEMBER_OFFSET("thread_struct", "s");

	if ((task_struct_thread == INVALID_OFFSET) ||
	    (thread_reg_ra == INVALID_OFFSET) ||
	    (thread_reg_sp == INVALID_OFFSET) ||
	    (thread_reg_fp == INVALID_OFFSET) )
		error(FATAL,
		      "cannot determine thread_struct offsets\n");

	ASSIGN_OFFSET(task_struct_thread_context_pc) =
		task_struct_thread + thread_reg_ra;
	ASSIGN_OFFSET(task_struct_thread_context_sp) =
		task_struct_thread + thread_reg_sp;
	ASSIGN_OFFSET(task_struct_thread_context_fp) =
		task_struct_thread + thread_reg_fp;
}

static void
riscv64_dump_backtrace_entry(struct bt_info *bt, struct syment *sym,
			     struct riscv64_unwind_frame *current,
			     struct riscv64_unwind_frame *previous, int level)
{
	const char *name = sym ? sym->name : "(invalid)";
	struct load_module *lm;
	char *name_plus_offset = NULL;
	struct syment *symp;
	ulong symbol_offset;
	char buf[BUFSIZE];

	if (bt->flags & BT_SYMBOL_OFFSET) {
		symp = value_search(current->pc, &symbol_offset);

		if (symp && symbol_offset)
			name_plus_offset =
				value_to_symstr(current->pc, buf, bt->radix);
	}

	fprintf(fp, "%s#%d [%016lx] %s at %016lx",
		level < 10 ? " " : "",
		level,
		current->sp,
		name_plus_offset ? name_plus_offset : name,
		current->pc);

	if (module_symbol(current->pc, NULL, &lm, NULL, 0))
		fprintf(fp, " [%s]", lm->mod_name);

	fprintf(fp, "\n");

	/*
	 * 'bt -l', get a line number associated with a current pc address.
	 */
	if (bt->flags & BT_LINE_NUMBERS) {
		get_line_number(current->pc, buf, FALSE);
		if (strlen(buf))
			fprintf(fp, "    %s\n", buf);
	}

	/* bt -f */
	if (bt->flags & BT_FULL) {
		fprintf(fp, "    "
			"[PC: %016lx RA: %016lx SP: %016lx SIZE: %ld]\n",
			current->pc,
			previous->pc,
			current->sp,
			previous->sp - current->sp);
		riscv64_display_full_frame(bt, current, previous);
	}
}

/*
 * Unroll a kernel stack.
 */
static void
riscv64_back_trace_cmd(struct bt_info *bt)
{
	struct riscv64_unwind_frame current, previous;
	struct stackframe curr_frame;
	struct riscv64_register *regs, *irq_regs, *overflow_regs;
	int level = 0;

	if (bt->flags & BT_REGS_NOT_FOUND)
		return;

	regs = (struct riscv64_register *) bt->machdep;

	if (riscv64_on_irq_stack(bt->tc->processor, bt->frameptr)) {
		riscv64_set_irq_stack(bt);
		bt->flags |= BT_IRQSTACK;
	}

	if (riscv64_on_overflow_stack(bt->tc->processor, bt->frameptr)) {
		riscv64_set_overflow_stack(bt);
		bt->flags |= BT_OVERFLOW_STACK;
	}

	current.pc = bt->instptr;
	current.sp = bt->stkptr;
	current.fp = bt->frameptr;

	if (!INSTACK(current.sp, bt))
		return;

	for (;;) {
		struct syment *symbol = NULL;
		struct stackframe *frameptr;
		ulong low, high;
		ulong offset;

		if (CRASHDEBUG(8))
			fprintf(fp, "level %d pc %#lx sp %lx fp 0x%lx\n",
				level, current.pc, current.sp, current.fp);

		/* Validate frame pointer */
		low = current.sp + sizeof(struct stackframe);
		high = bt->stacktop;
		if (current.fp < low || current.fp > high || current.fp & 0x7) {
			if (CRASHDEBUG(8))
				fprintf(fp, "fp 0x%lx sp 0x%lx low 0x%lx high 0x%lx\n",
					current.fp, current.sp, low, high);
			return;
		}

		symbol = value_search(current.pc, &offset);
		if (!symbol)
			return;

		frameptr = (struct stackframe *)current.fp - 1;
		if (!readmem((ulong)frameptr, KVADDR, &curr_frame,
		    sizeof(curr_frame), "get stack frame", RETURN_ON_ERROR))
			return;

		/* correct PC and FP of the second frame when the first frame has no callee */

		if (regs && (regs->regs[RISCV64_REGS_EPC] == current.pc) && curr_frame.fp & 0x7){
			previous.pc = regs->regs[RISCV64_REGS_RA];
			previous.fp = curr_frame.ra;
		} else {
			previous.pc = curr_frame.ra;
			previous.fp = curr_frame.fp;
		}

		previous.sp = current.fp;

		riscv64_dump_backtrace_entry(bt, symbol, &current, &previous, level++);

		current.pc = previous.pc;
		current.fp = previous.fp;
		current.sp = previous.sp;

		/*
		 * When backtracing to do_irq(), find the original FP of do_irq()
		 * and then use the saved pt_regs in process stack to continue
		 */
		if ((bt->flags & BT_IRQSTACK) &&
		    !riscv64_on_irq_stack(bt->tc->processor, current.fp)){
			if (riscv64_on_process_stack(bt, current.fp)){

				frameptr = (struct stackframe *)current.fp - 1;

				if (!readmem((ulong)frameptr, KVADDR, &curr_frame,
				    sizeof(curr_frame), "get do_irq stack frame", RETURN_ON_ERROR))
					return;

				riscv64_set_process_stack(bt);

				irq_regs = (struct riscv64_register *)
					&bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(curr_frame.fp))];

				current.pc = irq_regs->regs[RISCV64_REGS_EPC];
				current.fp = irq_regs->regs[RISCV64_REGS_FP];
				current.sp = irq_regs->regs[RISCV64_REGS_SP];

				bt->flags &= ~BT_IRQSTACK;
				riscv64_print_exception_frame(bt, curr_frame.fp, KERNEL_MODE);
				fprintf(fp, "--- <IRQ stack> ---\n");
			}
		}

		/*
		 * When backtracing to handle_kernel_stack_overflow()
		 * use pt_regs saved in overflow stack to continue
		 */
		if ((bt->flags & BT_OVERFLOW_STACK) &&
		    !riscv64_on_overflow_stack(bt->tc->processor, current.fp)) {

				overflow_regs = (struct riscv64_register *)
					&bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(current.sp))];

				riscv64_print_exception_frame(bt, current.sp, KERNEL_MODE);

				current.pc = overflow_regs->regs[RISCV64_REGS_EPC];
				current.fp = overflow_regs->regs[RISCV64_REGS_FP];
				current.sp = overflow_regs->regs[RISCV64_REGS_SP];

				riscv64_set_process_stack(bt);

				bt->flags &= ~BT_OVERFLOW_STACK;
				fprintf(fp, "--- <OVERFLOW stack> ---\n");
		}

		if (CRASHDEBUG(8))
			fprintf(fp, "next %d pc %#lx sp %#lx fp %lx\n",
				level, current.pc, current.sp, current.fp);
	}
}

/*
 * Get a stack frame combination of pc and ra from the most relevant spot.
 */
static void
riscv64_get_stack_frame(struct bt_info *bt, ulong *pcp, ulong *spp)
{
	ulong ksp = 0, nip = 0;
	int ret = 0;

	if (DUMPFILE() && is_task_active(bt->task))
		ret = riscv64_get_dumpfile_stack_frame(bt, &nip, &ksp);
	else
		ret = riscv64_get_frame(bt, &nip, &ksp);

	if (!ret)
		error(WARNING, "cannot determine starting stack frame for task %lx\n",
			bt->task);

	if (pcp)
		*pcp = nip;
	if (spp)
		*spp = ksp;
}

/*
 * Get the starting point for the active cpu in a diskdump.
 */
static int
riscv64_get_dumpfile_stack_frame(struct bt_info *bt, ulong *nip, ulong *ksp)
{
	const struct machine_specific *ms = machdep->machspec;
	struct riscv64_register *regs;
	ulong epc, sp;

	if (!ms->crash_task_regs) {
		bt->flags |= BT_REGS_NOT_FOUND;
		return FALSE;
	}

	/*
	 * We got registers for panic task from crash_notes. Just return them.
	 */
	regs = &ms->crash_task_regs[bt->tc->processor];
	epc = regs->regs[RISCV64_REGS_EPC];
	sp = regs->regs[RISCV64_REGS_SP];

	/*
	 * Set stack frame ptr.
	 */
	bt->frameptr = regs->regs[RISCV64_REGS_FP];

	if (nip)
		*nip = epc;
	if (ksp)
		*ksp = sp;

	bt->machdep = regs;

	return TRUE;
}

/*
 * Do the work for riscv64_get_stack_frame() for non-active tasks.
 * Get SP and PC values for idle tasks.
 */
static int
riscv64_get_frame(struct bt_info *bt, ulong *pcp, ulong *spp)
{
	if (!bt->tc || !(tt->flags & THREAD_INFO))
		return FALSE;

	if (!readmem(bt->task + OFFSET(task_struct_thread_context_pc),
		     KVADDR, pcp, sizeof(*pcp),
		     "thread_struct.ra",
		     RETURN_ON_ERROR))
		return FALSE;

	if (!readmem(bt->task + OFFSET(task_struct_thread_context_sp),
		     KVADDR, spp, sizeof(*spp),
		     "thread_struct.sp",
		     RETURN_ON_ERROR))
		return FALSE;

	if (!readmem(bt->task + OFFSET(task_struct_thread_context_fp),
		     KVADDR, &bt->frameptr, sizeof(bt->frameptr),
		     "thread_struct.fp",
		     RETURN_ON_ERROR))
		return FALSE;

	return TRUE;
}

static int
riscv64_vtop_4level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr, int verbose)
{
	ulong *pgd_ptr, pgd_val;
	ulong pud_val;
	ulong pmd_val;
	ulong pte_val, pte_pfn;
	ulong pt_phys;

	if (verbose)
		fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd);

	/* PGD */
	pgd_ptr = pgd + pgd_index_l4_4k(vaddr);
	FILL_PGD(pgd, KVADDR, PAGESIZE());
	pgd_val = ULONG(machdep->pgd + PAGEOFFSET(pgd_ptr));
	if (verbose)
		fprintf(fp, "  PGD: %lx => %lx\n", (ulong)pgd_ptr, pgd_val);
	if (!pgd_val)
		goto no_page;
	pgd_val &= PTE_PFN_PROT_MASK;
	pt_phys = (pgd_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();

	/* PUD */
	FILL_PUD(PAGEBASE(pt_phys), PHYSADDR, PAGESIZE());
	pud_val = ULONG(machdep->pud + PAGEOFFSET(sizeof(pud_t) *
			pud_index_l4_4k(vaddr)));
	if (verbose)
		fprintf(fp, "  PUD: %016lx => %016lx\n", pt_phys, pud_val);
	if (!pud_val)
		goto no_page;
	pud_val &= PTE_PFN_PROT_MASK;
	pt_phys = (pud_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();

	/* PMD */
	FILL_PMD(PAGEBASE(pt_phys), PHYSADDR, PAGESIZE());
	pmd_val = ULONG(machdep->pmd + PAGEOFFSET(sizeof(pmd_t) *
			pmd_index_l4_4k(vaddr)));
	if (verbose)
		fprintf(fp, "  PMD: %016lx => %016lx\n", pt_phys, pmd_val);
	if (!pmd_val)
		goto no_page;
	pmd_val &= PTE_PFN_PROT_MASK;
	pt_phys = (pmd_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();

	/* PTE */
	FILL_PTBL(PAGEBASE(pt_phys), PHYSADDR, PAGESIZE());
	pte_val = ULONG(machdep->ptbl + PAGEOFFSET(sizeof(pte_t) *
			pte_index_l4_4k(vaddr)));
	if (verbose)
		fprintf(fp, "  PTE: %lx => %lx\n", pt_phys, pte_val);
	if (!pte_val)
		goto no_page;
	pte_val &= PTE_PFN_PROT_MASK;
	pte_pfn = pte_val >> _PAGE_PFN_SHIFT;

	if (!(pte_val & _PAGE_PRESENT)) {
		if (verbose) {
			fprintf(fp, "\n");
			riscv64_translate_pte((ulong)pte_val, 0, 0);
		}
		fprintf(fp, " PAGE: %016lx not present\n\n", PAGEBASE(*paddr));
		return FALSE;
	}

	*paddr = PTOB(pte_pfn) + PAGEOFFSET(vaddr);

	if (verbose) {
		fprintf(fp, " PAGE: %016lx\n\n", PAGEBASE(*paddr));
		riscv64_translate_pte(pte_val, 0, 0);
	}

	return TRUE;
no_page:
	fprintf(fp, "invalid\n");
	return FALSE;
}

static int
riscv64_vtop_5level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr, int verbose)
{
	ulong *pgd_ptr, pgd_val;
	ulong p4d_val;
	ulong pud_val;
	ulong pmd_val;
	ulong pte_val, pte_pfn;
	ulong pt_phys;

	if (verbose)
		fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd);

	/* PGD */
	pgd_ptr = pgd + pgd_index_l5_4k(vaddr);
	FILL_PGD(pgd, KVADDR, PAGESIZE());
	pgd_val = ULONG(machdep->pgd + PAGEOFFSET(pgd_ptr));
	if (verbose)
		fprintf(fp, "  PGD: %lx => %lx\n", (ulong)pgd_ptr, pgd_val);
	if (!pgd_val)
		goto no_page;
	pgd_val &= PTE_PFN_PROT_MASK;
	pt_phys = (pgd_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();

	/* P4D */
	FILL_P4D(PAGEBASE(pt_phys), PHYSADDR, PAGESIZE());
	p4d_val = ULONG(machdep->machspec->p4d + PAGEOFFSET(sizeof(p4d_t) *
			p4d_index_l5_4k(vaddr)));
	if (verbose)
		fprintf(fp, "  P4D: %016lx => %016lx\n", pt_phys, p4d_val);
	if (!p4d_val)
		goto no_page;
	p4d_val &= PTE_PFN_PROT_MASK;
	pt_phys = (p4d_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();

	/* PUD */
	FILL_PUD(PAGEBASE(pt_phys), PHYSADDR, PAGESIZE());
	pud_val = ULONG(machdep->pud + PAGEOFFSET(sizeof(pud_t) *
			pud_index_l5_4k(vaddr)));
	if (verbose)
		fprintf(fp, "  PUD: %016lx => %016lx\n", pt_phys, pud_val);
	if (!pud_val)
		goto no_page;
	pud_val &= PTE_PFN_PROT_MASK;
	pt_phys = (pud_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();

	/* PMD */
	FILL_PMD(PAGEBASE(pt_phys), PHYSADDR, PAGESIZE());
	pmd_val = ULONG(machdep->pmd + PAGEOFFSET(sizeof(pmd_t) *
			pmd_index_l4_4k(vaddr)));
	if (verbose)
		fprintf(fp, "  PMD: %016lx => %016lx\n", pt_phys, pmd_val);
	if (!pmd_val)
		goto no_page;
	pmd_val &= PTE_PFN_PROT_MASK;
	pt_phys = (pmd_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();

	/* PTE */
	FILL_PTBL(PAGEBASE(pt_phys), PHYSADDR, PAGESIZE());
	pte_val = ULONG(machdep->ptbl + PAGEOFFSET(sizeof(pte_t) *
			pte_index_l4_4k(vaddr)));
	if (verbose)
		fprintf(fp, "  PTE: %lx => %lx\n", pt_phys, pte_val);
	if (!pte_val)
		goto no_page;
	pte_val &= PTE_PFN_PROT_MASK;
	pte_pfn = pte_val >> _PAGE_PFN_SHIFT;

	if (!(pte_val & _PAGE_PRESENT)) {
		if (verbose) {
			fprintf(fp, "\n");
			riscv64_translate_pte((ulong)pte_val, 0, 0);
		}
		printf("!_PAGE_PRESENT\n");
		return FALSE;
	}

	*paddr = PTOB(pte_pfn) + PAGEOFFSET(vaddr);

	if (verbose) {
		fprintf(fp, " PAGE: %016lx\n\n", PAGEBASE(*paddr));
		riscv64_translate_pte(pte_val, 0, 0);
	}

	return TRUE;
no_page:
	fprintf(fp, "invalid\n");
	return FALSE;
}

static int
riscv64_init_active_task_regs(void)
{
	int retval;

	retval = riscv64_get_crash_notes();
	if (retval == TRUE)
		return retval;

	return riscv64_get_elf_notes();
}

/*
 * Retrieve task registers for the time of the crash.
 */
static int
riscv64_get_crash_notes(void)
{
	struct machine_specific *ms = machdep->machspec;
	ulong crash_notes;
	Elf64_Nhdr *note;
	ulong offset;
	char *buf, *p;
	ulong *notes_ptrs;
	ulong i;

	/*
	 * crash_notes contains per cpu memory for storing cpu states
	 * in case of system crash.
	 */
	if (!symbol_exists("crash_notes"))
		return FALSE;

	crash_notes = symbol_value("crash_notes");

	notes_ptrs = (ulong *)GETBUF(kt->cpus*sizeof(notes_ptrs[0]));

	/*
	 * Read crash_notes for the first CPU. crash_notes are in standard ELF
	 * note format.
	 */
	if (!readmem(crash_notes, KVADDR, &notes_ptrs[kt->cpus-1],
	    sizeof(notes_ptrs[kt->cpus-1]), "crash_notes",
		     RETURN_ON_ERROR)) {
		error(WARNING, "cannot read crash_notes\n");
		FREEBUF(notes_ptrs);
		return FALSE;
	}

	if (symbol_exists("__per_cpu_offset")) {

		/*
		 * Add __per_cpu_offset for each cpu to form the pointer to the notes
		 */
		for (i = 0; i < kt->cpus; i++)
			notes_ptrs[i] = notes_ptrs[kt->cpus-1] + kt->__per_cpu_offset[i];
	}

	buf = GETBUF(SIZE(note_buf));

	if (!(panic_task_regs = calloc((size_t)kt->cpus, sizeof(*panic_task_regs))))
		error(FATAL, "cannot calloc panic_task_regs space\n");

	for (i = 0; i < kt->cpus; i++) {

		if (!readmem(notes_ptrs[i], KVADDR, buf, SIZE(note_buf), "note_buf_t",
			     RETURN_ON_ERROR)) {
			error(WARNING,
				"cannot find NT_PRSTATUS note for cpu: %d\n", i);
			goto fail;
		}

		/*
		 * Do some sanity checks for this note before reading registers from it.
		 */
		note = (Elf64_Nhdr *)buf;
		p = buf + sizeof(Elf64_Nhdr);

		/*
		 * dumpfiles created with qemu won't have crash_notes, but there will
		 * be elf notes; dumpfiles created by kdump do not create notes for
		 * offline cpus.
		 */
		if (note->n_namesz == 0 && (DISKDUMP_DUMPFILE() || KDUMP_DUMPFILE())) {
			if (DISKDUMP_DUMPFILE())
				note = diskdump_get_prstatus_percpu(i);
			else if (KDUMP_DUMPFILE())
				note = netdump_get_prstatus_percpu(i);
			if (note) {
				/*
				 * SIZE(note_buf) accounts for a "final note", which is a
				 * trailing empty elf note header.
				 */
				long notesz = SIZE(note_buf) - sizeof(Elf64_Nhdr);

				if (sizeof(Elf64_Nhdr) + roundup(note->n_namesz, 4) +
				    note->n_descsz == notesz)
					BCOPY((char *)note, buf, notesz);
			} else {
				error(WARNING,
					"cannot find NT_PRSTATUS note for cpu: %d\n", i);
				continue;
			}
		}

		/*
		 * Check the sanity of NT_PRSTATUS note only for each online cpu.
		 */
		if (note->n_type != NT_PRSTATUS) {
			error(WARNING, "invalid NT_PRSTATUS note (n_type != NT_PRSTATUS)\n");
			goto fail;
		}
		if (!STRNEQ(p, "CORE")) {
			error(WARNING, "invalid NT_PRSTATUS note (name != \"CORE\"\n");
			goto fail;
		}

		/*
		 * Find correct location of note data. This contains elf_prstatus
		 * structure which has registers etc. for the crashed task.
		 */
		offset = sizeof(Elf64_Nhdr);
		offset = roundup(offset + note->n_namesz, 4);
		p = buf + offset; /* start of elf_prstatus */

		BCOPY(p + OFFSET(elf_prstatus_pr_reg), &panic_task_regs[i],
		      sizeof(panic_task_regs[i]));
	}

	/*
	 * And finally we have the registers for the crashed task. This is
	 * used later on when dumping backtrace.
	 */
	ms->crash_task_regs = panic_task_regs;

	FREEBUF(buf);
	FREEBUF(notes_ptrs);
	return TRUE;

fail:
	FREEBUF(buf);
	FREEBUF(notes_ptrs);
	free(panic_task_regs);
	return FALSE;
}

static int
riscv64_get_elf_notes(void)
{
	struct machine_specific *ms = machdep->machspec;
	int i;

	if (!DISKDUMP_DUMPFILE() && !KDUMP_DUMPFILE())
		return FALSE;

	panic_task_regs = calloc(kt->cpus, sizeof(*panic_task_regs));
	if (!panic_task_regs)
		error(FATAL, "cannot calloc panic_task_regs space\n");

	for (i = 0; i < kt->cpus; i++) {
		Elf64_Nhdr *note = NULL;
		size_t len;

		if (DISKDUMP_DUMPFILE())
			note = diskdump_get_prstatus_percpu(i);
		else if (KDUMP_DUMPFILE())
			note = netdump_get_prstatus_percpu(i);

		if (!note) {
			error(WARNING,
				"cannot find NT_PRSTATUS note for cpu: %d\n", i);
			continue;
		}

		len = sizeof(Elf64_Nhdr);
		len = roundup(len + note->n_namesz, 4);

		BCOPY((char *)note + len + OFFSET(elf_prstatus_pr_reg),
		      &panic_task_regs[i], sizeof(panic_task_regs[i]));
	}

	ms->crash_task_regs = panic_task_regs;

	return TRUE;
}

/*
 * Translates a user virtual address to its physical address.
 */
static int
riscv64_uvtop(struct task_context *tc, ulong uvaddr, 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(uvaddr)) {
		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);
	}

	switch (machdep->flags & VM_FLAGS)
	{
	case VM_L3_4K:
		return riscv64_vtop_3level_4k(pgd, uvaddr, paddr, verbose);
	case VM_L4_4K:
		return riscv64_vtop_4level_4k(pgd, uvaddr, paddr, verbose);
	case VM_L5_4K:
		return riscv64_vtop_5level_4k(pgd, uvaddr, paddr, verbose);
	default:
		return FALSE;
	}
}

static int
riscv64_kvtop(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verbose)
{
	ulong kernel_pgd;

	if (!IS_KVADDR(kvaddr))
		return FALSE;

	if (!vt->vmalloc_start) {
		*paddr = VTOP(kvaddr);
		return TRUE;
	}

	if (!IS_VMALLOC_ADDR(kvaddr)) {
		*paddr = VTOP(kvaddr);
		if (!verbose)
			return TRUE;
	}

	kernel_pgd = vt->kernel_pgd[0];
	*paddr = 0;

	switch (machdep->flags & VM_FLAGS)
	{
	case VM_L3_4K:
		return riscv64_vtop_3level_4k((ulong *)kernel_pgd, kvaddr, paddr, verbose);
	case VM_L4_4K:
		return riscv64_vtop_4level_4k((ulong *)kernel_pgd, kvaddr, paddr, verbose);
	case VM_L5_4K:
		return riscv64_vtop_5level_4k((ulong *)kernel_pgd, kvaddr, paddr, verbose);
	default:
		return FALSE;
	}
}

void
riscv64_init(int when)
{
	switch (when) {
	case SETUP_ENV:
		machdep->process_elf_notes = process_elf64_notes;
		break;

	case PRE_SYMTAB:
		machdep->verify_symbol = riscv64_verify_symbol;
		machdep->machspec = &riscv64_machine_specific;
		if (pc->flags & KERNEL_DEBUG_QUERY)
			return;

		machdep->verify_paddr = generic_verify_paddr;
		machdep->ptrs_per_pgd = PTRS_PER_PGD;

		/*
		 * Even if CONFIG_RANDOMIZE_BASE is not configured,
		 * derive_kaslr_offset() should work and set
		 * kt->relocate to 0
		 */
		if (!kt->relocate && !(kt->flags2 & (RELOC_AUTO|KASLR)))
			kt->flags2 |= (RELOC_AUTO|KASLR);
		break;

	case PRE_GDB:
		machdep->pagesize = riscv64_get_page_size();
		machdep->pageshift = ffs(machdep->pagesize) - 1;
		machdep->pageoffset = machdep->pagesize - 1;
		machdep->pagemask = ~((ulonglong)machdep->pageoffset);
		machdep->stacksize = machdep->pagesize << THREAD_SIZE_ORDER;

		riscv64_get_phys_ram_base(machdep->machspec);
		riscv64_get_struct_page_size(machdep->machspec);
		riscv64_get_va_bits(machdep->machspec);
		riscv64_get_va_range(machdep->machspec);
		riscv64_get_va_kernel_pa_offset(machdep->machspec);

		pt_level_alloc(&machdep->pgd, "cannot malloc pgd space.");
		pt_level_alloc(&machdep->machspec->p4d, "cannot malloc p4d space.");
		pt_level_alloc(&machdep->pud, "cannot malloc pud space.");
		pt_level_alloc(&machdep->pmd, "cannot malloc pmd space.");
		pt_level_alloc(&machdep->ptbl, "cannot malloc ptbl space.");

		machdep->last_pgd_read = 0;
		machdep->machspec->last_p4d_read = 0;
		machdep->last_pud_read = 0;
		machdep->last_pmd_read = 0;
		machdep->last_ptbl_read = 0;

		machdep->kvbase = machdep->machspec->page_offset;
		machdep->identity_map_base = machdep->kvbase;
		machdep->is_kvaddr = riscv64_is_kvaddr;
		machdep->is_uvaddr = riscv64_is_uvaddr;
		machdep->uvtop = riscv64_uvtop;
		machdep->kvtop = riscv64_kvtop;
		machdep->cmd_mach = riscv64_cmd_mach;
		machdep->get_stack_frame = riscv64_get_stack_frame;
		machdep->back_trace = riscv64_back_trace_cmd;
		machdep->eframe_search = riscv64_eframe_search;

		machdep->vmalloc_start = riscv64_vmalloc_start;
		machdep->processor_speed = riscv64_processor_speed;
		machdep->get_stackbase = generic_get_stackbase;
		machdep->get_stacktop = generic_get_stacktop;
		machdep->translate_pte = riscv64_translate_pte;
		machdep->memory_size = generic_memory_size;
		machdep->is_task_addr = riscv64_is_task_addr;
		machdep->get_smp_cpus = riscv64_get_smp_cpus;
		machdep->value_to_symbol = generic_machdep_value_to_symbol;
		machdep->dis_filter = generic_dis_filter;
		machdep->dump_irq = generic_dump_irq;
		machdep->show_interrupts = generic_show_interrupts;
		machdep->get_irq_affinity = generic_get_irq_affinity;
		machdep->init_kernel_pgd = NULL; /* pgd set by symbol_value("swapper_pg_dir") */
		break;

	case POST_GDB:
		machdep->section_size_bits = _SECTION_SIZE_BITS;
		machdep->max_physmem_bits = _MAX_PHYSMEM_BITS;

		riscv64_irq_stack_init();
		riscv64_overflow_stack_init();
		riscv64_stackframe_init();
		riscv64_page_type_init();

		if (!machdep->hz)
			machdep->hz = 250;

		if (symbol_exists("irq_desc"))
			ARRAY_LENGTH_INIT(machdep->nr_irqs, irq_desc,
					  "irq_desc", NULL, 0);
		else if (kernel_symbol_exists("nr_irqs"))
			get_symbol_data("nr_irqs", sizeof(unsigned int),
					&machdep->nr_irqs);

		MEMBER_OFFSET_INIT(elf_prstatus_pr_reg, "elf_prstatus",
				   "pr_reg");

		STRUCT_SIZE_INIT(note_buf, "note_buf_t");
		break;

	case POST_VM:
		/*
		 * crash_notes contains machine specific information about the
		 * crash. In particular, it contains CPU registers at the time
		 * of the crash. We need this information to extract correct
		 * backtraces from the panic task.
		 */
		if (!ACTIVE() && !riscv64_init_active_task_regs())
			error(WARNING,
				"cannot retrieve registers for active task%s\n\n",
				kt->cpus > 1 ? "s" : "");
		break;
	}
}

/* bool pt_regs : pass 1 to dump pt_regs , pass 0 to dump user_regs_struct */
static void
riscv64_dump_pt_regs(struct riscv64_register *regs, FILE *ofp, bool pt_regs)
{

	/* Print riscv64 32 regs */
	fprintf(ofp,
		"epc : " REG_FMT " ra : " REG_FMT " sp : " REG_FMT "\n"
		" gp : " REG_FMT " tp : " REG_FMT " t0 : " REG_FMT "\n"
		" t1 : " REG_FMT " t2 : " REG_FMT " s0 : " REG_FMT "\n"
		" s1 : " REG_FMT " a0 : " REG_FMT " a1 : " REG_FMT "\n"
		" a2 : " REG_FMT " a3 : " REG_FMT " a4 : " REG_FMT "\n"
		" a5 : " REG_FMT " a6 : " REG_FMT " a7 : " REG_FMT "\n"
		" s2 : " REG_FMT " s3 : " REG_FMT " s4 : " REG_FMT "\n"
		" s5 : " REG_FMT " s6 : " REG_FMT " s7 : " REG_FMT "\n"
		" s8 : " REG_FMT " s9 : " REG_FMT " s10: " REG_FMT "\n"
		" s11: " REG_FMT " t3 : " REG_FMT " t4 : " REG_FMT "\n"
		" t5 : " REG_FMT " t6 : " REG_FMT "\n",
		regs->regs[0],  regs->regs[1],  regs->regs[2],
		regs->regs[3],  regs->regs[4],  regs->regs[5],
		regs->regs[6],  regs->regs[7],  regs->regs[8],
		regs->regs[9],  regs->regs[10], regs->regs[11],
		regs->regs[12], regs->regs[13], regs->regs[14],
		regs->regs[15], regs->regs[16], regs->regs[17],
		regs->regs[18], regs->regs[19], regs->regs[20],
		regs->regs[21], regs->regs[22], regs->regs[23],
		regs->regs[24], regs->regs[25], regs->regs[26],
		regs->regs[27], regs->regs[28], regs->regs[29],
		regs->regs[30], regs->regs[31]);

	if (pt_regs)
		fprintf(ofp,
		" status: " REG_FMT " badaddr: " REG_FMT "\n"
		"  cause: " REG_FMT " orig_a0: " REG_FMT "\n",
		regs->regs[32], regs->regs[33], regs->regs[34],
		regs->regs[35]);
}

/*
 * 'help -r' command output
 */
void
riscv64_display_regs_from_elf_notes(int cpu, FILE *ofp)
{
	const struct machine_specific *ms = machdep->machspec;
	struct riscv64_register *regs;

	if (!ms->crash_task_regs) {
		error(INFO, "registers not collected for cpu %d\n", cpu);
		return;
	}

	regs = &ms->crash_task_regs[cpu];
	if (!regs->regs[RISCV64_REGS_SP] && !regs->regs[RISCV64_REGS_EPC]) {
		error(INFO, "registers not collected for cpu %d\n", cpu);
		return;
	}

	riscv64_dump_pt_regs(regs, ofp, 0);
}

static void
riscv64_print_exception_frame(struct bt_info *bt, ulong ptr, int mode)
{

	struct syment *sp;
	ulong PC, RA, SP, offset;
	struct riscv64_register *regs;

	regs = (struct riscv64_register *)&bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(ptr))];

	PC = regs->regs[RISCV64_REGS_EPC];
	RA = regs->regs[RISCV64_REGS_RA];
	SP = regs->regs[RISCV64_REGS_SP];

	switch (mode) {
	case USER_MODE:
		fprintf(fp,
		    "     PC: %016lx   RA: %016lx   SP: %016lx\n"
		    "     ORIG_A0: %016lx   SYSCALLNO: %016lx\n",
		    PC, RA, SP, regs->regs[35], regs->regs[17]);

		break;

	case KERNEL_MODE:
		fprintf(fp, "     PC: %016lx  ", PC);
		if (is_kernel_text(PC) && (sp = value_search(PC, &offset))) {
			fprintf(fp, "[%s", sp->name);
			if (offset)
				fprintf(fp, (*gdb_output_radix == 16) ?
					"+0x%lx" : "+%ld", offset);
			fprintf(fp, "]\n");
		} else
			fprintf(fp, "[unknown or invalid address]\n");

		fprintf(fp, "     RA: %016lx  ", RA);
		if (is_kernel_text(RA) && (sp = value_search(RA, &offset))) {
			fprintf(fp, "[%s", sp->name);
			if (offset)
				fprintf(fp, (*gdb_output_radix == 16) ?
					"+0x%lx" : "+%ld", offset);
			fprintf(fp, "]\n");
		} else
			fprintf(fp, "[unknown or invalid address]\n");

		fprintf(fp, "     SP: %016lx  CAUSE: %016lx\n",
			SP, regs->regs[RISCV64_REGS_CAUSE]);

		break;
	}

	riscv64_dump_pt_regs(regs, fp, 1);

}

static int
riscv64_is_kernel_exception_frame(struct bt_info *bt, ulong stkptr)
{
	struct riscv64_register *regs;

	if (stkptr > STACKSIZE() && !INSTACK(stkptr, bt)) {
		if (CRASHDEBUG(1))
			error(WARNING, "stkptr: %lx is outside the kernel stack range\n", stkptr);
		return FALSE;
	}

	regs = (struct riscv64_register *)&bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(stkptr))];

	if (INSTACK(regs->regs[RISCV64_REGS_SP], bt) &&
	    INSTACK(regs->regs[RISCV64_REGS_FP], bt) &&
	    is_kernel_text(regs->regs[RISCV64_REGS_RA]) &&
	    is_kernel_text(regs->regs[RISCV64_REGS_EPC]) &&
	    ((regs->regs[RISCV64_REGS_STATUS] >> 8) & 0x1) && // sstatus.SPP != 0
	    !((regs->regs[RISCV64_REGS_CAUSE] >> 63) & 0x1 ) && // scause.Interrupt != 1
	    !(regs->regs[RISCV64_REGS_CAUSE] == 0x00000008UL)) { // scause != ecall from U-mode

		return TRUE;
	}

	return FALSE;
}

static int
riscv64_dump_kernel_eframes(struct bt_info *bt)
{
	ulong ptr;
	int count;

	/*
	 * use old_regs to avoid the identical contiguous kernel exception frames
	 * created by Linux handle_exception() path ending at riscv_crash_save_regs()
	 */
	struct riscv64_register *regs, *old_regs;

	count = 0;
	old_regs = NULL;

	for (ptr = bt->stackbase; ptr < bt->stacktop - SIZE(pt_regs); ptr++) {

		regs = (struct riscv64_register *)&bt->stackbuf[(ulong)(STACK_OFFSET_TYPE(ptr))];

		if (riscv64_is_kernel_exception_frame(bt, ptr)){
			if (!old_regs || (old_regs &&
			    memcmp(old_regs, regs, sizeof(struct riscv64_register))) != 0){
				old_regs = regs;
				fprintf(fp, "\nKERNEL-MODE EXCEPTION FRAME AT: %lx\n", ptr);
				riscv64_print_exception_frame(bt, ptr, KERNEL_MODE);
				count++;
			}
		}
	}

	return count;
}

static int
riscv64_eframe_search(struct bt_info *bt)
{
	ulong ptr;
	int count, c;
	struct machine_specific *ms = machdep->machspec;

	if (bt->flags & BT_EFRAME_SEARCH2) {
		if (!(machdep->flags & IRQ_STACKS))
			error(FATAL, "IRQ stacks do not exist in this kernel\n");

		for (c = 0; c < kt->cpus; c++) {
			if ((bt->flags & BT_CPUMASK) &&
			    !(NUM_IN_BITMAP(bt->cpumask, c)))
				continue;

			fprintf(fp, "CPU %d IRQ STACK: ", c);
			bt->stackbase = ms->irq_stacks[c];
			bt->stacktop = bt->stackbase + ms->irq_stack_size;
			alter_stackbuf(bt);

			count = riscv64_dump_kernel_eframes(bt);

			if (count)
				fprintf(fp, "\n");
			else
				fprintf(fp, "(none found)\n\n");
		}

		return 0;
	}

	count = riscv64_dump_kernel_eframes(bt);

	if (is_kernel_thread(bt->tc->task))
		return count;

	ptr = bt->stacktop - SIZE(pt_regs);
	fprintf(fp, "%sUSER-MODE EXCEPTION FRAME AT: %lx\n", count++ ? "\n" : "", ptr);
	riscv64_print_exception_frame(bt, ptr, USER_MODE);

	return count;
}

#else /* !RISCV64 */

void
riscv64_display_regs_from_elf_notes(int cpu, FILE *ofp)
{
	return;
}

#endif /* !RISCV64 */
