reg_t mmu_t::walk(reg_t addr, access_type type, bool supervisor, bool pum)
{
+ fprintf(stderr, "walk 0x%lx\n", addr);
int levels, ptidxbits, ptesize;
switch (get_field(proc->get_state()->mstatus, MSTATUS_VM))
{
int va_bits = PGSHIFT + levels * ptidxbits;
reg_t mask = (reg_t(1) << (proc->xlen - (va_bits-1))) - 1;
reg_t masked_msbs = (addr >> (va_bits-1)) & mask;
+ fprintf(stderr, "walk masked_msbs=0x%lx, mask=0x%lx\n", masked_msbs, mask);
if (masked_msbs != 0 && masked_msbs != mask)
return -1;
// check that physical address of PTE is legal
reg_t pte_addr = base + idx * ptesize;
+ fprintf(stderr, "pte_addr=0x%lx\n", pte_addr);
if (!sim->addr_is_mem(pte_addr))
break;
reg_t pte = ptesize == 4 ? *(uint32_t*)ppte : *(uint64_t*)ppte;
reg_t ppn = pte >> PTE_PPN_SHIFT;
+ fprintf(stderr, "pte=0x%lx\n", pte);
+
if (PTE_TABLE(pte)) { // next level of page table
base = ppn << PGSHIFT;
} else if (pum && PTE_CHECK_PERM(pte, 0, type == STORE, type == FETCH)) {
+ fprintf(stderr, "pum fail\n");
break;
} else if (!PTE_CHECK_PERM(pte, supervisor, type == STORE, type == FETCH)) {
+ fprintf(stderr, "perm(0x%lx, %d, %d, %d)\n",
+ pte, supervisor, type==STORE, type==FETCH);
break;
} else {
// set referenced and possibly dirty bits.
// for superpage mappings, make a fake leaf PTE for the TLB's benefit.
reg_t vpn = addr >> PGSHIFT;
reg_t value = (ppn | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT;
+ fprintf(stderr, " -> 0x%lx\n", value);
return value;
}
}
self.gdb.command("stepi")
self.assertNotEqual(0x1000, self.gdb.p("$pc"))
+ def test_change_pc(self):
+ """Change the PC right as we come out of reset."""
+ # 0x13 is nop
+ self.gdb.command("p *((int*) 0x80000000)=0x13")
+ self.gdb.command("p *((int*) 0x80000004)=0x13")
+ self.gdb.command("p *((int*) 0x80000008)=0x13")
+ self.gdb.command("p $pc=0x80000000")
+ self.gdb.command("stepi")
+ self.assertEqual(0x80000004, self.gdb.p("$pc"))
+ self.gdb.command("stepi")
+ self.assertEqual(0x80000008, self.gdb.p("$pc"))
+
class DebugTest(unittest.TestCase):
def setUp(self):
self.binary = testlib.compile("debug.c")
self.assertEqual(9, self.gdb.p("$x1"))
self.assertEqual(9, self.gdb.p("$csr1"))
-#class MprvTest(unittest.TestCase):
-# def setUp(self):
-# self.binary = testlib.compile("mprv.S")
-# self.spike, self.port = testlib.spike(self.binary, halted=False)
-# self.gdb = testlib.Gdb()
-# self.gdb.command("file %s" % self.binary)
-# self.gdb.command("target extended-remote localhost:%d" % self.port)
-#
-# def tearDown(self):
-# self.spike.kill()
-# self.spike.wait()
-#
-# def test_mprv(self):
-# """Test that the debugger can access memory when MPRV is set."""
-# output = self.gdb.command("p/x data");
-# self.assertIn("0xbead", output)
+class MprvTest(unittest.TestCase):
+ def setUp(self):
+ self.binary = testlib.compile("mprv.S", "-T", "standalone.lds",
+ "-nostartfiles")
+ self.spike, self.port = testlib.spike(None, halted=True)
+ self.gdb = testlib.Gdb()
+ self.gdb.command("file %s" % self.binary)
+ self.gdb.command("target extended-remote localhost:%d" % self.port)
+ self.gdb.command("load")
+
+ def tearDown(self):
+ self.spike.kill()
+ self.spike.wait()
+
+ def test_mprv(self):
+ """Test that the debugger can access memory when MPRV is set."""
+ self.gdb.c(wait=False)
+ self.gdb.interrupt()
+ output = self.gdb.command("p/x *(int*)(((char*)&data)-0x80000000)")
+ self.assertIn("0xbead", output)
if __name__ == '__main__':
+ # TROUBLESHOOTING TIPS
+ # If a particular test fails, run just that one test, eg.:
+ # ./tests/gdbserver.py MprvTest.test_mprv
+ # Then inspect gdb.log and spike.log to see what happened in more detail.
unittest.main()
--- /dev/null
+#include "../riscv/encoding.h"
+#define PGSHIFT 12
+
+ .global main
+
+ .section .text
+main:
+ # Set up a page table entry that maps 0x0... to 0x8...
+ la t0, page_table
+ srli t0, t0, PGSHIFT
+ csrw CSR_SPTBR, t0
+
+ # update mstatus
+ csrr t1, CSR_MSTATUS
+ li t0, (MSTATUS_MPRV | (VM_SV39 << 24))
+ #li t0, ((VM_SV39 << 24))
+ or t1, t0, t1
+ csrw CSR_MSTATUS, t1
+
+ la t0, (loop - 0x80000000)
+ csrw CSR_MEPC, t0
+
+ # Exit supervisor mode, entering user mode at loop.
+ mret
+
+loop:
+ la t0, data
+ lw t1, 0(t0)
+ j loop
+
+ .section .data
+data:
+ .word 0xbead
+
+ .balign 0x1000
+page_table:
+ .word ((0x80000000 >> 2) | PTE_V | PTE_TYPE_URWX_SRWX)
+ .word 0
--- /dev/null
+OUTPUT_ARCH( "riscv" )
+
+ENTRY( main )
+
+SECTIONS
+{
+
+ /*--------------------------------------------------------------------*/
+ /* Code and read-only segment */
+ /*--------------------------------------------------------------------*/
+
+ /* Begining of code and text segment */
+ . = 0x80000000;
+ _ftext = .;
+ PROVIDE( eprol = . );
+
+ .text :
+ {
+ *(.text.init)
+ }
+
+ /* text: Program code section */
+ .text :
+ {
+ *(.text)
+ *(.text.*)
+ *(.gnu.linkonce.t.*)
+ }
+
+ /* rodata: Read-only data */
+ .rodata :
+ {
+ *(.rdata)
+ *(.rodata)
+ *(.rodata.*)
+ *(.gnu.linkonce.r.*)
+ }
+
+ /* End of code and read-only segment */
+ PROVIDE( etext = . );
+ _etext = .;
+
+ /*--------------------------------------------------------------------*/
+ /* Initialized data segment */
+ /*--------------------------------------------------------------------*/
+
+ /* Start of initialized data segment */
+ . = ALIGN(16);
+ _fdata = .;
+
+ /* data: Writable data */
+ .data :
+ {
+ *(.data)
+ *(.data.*)
+ *(.srodata*)
+ *(.gnu.linkonce.d.*)
+ *(.comment)
+ }
+
+ /* End of initialized data segment */
+ . = ALIGN(4);
+ PROVIDE( edata = . );
+ _edata = .;
+
+ /*--------------------------------------------------------------------*/
+ /* Uninitialized data segment */
+ /*--------------------------------------------------------------------*/
+
+ /* Start of uninitialized data segment */
+ . = .;
+ _fbss = .;
+
+ /* sbss: Uninitialized writeable small data section */
+ . = .;
+
+ /* bss: Uninitialized writeable data section */
+ . = .;
+ _bss_start = .;
+ .bss :
+ {
+ *(.bss)
+ *(.bss.*)
+ *(.sbss*)
+ *(.gnu.linkonce.b.*)
+ *(COMMON)
+ }
+
+ _end = .;
+}
fullpath = os.path.join(directory, path)
if os.path.exists(fullpath):
return fullpath
- raise ValueError("Couldn't find %r." % path)
+ return None
-def compile(src):
+def compile(*args):
"""Compile a single .c file into a binary."""
- src = find_file(src)
- dst = os.path.splitext(src)[0]
+ dst = os.path.splitext(args[0])[0]
cc = os.path.expandvars("$RISCV/bin/riscv64-unknown-elf-gcc")
- cmd = "%s -g -o %s %s" % (cc, dst, src)
+ cmd = [cc, "-g", "-O", "-o", dst]
+ for arg in args:
+ found = find_file(arg)
+ if found:
+ cmd.append(found)
+ else:
+ cmd.append(arg)
+ cmd = " ".join(cmd)
result = os.system(cmd)
assert result == 0, "%r failed" % cmd
return dst
if with_gdb:
port = unused_port()
cmd += ['--gdb-port', str(port)]
- cmd += ['pk', binary]
+ cmd.append('pk')
+ if binary:
+ cmd.append(binary)
logfile = open("spike.log", "w")
process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=logfile,
stderr=logfile)