Skip to content

Commit 503932b

Browse files
committedDec 22, 2023
(ocaml-pkvm-proxy) port kvmtest to OCaml
0 parents  commit 503932b

9 files changed

+736
-0
lines changed
 

‎.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/_build
2+
/.cache
3+
/compile_commands.json

‎dune-project

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
(lang dune 3.0)
2+
3+
(generate_opam_files)
4+
5+
(source (github rems-project/linux-tests))
6+
(maintainers "dk505@cl.cam.ac.uk")
7+
8+
(package
9+
(name pkvm-proxy)
10+
(synopsis "Issue pKVM hypercalls using pkvm-proxy")
11+
(description "Break VMs.")
12+
(depends
13+
(fmt (>= 0.9.0))
14+
(logs (>= 0.7.0))))

‎pkvm-proxy.opam

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# This file is generated by dune, edit dune-project instead
2+
opam-version: "2.0"
3+
synopsis: "Issue pKVM hypercalls using pkvm-proxy"
4+
description: "Break VMs."
5+
maintainer: ["dk505@cl.cam.ac.uk"]
6+
homepage: "https://github.com/rems-project/linux-tests"
7+
bug-reports: "https://github.com/rems-project/linux-tests/issues"
8+
depends: [
9+
"dune" {>= "3.0"}
10+
"fmt" {>= "0.9.0"}
11+
"logs" {>= "0.7.0"}
12+
"odoc" {with-doc}
13+
]
14+
build: [
15+
["dune" "subst"] {dev}
16+
[
17+
"dune"
18+
"build"
19+
"-p"
20+
name
21+
"-j"
22+
jobs
23+
"@install"
24+
"@runtest" {with-test}
25+
"@doc" {with-doc}
26+
]
27+
]
28+
dev-repo: "git+https://github.com/rems-project/linux-tests.git"

‎src-bin/dune

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
(executable
2+
(name kvmtest)
3+
(libraries pkvm_proxy logs.fmt fmt.tty))
4+
5+
(env (release (link_flags (-ccopt -static))))

‎src-bin/kvmtest.ml

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
open Pkvm_proxy
2+
module Log = (val Logs.(Src.create "kvmtest" |> src_log))
3+
4+
let _ =
5+
Fmt_tty.setup_std_outputs ();
6+
Logs.set_level ~all:true (Some Logs.Debug);
7+
Logs.set_reporter (Logs_fmt.reporter ())
8+
9+
let _ =
10+
sched_setaffinity [|0|]; (* vcpu_load .. vcpu_put *)
11+
12+
let vm, handle = init_vm ~protected:true () in
13+
let vcpu = init_vcpu handle 0 in
14+
vcpu.@[vcpu_regs] <- { regs = [| 0x37L; 0x1300L; 0xdeadL |]; pc = 0x1000L; sp = 0L; pstate = 0L };
15+
vcpu_set_dirty vcpu;
16+
17+
topup_hyp_memcache vcpu.@[vcpu_memcache] 10;
18+
19+
vcpu_load handle 0;
20+
21+
let gcode = kernel_region_alloc page_size in
22+
region_memory gcode |> Bigstring.blit_from_string "\x20\x00\x00\x8b\x40\x00\x00\xf9\x00\x00\x20\xd4";
23+
kernel_region_release gcode;
24+
map_region_guest vcpu gcode vcpu.@[vcpu_regs].pc;
25+
26+
let exit_code = vcpu_run vcpu in
27+
Log.app (fun k -> k "@[<2>Ran VCPU 0,@ exit %a,@ fault %a@]"
28+
pp_arm_exception exit_code pp_fault_info vcpu.@[vcpu_fault]);
29+
vcpu_sync_state ();
30+
Log.app (fun k -> k "%a" pp_regs vcpu.@[vcpu_regs]);
31+
32+
vcpu_put ();
33+
teardown_vm handle vm;
34+
35+
kernel_region_unshare_hyp vcpu;
36+
kernel_region_free vcpu;
37+
38+
kernel_region_reclaim gcode;
39+
kernel_region_free gcode;
40+
41+
Gc.full_major(); (* see if it crashes *)
42+
43+
()

‎src/dune

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
(library
2+
(name pkvm_proxy)
3+
(public_name pkvm-proxy)
4+
(synopsis "pKVM userspace proxy")
5+
(wrapped false)
6+
(foreign_stubs
7+
(language c)
8+
(names pkvm-proxy)
9+
(flags (:standard -Wall -Wextra -O3)))
10+
(libraries unix bigarray fmt logs))

‎src/pkvm-proxy.c

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#include <sys/ioctl.h>
2+
#include <sys/mman.h>
3+
#include <strings.h>
4+
5+
#define __USE_GNU // glibc
6+
#define _GNU_SOURCE // musl
7+
#include <sched.h>
8+
9+
#include <caml/memory.h>
10+
#include <caml/alloc.h>
11+
#include <caml/unixsupport.h>
12+
#include <caml/bigarray.h>
13+
14+
#include "pkvm-proxy.h"
15+
16+
CAMLprim value caml_sizes(void) {
17+
CAMLparam0();
18+
CAMLlocal1(res);
19+
res = caml_alloc_tuple(4);
20+
Store_field(res, 0, Val_long(sizeof(void *)));
21+
Store_field(res, 1, Val_long(sizeof(int)));
22+
Store_field(res, 2, Val_long(sizeof(__u64)));
23+
Store_field(res, 3, Val_long(sizeof(struct hprox_memcache)));
24+
CAMLreturn(res);
25+
}
26+
27+
static inline int __ioc_wrap(int res) {
28+
if (res < 0) uerror("ioctl", Nothing);
29+
return Val_long(res);
30+
}
31+
32+
CAMLprim value caml_pkvm_ioctl (value fd, value req) {
33+
CAMLparam2(fd, req);
34+
CAMLreturn(__ioc_wrap(ioctl(Long_val(fd), Long_val(req))));
35+
}
36+
37+
CAMLprim value caml_pkvm_ioctl_long (value fd, value req, value arg) {
38+
CAMLparam3(fd, req, arg);
39+
CAMLreturn(__ioc_wrap(ioctl(Long_val(fd), Long_val(req), Long_val(arg))));
40+
}
41+
42+
CAMLprim value caml_pkvm_ioctl_ptr (value fd, value req, value arg) {
43+
CAMLparam3(fd, req, arg);
44+
CAMLreturn(__ioc_wrap(ioctl(Long_val(fd), Long_val(req), Caml_ba_data_val(arg))));
45+
}
46+
47+
/* Assumes _IOC_{NONE,WRITE,READ} haven't been redefined by arch, since we use
48+
* constructor indices.
49+
*/
50+
CAMLprim value caml__IOC(value dir, value type, value nr, value size) {
51+
CAMLparam4(dir, type, nr, size);
52+
CAMLreturn(Val_long(_IOC(Long_val(dir), Long_val(type), Long_val(nr), Long_val(size))));
53+
}
54+
55+
extern value caml_unix_mapped_alloc(int, int, void *, intnat *);
56+
57+
/* XXX Works around hyp-proxy not implementing stat.
58+
* It's super unsafe because the fd size is provided as an argument and cannot
59+
* be checked.
60+
*/
61+
CAMLprim value caml_super_unsafe_ba_mmap(value fd, value size) {
62+
CAMLparam2(fd, size);
63+
intnat sz = Long_val(size);
64+
void *addr = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_SHARED, Long_val(fd), 0);
65+
if (addr == (void *) MAP_FAILED) uerror("bad_map_file", Nothing);
66+
CAMLreturn(caml_unix_mapped_alloc(CAML_BA_UINT8|CAML_BA_C_LAYOUT, 1, addr, &sz));
67+
}
68+
69+
CAMLprim value caml_ba_munmap(value arr) {
70+
CAMLparam1(arr);
71+
if (munmap(Caml_ba_data_val(arr), caml_ba_byte_size(Caml_ba_array_val(arr))) != 0)
72+
uerror("munmap", Nothing);
73+
CAMLreturn(Val_unit);
74+
}
75+
76+
CAMLprim value caml_ba_bzero(value arr) {
77+
CAMLparam1(arr);
78+
bzero(Caml_ba_data_val(arr), caml_ba_byte_size(Caml_ba_array_val(arr)));
79+
CAMLreturn(Val_unit);
80+
}
81+
82+
CAMLprim value caml_sched_setaffinity(value thread, value cpus) {
83+
CAMLparam2(thread, cpus);
84+
cpu_set_t cpu_set;
85+
CPU_ZERO(&cpu_set);
86+
for (unsigned int i = 0; i < Wosize_val(cpus); ++i)
87+
CPU_SET(Long_val(Field(cpus, i)), &cpu_set);
88+
if (sched_setaffinity(Long_val(thread), sizeof(cpu_set_t), &cpu_set) < 0)
89+
uerror("sched_setaffinity", Nothing);
90+
CAMLreturn(Val_unit);
91+
}

‎src/pkvm-proxy.h

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#include <sys/types.h>
2+
#include <linux/types.h>
3+
#include <linux/kvm.h>
4+
#include <sys/sysinfo.h>
5+
6+
7+
/* XXX
8+
* This comes from linux/kvm.h, but only for aarch64. Mirrored here simply
9+
* to make it compile for x86_64.
10+
*/
11+
#ifdef __x86_64__
12+
struct user_pt_regs {
13+
__u64 regs[31];
14+
__u64 sp;
15+
__u64 pc;
16+
__u64 pstate;
17+
};
18+
#endif
19+
20+
#define __KVM_HOST_SMCCC_FUNC___kvm_hyp_init 0
21+
22+
enum __kvm_host_smccc_func {
23+
/* Hypercalls available only prior to pKVM finalisation */
24+
/* __KVM_HOST_SMCCC_FUNC___kvm_hyp_init */
25+
__KVM_HOST_SMCCC_FUNC___kvm_get_mdcr_el2 =
26+
__KVM_HOST_SMCCC_FUNC___kvm_hyp_init + 1,
27+
__KVM_HOST_SMCCC_FUNC___pkvm_init,
28+
__KVM_HOST_SMCCC_FUNC___pkvm_create_private_mapping,
29+
__KVM_HOST_SMCCC_FUNC___pkvm_cpu_set_vector,
30+
__KVM_HOST_SMCCC_FUNC___kvm_enable_ssbs,
31+
__KVM_HOST_SMCCC_FUNC___vgic_v3_init_lrs,
32+
__KVM_HOST_SMCCC_FUNC___vgic_v3_get_gic_config,
33+
__KVM_HOST_SMCCC_FUNC___kvm_flush_vm_context,
34+
__KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_ipa,
35+
__KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid, /* 10 */
36+
__KVM_HOST_SMCCC_FUNC___kvm_flush_cpu_context,
37+
__KVM_HOST_SMCCC_FUNC___pkvm_prot_finalize,
38+
39+
/* Hypercalls available after pKVM finalisation */
40+
__KVM_HOST_SMCCC_FUNC___pkvm_host_share_hyp,
41+
__KVM_HOST_SMCCC_FUNC___pkvm_host_unshare_hyp,
42+
__KVM_HOST_SMCCC_FUNC___pkvm_host_reclaim_page,
43+
__KVM_HOST_SMCCC_FUNC___pkvm_host_map_guest,
44+
__KVM_HOST_SMCCC_FUNC___kvm_adjust_pc,
45+
__KVM_HOST_SMCCC_FUNC___kvm_vcpu_run,
46+
__KVM_HOST_SMCCC_FUNC___kvm_timer_set_cntvoff,
47+
__KVM_HOST_SMCCC_FUNC___vgic_v3_save_vmcr_aprs, /* 20 */
48+
__KVM_HOST_SMCCC_FUNC___vgic_v3_restore_vmcr_aprs,
49+
__KVM_HOST_SMCCC_FUNC___pkvm_init_vm,
50+
__KVM_HOST_SMCCC_FUNC___pkvm_init_vcpu,
51+
__KVM_HOST_SMCCC_FUNC___pkvm_teardown_vm,
52+
__KVM_HOST_SMCCC_FUNC___pkvm_vcpu_load,
53+
__KVM_HOST_SMCCC_FUNC___pkvm_vcpu_put,
54+
__KVM_HOST_SMCCC_FUNC___pkvm_vcpu_sync_state,
55+
};
56+
57+
#define HPROX_HVC_TYPE 'h'
58+
#define HPROX_STRUCTS_TYPE 's'
59+
#define HPROX_ALLOC_TYPE 'a'
60+
#define HPROX_MEMCACHE_TYPE 'm'
61+
62+
63+
// Perform the HVC numbered hvcnum, with this number of arguments.
64+
// The ioctl parameter is an array containing the arguments
65+
#define HVC_PROXY_IOCTL(hvcnum, numarg) \
66+
_IOC(_IOC_WRITE, HPROX_HVC_TYPE, hvcnum, 8 * numarg)
67+
68+
// All those ioctl return a size or an offset as return value.
69+
#define HPROX_STRUCT_KVM_GET_SIZE _IO(HPROX_STRUCTS_TYPE, 0)
70+
// The argument must be a: enum struct_kvm_fields
71+
#define HPROX_STRUCT_KVM_GET_OFFSET _IO(HPROX_STRUCTS_TYPE, 1)
72+
#define HPROX_HYP_VM_GET_SIZE _IO(HPROX_STRUCTS_TYPE, 2)
73+
#define HPROX_PGD_GET_SIZE _IO(HPROX_STRUCTS_TYPE, 3)
74+
#define HPROX_STRUCT_KVM_VCPU_GET_SIZE _IO(HPROX_STRUCTS_TYPE, 4)
75+
// The argument must be a: enum struct_kvm_vcpu_fields
76+
#define HPROX_STRUCT_KVM_VCPU_GET_OFFSET _IO(HPROX_STRUCTS_TYPE, 5)
77+
#define HPROX_HYP_VCPU_GET_SIZE _IO(HPROX_STRUCTS_TYPE, 6)
78+
79+
enum struct_kvm_fields {
80+
HPROX_NR_MEM_SLOT_PAGES, /* unsigned long */
81+
HPROX_VCPU_ARRAY, /* xarray */
82+
HPROX_MAX_VCPUS, /* int */
83+
HPROX_CREATED_VCPUS, /* int */
84+
HPROX_ARCH_PKVM_ENABLED, /* bool */
85+
HPROX_ARCH_PKVM_TEARDOWN_MC, /* struct hprox_memcache */
86+
};
87+
88+
enum struct_kvm_vcpu_fields {
89+
HPROX_VCPU_ID, /* int */
90+
HPROX_VCPU_IDX, /* int */
91+
HPROX_VCPU_CFLAGS, /* 8 bits bitfield */
92+
HPROX_VCPU_IFLAGS, /* 8 bits bitfield */
93+
HPROX_VCPU_FEATURES, /* KVM_VCPU_MAX_FEATURES bits bitfield */
94+
HPROX_VCPU_HCR_EL2, /* u64 */
95+
HPROX_VCPU_FAULT, /* struct hprox_vcpu_fault_info */
96+
HPROX_VCPU_REGS, /* struct user_pt_regs */
97+
HPROX_VCPU_FP_REGS, /* struct user_fpsimd_state */
98+
HPROX_VCPU_MEMCACHE, /* struct hprox_memcache */
99+
// TODO add SVE state, for now SVE-less guests only
100+
};
101+
102+
struct hprox_vcpu_fault_info {
103+
__u64 esr_el2; /* Hyp Syndrom Register */
104+
__u64 far_el2; /* Hyp Fault Address Register */
105+
__u64 hpfar_el2; /* Hyp IPA Fault Address Register */
106+
__u64 disr_el1; /* Deferred [SError] Status Register */
107+
};
108+
109+
// Need to match up kvm_hyp_memcache
110+
struct hprox_memcache {
111+
__u64 head; // kernel address, might not be accessible, if not
112+
// donated from a hprox_alloc region.
113+
unsigned long nr_pages;
114+
};
115+
enum hprox_alloc_type { HPROX_VMALLOC, HPROX_PAGES_EXACT };
116+
117+
// the ioctl parameter is the size of the allocation
118+
#define HPROX_ALLOC(alloc) _IO(HPROX_ALLOC_TYPE, alloc)
119+
#define HPROX_ALLOC_PAGES HPROX_ALLOC(HPROX_PAGES_EXACT)
120+
121+
// ioctl on the mmapable fd from the HPROX_ALLOC ioctl
122+
#define HPROX_ALLOC_KADDR _IOR('A',0, __u64)
123+
#define HPROX_ALLOC_PHYS _IOR('A', 1, __u64)
124+
#define HPROX_ALLOC_RELEASE _IO('A', 2)
125+
#define HPROX_ALLOC_FREE _IO('A', 3)
126+
127+
// memcache ioctl, free is encoded as topup 0
128+
#define HPROX_MEMCACHE_FREE \
129+
_IOWR(HPROX_MEMCACHE_TYPE, 0, struct hprox_memcache)
130+
#define HPROX_MEMCACHE_TOPUP(n) \
131+
_IOWR(HPROX_MEMCACHE_TYPE, (n), struct hprox_memcache)

‎src/pkvm_proxy.ml

+411
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.