diff --git a/crates/vm-core/src/monitor.rs b/crates/vm-core/src/monitor.rs index 8f8a0bd..16bcc05 100644 --- a/crates/vm-core/src/monitor.rs +++ b/crates/vm-core/src/monitor.rs @@ -7,7 +7,7 @@ pub enum MonitorError { Stream(#[from] std::io::Error), #[error("{0}")] - CommandHandlerConflicat(String), + CommandHandlerConflict(String), #[error("{0}")] Serde(#[from] serde_json::Error), diff --git a/crates/vm-core/src/virtualization/hvp.rs b/crates/vm-core/src/virtualization/hvp.rs index e3e5834..161f46c 100644 --- a/crates/vm-core/src/virtualization/hvp.rs +++ b/crates/vm-core/src/virtualization/hvp.rs @@ -37,6 +37,6 @@ impl Hypervisor for AppleHypervisor { hv_unsafe_call!(hv_vm_create(vm_config)) .map_err(|err| HypervisorError::CreateVm(err.to_string()))?; - Ok(Arc::new(AppleHypervisorVm::default())) + Ok(Arc::new(AppleHypervisorVm)) } } diff --git a/crates/vm-core/src/virtualization/hvp/vm.rs b/crates/vm-core/src/virtualization/hvp/vm.rs index ca36380..e607a61 100644 --- a/crates/vm-core/src/virtualization/hvp/vm.rs +++ b/crates/vm-core/src/virtualization/hvp/vm.rs @@ -35,7 +35,6 @@ impl From for MemPerms { } } -#[derive(Default)] pub struct AppleHypervisorVm; impl HypervisorVm for AppleHypervisorVm { diff --git a/crates/vm-vmm/src/device.rs b/crates/vm-vmm/src/device.rs index cd4f1af..441b78e 100644 --- a/crates/vm-vmm/src/device.rs +++ b/crates/vm-vmm/src/device.rs @@ -18,7 +18,7 @@ use vm_virtio::transport::pci::VirtioPciDevice; use crate::device::irq_allocation::IrqAllocation; use crate::error::Error; -use crate::service::monitor::MonitorServerBuilder; +use crate::service::monitor::builder::MonitorServerBuilder; mod irq_allocation; diff --git a/crates/vm-vmm/src/lib.rs b/crates/vm-vmm/src/lib.rs index f6455c3..95abbc6 100644 --- a/crates/vm-vmm/src/lib.rs +++ b/crates/vm-vmm/src/lib.rs @@ -1,4 +1,4 @@ -// #![deny(warnings)] +#![deny(warnings)] mod device; mod firmware; diff --git a/crates/vm-vmm/src/service/gdbstub.rs b/crates/vm-vmm/src/service/gdbstub.rs index 8789301..ef7abba 100644 --- a/crates/vm-vmm/src/service/gdbstub.rs +++ b/crates/vm-vmm/src/service/gdbstub.rs @@ -3,9 +3,9 @@ use gdbstub_arch::aarch64::AArch64 as GdbStubArch; #[cfg(target_arch = "x86_64")] use gdbstub_arch::x86::X86_64_SSE as GdbStubArch; -pub mod command; -pub mod connection; -pub mod error; +pub(crate) mod command; +pub(crate) mod connection; +pub(crate) mod error; mod event_loop; mod target; diff --git a/crates/vm-vmm/src/service/gdbstub/command.rs b/crates/vm-vmm/src/service/gdbstub/command.rs index 0beb67f..7e145b2 100644 --- a/crates/vm-vmm/src/service/gdbstub/command.rs +++ b/crates/vm-vmm/src/service/gdbstub/command.rs @@ -2,12 +2,11 @@ use gdbstub_arch::aarch64::reg::AArch64CoreRegs as ArchGdbRegs; #[cfg(target_arch = "x86_64")] use gdbstub_arch::x86::reg::X86_64CoreRegs as ArchGdbRegs; -use thiserror::Error; use tokio::sync::mpsc; use tokio::sync::oneshot; use crate::service::gdbstub::error::VmGdbStubError; -use crate::vmm::command::VmmCommand; +use crate::vmm::handler::VmmCommand; pub enum GdbStubCommand { ReadRegisters { @@ -52,22 +51,16 @@ pub enum GdbStubCommandResponse { Err, } -#[derive(Error, Debug)] -pub enum GdbStubCommandError { - #[error("Err")] - Err, -} - pub struct GdbStubCommandRequest { pub command: GdbStubCommand, - pub response: oneshot::Sender>, + pub response: oneshot::Sender, } impl GdbStubCommand { pub fn send_and_then_wait( self, tx: &mpsc::Sender, - ) -> Result, VmGdbStubError> { + ) -> Result { let (response_tx, response_rx) = oneshot::channel(); let request = VmmCommand::GdbCommand(GdbStubCommandRequest { diff --git a/crates/vm-vmm/src/service/gdbstub/connection.rs b/crates/vm-vmm/src/service/gdbstub/connection.rs index f9541f7..9ef0437 100644 --- a/crates/vm-vmm/src/service/gdbstub/connection.rs +++ b/crates/vm-vmm/src/service/gdbstub/connection.rs @@ -9,7 +9,7 @@ use tracing::error; use crate::service::gdbstub::error::VmGdbStubError; use crate::service::gdbstub::event_loop::VmEventLoop; use crate::service::gdbstub::target::VmGdbStubTarget; -use crate::vmm::command::VmmCommand; +use crate::vmm::handler::VmmCommand; pub struct VmGdbStubConnector { tx: Arc>, diff --git a/crates/vm-vmm/src/service/gdbstub/target.rs b/crates/vm-vmm/src/service/gdbstub/target.rs index 01512bd..6730aeb 100644 --- a/crates/vm-vmm/src/service/gdbstub/target.rs +++ b/crates/vm-vmm/src/service/gdbstub/target.rs @@ -17,7 +17,7 @@ use crate::service::gdbstub::GdbStubArch; use crate::service::gdbstub::command::GdbStubCommand; use crate::service::gdbstub::command::GdbStubCommandResponse; use crate::service::gdbstub::error::VmGdbStubError; -use crate::vmm::command::VmmCommand; +use crate::vmm::handler::VmmCommand; fn vcpu_id_to_tid(vcpu_id: usize) -> Result { Tid::new(vcpu_id + 1).ok_or(VmGdbStubError::InvalidTid) @@ -50,17 +50,17 @@ impl MultiThreadBase for VmGdbStubTarget { .map_err(|_| TargetError::NonFatal)?; match response { - Ok(GdbStubCommandResponse::ReadRegisters { registers }) => { + GdbStubCommandResponse::ReadRegisters { registers } => { *regs = *registers; Ok(()) } - Ok(_) => { - error!("Unexpected response to ReadRegisters command"); + GdbStubCommandResponse::Err => { + error!("Failed to handle ReadRegisters command"); Err(TargetError::NonFatal) } - Err(err) => { - error!(?err, "Failed to read registers"); + _ => { + error!("Unexpected response to ReadRegisters command"); Err(TargetError::NonFatal) } } @@ -81,13 +81,13 @@ impl MultiThreadBase for VmGdbStubTarget { .map_err(|_| TargetError::NonFatal)?; match response { - Ok(GdbStubCommandResponse::WriteRegisters) => Ok(()), - Ok(_) => { - error!("Unexpected response to WriteRegisters command"); + GdbStubCommandResponse::WriteRegisters => Ok(()), + GdbStubCommandResponse::Err => { + error!("Failed to handle command"); Err(TargetError::NonFatal) } - Err(err) => { - error!(?err, "Failed to write registers"); + _ => { + error!("Unexpected response to command"); Err(TargetError::NonFatal) } } @@ -110,17 +110,16 @@ impl MultiThreadBase for VmGdbStubTarget { .map_err(|_| TargetError::NonFatal)?; match response { - Ok(GdbStubCommandResponse::ReadAddrs { buf }) => { + GdbStubCommandResponse::ReadAddrs { buf } => { data[..buf.len()].copy_from_slice(&buf); Ok(data.len()) } - Ok(GdbStubCommandResponse::Err) => Err(TargetError::NonFatal), - Ok(_) => { - error!("Unexpected response to ReadAddrs command"); + GdbStubCommandResponse::Err => { + error!("Failed to handle command"); Err(TargetError::NonFatal) } - Err(err) => { - error!(?err, "Failed to read addresses"); + _ => { + error!("Unexpected response to command"); Err(TargetError::NonFatal) } } @@ -143,13 +142,13 @@ impl MultiThreadBase for VmGdbStubTarget { .map_err(|_| TargetError::NonFatal)?; match response { - Ok(GdbStubCommandResponse::WriteAddrs) => Ok(()), - Ok(_) => { - error!("Unexpected response to WriteAddrs command"); + GdbStubCommandResponse::WriteAddrs => Ok(()), + GdbStubCommandResponse::Err => { + error!("Failed to handle command"); Err(TargetError::NonFatal) } - Err(err) => { - error!(?err, "Failed to write addresses"); + _ => { + error!("Unexpected response to command"); Err(TargetError::NonFatal) } } @@ -160,7 +159,7 @@ impl MultiThreadBase for VmGdbStubTarget { thread_is_active: &mut dyn FnMut(Tid), ) -> Result<(), VmGdbStubError> { match GdbStubCommand::ListActiveThreads.send_and_then_wait(&self.tx)? { - Ok(GdbStubCommandResponse::ListActiveThreads(len)) => { + GdbStubCommandResponse::ListActiveThreads(len) => { for vcpu_id in 0..len { let tid = vcpu_id_to_tid(vcpu_id)?; thread_is_active(tid); @@ -168,11 +167,14 @@ impl MultiThreadBase for VmGdbStubTarget { Ok(()) } - Ok(_) => Err(VmGdbStubError::InvalidResponse), - Err(err) => { - error!(?err, "Failed to list active threads"); + GdbStubCommandResponse::Err => { + error!("Failed to handle command"); Err(VmGdbStubError::ListActiveThreadsFailed) } + _ => { + error!("Unexpected response to command"); + Err(VmGdbStubError::InvalidResponse) + } } } @@ -185,12 +187,15 @@ impl MultiThreadBase for VmGdbStubTarget { impl MultiThreadResume for VmGdbStubTarget { fn resume(&mut self) -> Result<(), Self::Error> { match GdbStubCommand::Resume.send_and_then_wait(&self.tx)? { - Ok(GdbStubCommandResponse::Resume) => Ok(()), - Ok(_) => Err(VmGdbStubError::InvalidResponse), - Err(err) => { - error!(?err, "Failed to resume"); + GdbStubCommandResponse::Resume => Ok(()), + GdbStubCommandResponse::Err => { + error!("Failed to handle command"); Err(VmGdbStubError::ResumeFailed) } + _ => { + error!("Unexpected response to command"); + Err(VmGdbStubError::InvalidResponse) + } } } diff --git a/crates/vm-vmm/src/service/monitor.rs b/crates/vm-vmm/src/service/monitor.rs index b083159..2d12541 100644 --- a/crates/vm-vmm/src/service/monitor.rs +++ b/crates/vm-vmm/src/service/monitor.rs @@ -1,142 +1,3 @@ -use std::collections::HashMap; -use std::io; -use std::sync::Arc; - -use tokio::io::AsyncWriteExt; -use tokio::net::UnixListener; -use tokio::net::UnixStream; -use vm_core::monitor::MonitorCommand; -use vm_core::monitor::MonitorError; - -const PATH: &str = "/tmp/vm.sock"; - -struct MonitorConnection { - components: Arc>>, -} - -impl MonitorConnection { - fn start(&self, mut stream: UnixStream) { - tokio::spawn({ - let components = self.components.clone(); - - async move { - loop { - stream.readable().await?; - - let mut buf = vec![0u8; 1024]; - match stream.try_read(&mut buf) { - Ok(0) => break, - Ok(n) => { - let line = match str::from_utf8(&buf[..n]) { - Ok(line) => line.trim(), - Err(err) => { - stream.write_all(format!("ERR {err}\n").as_bytes()).await?; - - continue; - } - }; - if line.is_empty() { - continue; - } - - let mut tokens = line.split_whitespace(); - let command = match tokens.next() { - Some(f) => f, - None => continue, - }; - let subcommands: Vec<&str> = tokens.collect(); - - match components.get(command) { - Some(handler) => match handler.handle_command(&subcommands).await { - Ok(resp) => { - stream.writable().await?; - - stream.write_all(resp.as_bytes()).await?; - } - Err(e) => { - stream.write_all(format!("ERR {e}\n").as_bytes()).await?; - } - }, - None => { - stream - .write_all( - format!("ERR unknown command {command}\n").as_bytes(), - ) - .await?; - } - } - } - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - continue; - } - Err(e) => { - return Err(e.into()); - } - } - } - - Ok::<(), MonitorError>(()) - } - }); - } -} - -pub struct MonitorServer { - components: Arc>>, -} - -impl MonitorServer { - pub fn start(&self) { - let components = self.components.clone(); - - tokio::spawn(async move { - let Ok(listener) = UnixListener::bind(PATH) else { - return; - }; - - loop { - let stream = match listener.accept().await { - Ok((stream, _)) => stream, - Err(_err) => { - continue; - } - }; - - let monitor_connection = MonitorConnection { - components: components.clone(), - }; - - monitor_connection.start(stream); - } - }); - } -} - -#[derive(Default)] -pub struct MonitorServerBuilder { - components: HashMap>, -} - -impl MonitorServerBuilder { - pub fn register_command_handler( - &mut self, - name: &str, - handler: Box, - ) -> Result<(), MonitorError> { - let name = name.to_string(); - - if self.components.contains_key(&name) { - return Err(MonitorError::CommandHandlerConflicat(name)); - } - - self.components.insert(name, handler); - - Ok(()) - } - - pub fn build(self) -> MonitorServer { - MonitorServer { - components: self.components.into(), - } - } -} +pub(crate) mod builder; +pub(crate) mod command; +pub(crate) mod error; diff --git a/crates/vm-vmm/src/service/monitor/builder.rs b/crates/vm-vmm/src/service/monitor/builder.rs new file mode 100644 index 0000000..ce6582c --- /dev/null +++ b/crates/vm-vmm/src/service/monitor/builder.rs @@ -0,0 +1,27 @@ +use std::collections::HashMap; + +use vm_core::monitor::MonitorCommand; +use vm_core::monitor::MonitorError; + +#[derive(Default)] +pub struct MonitorServerBuilder { + pub components: HashMap>, +} + +impl MonitorServerBuilder { + pub fn register_command_handler( + &mut self, + name: &str, + handler: Box, + ) -> Result<(), MonitorError> { + let name = name.to_string(); + + if self.components.contains_key(&name) { + return Err(MonitorError::CommandHandlerConflict(name)); + } + + self.components.insert(name, handler); + + Ok(()) + } +} diff --git a/crates/vm-vmm/src/service/monitor/command.rs b/crates/vm-vmm/src/service/monitor/command.rs new file mode 100644 index 0000000..6fecd2f --- /dev/null +++ b/crates/vm-vmm/src/service/monitor/command.rs @@ -0,0 +1,38 @@ +use tokio::sync::mpsc; +use tokio::sync::oneshot; + +use crate::service::monitor::error::MonitorServerError; +use crate::vmm::handler::VmmCommand; + +pub struct MonitorCommand(pub String); + +pub struct MonitorCommandRequest { + pub command: MonitorCommand, + pub response: oneshot::Sender, +} + +pub struct MonitorCommandResponse(pub String); + +impl MonitorCommand { + pub async fn send_and_then_wait( + self, + tx: &mpsc::Sender, + ) -> Result { + let (response_tx, response_rx) = oneshot::channel(); + + let request = VmmCommand::MonitorCommand(MonitorCommandRequest { + command: self, + response: response_tx, + }); + + if let Err(_err) = tx.send(request).await { + return Err(MonitorServerError::FailedToSendRequest); + } + + let response = response_rx + .await + .map_err(|_| MonitorServerError::FailedToReceiveResponse)?; + + Ok(response) + } +} diff --git a/crates/vm-vmm/src/service/monitor/error.rs b/crates/vm-vmm/src/service/monitor/error.rs new file mode 100644 index 0000000..a256b50 --- /dev/null +++ b/crates/vm-vmm/src/service/monitor/error.rs @@ -0,0 +1,10 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum MonitorServerError { + #[error("Failed to send request")] + FailedToSendRequest, + + #[error("Failed to receive response")] + FailedToReceiveResponse, +} diff --git a/crates/vm-vmm/src/vm.rs b/crates/vm-vmm/src/vm.rs index 657f3e3..fe05ca3 100644 --- a/crates/vm-vmm/src/vm.rs +++ b/crates/vm-vmm/src/vm.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; #[cfg(not(target_arch = "aarch64"))] use std::hint::black_box; use std::sync::Arc; @@ -24,6 +25,7 @@ use vm_core::arch::x86_64::layout::RAM_BASE; use vm_core::cpu::vcpu_manager::VcpuManager; use vm_core::device::mmio::layout::MmioLayout; use vm_core::device_manager::DeviceManager; +use vm_core::monitor::MonitorCommand; use vm_core::virtualization::hypervisor::Hypervisor; use vm_core::virtualization::vm::HypervisorVm; use vm_core::virtualization::vm::SetUserMemoryRegionFlags; @@ -38,11 +40,10 @@ use crate::device::InitDevice; use crate::error::Error; use crate::error::Result; use crate::service::gdbstub::connection::VmGdbStubConnector; -use crate::service::monitor::MonitorServer; -use crate::service::monitor::MonitorServerBuilder; +use crate::service::monitor::builder::MonitorServerBuilder; use crate::vm::config::VmConfig; use crate::vm::vm_exit_handler::VmExitHandler; -use crate::vmm::command::VmmCommand; +use crate::vmm::handler::VmmCommand; pub mod config; @@ -57,7 +58,7 @@ pub struct Vm { _irq_chip: Arc, _device_manager: Arc, gdb_stub: Option, - monitor: MonitorServer, + monitor_handlers: HashMap>, _vm_config: VmConfig, #[cfg(target_arch = "aarch64")] start_pc: u64, @@ -162,7 +163,7 @@ impl Vm { gdb_stub: vm_config .gdb_port .map(|port| VmGdbStubConnector::new(vmm_tx, port)), - monitor: monitor_server_builder.build(), + monitor_handlers: monitor_server_builder.components, _vm_config: vm_config, #[cfg(target_arch = "aarch64")] start_pc, @@ -179,11 +180,13 @@ impl Vm { self.memory_address_space.as_ref() } + pub fn monitor_handlers(&self) -> &HashMap> { + &self.monitor_handlers + } + pub async fn boot(&mut self) -> Result<()> { let mut stop_on_boot = false; - self.monitor.start(); - if let Some(gdb_stub) = &self.gdb_stub { stop_on_boot = true; gdb_stub.wait_for_connection()?; diff --git a/crates/vm-vmm/src/vmm.rs b/crates/vm-vmm/src/vmm.rs index 35d5d85..5766e95 100644 --- a/crates/vm-vmm/src/vmm.rs +++ b/crates/vm-vmm/src/vmm.rs @@ -3,16 +3,17 @@ use std::sync::Arc; use tokio::sync::mpsc; use tokio::sync::mpsc::Receiver; use tokio::sync::mpsc::Sender; -use tracing::error; use vm_core::virtualization::hypervisor::Hypervisor; use crate::error::Error; use crate::error::Result; use crate::vm::Vm; use crate::vm::config::VmConfig; -use crate::vmm::command::VmmCommand; +use crate::vmm::handler::VmmCommand; -pub mod command; +pub mod handler; + +mod service; pub struct Vmm { hypervisor: Box, @@ -33,6 +34,14 @@ impl Vmm { } } + pub fn try_get_vm(&self) -> Result<&Vm> { + self.vm.as_ref().ok_or(Error::VmNotExists) + } + + pub fn try_get_vm_mut(&mut self) -> Result<&mut Vm> { + self.vm.as_mut().ok_or(Error::VmNotExists) + } + pub async fn create_vm_from_config(&mut self, vm_config: VmConfig) -> Result<()> { if self.vm.is_some() { return Err(Error::VmAlreadyExists); @@ -49,9 +58,7 @@ impl Vmm { pub async fn run(&mut self) -> Result<()> { self.vm.as_mut().ok_or(Error::VmNotExists)?.boot().await?; - if let Err(err) = self.run_monitor().await { - error!(?err, "monitor error"); - } + self.run_monitor().await; Ok(()) } diff --git a/crates/vm-vmm/src/vmm/handler.rs b/crates/vm-vmm/src/vmm/handler.rs new file mode 100644 index 0000000..c0ce277 --- /dev/null +++ b/crates/vm-vmm/src/vmm/handler.rs @@ -0,0 +1,31 @@ +use thiserror::Error; +use vm_core::cpu::error::CpuError; + +use crate::service::gdbstub::command::GdbStubCommandRequest; +use crate::service::monitor::command::MonitorCommandRequest; + +pub(crate) mod gdbstub; +pub(crate) mod monitor; + +pub enum VmmCommand { + GdbCommand(GdbStubCommandRequest), + MonitorCommand(MonitorCommandRequest), +} + +#[derive(Error, Debug)] +pub enum CommandError { + #[error("vCPU with ID {vcpu_id} does not exist")] + VcpuNotExists { vcpu_id: usize }, + + #[error("Vm error: {0}")] + VmError(#[from] crate::error::Error), + + #[error("Cpu error: {0}")] + CpuError(#[from] CpuError), + + #[error("Failed to send response to command request")] + FailedToSendResponse, + + #[error("Invalid Command")] + InvalidCommand, +} diff --git a/crates/vm-vmm/src/vmm/command.rs b/crates/vm-vmm/src/vmm/handler/gdbstub.rs similarity index 66% rename from crates/vm-vmm/src/vmm/command.rs rename to crates/vm-vmm/src/vmm/handler/gdbstub.rs index 25271dc..29970c2 100644 --- a/crates/vm-vmm/src/vmm/command.rs +++ b/crates/vm-vmm/src/vmm/handler/gdbstub.rs @@ -1,47 +1,12 @@ -use thiserror::Error; -use tracing::error; use tracing::trace; -use vm_core::cpu::error::CpuError; use crate::service::gdbstub::command::GdbStubCommand; -use crate::service::gdbstub::command::GdbStubCommandError; -use crate::service::gdbstub::command::GdbStubCommandRequest; use crate::service::gdbstub::command::GdbStubCommandResponse; -use crate::vm::Vm; use crate::vmm::Vmm; - -pub enum VmmCommand { - GdbCommand(GdbStubCommandRequest), -} - -#[derive(Error, Debug)] -pub enum CommandError { - #[error("VM instance does not exist")] - VmNotExists, - - #[error("vCPU with ID {vcpu_id} does not exist")] - VcpuNotExists { vcpu_id: usize }, - - #[error("Vm error: {0}")] - VmError(#[from] crate::error::Error), - - #[error("Cpu error: {0}")] - CpuError(#[from] CpuError), - - #[error("Failed to send response to command request")] - FailedToSendResponse, -} +use crate::vmm::handler::CommandError; impl Vmm { - fn try_get_vm(&self) -> Result<&Vm, CommandError> { - self.vm.as_ref().ok_or(CommandError::VmNotExists) - } - - fn try_get_vm_mut(&mut self) -> Result<&mut Vm, CommandError> { - self.vm.as_mut().ok_or(CommandError::VmNotExists) - } - - async fn handle_gdbstub_command( + pub async fn handle_gdbstub_command( &mut self, cmd: GdbStubCommand, ) -> Result { @@ -129,32 +94,4 @@ impl Vmm { } } } - - async fn handle_command(&mut self, command: VmmCommand) -> Result<(), CommandError> { - match command { - VmmCommand::GdbCommand(cmd) => { - let r = self - .handle_gdbstub_command(cmd.command) - .await - .inspect_err(|err| { - error!(?err, "Failed to handle GDB stub command"); - }) - .map_err(|_| GdbStubCommandError::Err); - - cmd.response - .send(r) - .map_err(|_| CommandError::FailedToSendResponse)?; - } - } - - Ok(()) - } - - pub async fn run_monitor(&mut self) -> Result<(), CommandError> { - while let Some(command) = self.command_rx.recv().await { - self.handle_command(command).await?; - } - - Ok(()) - } } diff --git a/crates/vm-vmm/src/vmm/handler/monitor.rs b/crates/vm-vmm/src/vmm/handler/monitor.rs new file mode 100644 index 0000000..b1c2297 --- /dev/null +++ b/crates/vm-vmm/src/vmm/handler/monitor.rs @@ -0,0 +1,29 @@ +use crate::service::monitor::command::MonitorCommand; +use crate::service::monitor::command::MonitorCommandResponse; +use crate::vmm::Vmm; +use crate::vmm::handler::CommandError; + +impl Vmm { + pub async fn handle_monitor_client_command( + &mut self, + cmd: MonitorCommand, + ) -> Result { + let mut tokens = cmd.0.split_whitespace(); + + let Some(command) = tokens.next() else { + return Err(CommandError::InvalidCommand); + }; + + let subcommands: Vec<&str> = tokens.collect(); + + let vm = self.try_get_vm()?; + let Some(handler) = vm.monitor_handlers().get(command) else { + return Err(CommandError::InvalidCommand); + }; + + match handler.handle_command(&subcommands).await { + Ok(resp) => Ok(MonitorCommandResponse(resp)), + Err(err) => Ok(MonitorCommandResponse(err.to_string())), + } + } +} diff --git a/crates/vm-vmm/src/vmm/service.rs b/crates/vm-vmm/src/vmm/service.rs new file mode 100644 index 0000000..570aa89 --- /dev/null +++ b/crates/vm-vmm/src/vmm/service.rs @@ -0,0 +1,40 @@ +use tracing::error; + +use crate::vmm::Vmm; +use crate::vmm::handler::CommandError; +use crate::vmm::handler::VmmCommand; + +mod monitor; + +impl Vmm { + pub async fn run_monitor(&mut self) { + self.listen_for_monitor_client(); + + while let Some(command) = self.command_rx.recv().await { + if let Err(err) = self.handle_command(command).await { + error!(?err, "Failed to handle command"); + } + } + } + + async fn handle_command(&mut self, command: VmmCommand) -> Result<(), CommandError> { + match command { + VmmCommand::GdbCommand(cmd) => { + let r = self.handle_gdbstub_command(cmd.command).await?; + + cmd.response + .send(r) + .map_err(|_| CommandError::FailedToSendResponse)?; + } + VmmCommand::MonitorCommand(cmd) => { + let r = self.handle_monitor_client_command(cmd.command).await?; + + cmd.response + .send(r) + .map_err(|_| CommandError::FailedToSendResponse)?; + } + } + + Ok(()) + } +} diff --git a/crates/vm-vmm/src/vmm/service/monitor.rs b/crates/vm-vmm/src/vmm/service/monitor.rs new file mode 100644 index 0000000..2c6f502 --- /dev/null +++ b/crates/vm-vmm/src/vmm/service/monitor.rs @@ -0,0 +1,95 @@ +use std::io; +use std::sync::Arc; + +use tokio::io::AsyncWriteExt; +use tokio::net::UnixListener; +use tokio::net::UnixStream; +use tokio::sync::mpsc::Sender; +use vm_core::monitor::MonitorError; + +use crate::service::monitor::command::MonitorCommand; +use crate::vmm::Vmm; +use crate::vmm::handler::VmmCommand; + +const PATH: &str = "/tmp/vm.sock"; + +struct MonitorConnection { + tx: Arc>, +} + +impl MonitorConnection { + fn start(&self, mut stream: UnixStream) { + let tx = self.tx.clone(); + + tokio::spawn({ + async move { + loop { + stream.readable().await?; + + let mut buf = vec![0u8; 1024]; + match stream.try_read(&mut buf) { + Ok(0) => break, + Ok(n) => { + let line = match str::from_utf8(&buf[..n]) { + Ok(line) => line.trim(), + Err(err) => { + stream.write_all(format!("ERR {err}\n").as_bytes()).await?; + + continue; + } + }; + if line.is_empty() { + continue; + } + + let cmd = MonitorCommand(line.to_string()); + match cmd.send_and_then_wait(&tx).await { + Ok(resp) => { + stream.writable().await?; + + stream.write_all(resp.0.as_bytes()).await?; + } + Err(err) => { + stream.write_all(format!("ERR {err}\n").as_bytes()).await?; + } + } + } + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + continue; + } + Err(e) => { + return Err(e.into()); + } + } + } + + Ok::<(), MonitorError>(()) + } + }); + } +} + +impl Vmm { + pub fn listen_for_monitor_client(&self) { + let tx = self.command_tx.clone(); + + tokio::spawn(async move { + let Ok(listener) = UnixListener::bind(PATH) else { + return; + }; + + loop { + let stream = match listener.accept().await { + Ok((stream, _)) => stream, + Err(_err) => { + continue; + } + }; + + let monitor_connection = MonitorConnection { tx: tx.clone() }; + + monitor_connection.start(stream); + } + }); + } +}