Skip to content

Commit 570b5f1

Browse files
committed
pkg, prog: enable ifuzz for riscv64
1 parent 6f1aa2f commit 570b5f1

File tree

1,309 files changed

+97484
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,309 files changed

+97484
-2
lines changed

pkg/compiler/types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ func genTextType(t *ast.Type) prog.TextKind {
661661
case "ppc64":
662662
return prog.TextPpc64
663663
case "riscv64":
664-
return prog.TextTarget
664+
return prog.TextRiscv64
665665
default:
666666
panic(fmt.Sprintf("unknown text type %q", t.Ident))
667667
}

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: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
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+
// 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+
Access struct {
35+
U string `yaml:"u"`
36+
VU string `yaml:"vu"`
37+
} `yaml:"access"`
38+
}
39+
40+
func main() {
41+
if len(os.Args) != 3 {
42+
tool.Failf("usage: go run gen.go <riscv-unified-db/spec/std/isa/inst> <output.go>")
43+
}
44+
root := os.Args[1]
45+
outFile := os.Args[2]
46+
47+
insns := []*riscv64.Insn{}
48+
49+
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
50+
if err != nil {
51+
return err
52+
}
53+
if d.IsDir() || !strings.HasSuffix(path, ".yaml") {
54+
return nil
55+
}
56+
57+
data, err := os.ReadFile(path)
58+
if err != nil {
59+
return nil
60+
}
61+
62+
var inst instYAML
63+
if err := yaml.Unmarshal(data, &inst); err != nil {
64+
return nil
65+
}
66+
if inst.Kind != "instruction" {
67+
return nil
68+
}
69+
70+
match := inst.Encoding.Match
71+
if len(match) != 32 {
72+
return nil
73+
}
74+
75+
insn, ok := buildInsn(inst)
76+
if ok {
77+
insns = append(insns, insn)
78+
}
79+
return nil
80+
})
81+
if err != nil {
82+
tool.Fail(err)
83+
}
84+
85+
var out bytes.Buffer
86+
fmt.Fprintf(&out, `// Code generated by pkg/ifuzz/riscv64/gen. DO NOT EDIT.
87+
88+
// go:build !codeanalysis
89+
90+
package generated
91+
92+
import (
93+
. "github.com/google/syzkaller/pkg/ifuzz/riscv64"
94+
)
95+
96+
func init() {
97+
Register(insns_riscv64)
98+
}
99+
100+
var insns_riscv64 =
101+
`)
102+
serializer.Write(&out, insns)
103+
104+
if err := osutil.WriteFileAtomically(outFile, out.Bytes()); err != nil {
105+
tool.Fail(err)
106+
}
107+
108+
fmt.Fprintf(os.Stderr, "generated %d instructions\n", len(insns))
109+
}
110+
111+
func buildInsn(inst instYAML) (*riscv64.Insn, bool) {
112+
match := inst.Encoding.Match
113+
114+
var opcode uint32
115+
var mask uint32
116+
117+
for i, ch := range match {
118+
bit := uint(31 - i)
119+
switch ch {
120+
case '0':
121+
mask |= 1 << bit
122+
case '1':
123+
mask |= 1 << bit
124+
opcode |= 1 << bit
125+
case '-':
126+
default:
127+
return nil, false
128+
}
129+
}
130+
131+
fields := []riscv64.InsnField{}
132+
for _, v := range inst.Encoding.Variables {
133+
subFields, ok := parseLocations(v.Name, v.Location)
134+
if !ok {
135+
return nil, false
136+
}
137+
fields = append(fields, subFields...)
138+
}
139+
140+
priv := false
141+
if inst.Access.U == "never" || inst.Access.VU == "never" {
142+
priv = true
143+
}
144+
145+
return &riscv64.Insn{
146+
Name: inst.Name,
147+
OpcodeMask: mask,
148+
Opcode: opcode,
149+
Fields: fields,
150+
AsUInt32: opcode,
151+
Generator: nil,
152+
Priv: priv,
153+
}, true
154+
}
155+
156+
func parseLocations(name, loc string) ([]riscv64.InsnField, bool) {
157+
// Support multiple ranges separated by '|', e.g.:
158+
// "31-25|11-7"
159+
ranges := strings.Split(loc, "|")
160+
fields := make([]riscv64.InsnField, 0, len(ranges))
161+
162+
for _, r := range ranges {
163+
start, length, hi, lo, ok := parseRange(r)
164+
if !ok {
165+
return nil, false
166+
}
167+
168+
fieldName := name
169+
if len(ranges) > 1 {
170+
fieldName = fmt.Sprintf("%s_%d_%d", name, hi, lo)
171+
}
172+
173+
fields = append(fields, riscv64.InsnField{
174+
Name: fieldName,
175+
Start: start,
176+
Length: length,
177+
})
178+
}
179+
180+
return fields, true
181+
}
182+
183+
func parseRange(r string) (start, length, hi, lo uint, ok bool) {
184+
parts := strings.Split(r, "-")
185+
if len(parts) != 2 {
186+
return 0, 0, 0, 0, false
187+
}
188+
189+
if _, err := fmt.Sscanf(parts[0], "%d", &hi); err != nil {
190+
return 0, 0, 0, 0, false
191+
}
192+
if _, err := fmt.Sscanf(parts[1], "%d", &lo); err != nil {
193+
return 0, 0, 0, 0, false
194+
}
195+
if hi < lo {
196+
return 0, 0, 0, 0, false
197+
}
198+
199+
start = hi
200+
length = hi - lo + 1
201+
return start, length, hi, lo, true
202+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
2+
# SPDX-License-Identifier: BSD-3-Clause-Clear
3+
4+
# yaml-language-server: $schema=../../../../schemas/inst_schema.json
5+
6+
$schema: "inst_schema.json#"
7+
kind: instruction
8+
name: andn
9+
long_name: AND with inverted operand
10+
description: |
11+
Performs the bitwise logical AND operation between `xs1` and the
12+
bitwise inversion of `xs2`.
13+
definedBy:
14+
extension:
15+
anyOf:
16+
- name: Zbb
17+
- name: Zbkb
18+
assembly: xd, xs1, xs2
19+
format:
20+
$inherits:
21+
- inst_subtype/R/R-x.yaml#/data
22+
opcodes:
23+
funct7:
24+
display_name: ANDN
25+
value: 0b0100000
26+
funct3:
27+
display_name: ANDN
28+
value: 0b111
29+
opcode: { $inherits: inst_opcode/OP.yaml#/data }
30+
access:
31+
s: always
32+
u: always
33+
vs: always
34+
vu: always
35+
data_independent_timing: true
36+
operation(): |
37+
if (implemented?(ExtensionName::B) && (CSR[misa].B == 1'b0)) {
38+
raise (ExceptionCode::IllegalInstruction, mode(), $encoding);
39+
}
40+
41+
X[xd] = X[xs2] & ~X[xs1];
42+
43+
# SPDX-SnippetBegin
44+
# SPDX-FileCopyrightText: 2017-2025 Contributors to the RISCV Sail Model <https://github.com/riscv/sail-riscv/blob/master/LICENCE>
45+
# SPDX-License-Identifier: BSD-2-Clause
46+
sail(): |
47+
{
48+
let rs1_val = X(rs1);
49+
let rs2_val = X(rs2);
50+
let result : xlenbits = match op {
51+
RISCV_ANDN => rs1_val & ~(rs2_val),
52+
RISCV_ORN => rs1_val | ~(rs2_val),
53+
RISCV_XNOR => ~(rs1_val ^ rs2_val),
54+
RISCV_MAX => to_bits(sizeof(xlen), max(signed(rs1_val), signed(rs2_val))),
55+
RISCV_MAXU => to_bits(sizeof(xlen), max(unsigned(rs1_val), unsigned(rs2_val))),
56+
RISCV_MIN => to_bits(sizeof(xlen), min(signed(rs1_val), signed(rs2_val))),
57+
RISCV_MINU => to_bits(sizeof(xlen), min(unsigned(rs1_val), unsigned(rs2_val))),
58+
RISCV_ROL => if sizeof(xlen) == 32
59+
then rs1_val <<< rs2_val[4..0]
60+
else rs1_val <<< rs2_val[5..0],
61+
RISCV_ROR => if sizeof(xlen) == 32
62+
then rs1_val >>> rs2_val[4..0]
63+
else rs1_val >>> rs2_val[5..0]
64+
};
65+
X(rd) = result;
66+
RETIRE_SUCCESS
67+
}
68+
69+
# SPDX-SnippetEnd
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
2+
# SPDX-License-Identifier: BSD-3-Clause-Clear
3+
4+
# yaml-language-server: $schema=../../../../schemas/inst_schema.json
5+
6+
$schema: "inst_schema.json#"
7+
kind: instruction
8+
name: clmul
9+
long_name: Carry-less multiply (low-part)
10+
description: |
11+
`clmul` produces the lower half of the 2*XLEN carry-less product
12+
definedBy:
13+
extension:
14+
anyOf:
15+
- name: Zbc
16+
- name: Zbkc
17+
assembly: xd, xs1, xs2
18+
format:
19+
$inherits:
20+
- inst_subtype/R/R-x.yaml#/data
21+
opcodes:
22+
funct7:
23+
display_name: CLMUL
24+
value: 0b0000101
25+
funct3:
26+
display_name: CLMUL
27+
value: 0b001
28+
opcode: { $inherits: inst_opcode/OP.yaml#/data }
29+
access:
30+
s: always
31+
u: always
32+
vs: always
33+
vu: always
34+
data_independent_timing: true
35+
operation(): |
36+
if (implemented?(ExtensionName::B) && (CSR[misa].B == 1'b0)) {
37+
raise (ExceptionCode::IllegalInstruction, mode(), $encoding);
38+
}
39+
40+
XReg xs1_val = X[xs1];
41+
XReg xs2_val = X[xs2];
42+
XReg output = 0;
43+
44+
for (U32 i=0; i < xlen(); i++) {
45+
output = (((xs2_val >> i) & 1) == 1)
46+
? output ^ (xs1_val << i)
47+
: output;
48+
}
49+
50+
X[xd] = output;
51+
52+
# SPDX-SnippetBegin
53+
# SPDX-FileCopyrightText: 2017-2025 Contributors to the RISCV Sail Model <https://github.com/riscv/sail-riscv/blob/master/LICENCE>
54+
# SPDX-License-Identifier: BSD-2-Clause
55+
sail(): |
56+
{
57+
let rs1_val = X(rs1);
58+
let rs2_val = X(rs2);
59+
result : xlenbits = zeros();
60+
foreach (i from 0 to (xlen_val - 1))
61+
if rs2_val[i] == bitone then result = result ^ (rs1_val << i);
62+
X(rd) = result;
63+
RETIRE_SUCCESS
64+
}
65+
66+
# SPDX-SnippetEnd

0 commit comments

Comments
 (0)