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
  • predecode_test.gnogno
    1package riscv23import (4	"testing"56	"gno.land/p/moul/x/vm/vmkit/v0"7	"gno.land/p/nt/uassert/v0"8

    Functions

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

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

    )
    9
    10// classify collapses the spec's opcode / funct3 / funct7 tree onto one byte,
    11// and every arm of the dispatch loop trusts it. These are the pairs that share
    12// a funct3 and differ only in funct7, which is where a collapse goes wrong.
    13func TestClassifySeparatesTheAmbiguousPairs(t *testing.T) {
    14 cases := []struct {
    15 inst uint32
    16 want uint8
    17 }{
    18 {add(1, 2, 3), iADD},
    19 {sub(1, 2, 3), iSUB},
    20 {mul(1, 2, 3), iMUL},
    21 {divi(1, 2, 3), iDIV},
    22 {remi(1, 2, 3), iREM},
    23 {addi(1, 2, 5), iADDI},
    24 {lw(1, 2, 0), iLW},
    25 {lbu(1, 2, 0), iLBU},
    26 {sw(1, 2, 0), iSW},
    27 {bne(1, 2, 4), iBNE},
    28 {lui(1, 0x1000), iLUI},
    29 {jal(1, 4), iJAL},
    30 {ecall(), iECALL},
    31 {0, iILLEGAL},
    32 }
    33 for _, c := range cases {
    34 op, _ := decodeWord(c.inst)
    35 uassert.Equal(t, uint64(c.want), uint64(op))
    36 }
    37}
    38
    39// A restored hart has to predecode again, because the arrays are not in the
    40// snapshot: only the memory pages are. Restoring into a machine that has never
    41// seen the image is the path a realm actually takes between transactions, and
    42// it is the one the existing slice test did not cover, because it always
    43// restored into a machine NewMachine had already loaded.
    44func TestRestoreIntoABareMachine(t *testing.T) {
    45 prog := append([]uint32{
    46 addi(5, 0, 0),
    47 addi(6, 0, 10),
    48 add(5, 5, 6),
    49 addi(6, 6, 0xFFF), // -1
    50 bne(6, 0, 0x1FF8), // -8
    51 }, exitWith()...)
    52 image := asm(prog)
    53
    54 one, err := NewMachine(image, entry)
    55 uassert.NoError(t, err)
    56 one.Step(vmkit.NewTestHost(), vmkit.Unmetered)
    57
    58 part, err := NewMachine(image, entry)
    59 uassert.NoError(t, err)
    60 _, status := part.Step(vmkit.NewTestHost(), 7)
    61 uassert.Equal(t, "running", status.String())
    62
    63 var bare Machine
    64 uassert.NoError(t, bare.Restore(part.Snapshot()))
    65 _, status = bare.Step(vmkit.NewTestHost(), vmkit.Unmetered)
    66 uassert.Equal(t, "halted", status.String())
    67 uassert.Equal(t, uint64(one.Registers()[5]), uint64(bare.Registers()[5]))
    68 uassert.Equal(t, uint64(55), uint64(bare.Registers()[5]))
    69}
    70
    71// Predecoding is only sound if the text cannot change under it, so a store
    72// into the text segment is a trap rather than a silent disagreement between
    73// the arrays and the memory. That is W xor X, which is what an ELF text
    74// segment is anyway, and it removes self-modifying code from the VM entirely.
    75func TestStoreIntoTextTraps(t *testing.T) {
    76 prog := append(li(5, entry), addi(6, 0, 1), sw(6, 5, 0))
    77 prog = append(prog, exitWith()...)
    78 m, err := NewMachine(asm(prog), entry)
    79 uassert.NoError(t, err)
    80 _, status := m.Step(vmkit.NewTestHost(), vmkit.Unmetered)
    81 uassert.Equal(t, "trapped", status.String())
    82 uassert.Equal(t, "store into text segment", m.Trap())
    83}
    84
    85// A store just below the text segment is legal, or the trap above would be
    86// catching the wrong thing.
    87func TestStoreBelowTextIsFine(t *testing.T) {
    88 prog := append(li(5, entry-4), addi(6, 0, 7), sw(6, 5, 0))
    89 prog = append(prog, exitWith()...)
    90 m, err := NewMachine(asm(prog), entry)
    91 uassert.NoError(t, err)
    92 _, status := m.Step(vmkit.NewTestHost(), vmkit.Unmetered)
    93 uassert.Equal(t, "halted", status.String())
    94 v, ok := m.Memory().Load32(entry - 4)
    95 uassert.True(t, ok)
    96 uassert.Equal(t, uint64(7), uint64(v))
    97}
    98
    99// A halted hart points past the ecall it halted on, not at it. The deferred
    100// write-back is the only thing that sets m.pc, so an arm that assigned m.pc
    101// directly had its assignment silently undone on the way out.
    102func TestHaltedPCIsPastTheEcall(t *testing.T) {
    103 m, err := NewMachine(asm(exitWith()), entry)
    104 uassert.NoError(t, err)
    105 _, status := m.Step(vmkit.NewTestHost(), vmkit.Unmetered)
    106 uassert.Equal(t, "halted", status.String())
    107 // entry: addi a7, x0, 93 -- entry+4: ecall -- so the next pc is entry+8.
    108 uassert.Equal(t, uint64(entry+8), uint64(m.PC()))
    109}
    110
    111// An entry point or an image that is not a whole number of instructions has no
    112// predecode, so it is refused at load rather than half-executed.
    113func TestNewMachineRefusesMisalignedImages(t *testing.T) {
    114 _, err := NewMachine(asm(exitWith()), entry+1)
    115 uassert.Error(t, err)
    116 _, err = NewMachine([]byte{1, 2, 3}, entry)
    117 uassert.Error(t, err)
    118}
    119
    120// The index into the predecoded arrays is a shift, so a pc that is not
    121// 4-aligned would silently execute the word containing it. Only a snapshot can
    122// deliver one, because JALR checks its own target, so the guard is at entry to
    123// the loop rather than inside it.
    124func TestMisalignedPCTraps(t *testing.T) {
    125 m, err := NewMachine(asm(exitWith()), entry)
    126 uassert.NoError(t, err)
    127 m.pc = entry + 2
    128 _, status := m.Step(vmkit.NewTestHost(), vmkit.Unmetered)
    129 uassert.Equal(t, "trapped", status.String())
    130 uassert.Equal(t, "misaligned program counter", m.Trap())
    131}
    132
    133// JALR is the only instruction that can compute an odd target, and it clears
    134// only bit 0, so a target of 2 mod 4 has to be refused where it is made.
    135func TestJalrRefusesAMisalignedTarget(t *testing.T) {
    136 prog := append(li(5, entry+6), []uint32{jalr(1, 5, 0)}...)
    137 prog = append(prog, exitWith()...)
    138 m, err := NewMachine(asm(prog), entry)
    139 uassert.NoError(t, err)
    140 _, status := m.Step(vmkit.NewTestHost(), vmkit.Unmetered)
    141 uassert.Equal(t, "trapped", status.String())
    142 uassert.Equal(t, "misaligned jump target", m.Trap())
    143}
    144
    145// A .data section has to land somewhere writable, and refusing an overlap with
    146// the text is the check that keeps a bad linker script from producing a program
    147// that traps on its first store instead of at load.
    148func TestLoadDataRefusesTheTextSegment(t *testing.T) {
    149 m, err := NewMachine(asm(exitWith()), entry)
    150 uassert.NoError(t, err)
    151
    152 uassert.Error(t, m.LoadData(entry, []byte{1, 2, 3, 4}))
    153 uassert.Error(t, m.LoadData(entry-2, []byte{1, 2, 3, 4}))
    154 uassert.NoError(t, m.LoadData(entry-4, []byte{1, 2, 3, 4}))
    155 uassert.NoError(t, m.LoadData(0x40000, []byte{9, 8, 7, 6}))
    156 uassert.Error(t, m.LoadData(MemSize-2, []byte{1, 2, 3, 4}))
    157
    158 v, ok := m.Memory().Load32(0x40000)
    159 uassert.True(t, ok)
    160 uassert.Equal(t, uint64(0x06070809), uint64(v))
    161}
    162
    163// Data is memory, so it rides in the snapshot like everything else the guest
    164// touched. A resumed program must not find its constants gone.
    165func TestLoadedDataSurvivesASnapshot(t *testing.T) {
    166 m, err := NewMachine(asm(exitWith()), entry)
    167 uassert.NoError(t, err)
    168 uassert.NoError(t, m.LoadData(0x40000, []byte{0xDE, 0xAD, 0xBE, 0xEF}))
    169
    170 var restored Machine
    171 uassert.NoError(t, restored.Restore(m.Snapshot()))
    172 v, ok := restored.Memory().Load32(0x40000)
    173 uassert.True(t, ok)
    174 uassert.Equal(t, uint64(0xEFBEADDE), uint64(v))
    175}
    176