PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/p/moul/x/vm/riscv/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
moul / x / vm / riscv
Files
17 (README)(gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/moul/x/vm/riscv/v0
gno
0.9

Files (17)

  • README.mdmarkdown
  • gnomod.tomltoml
  • decode.gnogno
  • ecall.gnogno
  • exec.gnogno
  • guests.gnogno
  • mem.gnogno
  • predecode.gnogno
  • snapshot.gnogno
  • bench_test.gnogno
  • clang_test.gnogno
  • conformance_test.gnogno
  • decode_test.gnogno
  • predecode_test.gnogno
  • riscv_test.gnogno
  • rust_test.gnogno
  • switchcost_test.gnogno
  • ecall.gnogno
    1package riscv23import "gno.land/p/moul/x/vm/vmkit/v0"45// The syscall table: the entire interface between a guest and the chain.6//7// Register-passing, in the RISC-V Linux convention a compiler already knows:8

    Functions

    not supported for pure packages by the node (vm/qfuncs)

    Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

    // a7 (x17) selects the call, a0..a2 (x10..x12) carry arguments, a0 receives the
    9// result. A guest written against these needs no gno-specific runtime, only the
    10// three lines of inline assembly every no_std program already has for ecall.
    11//
    12// Deliberately small. Every call here maps onto something [vmkit.Host] already
    13// offers, because the Host is the audited surface and a syscall that reaches
    14// past it would be a second, unreviewed one.
    15const (
    16 sysExit = 93 // a0 = status. The Linux number, so a guest can reuse its libc shim.
    17 sysWrite = 64 // a0 = fd (ignored), a1 = buf, a2 = len -> Host.Output
    18 sysRead = 63 // a0 = fd (ignored), a1 = buf, a2 = len <- Host.Input
    19 sysHeight = 90 // -> block height
    20 sysNow = 91 // -> block time, Unix seconds
    21 sysCaller = 92 // a0 = buf, a1 = len; writes the caller address, returns bytes written
    22 sysLog = 94 // a0 = buf, a1 = len -> Host.Log
    23 sysEmit = 95 // a0 = type buf, a1 = type len, a2 = body buf, a3 = body len
    24)
    25
    26// maxSyscallBytes bounds one call's buffer. A guest asking to write a gigabyte
    27// is a guest that found the host's allocator, not one with something to say.
    28const maxSyscallBytes = 64 * 1024
    29
    30// ecall dispatches a syscall. It returns the machine status and whether the
    31// machine should stop: only exit stops it, everything else returns to the guest
    32// with a0 set.
    33func (m *Machine) ecall(h vmkit.Host) (vmkit.Status, bool) {
    34 switch m.reg[17] { // a7
    35 case sysExit:
    36 // The guest's exit status is not the machine's: a program that returns
    37 // 1 has halted, it has not trapped. Trapping is for what the machine
    38 // could not do, and a chain should not confuse "your code said no"
    39 // with "the VM broke".
    40 return vmkit.Halted, true
    41
    42 case sysWrite:
    43 buf, ok := m.readGuest(m.reg[11], m.reg[12])
    44 if !ok {
    45 m.reg[10] = ^uint32(0)
    46 return vmkit.Running, false
    47 }
    48 h.Output(buf)
    49 m.outLen += len(buf)
    50 m.reg[10] = uint32(len(buf))
    51
    52 case sysRead:
    53 in := h.Input()
    54 n := int(m.reg[12])
    55 if n > len(in) {
    56 n = len(in)
    57 }
    58 if n > maxSyscallBytes {
    59 n = maxSyscallBytes
    60 }
    61 if !m.writeGuest(m.reg[11], in[:n]) {
    62 m.reg[10] = ^uint32(0)
    63 return vmkit.Running, false
    64 }
    65 m.reg[10] = uint32(n)
    66
    67 case sysHeight:
    68 m.reg[10] = uint32(h.Height())
    69
    70 case sysNow:
    71 m.reg[10] = uint32(h.Now())
    72
    73 case sysCaller:
    74 addr := []byte(h.Caller().String())
    75 n := int(m.reg[11])
    76 if n > len(addr) {
    77 n = len(addr)
    78 }
    79 if !m.writeGuest(m.reg[10], addr[:n]) {
    80 m.reg[10] = ^uint32(0)
    81 return vmkit.Running, false
    82 }
    83 m.reg[10] = uint32(n)
    84
    85 case sysLog:
    86 buf, ok := m.readGuest(m.reg[10], m.reg[11])
    87 if !ok {
    88 m.reg[10] = ^uint32(0)
    89 return vmkit.Running, false
    90 }
    91 h.Log(string(buf))
    92 m.reg[10] = uint32(len(buf))
    93
    94 case sysEmit:
    95 typ, ok1 := m.readGuest(m.reg[10], m.reg[11])
    96 body, ok2 := m.readGuest(m.reg[12], m.reg[13])
    97 if !ok1 || !ok2 {
    98 m.reg[10] = ^uint32(0)
    99 return vmkit.Running, false
    100 }
    101 h.Emit(string(typ), "body", string(body))
    102 m.reg[10] = 0
    103
    104 default:
    105 // An unknown syscall is a trap, not a silent zero. A guest compiled
    106 // against a newer table must fail loudly on an older host rather than
    107 // read a success it did not get.
    108 m.trap = "unknown syscall"
    109 return vmkit.Trapped, true
    110 }
    111 return vmkit.Running, false
    112}
    113
    114// readGuest copies a buffer out of guest memory, refusing anything out of range
    115// or larger than maxSyscallBytes.
    116func (m *Machine) readGuest(addr, length uint32) ([]byte, bool) {
    117 if length > maxSyscallBytes || !m.mem.inRange(addr, int(length)) {
    118 return nil, false
    119 }
    120 out := make([]byte, length)
    121 copy(out, m.mem.b[addr:addr+length])
    122 return out, true
    123}
    124
    125// writeGuest copies into guest memory, marking the pages it touched.
    126func (m *Machine) writeGuest(addr uint32, b []byte) bool {
    127 if len(b) > maxSyscallBytes || !m.mem.inRange(addr, len(b)) {
    128 return false
    129 }
    130 copy(m.mem.b[addr:], b)
    131 for i := 0; i < len(b); i++ {
    132 m.mem.dirty[(uint64(addr)+uint64(i))/PageSize] = true
    133 }
    134 return true
    135}
    136