aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--executor/common_kvm_riscv64.h157
-rw-r--r--executor/common_linux.h2
-rw-r--r--pkg/compiler/types.go4
-rw-r--r--pkg/vminfo/linux_syscalls.go4
-rw-r--r--sys/linux/dev_kvm.txt14
-rw-r--r--sys/linux/dev_kvm_riscv64.txt19
-rw-r--r--sys/linux/test/syz_kvm_setup_cpu_riscv6424
7 files changed, 223 insertions, 1 deletions
diff --git a/executor/common_kvm_riscv64.h b/executor/common_kvm_riscv64.h
new file mode 100644
index 000000000..dcccd6dad
--- /dev/null
+++ b/executor/common_kvm_riscv64.h
@@ -0,0 +1,157 @@
+// Copyright 2026 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+#ifndef EXECUTOR_COMMON_KVM_RISCV64_H
+#define EXECUTOR_COMMON_KVM_RISCV64_H
+
+// This file is shared between executor and csource package.
+
+// Implementation of syz_kvm_setup_cpu pseudo-syscall.
+
+#include <stdint.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+struct kvm_text {
+ uintptr_t type;
+ const void* text;
+ uintptr_t size;
+};
+
+// Construct RISC-V register id for KVM.
+#define RISCV_CORE_REG(idx) (KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_CORE | (idx))
+#define RISCV_CSR_REG(idx) (KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_CSR | (idx))
+
+// Represent CSR indices in the kvm_riscv_csr structure.
+enum riscv_csr_index {
+ CSR_SSTATUS = 0,
+ CSR_SIE,
+ CSR_STVEC,
+ CSR_SSCRATCH,
+ CSR_SEPC,
+ CSR_SCAUSE,
+ CSR_STVAL,
+ CSR_SIP,
+ CSR_SATP,
+ CSR_SCOUNTEREN,
+ CSR_SENVCFG
+};
+
+// Represent CORE register indices in the kvm_riscv_core structure.
+enum riscv_core_index {
+ CORE_PC = 0x00,
+ CORE_RA,
+ CORE_SP,
+ CORE_GP,
+ CORE_TP,
+ CORE_T0,
+ CORE_T1,
+ CORE_T2,
+ CORE_S0,
+ CORE_S1,
+ CORE_A0,
+ CORE_A1,
+ CORE_A2,
+ CORE_A3,
+ CORE_A4,
+ CORE_A5,
+ CORE_A6,
+ CORE_A7,
+ CORE_S2,
+ CORE_S3,
+ CORE_S4,
+ CORE_S5,
+ CORE_S6,
+ CORE_S7,
+ CORE_S8,
+ CORE_S9,
+ CORE_S10,
+ CORE_S11,
+ CORE_T3,
+ CORE_T4,
+ CORE_T5,
+ CORE_T6,
+ // Store the privilege mode: 1=S-mode, 0=U-mode.
+ CORE_MODE
+};
+
+// Indicate the Supervisor Previous Privilege mode.
+#define SSTATUS_SPP (1UL << 8)
+// Indicate the Supervisor Previous Interrupt Enable state.
+#define SSTATUS_SPIE (1UL << 5)
+// Indicate the Supervisor Interrupt Enable state.
+#define SSTATUS_SIE (1UL << 1)
+
+// Define the starting physical address for the guest code.
+#define CODE_START 0x80000000ULL
+
+// Set a single register value for the specified CPU file descriptor.
+static inline int kvm_set_reg(int cpufd, unsigned long id, unsigned long value)
+{
+ struct kvm_one_reg reg = {
+ .id = id,
+ .addr = (unsigned long)&value,
+ };
+ return ioctl(cpufd, KVM_SET_ONE_REG, &reg);
+}
+
+// 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])
+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)
+{
+ const int vmfd = a0;
+ const int cpufd = a1;
+ char* const host_mem = (char*)a2;
+ const struct kvm_text* const text_array_ptr = (struct kvm_text*)a3;
+
+ const uintptr_t page_size = 4096;
+ const uintptr_t guest_pages = 24;
+ const uintptr_t guest_mem_size = guest_pages * page_size;
+
+ // Install guest memory.
+ for (uintptr_t i = 0; i < guest_pages; i++) {
+ struct kvm_userspace_memory_region mem = {
+ .slot = (unsigned int)i,
+ .flags = 0,
+ .guest_phys_addr = CODE_START + i * page_size,
+ .memory_size = page_size,
+ .userspace_addr =
+ (uintptr_t)(host_mem + i * page_size),
+ };
+
+ if (ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &mem))
+ return -1;
+ }
+
+ // Copy guest code.
+ const void* text = 0;
+ uintptr_t size = 0;
+ NONFAILING(text = text_array_ptr[0].text);
+ NONFAILING(size = text_array_ptr[0].size);
+ if (size > guest_mem_size)
+ size = guest_mem_size;
+ memcpy(host_mem, text, size);
+
+ // Initialize VCPU registers.
+ // Set PC (program counter) to start of code.
+ if (kvm_set_reg(cpufd, RISCV_CORE_REG(CORE_PC), CODE_START))
+ return -1;
+ // Set SP (stack pointer) at end of memory, reserving space for stack.
+ unsigned long stack_top = CODE_START + guest_mem_size - page_size;
+ if (kvm_set_reg(cpufd, RISCV_CORE_REG(CORE_SP), stack_top))
+ return -1;
+ // Set privilege mode to S-mode.
+ if (kvm_set_reg(cpufd, RISCV_CORE_REG(CORE_MODE), 1))
+ return -1;
+ // Set SSTATUS CSR with SPP and SPIE.
+ unsigned long sstatus = SSTATUS_SPP | SSTATUS_SPIE;
+ if (kvm_set_reg(cpufd, RISCV_CSR_REG(CSR_SSTATUS), sstatus))
+ return -1;
+ // Set STVEC.
+ unsigned long stvec = CODE_START + page_size;
+ if (kvm_set_reg(cpufd, RISCV_CSR_REG(CSR_STVEC), stvec))
+ return -1;
+
+ return 0;
+}
+
+#endif // EXECUTOR_COMMON_KVM_RISCV64_H \ No newline at end of file
diff --git a/executor/common_linux.h b/executor/common_linux.h
index de393227d..7c4fd9b68 100644
--- a/executor/common_linux.h
+++ b/executor/common_linux.h
@@ -3216,6 +3216,8 @@ error_clear_loop:
#include "common_kvm_arm64.h"
#elif GOARCH_ppc64 || GOARCH_ppc64le
#include "common_kvm_ppc64.h"
+#elif GOARCH_riscv64
+#include "common_kvm_riscv64.h"
#elif SYZ_EXECUTOR || __NR_syz_kvm_setup_cpu
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)
{
diff --git a/pkg/compiler/types.go b/pkg/compiler/types.go
index e022efafe..86876b69d 100644
--- a/pkg/compiler/types.go
+++ b/pkg/compiler/types.go
@@ -641,7 +641,7 @@ var typeText = &typeDesc{
var typeArgTextType = &typeArg{
Kind: kindIdent,
- Names: []string{"target", "x86_real", "x86_16", "x86_32", "x86_64", "arm64", "ppc64"},
+ Names: []string{"target", "x86_real", "x86_16", "x86_32", "x86_64", "arm64", "ppc64", "riscv64"},
}
func genTextType(t *ast.Type) prog.TextKind {
@@ -660,6 +660,8 @@ func genTextType(t *ast.Type) prog.TextKind {
return prog.TextArm64
case "ppc64":
return prog.TextPpc64
+ case "riscv64":
+ return prog.TextTarget
default:
panic(fmt.Sprintf("unknown text type %q", t.Ident))
}
diff --git a/pkg/vminfo/linux_syscalls.go b/pkg/vminfo/linux_syscalls.go
index 605b939d2..11a7b9ddd 100644
--- a/pkg/vminfo/linux_syscalls.go
+++ b/pkg/vminfo/linux_syscalls.go
@@ -192,6 +192,10 @@ func linuxSyzKvmSupported(ctx *checkContext, call *prog.Syscall) string {
if ctx.target.Arch == targets.ARM64 {
return ""
}
+ case "syz_kvm_setup_cpu$riscv64":
+ if ctx.target.Arch == targets.RiscV64 {
+ return ""
+ }
case "syz_kvm_setup_cpu$ppc64":
if ctx.target.Arch == targets.PPC64LE {
return ""
diff --git a/sys/linux/dev_kvm.txt b/sys/linux/dev_kvm.txt
index 73e5edccc..aa13f723a 100644
--- a/sys/linux/dev_kvm.txt
+++ b/sys/linux/dev_kvm.txt
@@ -350,8 +350,17 @@ kvm_one_reg [
arm64_sve kvm_one_reg_arm64_range[0x6080000000150000:0x6080000000150620]
arm64_sve_vls kvm_one_reg_arm64_range[0x606000000015ffff]
other kvm_one_reg_other
+# For riscv64
+ riscv64_config kvm_one_reg_riscv64[kvm_regs_riscv64_config]
+ riscv64_core kvm_one_reg_riscv64[kvm_regs_riscv64_core]
+ riscv64_csr kvm_one_reg_riscv64[kvm_regs_riscv64_csr]
]
+type kvm_one_reg_riscv64[FTYPE] {
+ id flags[FTYPE, int64]
+ addr ptr64[inout, int64]
+}
+
type kvm_one_reg_arm64[FTYPE] {
id flags[FTYPE, int64]
addr ptr64[inout, int64]
@@ -623,3 +632,8 @@ kvm_regs_arm64_sys = 0x6030000000138002, 0x6030000000138010, 0x6030000000138012,
# Extra registers that KVM_GET_REG_LIST prints on QEMU
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
# End of register descriptions generated by tools/arm64/registers.go
+
+# For riscv64, https://elixir.bootlin.com/linux/v6.19-rc4/source/Documentation/virt/kvm/api.rst#L2765
+kvm_regs_riscv64_config = 0x8030000000100000
+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
+kvm_regs_riscv64_csr = 0x8030000000300000, 0x8030000000300001, 0x8030000000300002, 0x8030000000300003, 0x8030000000300004, 0x8030000000300005, 0x8030000000300006, 0x8030000000300007, 0x8030000000300008
diff --git a/sys/linux/dev_kvm_riscv64.txt b/sys/linux/dev_kvm_riscv64.txt
index 1079853a7..c6ecde793 100644
--- a/sys/linux/dev_kvm_riscv64.txt
+++ b/sys/linux/dev_kvm_riscv64.txt
@@ -12,3 +12,22 @@ ioctl$KVM_SET_GUEST_DEBUG_riscv64(fd fd_kvmcpu, cmd const[KVM_SET_GUEST_DEBUG],
kvm_guest_debug_arch_riscv64 {
reg array[int64, 8]
}
+
+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])
+
+kvm_setup_opt_riscv64 [
+# unions need at least 2 fields, but we have only 1 now, but we want to have it as union for future extention
+ featur1 kvm_setup_opt_riscv64_feature
+ featur2 kvm_setup_opt_riscv64_feature
+]
+
+kvm_setup_opt_riscv64_feature {
+ typ const[1, int64]
+ val int64
+}
+
+kvm_text_riscv64 {
+ typ const[0, intptr]
+ text ptr[in, text[riscv64]]
+ size len[text, intptr]
+}
diff --git a/sys/linux/test/syz_kvm_setup_cpu_riscv64 b/sys/linux/test/syz_kvm_setup_cpu_riscv64
new file mode 100644
index 000000000..3e7c222f1
--- /dev/null
+++ b/sys/linux/test/syz_kvm_setup_cpu_riscv64
@@ -0,0 +1,24 @@
+#
+# requires: arch=riscv64
+#
+
+r0 = openat$kvm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0)
+r1 = ioctl$KVM_CREATE_VM(r0, AUTO, 0x0)
+r2 = ioctl$KVM_CREATE_VCPU(r1, AUTO, 0x0)
+
+#
+# Set the register
+# 0x04200513, li a0, 0x42 (addi a0, zero, 0x42)
+# 0x06300593, li a1, 0x63 (addi a1, zero, 0x63)
+# Load the MMIO address to t1 (without affecting a0, a1)
+# 0x40000337, lui t1, 0x40000 (load 20 bits high to t1)
+# Read from the MMIO address (this triggers KVM_EXIT_MMIO)
+# 0x00032683, lw a3, 0(t1) (read from address 0x40000000 to a3)
+#
+syz_kvm_setup_cpu$riscv64(r1, r2, &(0x7f0000fe8000/0x180000)=nil,&(0x7f0000000000)=[{0x0, &(0x7f0000001000)="13052004930530063703004083260300", 0xf}], 0x1, 0x0, 0x0, 0x0)
+
+ioctl$KVM_RUN(r2, AUTO, 0x0)
+
+ioctl$KVM_GET_ONE_REG(r2, AUTO, &AUTO=@riscv64_core={0x803000000200000a, &AUTO})
+ioctl$KVM_GET_ONE_REG(r2, AUTO, &AUTO=@riscv64_config={0x8030000001000000, &AUTO})
+ioctl$KVM_GET_ONE_REG(r2, AUTO, &AUTO=@riscv64_csr={0x8030000003000000, &AUTO}) \ No newline at end of file