Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

snapshot.gno

2.59 Kb · 83 lines
 1package riscv
 2
 3import "gno.land/p/moul/x/vm/vmkit/v0"
 4
 5// Snapshot serializes the hart: registers, pc, status, and only the memory
 6// pages the guest has written.
 7//
 8// A 1 MiB address space serialized on every pause would make continuations
 9// cost more than re-running, which is exactly the kill criterion vmkit set for
10// them. The dirty bitmap is what avoids it: a guest that has touched two pages
11// pauses in about 8 KiB regardless of how much memory it was given.
12func (m *Machine) Snapshot() []byte {
13	if m.code == nil {
14		m.code = &code{}
15	}
16	w := vmkit.NewWriter(4096)
17	w.Uint32(snapMagic)
18	w.Byte(snapVersion)
19	for i := 0; i < 32; i++ {
20		w.Uint32(m.reg[i])
21	}
22	w.Uint32(m.pc)
23	w.Byte(byte(m.status))
24	w.String(m.trap)
25	w.Int(int64(m.outLen))
26	// The text segment's extent, so a restored hart knows which of its pages
27	// are code and can predecode them again. The code itself is not written:
28	// it is already in the memory pages, which WriteImage marked dirty.
29	w.Uint32(m.code.base)
30	w.Uint32(m.code.words)
31	m.mem.Snapshot(w)
32	return w.Out()
33}
34
35// Restore loads a snapshot. The program image is not carried separately: it
36// lives in the memory pages, which were marked dirty when it was written, so a
37// restored hart has its code without the instance storing it twice.
38func (m *Machine) Restore(b []byte) error {
39	r := vmkit.NewReader(b)
40	if r.Uint32() != snapMagic {
41		return vmkit.ErrBadSnapshot
42	}
43	if r.Byte() != snapVersion {
44		return vmkit.ErrBadSnapshot
45	}
46	var reg [32]uint32
47	for i := 0; i < 32; i++ {
48		reg[i] = r.Uint32()
49	}
50	pc := r.Uint32()
51	status := vmkit.Status(r.Byte())
52	trap := r.String()
53	outLen := int(r.Int())
54	base := r.Uint32()
55	words := r.Uint32()
56	if err := r.Err(); err != nil {
57		return err
58	}
59	// A text segment that does not fit in the address space is a snapshot this
60	// machine did not write, and predecoding it would index past the memory.
61	if base%4 != 0 || uint64(base)+uint64(words)*4 > uint64(MemSize) {
62		return vmkit.ErrBadSnapshot
63	}
64
65	mem := NewMemory()
66	if err := mem.Restore(r); err != nil {
67		return err
68	}
69	if err := r.Err(); err != nil {
70		return err
71	}
72
73	m.reg, m.pc, m.status, m.trap, m.outLen, m.mem = reg, pc, status, trap, outLen, mem
74	// Predecode again from the restored pages. This is the work a resume pays
75	// for, once, in exchange for not paying a fetch on every instruction after
76	// it, and it is why stores into the text segment trap: the arrays and the
77	// memory would otherwise be free to disagree.
78	m.code = predecode(base, mem.b[base:base+words*4])
79	// x0 is hardwired, and a snapshot that claims otherwise is not one this
80	// machine wrote.
81	m.reg[0] = 0
82	return nil
83}