summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/tests/test_probe.py
blob: f97a4bf2788c32fa3104fa1fbd48ba8bc23a708a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
"""Unit tests for the ELF trampoline/flat-kernel path and QEMU fault parsing."""
from __future__ import annotations

import os
import struct
import tempfile
import unittest

from greeneye.analyze import describe_fault, parse_qemu_faults
from greeneye.elf import _trampoline, load_elf, write_flat_kernel

DEMO_ELF = os.path.join(os.path.dirname(__file__), "..", "firmware", "uproc_demo", "build", "uproc_demo.elf")

SAMPLE_LOG = """
Invalid read at addr 0x2000000000000a00, size 4, region '(null)', reason: rejected
riscv_cpu_do_interrupt: hart:0, async:0, cause:0000000000000005, epc:0x0000000084009014, tval:0x2000000000000a00, desc=fault_load
Invalid read at addr 0x0, size 2, region '(null)', reason: rejected
riscv_cpu_do_interrupt: hart:0, async:0, cause:0000000000000001, epc:0x0000000000000000, tval:0x0000000000000000, desc=fault_fetch
"""


class TrampolineTests(unittest.TestCase):
    def test_encoding(self):
        stub = _trampoline(0x4009000)
        auipc, jalr = struct.unpack("<II", stub)
        self.assertEqual(auipc & 0x7F, 0x17)  # AUIPC
        self.assertEqual((auipc >> 7) & 0x1F, 5)  # t0
        self.assertEqual((auipc >> 12) & 0xFFFFF, 0x4009)
        self.assertEqual(jalr & 0x7F, 0x67)  # JALR
        self.assertEqual((jalr >> 15) & 0x1F, 5)
        self.assertEqual(jalr >> 20, 0)

    def test_flat_demo(self):
        if not os.path.exists(DEMO_ELF):
            self.skipTest("demo firmware not built")
        with tempfile.TemporaryDirectory() as tmp:
            out = os.path.join(tmp, "flat.bin")
            path, entry = write_flat_kernel(DEMO_ELF, out, load_addr=0x80000000)
            self.assertEqual(entry, 0x80000000)
            image = load_elf(DEMO_ELF)
            self.assertEqual(os.path.getsize(path), image.max_vaddr - image.min_vaddr)
            with open(path, "rb") as fh:
                # entry at offset 0: the real first instruction must be present
                first = fh.read(4)
            self.assertNotEqual(first, b"\x00\x00\x00\x00")


class FaultParseTests(unittest.TestCase):
    def test_parse_and_describe(self):
        faults = parse_qemu_faults(SAMPLE_LOG)
        self.assertEqual(len(faults), 2)
        first = faults[0]
        self.assertEqual(first["cause"], 5)
        self.assertEqual(first["epc"], 0x84009014)
        self.assertEqual(first["access"]["access_addr"], 0x2000000000000A00)
        self.assertEqual(first["access"]["size"], 4)
        text = describe_fault(first, bias=0x80000000)
        self.assertIn("0x4009014", text)
        self.assertIn("load access fault", text)
        self.assertIn("0x2000000000000a00", text)
        self.assertEqual(faults[1]["desc"], "fault_fetch")


if __name__ == "__main__":
    unittest.main()