Skip to content

Commit 8371fac

Browse files
committed
pkg, prog: enable ifuzz for riscv64
1 parent 572effc commit 8371fac

File tree

15 files changed

+6699
-3
lines changed

15 files changed

+6699
-3
lines changed

pkg/compiler/types.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ var typeText = &typeDesc{
641641

642642
var typeArgTextType = &typeArg{
643643
Kind: kindIdent,
644-
Names: []string{"target", "x86_real", "x86_16", "x86_32", "x86_64", "arm64", "ppc64"},
644+
Names: []string{"target", "x86_real", "x86_16", "x86_32", "x86_64", "arm64", "ppc64", "riscv64"},
645645
}
646646

647647
func genTextType(t *ast.Type) prog.TextKind {
@@ -660,6 +660,8 @@ func genTextType(t *ast.Type) prog.TextKind {
660660
return prog.TextArm64
661661
case "ppc64":
662662
return prog.TextPpc64
663+
case "riscv64":
664+
return prog.TextRiscv64
663665
default:
664666
panic(fmt.Sprintf("unknown text type %q", t.Ident))
665667
}

pkg/ifuzz/arm64_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,4 @@ func TestDecodeSamples(t *testing.T) {
9191
text = text[size:]
9292
}
9393
}
94-
}
94+
}

pkg/ifuzz/ifuzz.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
_ "github.com/google/syzkaller/pkg/ifuzz/arm64/generated" // pull in generated instruction descriptions
1010
"github.com/google/syzkaller/pkg/ifuzz/iset"
1111
_ "github.com/google/syzkaller/pkg/ifuzz/powerpc/generated" // pull in generated instruction descriptions
12+
_ "github.com/google/syzkaller/pkg/ifuzz/riscv64/generated" // pull in generated instruction descriptions
1213
_ "github.com/google/syzkaller/pkg/ifuzz/x86/generated" // pull in generated instruction descriptions
1314
)
1415

@@ -22,6 +23,7 @@ const (
2223
ArchX86 = iset.ArchX86
2324
ArchPowerPC = iset.ArchPowerPC
2425
ArchArm64 = iset.ArchArm64
26+
ArchRiscv64 = iset.ArchRiscv64
2527
ModeLong64 = iset.ModeLong64
2628
ModeProt32 = iset.ModeProt32
2729
ModeProt16 = iset.ModeProt16

pkg/ifuzz/ifuzz_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
"github.com/google/syzkaller/pkg/testutil"
1313
)
1414

15-
var allArches = []string{ArchX86, ArchPowerPC, ArchArm64}
15+
var allArches = []string{ArchX86, ArchPowerPC, ArchArm64, ArchRiscv64}
1616

1717
func TestMode(t *testing.T) {
1818
for _, arch := range allArches {
@@ -139,6 +139,10 @@ func testGenerate(t *testing.T, arch string) {
139139
Exec: true,
140140
Len: repeat,
141141
}
142+
if arch == ArchRiscv64 {
143+
cfg.Priv = false
144+
cfg.Exec = false
145+
}
142146
text := Generate(cfg, r)
143147
for len(text) != 0 {
144148
size, err := insnset.Decode(mode, text)

pkg/ifuzz/iset/iset.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const (
1212
ArchX86 = "x86"
1313
ArchPowerPC = "powerpc"
1414
ArchArm64 = "arm64"
15+
ArchRiscv64 = "riscv64"
1516
)
1617

1718
var Arches = make(map[string]InsnSet)

pkg/ifuzz/riscv64/gen/gen.go

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
// Copyright 2026
2+
// SPDX-License-Identifier: Apache-2.0.
3+
4+
// gen generates riscv64 instruction tables from riscv-unified-db YAML.
5+
// https://github.com/riscv-software-src/riscv-unified-db/tree/main/spec/std/isa/inst
6+
package main
7+
8+
import (
9+
"bytes"
10+
"fmt"
11+
"io/fs"
12+
"os"
13+
"path/filepath"
14+
"strings"
15+
16+
"gopkg.in/yaml.v3"
17+
18+
"github.com/google/syzkaller/pkg/ifuzz/riscv64"
19+
"github.com/google/syzkaller/pkg/osutil"
20+
"github.com/google/syzkaller/pkg/serializer"
21+
"github.com/google/syzkaller/pkg/tool"
22+
)
23+
24+
type instYAML struct {
25+
Kind string `yaml:"kind"`
26+
Name string `yaml:"name"`
27+
Encoding struct {
28+
Match string `yaml:"match"`
29+
Variables []struct {
30+
Name string `yaml:"name"`
31+
Location string `yaml:"location"` // e.g. "24-20"
32+
} `yaml:"variables"`
33+
} `yaml:"encoding"`
34+
}
35+
36+
func main() {
37+
if len(os.Args) != 3 {
38+
tool.Failf("usage: go run gen.go <riscv-unified-db/spec/std/isa/inst> <output.go>")
39+
}
40+
root := os.Args[1]
41+
outFile := os.Args[2]
42+
43+
insns := []*riscv64.Insn{}
44+
45+
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
46+
if err != nil {
47+
return err
48+
}
49+
if d.IsDir() || !strings.HasSuffix(path, ".yaml") {
50+
return nil
51+
}
52+
53+
data, err := os.ReadFile(path)
54+
if err != nil {
55+
return nil
56+
}
57+
58+
var inst instYAML
59+
if err := yaml.Unmarshal(data, &inst); err != nil {
60+
return nil
61+
}
62+
if inst.Kind != "instruction" {
63+
return nil
64+
}
65+
66+
match := inst.Encoding.Match
67+
if len(match) != 32 {
68+
return nil
69+
}
70+
71+
insn, ok := buildInsn(inst)
72+
if ok {
73+
insns = append(insns, insn)
74+
}
75+
return nil
76+
})
77+
if err != nil {
78+
tool.Fail(err)
79+
}
80+
81+
var out bytes.Buffer
82+
fmt.Fprintf(&out, `// Code generated by pkg/ifuzz/riscv64/gen. DO NOT EDIT.
83+
84+
// go:build !codeanalysis
85+
86+
package generated
87+
88+
import (
89+
. "github.com/google/syzkaller/pkg/ifuzz/riscv64"
90+
)
91+
92+
func init() {
93+
Register(insns_riscv64)
94+
}
95+
96+
var insns_riscv64 =
97+
`)
98+
serializer.Write(&out, insns)
99+
100+
if err := osutil.WriteFileAtomically(outFile, out.Bytes()); err != nil {
101+
tool.Fail(err)
102+
}
103+
104+
fmt.Fprintf(os.Stderr, "generated %d instructions\n", len(insns))
105+
}
106+
107+
func buildInsn(inst instYAML) (*riscv64.Insn, bool) {
108+
match := inst.Encoding.Match
109+
110+
var opcode uint32
111+
var mask uint32
112+
113+
for i, ch := range match {
114+
bit := uint(31 - i)
115+
switch ch {
116+
case '0':
117+
mask |= 1 << bit
118+
case '1':
119+
mask |= 1 << bit
120+
opcode |= 1 << bit
121+
case '-':
122+
default:
123+
return nil, false
124+
}
125+
}
126+
127+
fields := []riscv64.InsnField{}
128+
for _, v := range inst.Encoding.Variables {
129+
subFields, ok := parseLocations(v.Name, v.Location)
130+
if !ok {
131+
return nil, false
132+
}
133+
fields = append(fields, subFields...)
134+
}
135+
136+
return &riscv64.Insn{
137+
Name: inst.Name,
138+
OpcodeMask: mask,
139+
Opcode: opcode,
140+
Fields: fields,
141+
AsUInt32: opcode,
142+
Generator: nil,
143+
}, true
144+
}
145+
146+
func parseLocations(name, loc string) ([]riscv64.InsnField, bool) {
147+
// Support multiple ranges separated by '|', e.g.:
148+
// "31-25|11-7"
149+
ranges := strings.Split(loc, "|")
150+
fields := make([]riscv64.InsnField, 0, len(ranges))
151+
152+
for _, r := range ranges {
153+
start, length, hi, lo, ok := parseRange(r)
154+
if !ok {
155+
return nil, false
156+
}
157+
158+
fieldName := name
159+
if len(ranges) > 1 {
160+
fieldName = fmt.Sprintf("%s_%d_%d", name, hi, lo)
161+
}
162+
163+
fields = append(fields, riscv64.InsnField{
164+
Name: fieldName,
165+
Start: start,
166+
Length: length,
167+
})
168+
}
169+
170+
return fields, true
171+
}
172+
173+
func parseRange(r string) (start, length, hi, lo uint, ok bool) {
174+
parts := strings.Split(r, "-")
175+
if len(parts) != 2 {
176+
return 0, 0, 0, 0, false
177+
}
178+
179+
if _, err := fmt.Sscanf(parts[0], "%d", &hi); err != nil {
180+
return 0, 0, 0, 0, false
181+
}
182+
if _, err := fmt.Sscanf(parts[1], "%d", &lo); err != nil {
183+
return 0, 0, 0, 0, false
184+
}
185+
if hi < lo {
186+
return 0, 0, 0, 0, false
187+
}
188+
189+
start = lo
190+
length = hi - lo + 1
191+
return start, length, hi, lo, true
192+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright 2026 syzkaller project authors. All rights reserved.
2+
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
3+
4+
// To unbreak build with insns.go is excluded by build tags.
5+
6+
package generated

0 commit comments

Comments
 (0)