hypervisor kernel integrity enforcement via EPT/NPT write interception for Xen/HVM guests
4.1 kB
150 lines
1use std::fs::{File, OpenOptions};
2use std::os::unix::io::AsRawFd;
3
4use crate::error::{Result, TurnstileError};
5use crate::xen::*;
6
7pub struct XenHandle {
8 privcmd: File,
9}
10
11impl XenHandle {
12 pub fn open() -> Result<Self> {
13 let privcmd = OpenOptions::new()
14 .read(true)
15 .write(true)
16 .open("/dev/xen/privcmd")?;
17 Ok(Self { privcmd })
18 }
19
20 fn hypercall(&self, op: u32, domid: u16, arg: u64) -> Result<i64> {
21 let mut hc = PrivcmdHypercall {
22 op: HYPERVISOR_TURNSTILE,
23 arg: [op as _, domid as _, arg, 0, 0],
24 };
25
26 let ret = unsafe { privcmd_hypercall(self.privcmd.as_raw_fd(), &mut hc) };
27
28 match ret {
29 Ok(v) => Ok(v as i64),
30 Err(nix::errno::Errno::ESRCH) => Err(TurnstileError::DomainNotFound(domid)),
31 Err(e) => Err(TurnstileError::Hypercall(e as i32)),
32 }
33 }
34
35 pub fn set_policy(&self, domid: u16, policy: u32) -> Result<()> {
36 let op = TurnstileOpSetPolicy { policy, _pad: 0 };
37 self.hypercall(
38 TURNSTILE_OP_SET_POLICY,
39 domid,
40 &op as *const _ as u64,
41 )?;
42 Ok(())
43 }
44
45 pub fn protect_range(&self, domid: u16, gpa_start: u64, length: u64) -> Result<()> {
46 let op = TurnstileOpProtectRange { gpa_start, length };
47 self.hypercall(
48 TURNSTILE_OP_PROTECT_RANGE,
49 domid,
50 &op as *const _ as u64,
51 )?;
52 Ok(())
53 }
54
55 pub fn unprotect_range(&self, domid: u16, gpa_start: u64, length: u64) -> Result<()> {
56 let op = TurnstileOpProtectRange { gpa_start, length };
57 self.hypercall(
58 TURNSTILE_OP_UNPROTECT_RANGE,
59 domid,
60 &op as *const _ as u64,
61 )?;
62 Ok(())
63 }
64
65 pub fn get_stats(&self, domid: u16) -> Result<TurnstileStats> {
66 let mut stats = TurnstileStats::default();
67 self.hypercall(
68 TURNSTILE_OP_GET_STATS,
69 domid,
70 &mut stats as *mut _ as u64,
71 )?;
72 Ok(stats)
73 }
74
75 pub fn get_violations(
76 &self,
77 domid: u16,
78 buffer: &mut [TurnstileViolation],
79 ) -> Result<(u32, u32)> {
80 let mut op = TurnstileOpGetViolations {
81 buffer_ptr: buffer.as_ptr() as u64,
82 max_entries: buffer.len() as u32,
83 entries_returned: 0,
84 overflow_count: 0,
85 _pad: 0,
86 };
87
88 self.hypercall(
89 TURNSTILE_OP_GET_VIOLATIONS,
90 domid,
91 &mut op as *mut _ as u64,
92 )?;
93
94 Ok((op.entries_returned, op.overflow_count))
95 }
96
97 pub fn upload_func_entries(&self, domid: u16, entries: &[u64]) -> Result<()> {
98 if entries.is_empty() {
99 return Ok(());
100 }
101
102 if entries.len() > TURNSTILE_MAX_FUNC_ENTRIES {
103 return Err(TurnstileError::MetadataLimitExceeded(
104 entries.len(),
105 TURNSTILE_MAX_FUNC_ENTRIES,
106 ));
107 }
108
109 let op = TurnstileOpUploadMetadata {
110 buffer_ptr: entries.as_ptr() as u64,
111 metadata_type: TURNSTILE_META_FUNC_ENTRIES,
112 count: entries.len() as u32,
113 };
114
115 self.hypercall(
116 TURNSTILE_OP_UPLOAD_METADATA,
117 domid,
118 &op as *const _ as u64,
119 )?;
120
121 Ok(())
122 }
123
124 pub fn upload_jump_entries(&self, domid: u16, entries: &[JumpEntryUpload]) -> Result<()> {
125 if entries.is_empty() {
126 return Ok(());
127 }
128
129 if entries.len() > TURNSTILE_MAX_JUMP_ENTRIES {
130 return Err(TurnstileError::MetadataLimitExceeded(
131 entries.len(),
132 TURNSTILE_MAX_JUMP_ENTRIES,
133 ));
134 }
135
136 let op = TurnstileOpUploadMetadata {
137 buffer_ptr: entries.as_ptr() as u64,
138 metadata_type: TURNSTILE_META_JUMP_ENTRIES,
139 count: entries.len() as u32,
140 };
141
142 self.hypercall(
143 TURNSTILE_OP_UPLOAD_METADATA,
144 domid,
145 &op as *const _ as u64,
146 )?;
147
148 Ok(())
149 }
150}