diff options
| -rw-r--r-- | executor/common_kvm_riscv64.h | 157 | ||||
| -rw-r--r-- | executor/common_linux.h | 2 | ||||
| -rw-r--r-- | pkg/compiler/types.go | 4 | ||||
| -rw-r--r-- | pkg/vminfo/linux_syscalls.go | 4 | ||||
| -rw-r--r-- | sys/linux/dev_kvm.txt | 14 | ||||
| -rw-r--r-- | sys/linux/dev_kvm_riscv64.txt | 19 | ||||
| -rw-r--r-- | sys/linux/test/syz_kvm_setup_cpu_riscv64 | 24 |
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, ®); +} + +// 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 |
