mprv test now breaks like it's supposed to.
authorTim Newsome <tim@sifive.com>
Sat, 7 May 2016 18:15:59 +0000 (11:15 -0700)
committerTim Newsome <tim@sifive.com>
Mon, 23 May 2016 19:12:12 +0000 (12:12 -0700)
riscv/mmu.cc
tests/gdbserver.py
tests/mprv.S [new file with mode: 0644]
tests/standalone.lds [new file with mode: 0644]
tests/testlib.py

index 9b623aee6a595db589c33cc72c67a8b289a4ee42..b88cade8d8a599665f18b8997df232a103d56218 100644 (file)
@@ -117,6 +117,7 @@ void mmu_t::refill_tlb(reg_t vaddr, reg_t paddr, access_type type)
 
 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))
   {
@@ -130,6 +131,7 @@ reg_t mmu_t::walk(reg_t addr, access_type type, bool supervisor, bool pum)
   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;
 
@@ -140,6 +142,7 @@ reg_t mmu_t::walk(reg_t addr, access_type type, bool supervisor, bool pum)
 
     // 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;
 
@@ -147,11 +150,16 @@ reg_t mmu_t::walk(reg_t addr, access_type type, bool supervisor, bool pum)
     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.
@@ -159,6 +167,7 @@ reg_t mmu_t::walk(reg_t addr, access_type type, bool supervisor, bool pum)
       // 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;
     }
   }
index 0f9ee1dd624083192b1e8e84ab7c24ccdad2d5c6..6ca9af673089e03f40c1bc9fd009508da98ad0ae 100755 (executable)
@@ -25,6 +25,18 @@ class InstantHaltTest(unittest.TestCase):
         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")
@@ -156,22 +168,30 @@ class RegsTest(unittest.TestCase):
         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()
diff --git a/tests/mprv.S b/tests/mprv.S
new file mode 100644 (file)
index 0000000..114918a
--- /dev/null
@@ -0,0 +1,38 @@
+#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
diff --git a/tests/standalone.lds b/tests/standalone.lds
new file mode 100644 (file)
index 0000000..6705cfd
--- /dev/null
@@ -0,0 +1,90 @@
+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 = .;
+}
index 62339016674167c6494d2187649d00650057d781..3b439b1dd2ccb5c225880937c8738fa45cf4d82d 100644 (file)
@@ -13,14 +13,20 @@ def find_file(path):
         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
@@ -46,7 +52,9 @@ def spike(binary, halted=False, with_gdb=True, timeout=None):
     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)