summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/greeneye/__init__.py
blob: 8b1c1e59eef8c2bcc7e382cae81cdfab8ed6d6a7 (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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
"""green-eye: Frida-like runtime instrumentation for GSP/uproc firmware in a
RISC-V QEMU VM.

green-eye talks to QEMU's gdbstub directly (no GDB dependency) and layers a
scriptable instrumentation API on top: breakpoints, hooks, watchpoints,
memory inspection, structure decoding, session recording and post-run analysis.

Public API (stable-ish for v0.1):

    import greeneye as ge
    s = ge.launch(kernel="firmware/uproc_demo/build/uproc_demo.elf")
    fn = s.symbols.lookup("rpc_sum")
    h = s.hook(fn, on_enter=lambda hit: print(hit))
    s.run(duration=2.0)
    s.close()

Everything is development tooling; see docs/00-OVERVIEW.md.
"""
from __future__ import annotations

__version__ = "0.1.0"

from .errors import (  # noqa: F401
    GreenEyeError,
    RspError,
    RspTimeout,
    RspProtocolError,
    SymbolNotFound,
    TargetError,
    StructError,
)
from .rsp import RspClient, StopReply, Register  # noqa: F401
from .elf import FirmwareImage, Segment, ElfSymbol, load_elf  # noqa: F401
from .symbols import Symbol, SymbolDB  # noqa: F401
from .memory import Memory  # noqa: F401
from .structs import Field, Schema, decode, schema_from_json  # noqa: F401
from .record import Recorder, NullRecorder, load_events  # noqa: F401
from .session import Session, Hit, HookHandle, Breakpoint, Watchpoint  # noqa: F401
from .target import QemuTarget  # noqa: F401

__all__ = [
    "attach",
    "launch",
    "Session",
    "Hit",
    "HookHandle",
    "Breakpoint",
    "Watchpoint",
    "QemuTarget",
    "RspClient",
    "SymbolDB",
    "Symbol",
    "Memory",
    "Recorder",
]


def attach(
    host: str = "127.0.0.1",
    port: int = 4444,
    *,
    timeout: float = 10.0,
    record: str | None = None,
    symbols: SymbolDB | None = None,
    name: str = "attach",
) -> Session:
    """Attach to an already-running QEMU gdbstub and return a Session."""
    client = RspClient(host, port, timeout=timeout)
    client.connect()
    client.sync()
    return Session(client=client, symbols=symbols, record=record, name=name)


def launch(
    *,
    kernel: str | None = None,
    machine: str = "virt",
    memory_mb: int = 256,
    gdb_port: int = 0,
    bios: str = "none",
    extra_args: list[str] | None = None,
    timeout: float = 10.0,
    record: str | None = None,
    symbols: SymbolDB | None = None,
    name: str = "session",
    load_symbols: bool = True,
    keep_alive: bool = False,
) -> Session:
    """Launch QEMU (paused), attach, and return a ready Session.

    The target starts paused at its reset vector; use Session.load_image() to
    load an ELF (which also sets PC), or QemuTarget.run_kernel() style helper
    launch with kernel= for the fast `-kernel` path.
    """
    target = QemuTarget(
        machine=machine,
        memory_mb=memory_mb,
        gdb_port=gdb_port,
        bios=bios,
        extra_args=extra_args,
        kernel=kernel,
    )
    if keep_alive:
        target.keep_alive = True
    target.start(paused=True)
    client = RspClient(target.host, target.port, timeout=timeout)
    client.connect()
    client.sync()

    symdb = symbols or SymbolDB()
    sess = Session(client=client, target=target, symbols=symdb, record=record, name=name)
    if kernel:
        image = load_elf(kernel)
        sess.load_image(image, load_symbols=load_symbols)
    return sess