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