aboutsummaryrefslogtreecommitdiffstats
path: root/executor/common_kvm_riscv64.h
blob: dcccd6dade040c9ed24524b4afbd356cfd2bf563 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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