1use std::convert::TryFrom;
18use std::fmt::Debug;
19#[cfg(gdb)]
20use std::sync::{Arc, Mutex};
21
22use kvm_bindings::{kvm_fpu, kvm_regs, kvm_userspace_memory_region, KVM_MEM_READONLY};
23use kvm_ioctls::Cap::UserMemory;
24use kvm_ioctls::{Kvm, VcpuExit, VcpuFd, VmFd};
25use log::LevelFilter;
26use tracing::{instrument, Span};
27
28use super::fpu::{FP_CONTROL_WORD_DEFAULT, FP_TAG_WORD_DEFAULT, MXCSR_DEFAULT};
29#[cfg(gdb)]
30use super::gdb::{DebugCommChannel, DebugMsg, DebugResponse, GuestDebug, KvmDebug, VcpuStopReason};
31#[cfg(gdb)]
32use super::handlers::DbgMemAccessHandlerWrapper;
33use super::handlers::{MemAccessHandlerWrapper, OutBHandlerWrapper};
34use super::{
35 HyperlightExit, Hypervisor, VirtualCPU, CR0_AM, CR0_ET, CR0_MP, CR0_NE, CR0_PE, CR0_PG, CR0_WP,
36 CR4_OSFXSR, CR4_OSXMMEXCPT, CR4_PAE, EFER_LMA, EFER_LME, EFER_NX, EFER_SCE,
37};
38use crate::hypervisor::hypervisor_handler::HypervisorHandler;
39use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
40use crate::mem::ptr::{GuestPtr, RawPtr};
41#[cfg(gdb)]
42use crate::HyperlightError;
43use crate::{log_then_return, new_error, Result};
44
45#[instrument(skip_all, parent = Span::current(), level = "Trace")]
47pub(crate) fn is_hypervisor_present() -> bool {
48 if let Ok(kvm) = Kvm::new() {
49 let api_version = kvm.get_api_version();
50 match api_version {
51 version if version == 12 && kvm.check_extension(UserMemory) => true,
52 12 => {
53 log::info!("KVM does not have KVM_CAP_USER_MEMORY capability");
54 false
55 }
56 version => {
57 log::info!("KVM GET_API_VERSION returned {}, expected 12", version);
58 false
59 }
60 }
61 } else {
62 log::info!("KVM is not available on this system");
63 false
64 }
65}
66
67#[cfg(gdb)]
68mod debug {
69 use std::sync::{Arc, Mutex};
70
71 use kvm_bindings::kvm_debug_exit_arch;
72
73 use super::KVMDriver;
74 use crate::hypervisor::gdb::{
75 DebugMsg, DebugResponse, GuestDebug, KvmDebug, VcpuStopReason, X86_64Regs,
76 };
77 use crate::hypervisor::handlers::DbgMemAccessHandlerCaller;
78 use crate::{new_error, Result};
79
80 impl KVMDriver {
81 fn disable_debug(&mut self) -> Result<()> {
83 let mut debug = KvmDebug::default();
84
85 debug.set_single_step(&self.vcpu_fd, false)?;
86
87 self.debug = Some(debug);
88
89 Ok(())
90 }
91
92 pub(crate) fn get_stop_reason(
94 &mut self,
95 debug_exit: kvm_debug_exit_arch,
96 ) -> Result<VcpuStopReason> {
97 let debug = self
98 .debug
99 .as_mut()
100 .ok_or_else(|| new_error!("Debug is not enabled"))?;
101
102 debug.get_stop_reason(&self.vcpu_fd, debug_exit, self.entrypoint)
103 }
104
105 pub(crate) fn process_dbg_request(
106 &mut self,
107 req: DebugMsg,
108 dbg_mem_access_fn: Arc<Mutex<dyn DbgMemAccessHandlerCaller>>,
109 ) -> Result<DebugResponse> {
110 if let Some(debug) = self.debug.as_mut() {
111 match req {
112 DebugMsg::AddHwBreakpoint(addr) => Ok(DebugResponse::AddHwBreakpoint(
113 debug
114 .add_hw_breakpoint(&self.vcpu_fd, addr)
115 .map_err(|e| {
116 log::error!("Failed to add hw breakpoint: {:?}", e);
117
118 e
119 })
120 .is_ok(),
121 )),
122 DebugMsg::AddSwBreakpoint(addr) => Ok(DebugResponse::AddSwBreakpoint(
123 debug
124 .add_sw_breakpoint(&self.vcpu_fd, addr, dbg_mem_access_fn)
125 .map_err(|e| {
126 log::error!("Failed to add sw breakpoint: {:?}", e);
127
128 e
129 })
130 .is_ok(),
131 )),
132 DebugMsg::Continue => {
133 debug.set_single_step(&self.vcpu_fd, false).map_err(|e| {
134 log::error!("Failed to continue execution: {:?}", e);
135
136 e
137 })?;
138
139 Ok(DebugResponse::Continue)
140 }
141 DebugMsg::DisableDebug => {
142 self.disable_debug().map_err(|e| {
143 log::error!("Failed to disable debugging: {:?}", e);
144
145 e
146 })?;
147
148 Ok(DebugResponse::DisableDebug)
149 }
150 DebugMsg::GetCodeSectionOffset => {
151 let offset = dbg_mem_access_fn
152 .try_lock()
153 .map_err(|e| {
154 new_error!("Error locking at {}:{}: {}", file!(), line!(), e)
155 })?
156 .get_code_offset()
157 .map_err(|e| {
158 log::error!("Failed to get code offset: {:?}", e);
159
160 e
161 })?;
162
163 Ok(DebugResponse::GetCodeSectionOffset(offset as u64))
164 }
165 DebugMsg::ReadAddr(addr, len) => {
166 let mut data = vec![0u8; len];
167
168 debug
169 .read_addrs(&self.vcpu_fd, addr, &mut data, dbg_mem_access_fn)
170 .map_err(|e| {
171 log::error!("Failed to read from address: {:?}", e);
172
173 e
174 })?;
175
176 Ok(DebugResponse::ReadAddr(data))
177 }
178 DebugMsg::ReadRegisters => {
179 let mut regs = X86_64Regs::default();
180
181 debug
182 .read_regs(&self.vcpu_fd, &mut regs)
183 .map_err(|e| {
184 log::error!("Failed to read registers: {:?}", e);
185
186 e
187 })
188 .map(|_| DebugResponse::ReadRegisters(regs))
189 }
190 DebugMsg::RemoveHwBreakpoint(addr) => Ok(DebugResponse::RemoveHwBreakpoint(
191 debug
192 .remove_hw_breakpoint(&self.vcpu_fd, addr)
193 .map_err(|e| {
194 log::error!("Failed to remove hw breakpoint: {:?}", e);
195
196 e
197 })
198 .is_ok(),
199 )),
200 DebugMsg::RemoveSwBreakpoint(addr) => Ok(DebugResponse::RemoveSwBreakpoint(
201 debug
202 .remove_sw_breakpoint(&self.vcpu_fd, addr, dbg_mem_access_fn)
203 .map_err(|e| {
204 log::error!("Failed to remove sw breakpoint: {:?}", e);
205
206 e
207 })
208 .is_ok(),
209 )),
210 DebugMsg::Step => {
211 debug.set_single_step(&self.vcpu_fd, true).map_err(|e| {
212 log::error!("Failed to enable step instruction: {:?}", e);
213
214 e
215 })?;
216
217 Ok(DebugResponse::Step)
218 }
219 DebugMsg::WriteAddr(addr, data) => {
220 debug
221 .write_addrs(&self.vcpu_fd, addr, &data, dbg_mem_access_fn)
222 .map_err(|e| {
223 log::error!("Failed to write to address: {:?}", e);
224
225 e
226 })?;
227
228 Ok(DebugResponse::WriteAddr)
229 }
230 DebugMsg::WriteRegisters(regs) => debug
231 .write_regs(&self.vcpu_fd, ®s)
232 .map_err(|e| {
233 log::error!("Failed to write registers: {:?}", e);
234
235 e
236 })
237 .map(|_| DebugResponse::WriteRegisters),
238 }
239 } else {
240 Err(new_error!("Debugging is not enabled"))
241 }
242 }
243
244 pub(crate) fn recv_dbg_msg(&mut self) -> Result<DebugMsg> {
245 let gdb_conn = self
246 .gdb_conn
247 .as_mut()
248 .ok_or_else(|| new_error!("Debug is not enabled"))?;
249
250 gdb_conn.recv().map_err(|e| {
251 new_error!(
252 "Got an error while waiting to receive a message from the gdb thread: {:?}",
253 e
254 )
255 })
256 }
257
258 pub(crate) fn send_dbg_msg(&mut self, cmd: DebugResponse) -> Result<()> {
259 log::debug!("Sending {:?}", cmd);
260
261 let gdb_conn = self
262 .gdb_conn
263 .as_mut()
264 .ok_or_else(|| new_error!("Debug is not enabled"))?;
265
266 gdb_conn.send(cmd).map_err(|e| {
267 new_error!(
268 "Got an error while sending a response message to the gdb thread: {:?}",
269 e
270 )
271 })
272 }
273 }
274}
275
276pub(super) struct KVMDriver {
278 _kvm: Kvm,
279 _vm_fd: VmFd,
280 vcpu_fd: VcpuFd,
281 entrypoint: u64,
282 orig_rsp: GuestPtr,
283 mem_regions: Vec<MemoryRegion>,
284
285 #[cfg(gdb)]
286 debug: Option<KvmDebug>,
287 #[cfg(gdb)]
288 gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
289}
290
291impl KVMDriver {
292 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
296 pub(super) fn new(
297 mem_regions: Vec<MemoryRegion>,
298 pml4_addr: u64,
299 entrypoint: u64,
300 rsp: u64,
301 #[cfg(gdb)] gdb_conn: Option<DebugCommChannel<DebugResponse, DebugMsg>>,
302 ) -> Result<Self> {
303 let kvm = Kvm::new()?;
304
305 let vm_fd = kvm.create_vm_with_type(0)?;
306
307 let perm_flags =
308 MemoryRegionFlags::READ | MemoryRegionFlags::WRITE | MemoryRegionFlags::EXECUTE;
309
310 mem_regions.iter().enumerate().try_for_each(|(i, region)| {
311 let perm_flags = perm_flags.intersection(region.flags);
312 let kvm_region = kvm_userspace_memory_region {
313 slot: i as u32,
314 guest_phys_addr: region.guest_region.start as u64,
315 memory_size: (region.guest_region.end - region.guest_region.start) as u64,
316 userspace_addr: region.host_region.start as u64,
317 flags: match perm_flags {
318 MemoryRegionFlags::READ => KVM_MEM_READONLY,
319 _ => 0, },
321 };
322 unsafe { vm_fd.set_user_memory_region(kvm_region) }
323 })?;
324
325 let mut vcpu_fd = vm_fd.create_vcpu(0)?;
326 Self::setup_initial_sregs(&mut vcpu_fd, pml4_addr)?;
327
328 #[cfg(gdb)]
329 let (debug, gdb_conn) = if let Some(gdb_conn) = gdb_conn {
330 let mut debug = KvmDebug::new();
331 debug.add_hw_breakpoint(&vcpu_fd, entrypoint)?;
333
334 (Some(debug), Some(gdb_conn))
335 } else {
336 (None, None)
337 };
338
339 let rsp_gp = GuestPtr::try_from(RawPtr::from(rsp))?;
340
341 let ret = Self {
342 _kvm: kvm,
343 _vm_fd: vm_fd,
344 vcpu_fd,
345 entrypoint,
346 orig_rsp: rsp_gp,
347 mem_regions,
348
349 #[cfg(gdb)]
350 debug,
351 #[cfg(gdb)]
352 gdb_conn,
353 };
354
355 Ok(ret)
356 }
357
358 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
359 fn setup_initial_sregs(vcpu_fd: &mut VcpuFd, pml4_addr: u64) -> Result<()> {
360 let mut sregs = vcpu_fd.get_sregs()?;
362 sregs.cr3 = pml4_addr;
363 sregs.cr4 = CR4_PAE | CR4_OSFXSR | CR4_OSXMMEXCPT;
364 sregs.cr0 = CR0_PE | CR0_MP | CR0_ET | CR0_NE | CR0_AM | CR0_PG | CR0_WP;
365 sregs.efer = EFER_LME | EFER_LMA | EFER_SCE | EFER_NX;
366 sregs.cs.l = 1; vcpu_fd.set_sregs(&sregs)?;
368 Ok(())
369 }
370}
371
372impl Debug for KVMDriver {
373 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
374 let mut f = f.debug_struct("KVM Driver");
375 for region in &self.mem_regions {
378 f.field("Memory Region", ®ion);
379 }
380 let regs = self.vcpu_fd.get_regs();
381 if let Ok(regs) = regs {
384 f.field("Registers", ®s);
385 }
386
387 let sregs = self.vcpu_fd.get_sregs();
388
389 if let Ok(sregs) = sregs {
392 f.field("Special Registers", &sregs);
393 }
394
395 f.finish()
396 }
397}
398
399impl Hypervisor for KVMDriver {
400 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
402 fn initialise(
403 &mut self,
404 peb_addr: RawPtr,
405 seed: u64,
406 page_size: u32,
407 outb_hdl: OutBHandlerWrapper,
408 mem_access_hdl: MemAccessHandlerWrapper,
409 hv_handler: Option<HypervisorHandler>,
410 max_guest_log_level: Option<LevelFilter>,
411 #[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
412 ) -> Result<()> {
413 let max_guest_log_level: u64 = match max_guest_log_level {
414 Some(level) => level as u64,
415 None => self.get_max_log_level().into(),
416 };
417
418 let regs = kvm_regs {
419 rip: self.entrypoint,
420 rsp: self.orig_rsp.absolute()?,
421
422 rdi: peb_addr.into(),
424 rsi: seed,
425 rdx: page_size.into(),
426 rcx: max_guest_log_level,
427
428 ..Default::default()
429 };
430 self.vcpu_fd.set_regs(®s)?;
431
432 VirtualCPU::run(
433 self.as_mut_hypervisor(),
434 hv_handler,
435 outb_hdl,
436 mem_access_hdl,
437 #[cfg(gdb)]
438 dbg_mem_access_fn,
439 )?;
440
441 Ok(())
442 }
443
444 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
445 fn dispatch_call_from_host(
446 &mut self,
447 dispatch_func_addr: RawPtr,
448 outb_handle_fn: OutBHandlerWrapper,
449 mem_access_fn: MemAccessHandlerWrapper,
450 hv_handler: Option<HypervisorHandler>,
451 #[cfg(gdb)] dbg_mem_access_fn: DbgMemAccessHandlerWrapper,
452 ) -> Result<()> {
453 let regs = kvm_regs {
455 rip: dispatch_func_addr.into(),
456 rsp: self.orig_rsp.absolute()?,
457 ..Default::default()
458 };
459 self.vcpu_fd.set_regs(®s)?;
460
461 let fpu = kvm_fpu {
463 fcw: FP_CONTROL_WORD_DEFAULT,
464 ftwx: FP_TAG_WORD_DEFAULT,
465 mxcsr: MXCSR_DEFAULT,
466 ..Default::default() };
468 self.vcpu_fd.set_fpu(&fpu)?;
469
470 VirtualCPU::run(
472 self.as_mut_hypervisor(),
473 hv_handler,
474 outb_handle_fn,
475 mem_access_fn,
476 #[cfg(gdb)]
477 dbg_mem_access_fn,
478 )?;
479
480 Ok(())
481 }
482
483 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
484 fn handle_io(
485 &mut self,
486 port: u16,
487 data: Vec<u8>,
488 _rip: u64,
489 _instruction_length: u64,
490 outb_handle_fn: OutBHandlerWrapper,
491 ) -> Result<()> {
492 if data.is_empty() {
498 log_then_return!("no data was given in IO interrupt");
499 } else {
500 let mut padded = [0u8; 4];
501 let copy_len = data.len().min(4);
502 padded[..copy_len].copy_from_slice(&data[..copy_len]);
503 let value = u32::from_le_bytes(padded);
504
505 outb_handle_fn
506 .try_lock()
507 .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
508 .call(port, value)?;
509 }
510
511 Ok(())
512 }
513
514 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
515 fn run(&mut self) -> Result<HyperlightExit> {
516 let exit_reason = self.vcpu_fd.run();
517 let result = match exit_reason {
518 Ok(VcpuExit::Hlt) => {
519 crate::debug!("KVM - Halt Details : {:#?}", &self);
520 HyperlightExit::Halt()
521 }
522 Ok(VcpuExit::IoOut(port, data)) => {
523 crate::debug!("KVM IO Details : \nPort : {}\nData : {:?}", port, data);
525 HyperlightExit::IoOut(port, data.to_vec(), 0, 0)
527 }
528 Ok(VcpuExit::MmioRead(addr, _)) => {
529 crate::debug!("KVM MMIO Read -Details: Address: {} \n {:#?}", addr, &self);
530
531 match self.get_memory_access_violation(
532 addr as usize,
533 &self.mem_regions,
534 MemoryRegionFlags::READ,
535 ) {
536 Some(access_violation_exit) => access_violation_exit,
537 None => HyperlightExit::Mmio(addr),
538 }
539 }
540 Ok(VcpuExit::MmioWrite(addr, _)) => {
541 crate::debug!("KVM MMIO Write -Details: Address: {} \n {:#?}", addr, &self);
542
543 match self.get_memory_access_violation(
544 addr as usize,
545 &self.mem_regions,
546 MemoryRegionFlags::WRITE,
547 ) {
548 Some(access_violation_exit) => access_violation_exit,
549 None => HyperlightExit::Mmio(addr),
550 }
551 }
552 #[cfg(gdb)]
553 Ok(VcpuExit::Debug(debug_exit)) => match self.get_stop_reason(debug_exit) {
555 Ok(reason) => HyperlightExit::Debug(reason),
556 Err(e) => {
557 log_then_return!("Error getting stop reason: {:?}", e);
558 }
559 },
560 Err(e) => match e.errno() {
561 #[cfg(gdb)]
565 libc::EINTR => HyperlightExit::Debug(VcpuStopReason::Interrupt),
566 #[cfg(not(gdb))]
568 libc::EINTR => HyperlightExit::Cancelled(),
569 libc::EAGAIN => HyperlightExit::Retry(),
570 _ => {
571 crate::debug!("KVM Error -Details: Address: {} \n {:#?}", e, &self);
572 log_then_return!("Error running VCPU {:?}", e);
573 }
574 },
575 Ok(other) => {
576 let err_msg = format!("Unexpected KVM Exit {:?}", other);
577 crate::debug!("KVM Other Exit Details: {:#?}", &self);
578 HyperlightExit::Unknown(err_msg)
579 }
580 };
581 Ok(result)
582 }
583
584 #[instrument(skip_all, parent = Span::current(), level = "Trace")]
585 fn as_mut_hypervisor(&mut self) -> &mut dyn Hypervisor {
586 self as &mut dyn Hypervisor
587 }
588
589 #[cfg(crashdump)]
590 fn get_memory_regions(&self) -> &[MemoryRegion] {
591 &self.mem_regions
592 }
593
594 #[cfg(gdb)]
595 fn handle_debug(
596 &mut self,
597 dbg_mem_access_fn: Arc<Mutex<dyn super::handlers::DbgMemAccessHandlerCaller>>,
598 stop_reason: VcpuStopReason,
599 ) -> Result<()> {
600 self.send_dbg_msg(DebugResponse::VcpuStopped(stop_reason))
601 .map_err(|e| new_error!("Couldn't signal vCPU stopped event to GDB thread: {:?}", e))?;
602
603 loop {
604 log::debug!("Debug wait for event to resume vCPU");
605 let req = self.recv_dbg_msg()?;
607
608 let result = self.process_dbg_request(req, dbg_mem_access_fn.clone());
609
610 let response = match result {
611 Ok(response) => response,
612 Err(HyperlightError::TranslateGuestAddress(_)) => DebugResponse::ErrorOccurred,
614 Err(e) => {
615 return Err(e);
616 }
617 };
618
619 let cont = matches!(
621 response,
622 DebugResponse::Step | DebugResponse::Continue | DebugResponse::DisableDebug
623 );
624
625 self.send_dbg_msg(response)
626 .map_err(|e| new_error!("Couldn't send response to gdb: {:?}", e))?;
627
628 if cont {
629 break;
630 }
631 }
632
633 Ok(())
634 }
635}
636
637#[cfg(test)]
638mod tests {
639 use std::sync::{Arc, Mutex};
640
641 #[cfg(gdb)]
642 use crate::hypervisor::handlers::DbgMemAccessHandlerCaller;
643 use crate::hypervisor::handlers::{MemAccessHandler, OutBHandler};
644 use crate::hypervisor::tests::test_initialise;
645 use crate::Result;
646
647 #[cfg(gdb)]
648 struct DbgMemAccessHandler {}
649
650 #[cfg(gdb)]
651 impl DbgMemAccessHandlerCaller for DbgMemAccessHandler {
652 fn read(&mut self, _offset: usize, _data: &mut [u8]) -> Result<()> {
653 Ok(())
654 }
655
656 fn write(&mut self, _offset: usize, _data: &[u8]) -> Result<()> {
657 Ok(())
658 }
659
660 fn get_code_offset(&mut self) -> Result<usize> {
661 Ok(0)
662 }
663 }
664
665 #[test]
666 fn test_init() {
667 if !super::is_hypervisor_present() {
668 return;
669 }
670
671 let outb_handler: Arc<Mutex<OutBHandler>> = {
672 let func: Box<dyn FnMut(u16, u32) -> Result<()> + Send> =
673 Box::new(|_, _| -> Result<()> { Ok(()) });
674 Arc::new(Mutex::new(OutBHandler::from(func)))
675 };
676 let mem_access_handler = {
677 let func: Box<dyn FnMut() -> Result<()> + Send> = Box::new(|| -> Result<()> { Ok(()) });
678 Arc::new(Mutex::new(MemAccessHandler::from(func)))
679 };
680 #[cfg(gdb)]
681 let dbg_mem_access_handler = Arc::new(Mutex::new(DbgMemAccessHandler {}));
682
683 test_initialise(
684 outb_handler,
685 mem_access_handler,
686 #[cfg(gdb)]
687 dbg_mem_access_handler,
688 )
689 .unwrap();
690 }
691}