Skip to content

Commit 06648d9

Browse files
6eanutramosian-glider
authored andcommitted
executor, sys/linux, pkg: enable syz_kvm_setup_cpu for riscv64
This patch implements syz_kvm_setup_cpu for riscv64 architecture. The pseudo-syscall accepts VM fd, vCPU fd, host memory, and guest code as parameters. Additional parameters (ntext, flags, opts, nopt) are included for interface consistency with other architectures but are currently unused on riscv64. Implementation: - Set up guest memory via KVM_SET_USER_MEMORY_REGION - Copy guest code to guest memory - Initialize guest registers to enable code execution in S-mode - Return 0 on success, -1 on failure Testing: A test file syz_kvm_setup_cpu_riscv64 is included in sys/linux/test/ to verify basic functionality. Known limitations: - ifuzz is not yet compatible with riscv64. Temporary workaround: set text[riscv64] to TextTarget and return nil in createTargetIfuzzConfig for riscv64 to ensure generateText and mutateText work correctly. This patch also adds support for KVM_GET_ONE_REG ioctl.
1 parent 300ecf9 commit 06648d9

File tree

7 files changed

+223
-1
lines changed

7 files changed

+223
-1
lines changed

executor/common_kvm_riscv64.h

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
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+
#ifndef EXECUTOR_COMMON_KVM_RISCV64_H
5+
#define EXECUTOR_COMMON_KVM_RISCV64_H
6+
7+
// This file is shared between executor and csource package.
8+
9+
// Implementation of syz_kvm_setup_cpu pseudo-syscall.
10+
11+
#include <stdint.h>
12+
#include <string.h>
13+
#include <sys/ioctl.h>
14+
15+
struct kvm_text {
16+
uintptr_t type;
17+
const void* text;
18+
uintptr_t size;
19+
};
20+
21+
// Construct RISC-V register id for KVM.
22+
#define RISCV_CORE_REG(idx) (KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_CORE | (idx))
23+
#define RISCV_CSR_REG(idx) (KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_CSR | (idx))
24+
25+
// Represent CSR indices in the kvm_riscv_csr structure.
26+
enum riscv_csr_index {
27+
CSR_SSTATUS = 0,
28+
CSR_SIE,
29+
CSR_STVEC,
30+
CSR_SSCRATCH,
31+
CSR_SEPC,
32+
CSR_SCAUSE,
33+
CSR_STVAL,
34+
CSR_SIP,
35+
CSR_SATP,
36+
CSR_SCOUNTEREN,
37+
CSR_SENVCFG
38+
};
39+
40+
// Represent CORE register indices in the kvm_riscv_core structure.
41+
enum riscv_core_index {
42+
CORE_PC = 0x00,
43+
CORE_RA,
44+
CORE_SP,
45+
CORE_GP,
46+
CORE_TP,
47+
CORE_T0,
48+
CORE_T1,
49+
CORE_T2,
50+
CORE_S0,
51+
CORE_S1,
52+
CORE_A0,
53+
CORE_A1,
54+
CORE_A2,
55+
CORE_A3,
56+
CORE_A4,
57+
CORE_A5,
58+
CORE_A6,
59+
CORE_A7,
60+
CORE_S2,
61+
CORE_S3,
62+
CORE_S4,
63+
CORE_S5,
64+
CORE_S6,
65+
CORE_S7,
66+
CORE_S8,
67+
CORE_S9,
68+
CORE_S10,
69+
CORE_S11,
70+
CORE_T3,
71+
CORE_T4,
72+
CORE_T5,
73+
CORE_T6,
74+
// Store the privilege mode: 1=S-mode, 0=U-mode.
75+
CORE_MODE
76+
};
77+
78+
// Indicate the Supervisor Previous Privilege mode.
79+
#define SSTATUS_SPP (1UL << 8)
80+
// Indicate the Supervisor Previous Interrupt Enable state.
81+
#define SSTATUS_SPIE (1UL << 5)
82+
// Indicate the Supervisor Interrupt Enable state.
83+
#define SSTATUS_SIE (1UL << 1)
84+
85+
// Define the starting physical address for the guest code.
86+
#define CODE_START 0x80000000ULL
87+
88+
// Set a single register value for the specified CPU file descriptor.
89+
static inline int kvm_set_reg(int cpufd, unsigned long id, unsigned long value)
90+
{
91+
struct kvm_one_reg reg = {
92+
.id = id,
93+
.addr = (unsigned long)&value,
94+
};
95+
return ioctl(cpufd, KVM_SET_ONE_REG, &reg);
96+
}
97+
98+
// syz_kvm_setup_cpu$riscv64(fd fd_kvmvm, cpufd fd_kvmcpu, usermem vma[24], text ptr[in, array[kvm_text_riscv64, 1]], ntext len[text], flags const[0], opts ptr[in, array[kvm_setup_opt_riscv64, 1]], nopt len[opts])
99+
static volatile long syz_kvm_setup_cpu(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6, volatile long a7)
100+
{
101+
const int vmfd = a0;
102+
const int cpufd = a1;
103+
char* const host_mem = (char*)a2;
104+
const struct kvm_text* const text_array_ptr = (struct kvm_text*)a3;
105+
106+
const uintptr_t page_size = 4096;
107+
const uintptr_t guest_pages = 24;
108+
const uintptr_t guest_mem_size = guest_pages * page_size;
109+
110+
// Install guest memory.
111+
for (uintptr_t i = 0; i < guest_pages; i++) {
112+
struct kvm_userspace_memory_region mem = {
113+
.slot = (unsigned int)i,
114+
.flags = 0,
115+
.guest_phys_addr = CODE_START + i * page_size,
116+
.memory_size = page_size,
117+
.userspace_addr =
118+
(uintptr_t)(host_mem + i * page_size),
119+
};
120+
121+
if (ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &mem))
122+
return -1;
123+
}
124+
125+
// Copy guest code.
126+
const void* text = 0;
127+
uintptr_t size = 0;
128+
NONFAILING(text = text_array_ptr[0].text);
129+
NONFAILING(size = text_array_ptr[0].size);
130+
if (size > guest_mem_size)
131+
size = guest_mem_size;
132+
memcpy(host_mem, text, size);
133+
134+
// Initialize VCPU registers.
135+
// Set PC (program counter) to start of code.
136+
if (kvm_set_reg(cpufd, RISCV_CORE_REG(CORE_PC), CODE_START))
137+
return -1;
138+
// Set SP (stack pointer) at end of memory, reserving space for stack.
139+
unsigned long stack_top = CODE_START + guest_mem_size - page_size;
140+
if (kvm_set_reg(cpufd, RISCV_CORE_REG(CORE_SP), stack_top))
141+
return -1;
142+
// Set privilege mode to S-mode.
143+
if (kvm_set_reg(cpufd, RISCV_CORE_REG(CORE_MODE), 1))
144+
return -1;
145+
// Set SSTATUS CSR with SPP and SPIE.
146+
unsigned long sstatus = SSTATUS_SPP | SSTATUS_SPIE;
147+
if (kvm_set_reg(cpufd, RISCV_CSR_REG(CSR_SSTATUS), sstatus))
148+
return -1;
149+
// Set STVEC.
150+
unsigned long stvec = CODE_START + page_size;
151+
if (kvm_set_reg(cpufd, RISCV_CSR_REG(CSR_STVEC), stvec))
152+
return -1;
153+
154+
return 0;
155+
}
156+
157+
#endif // EXECUTOR_COMMON_KVM_RISCV64_H

executor/common_linux.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3216,6 +3216,8 @@ static long syz_mount_image(
32163216
#include "common_kvm_arm64.h"
32173217
#elif GOARCH_ppc64 || GOARCH_ppc64le
32183218
#include "common_kvm_ppc64.h"
3219+
#elif GOARCH_riscv64
3220+
#include "common_kvm_riscv64.h"
32193221
#elif SYZ_EXECUTOR || __NR_syz_kvm_setup_cpu
32203222
static volatile long syz_kvm_setup_cpu(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6, volatile long a7)
32213223
{

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.TextTarget
663665
default:
664666
panic(fmt.Sprintf("unknown text type %q", t.Ident))
665667
}

pkg/vminfo/linux_syscalls.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ func linuxSyzKvmSupported(ctx *checkContext, call *prog.Syscall) string {
192192
if ctx.target.Arch == targets.ARM64 {
193193
return ""
194194
}
195+
case "syz_kvm_setup_cpu$riscv64":
196+
if ctx.target.Arch == targets.RiscV64 {
197+
return ""
198+
}
195199
case "syz_kvm_setup_cpu$ppc64":
196200
if ctx.target.Arch == targets.PPC64LE {
197201
return ""

sys/linux/dev_kvm.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,17 @@ kvm_one_reg [
350350
arm64_sve kvm_one_reg_arm64_range[0x6080000000150000:0x6080000000150620]
351351
arm64_sve_vls kvm_one_reg_arm64_range[0x606000000015ffff]
352352
other kvm_one_reg_other
353+
# For riscv64
354+
riscv64_config kvm_one_reg_riscv64[kvm_regs_riscv64_config]
355+
riscv64_core kvm_one_reg_riscv64[kvm_regs_riscv64_core]
356+
riscv64_csr kvm_one_reg_riscv64[kvm_regs_riscv64_csr]
353357
]
354358

359+
type kvm_one_reg_riscv64[FTYPE] {
360+
id flags[FTYPE, int64]
361+
addr ptr64[inout, int64]
362+
}
363+
355364
type kvm_one_reg_arm64[FTYPE] {
356365
id flags[FTYPE, int64]
357366
addr ptr64[inout, int64]
@@ -623,3 +632,8 @@ kvm_regs_arm64_sys = 0x6030000000138002, 0x6030000000138010, 0x6030000000138012,
623632
# Extra registers that KVM_GET_REG_LIST prints on QEMU
624633
kvm_regs_arm64_extra = 0x603000000013c01b, 0x603000000013c01f, 0x603000000013c022, 0x603000000013c023, 0x603000000013c025, 0x603000000013c026, 0x603000000013c027, 0x603000000013c02a, 0x603000000013c02b, 0x603000000013c02e, 0x603000000013c02f, 0x603000000013c033, 0x603000000013c034, 0x603000000013c035, 0x603000000013c036, 0x603000000013c037, 0x603000000013c03b, 0x603000000013c03c, 0x603000000013c03d, 0x603000000013c03e, 0x603000000013c03f, 0x603000000013c103, 0x603000000013c512, 0x603000000013c513
625634
# End of register descriptions generated by tools/arm64/registers.go
635+
636+
# For riscv64, https://elixir.bootlin.com/linux/v6.19-rc4/source/Documentation/virt/kvm/api.rst#L2765
637+
kvm_regs_riscv64_config = 0x8030000000100000
638+
kvm_regs_riscv64_core = 0x8030000000200000, 0x8030000000200001, 0x8030000000200002, 0x8030000000200003, 0x8030000000200004, 0x8030000000200005, 0x8030000000200006, 0x8030000000200007, 0x8030000000200008, 0x8030000000200009, 0x803000000020000a, 0x803000000020000b, 0x803000000020000c, 0x803000000020000d, 0x803000000020000e, 0x803000000020000f, 0x8030000000200010, 0x8030000000200011, 0x8030000000200012, 0x8030000000200013, 0x8030000000200014, 0x8030000000200015, 0x8030000000200016, 0x8030000000200017, 0x8030000000200018, 0x8030000000200019, 0x803000000020001a, 0x803000000020001b, 0x803000000020001c, 0x803000000020001d, 0x803000000020001e, 0x803000000020001f, 0x8030000000200020
639+
kvm_regs_riscv64_csr = 0x8030000000300000, 0x8030000000300001, 0x8030000000300002, 0x8030000000300003, 0x8030000000300004, 0x8030000000300005, 0x8030000000300006, 0x8030000000300007, 0x8030000000300008

sys/linux/dev_kvm_riscv64.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,22 @@ ioctl$KVM_SET_GUEST_DEBUG_riscv64(fd fd_kvmcpu, cmd const[KVM_SET_GUEST_DEBUG],
1212
kvm_guest_debug_arch_riscv64 {
1313
reg array[int64, 8]
1414
}
15+
16+
syz_kvm_setup_cpu$riscv64(fd fd_kvmvm, cpufd fd_kvmcpu, usermem vma[24], text ptr[in, array[kvm_text_riscv64, 1]], ntext len[text], flags const[0], opts ptr[in, array[kvm_setup_opt_riscv64, 1]], nopt len[opts])
17+
18+
kvm_setup_opt_riscv64 [
19+
# unions need at least 2 fields, but we have only 1 now, but we want to have it as union for future extention
20+
featur1 kvm_setup_opt_riscv64_feature
21+
featur2 kvm_setup_opt_riscv64_feature
22+
]
23+
24+
kvm_setup_opt_riscv64_feature {
25+
typ const[1, int64]
26+
val int64
27+
}
28+
29+
kvm_text_riscv64 {
30+
typ const[0, intptr]
31+
text ptr[in, text[riscv64]]
32+
size len[text, intptr]
33+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#
2+
# requires: arch=riscv64
3+
#
4+
5+
r0 = openat$kvm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
6+
r1 = ioctl$KVM_CREATE_VM(r0, AUTO, 0x0)
7+
r2 = ioctl$KVM_CREATE_VCPU(r1, AUTO, 0x0)
8+
9+
#
10+
# Set the register
11+
# 0x04200513, li a0, 0x42 (addi a0, zero, 0x42)
12+
# 0x06300593, li a1, 0x63 (addi a1, zero, 0x63)
13+
# Load the MMIO address to t1 (without affecting a0, a1)
14+
# 0x40000337, lui t1, 0x40000 (load 20 bits high to t1)
15+
# Read from the MMIO address (this triggers KVM_EXIT_MMIO)
16+
# 0x00032683, lw a3, 0(t1) (read from address 0x40000000 to a3)
17+
#
18+
syz_kvm_setup_cpu$riscv64(r1, r2, &(0x7f0000fe8000/0x180000)=nil,&(0x7f0000000000)=[{0x0, &(0x7f0000001000)="13052004930530063703004083260300", 0xf}], 0x1, 0x0, 0x0, 0x0)
19+
20+
ioctl$KVM_RUN(r2, AUTO, 0x0)
21+
22+
ioctl$KVM_GET_ONE_REG(r2, AUTO, &AUTO=@riscv64_core={0x803000000200000a, &AUTO})
23+
ioctl$KVM_GET_ONE_REG(r2, AUTO, &AUTO=@riscv64_config={0x8030000001000000, &AUTO})
24+
ioctl$KVM_GET_ONE_REG(r2, AUTO, &AUTO=@riscv64_csr={0x8030000003000000, &AUTO})

0 commit comments

Comments
 (0)