diff --git a/Cargo.lock b/Cargo.lock index 0597894..372c736 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,6 +85,17 @@ dependencies = [ "libc", ] +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -244,6 +255,12 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + [[package]] name = "kvm-bindings" version = "0.14.0" @@ -526,6 +543,49 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -790,14 +850,17 @@ dependencies = [ "anyhow", "applevisor", "applevisor-sys", + "async-trait", "bitflags 2.10.0", "gdbstub", "kvm-bindings", "kvm-ioctls", "memmap2", + "serde_json", "static_assertions", "strum_macros", "thiserror", + "tokio", "tracing", "vm-fdt", "vm-mm", @@ -808,9 +871,12 @@ name = "vm-device" version = "0.1.0" dependencies = [ "anyhow", + "async-trait", "bitflags 2.10.0", "lazy_static", "maplit", + "serde", + "serde_json", "strum_macros", "thiserror", "tokio", @@ -839,6 +905,7 @@ dependencies = [ "lazy_static", "maplit", "thiserror", + "tokio", "tracing", "vm-bootloader", "vm-core", @@ -1091,3 +1158,9 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index 899823d..1d39201 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,8 @@ lazy_static = "1.5.0" maplit = "1.0.2" memmap2 = "0.9.9" rustyline = "17.0.2" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.149" static_assertions = "1.1.0" strum_macros = "0.27.2" termios = "0.3.3" diff --git a/crates/vm-core/Cargo.toml b/crates/vm-core/Cargo.toml index 29a721a..9b84f52 100644 --- a/crates/vm-core/Cargo.toml +++ b/crates/vm-core/Cargo.toml @@ -7,11 +7,14 @@ edition = "2024" applevisor = { workspace = true, optional = true } applevisor-sys = { workspace = true, optional = true } anyhow = { workspace = true } +async-trait = { workspace = true } bitflags = { workspace = true } kvm-bindings = { workspace = true, optional = true } kvm-ioctls = { workspace = true, optional = true } memmap2 = { workspace = true } +serde_json = { workspace = true } thiserror = { workspace = true } +tokio = { workspace = true } tracing = { workspace = true } vm-fdt = { workspace = true } vm-mm = { workspace = true } diff --git a/crates/vm-core/src/lib.rs b/crates/vm-core/src/lib.rs index 5a964a9..b5ddb4b 100644 --- a/crates/vm-core/src/lib.rs +++ b/crates/vm-core/src/lib.rs @@ -4,4 +4,5 @@ pub mod arch; pub mod debug; pub mod device; pub mod error; +pub mod monitor; pub mod virt; diff --git a/crates/vm-core/src/monitor.rs b/crates/vm-core/src/monitor.rs new file mode 100644 index 0000000..2a27672 --- /dev/null +++ b/crates/vm-core/src/monitor.rs @@ -0,0 +1,167 @@ +use std::collections::HashMap; +use std::io; +use std::sync::Arc; + +use async_trait::async_trait; +use tokio::io::AsyncWriteExt; +use tokio::net::UnixListener; +use tokio::net::UnixStream; + +const PATH: &str = "/tmp/vm.sock"; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("{0}")] + Stream(#[from] std::io::Error), + + #[error("{0}")] + CommandHandlerConflicat(String), + + #[error("{0}")] + Serde(#[from] serde_json::Error), + + #[error("Unknown cmd {0}")] + UnknownCmd(String), + + #[error("unknown subcommand {0:?}")] + UnknownSubcommand(Vec), + + #[error("{0}")] + Error(String), +} + +#[async_trait] +pub trait MonitorCommand: Send + Sync { + async fn handle_command(&self, subcommands: &[&str]) -> Result; +} + +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::<(), Error>(()) + } + }); + } +} + +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<(), Error> { + let name = name.to_string(); + + if self.components.contains_key(&name) { + return Err(Error::CommandHandlerConflicat(name)); + } + + self.components.insert(name, handler); + + Ok(()) + } + + pub fn build(self) -> MonitorServer { + MonitorServer { + components: self.components.into(), + } + } +} diff --git a/crates/vm-device/Cargo.toml b/crates/vm-device/Cargo.toml index 27e8e2c..4330386 100644 --- a/crates/vm-device/Cargo.toml +++ b/crates/vm-device/Cargo.toml @@ -5,9 +5,12 @@ edition = "2024" [dependencies] anyhow.workspace = true +async-trait.workspace = true bitflags.workspace = true lazy_static.workspace = true maplit.workspace = true +serde.workspace = true +serde_json.workspace = true strum_macros.workspace = true thiserror.workspace = true tokio.workspace = true diff --git a/crates/vm-device/src/device/virtio/virtio_balloon_traditional.rs b/crates/vm-device/src/device/virtio/virtio_balloon_traditional.rs index 1ea301e..56fa6ba 100644 --- a/crates/vm-device/src/device/virtio/virtio_balloon_traditional.rs +++ b/crates/vm-device/src/device/virtio/virtio_balloon_traditional.rs @@ -1,202 +1,2 @@ -use std::collections::HashSet; -use std::sync::Arc; -use std::sync::Mutex; - -use tokio::sync::Notify; -use vm_core::arch::irq::InterruptController; -use vm_mm::manager::MemoryAddressSpace; -use vm_mm::memory_container::MemoryContainer; -use vm_virtio::device::VirtioDevice; -use vm_virtio::device::transport::TransportContext; -use vm_virtio::device::virtqueue::VirtqueueHandler; -use vm_virtio::device::virtqueue::VirtqueueHandlerFn; -use vm_virtio::result::Result; -use vm_virtio::transport::VirtioDev; -use vm_virtio::transport::mmio::VirtioMmioTransport; -use vm_virtio::types::device::balloon_tranditional::VirtioBalloonTranditionalConfig; -use vm_virtio::types::device::balloon_tranditional::VirtioBalloonTranditionalVirtqueue; -use vm_virtio::types::device_features::VIRTIO_F_VERSION_1; -use vm_virtio::types::device_id::DeviceId; -use zerocopy::IntoBytes; - -const INFLATEQ_QUEUE_SIZE_MAX: u32 = 512; -const DEFLATEQ_QUEUE_SIZE_MAX: u32 = 512; - -fn inflateq_handler() -> VirtqueueHandlerFn> -where - C: MemoryContainer, -{ - Box::new(|mm, dev, desc_ring, desc_id| { - let desc = desc_ring.get(desc_id); - let len = desc.len; - assert!(len.is_multiple_of(4)); - - let array = desc.addr(mm).unwrap().as_ptr() as *const u32; - - for i in 0..(len / 4) { - let pfn = unsafe { *array.add(i as usize) }; - assert!(dev.device.balloon.insert(pfn)); - let gpa = (pfn as u64) << 12; - let _hva = mm.gpa_to_hva(gpa).unwrap(); - // TODO: mmap - } - - len - }) -} - -fn deflateq_handler() -> VirtqueueHandlerFn> -where - C: MemoryContainer, -{ - Box::new(|mm, dev, desc_ring, desc_id| { - let desc = desc_ring.get(desc_id); - let len = desc.len; - assert!(len.is_multiple_of(4)); - - let array = desc.addr(mm).unwrap().as_ptr() as *const u32; - - for i in 0..(len / 4) { - let pfn = unsafe { *array.add(i as usize) }; - assert!(dev.device.balloon.remove(&pfn)); - let gpa = (pfn as u64) << 12; - let _hva = mm.gpa_to_hva(gpa).unwrap(); - // TODO: mmap - } - - len - }) -} - -pub struct VirtioBalloonTranditional -where - C: MemoryContainer, -{ - irq: u32, - irq_chip: Arc, - mm: Arc>, - cfg: VirtioBalloonTranditionalConfig, - balloon: HashSet, -} - -impl VirtioBalloonTranditional -where - C: MemoryContainer, -{ - pub fn new( - irq: u32, - irq_chip: Arc, - mm: Arc>, - ) -> Self { - VirtioBalloonTranditional { - irq, - irq_chip, - mm, - cfg: VirtioBalloonTranditionalConfig::default(), - balloon: Default::default(), - } - } - - pub fn set_num_pages(&mut self, num_pages: u32) { - self.cfg.num_pages = num_pages; - - todo!("notify config generation changes"); - } -} - -impl VirtioDevice for VirtioBalloonTranditional -where - C: MemoryContainer, -{ - const NAME: &str = "virtio-balloon-tranditional"; - const DEVICE_ID: u16 = DeviceId::Balloon as u16; - const DEVICE_FEATURES: u64 = (1 << VIRTIO_F_VERSION_1); - - fn virtqueues_size_max(&self) -> Vec> { - vec![Some(INFLATEQ_QUEUE_SIZE_MAX), Some(DEFLATEQ_QUEUE_SIZE_MAX)] - } - - fn irq(&self) -> Option { - Some(self.irq) - } - - fn trigger_irq(&self, active: bool) { - self.irq_chip.trigger_irq(32 + self.irq, active); - } - - fn reset(&mut self) {} - - fn virtqueue_handler( - &self, - queue: usize, - notifier: Arc, - dev: Arc>>, - ) -> Option> { - match VirtioBalloonTranditionalVirtqueue::from_repr(queue) { - Some(virtq) => match virtq { - VirtioBalloonTranditionalVirtqueue::Inflateq => Some(VirtqueueHandler { - queue_sel: VirtioBalloonTranditionalVirtqueue::Inflateq as usize, - notifier, - dev, - mm: self.mm.clone(), - irq_chip: self.irq_chip.clone(), - irq_line: self.irq + 32, - handle_desc: inflateq_handler(), - }), - VirtioBalloonTranditionalVirtqueue::Defalteq => Some(VirtqueueHandler { - queue_sel: VirtioBalloonTranditionalVirtqueue::Defalteq as usize, - notifier, - dev, - mm: self.mm.clone(), - irq_chip: self.irq_chip.clone(), - irq_line: self.irq + 32, - handle_desc: deflateq_handler(), - }), - VirtioBalloonTranditionalVirtqueue::Statsq => None, - VirtioBalloonTranditionalVirtqueue::FreePageVq => None, - VirtioBalloonTranditionalVirtqueue::ReportingVq => None, - }, - None => None, - } - } - - fn read_config(&self, offset: usize, buf: &mut [u8]) -> Result<()> { - buf.copy_from_slice(&self.cfg.as_bytes()[offset..offset + buf.len()]); - Ok(()) - } - - fn write_config(&mut self, offset: usize, buf: &[u8]) -> Result<()> { - self.cfg.as_mut_bytes()[offset..offset + buf.len()].copy_from_slice(buf); - Ok(()) - } - - fn transport_context(&self) -> &dyn TransportContext { - todo!() - } - - fn transport_context_mit(&mut self) -> &mut dyn TransportContext { - todo!() - } -} - -pub trait VirtioBalloonApi { - fn update_num_pages(&mut self, num_pages: u32); -} - -pub type VirtioBalloonDev = VirtioDev>; - -impl VirtioBalloonApi for VirtioBalloonDev -where - C: MemoryContainer, -{ - fn update_num_pages(&mut self, num_pages: u32) { - if self.device.cfg.num_pages == num_pages { - return; - } - - self.device.cfg.num_pages = num_pages; - self.update_config_generation_and_notify(); - } -} - -pub type VirtioMmioBalloonDevice = VirtioMmioTransport>; +pub mod device; +pub mod monitor; diff --git a/crates/vm-device/src/device/virtio/virtio_balloon_traditional/device.rs b/crates/vm-device/src/device/virtio/virtio_balloon_traditional/device.rs new file mode 100644 index 0000000..09a3527 --- /dev/null +++ b/crates/vm-device/src/device/virtio/virtio_balloon_traditional/device.rs @@ -0,0 +1,196 @@ +use std::collections::HashSet; +use std::sync::Arc; +use std::sync::Mutex; + +use tokio::sync::Notify; +use vm_core::arch::irq::InterruptController; +use vm_mm::manager::MemoryAddressSpace; +use vm_mm::memory_container::MemoryContainer; +use vm_virtio::device::VirtioDevice; +use vm_virtio::device::transport::TransportContext; +use vm_virtio::device::virtqueue::VirtqueueHandler; +use vm_virtio::device::virtqueue::VirtqueueHandlerFn; +use vm_virtio::result::Result; +use vm_virtio::transport::VirtioDev; +use vm_virtio::transport::mmio::VirtioMmioTransport; +use vm_virtio::types::device::balloon_tranditional::VirtioBalloonTranditionalConfig; +use vm_virtio::types::device::balloon_tranditional::VirtioBalloonTranditionalVirtqueue; +use vm_virtio::types::device_features::VIRTIO_F_VERSION_1; +use vm_virtio::types::device_id::DeviceId; +use zerocopy::IntoBytes; + +pub trait VirtioBalloonApi { + fn update_num_pages(&mut self, num_pages: u32); +} + +const INFLATEQ_QUEUE_SIZE_MAX: u32 = 512; +const DEFLATEQ_QUEUE_SIZE_MAX: u32 = 512; + +fn inflateq_handler() -> VirtqueueHandlerFn> +where + C: MemoryContainer, +{ + Box::new(|mm, dev, desc_ring, desc_id| { + let desc = desc_ring.get(desc_id); + let len = desc.len; + assert!(len.is_multiple_of(4)); + + let array = desc.addr(mm).unwrap().as_ptr() as *const u32; + + for i in 0..(len / 4) { + let pfn = unsafe { *array.add(i as usize) }; + assert!(dev.device.balloon.insert(pfn)); + let gpa = (pfn as u64) << 12; + let _hva = mm.gpa_to_hva(gpa).unwrap(); + // TODO: mmap + } + + len + }) +} + +fn deflateq_handler() -> VirtqueueHandlerFn> +where + C: MemoryContainer, +{ + Box::new(|mm, dev, desc_ring, desc_id| { + let desc = desc_ring.get(desc_id); + let len = desc.len; + assert!(len.is_multiple_of(4)); + + let array = desc.addr(mm).unwrap().as_ptr() as *const u32; + + for i in 0..(len / 4) { + let pfn = unsafe { *array.add(i as usize) }; + assert!(dev.device.balloon.remove(&pfn)); + let gpa = (pfn as u64) << 12; + let _hva = mm.gpa_to_hva(gpa).unwrap(); + // TODO: mmap + } + + len + }) +} + +pub struct VirtioBalloonTranditional +where + C: MemoryContainer, +{ + pub(crate) irq: u32, + pub(crate) irq_chip: Arc, + pub(crate) mm: Arc>, + pub(crate) cfg: VirtioBalloonTranditionalConfig, + pub(crate) balloon: HashSet, +} + +impl VirtioBalloonTranditional +where + C: MemoryContainer, +{ + pub fn new( + irq: u32, + irq_chip: Arc, + mm: Arc>, + ) -> Self { + VirtioBalloonTranditional { + irq, + irq_chip, + mm, + cfg: VirtioBalloonTranditionalConfig::default(), + balloon: Default::default(), + } + } +} + +impl VirtioDevice for VirtioBalloonTranditional +where + C: MemoryContainer, +{ + const NAME: &str = "virtio-balloon-tranditional"; + const DEVICE_ID: u16 = DeviceId::Balloon as u16; + const DEVICE_FEATURES: u64 = (1 << VIRTIO_F_VERSION_1); + + fn virtqueues_size_max(&self) -> Vec> { + vec![Some(INFLATEQ_QUEUE_SIZE_MAX), Some(DEFLATEQ_QUEUE_SIZE_MAX)] + } + + fn irq(&self) -> Option { + Some(self.irq) + } + + fn trigger_irq(&self, active: bool) { + self.irq_chip.trigger_irq(32 + self.irq, active); + } + + fn reset(&mut self) {} + + fn virtqueue_handler( + &self, + queue: usize, + notifier: Arc, + dev: Arc>>, + ) -> Option> { + match VirtioBalloonTranditionalVirtqueue::from_repr(queue) { + Some(virtq) => match virtq { + VirtioBalloonTranditionalVirtqueue::Inflateq => Some(VirtqueueHandler { + queue_sel: VirtioBalloonTranditionalVirtqueue::Inflateq as usize, + notifier, + dev, + mm: self.mm.clone(), + irq_chip: self.irq_chip.clone(), + irq_line: self.irq + 32, + handle_desc: inflateq_handler(), + }), + VirtioBalloonTranditionalVirtqueue::Defalteq => Some(VirtqueueHandler { + queue_sel: VirtioBalloonTranditionalVirtqueue::Defalteq as usize, + notifier, + dev, + mm: self.mm.clone(), + irq_chip: self.irq_chip.clone(), + irq_line: self.irq + 32, + handle_desc: deflateq_handler(), + }), + VirtioBalloonTranditionalVirtqueue::Statsq => None, + VirtioBalloonTranditionalVirtqueue::FreePageVq => None, + VirtioBalloonTranditionalVirtqueue::ReportingVq => None, + }, + None => None, + } + } + + fn read_config(&self, offset: usize, buf: &mut [u8]) -> Result<()> { + buf.copy_from_slice(&self.cfg.as_bytes()[offset..offset + buf.len()]); + Ok(()) + } + + fn write_config(&mut self, offset: usize, buf: &[u8]) -> Result<()> { + self.cfg.as_mut_bytes()[offset..offset + buf.len()].copy_from_slice(buf); + Ok(()) + } + + fn transport_context(&self) -> &dyn TransportContext { + todo!() + } + + fn transport_context_mit(&mut self) -> &mut dyn TransportContext { + todo!() + } +} + +pub type VirtioBalloonDev = VirtioDev>; + +pub type VirtioMmioBalloonDevice = VirtioMmioTransport>; + +impl VirtioBalloonApi for VirtioBalloonDev +where + C: MemoryContainer, +{ + fn update_num_pages(&mut self, num_pages: u32) { + if self.device.cfg.num_pages == num_pages { + return; + } + + self.device.cfg.num_pages = num_pages; + self.update_config_generation_and_notify(); + } +} diff --git a/crates/vm-device/src/device/virtio/virtio_balloon_traditional/monitor.rs b/crates/vm-device/src/device/virtio/virtio_balloon_traditional/monitor.rs new file mode 100644 index 0000000..609f89c --- /dev/null +++ b/crates/vm-device/src/device/virtio/virtio_balloon_traditional/monitor.rs @@ -0,0 +1,65 @@ +use std::sync::Arc; +use std::sync::Mutex; + +use async_trait::async_trait; +use serde::Serialize; +use vm_core::monitor::Error; +use vm_core::monitor::MonitorCommand; +use vm_mm::memory_container::MemoryContainer; +use vm_virtio::transport::VirtioDev; + +use crate::device::virtio::virtio_balloon_traditional::device::VirtioBalloonApi; +use crate::device::virtio::virtio_balloon_traditional::device::VirtioBalloonTranditional; + +#[derive(Serialize)] +pub struct BalloonInfo { + actual: u32, + num_pages: u32, +} + +pub struct VirtioBalloonMonitor +where + C: MemoryContainer, +{ + device: Arc>>>, +} + +impl VirtioBalloonMonitor +where + C: MemoryContainer, +{ + pub fn new(device: Arc>>>) -> Self { + VirtioBalloonMonitor { device } + } +} + +#[async_trait] +impl MonitorCommand for VirtioBalloonMonitor +where + C: MemoryContainer, +{ + async fn handle_command(&self, subcommands: &[&str]) -> Result { + match *subcommands { + ["info"] => { + let dev = self.device.lock().unwrap(); + Ok(serde_json::to_string_pretty(&BalloonInfo { + actual: dev.device.cfg.actual, + num_pages: dev.device.cfg.num_pages, + })?) + } + ["update_num_pages", num_pages] => { + let num_pages = num_pages.parse().map_err(|_err| { + Error::Error(format!("failed to parse num_pages: {num_pages}")) + })?; + + let mut dev = self.device.lock().unwrap(); + dev.update_num_pages(num_pages); + + Ok(num_pages.to_string()) + } + _ => Err(Error::UnknownSubcommand( + subcommands.iter().map(|s| s.to_string()).collect(), + )), + } + } +} diff --git a/crates/vm-machine/Cargo.toml b/crates/vm-machine/Cargo.toml index 0aa17d8..b2411a1 100644 --- a/crates/vm-machine/Cargo.toml +++ b/crates/vm-machine/Cargo.toml @@ -10,6 +10,7 @@ kvm-ioctls = { workspace = true, optional = true } lazy_static = { workspace = true } maplit = { workspace = true } thiserror = { workspace = true } +tokio = { workspace = true } tracing = { workspace = true } vm-bootloader = { workspace = true } vm-core = { workspace = true } diff --git a/crates/vm-machine/src/device.rs b/crates/vm-machine/src/device.rs index 27e3058..9dc4bbe 100644 --- a/crates/vm-machine/src/device.rs +++ b/crates/vm-machine/src/device.rs @@ -1,14 +1,13 @@ use std::sync::Arc; -use std::thread::sleep; -use std::time::Duration; use vm_core::arch::irq::InterruptController; use vm_core::device::device_manager::DeviceManager; use vm_core::device::mmio::MmioRange; +use vm_core::monitor::MonitorServerBuilder; use vm_device::device::Device; -use vm_device::device::virtio::virtio_balloon_traditional::VirtioBalloonApi; -use vm_device::device::virtio::virtio_balloon_traditional::VirtioBalloonTranditional; -use vm_device::device::virtio::virtio_balloon_traditional::VirtioMmioBalloonDevice; +use vm_device::device::virtio::virtio_balloon_traditional::device::VirtioBalloonTranditional; +use vm_device::device::virtio::virtio_balloon_traditional::device::VirtioMmioBalloonDevice; +use vm_device::device::virtio::virtio_balloon_traditional::monitor::VirtioBalloonMonitor; use vm_device::device::virtio::virtio_blk::VirtioBlkDevice; use vm_device::device::virtio::virtio_blk::VirtioMmioBlkDevice; use vm_mm::manager::MemoryAddressSpace; @@ -22,6 +21,7 @@ use crate::error::Error; pub trait InitDevice { fn init_devices( &mut self, + monitor_server_builder: &mut MonitorServerBuilder, mm: Arc>, devices: Vec, irq_chip: Arc, @@ -33,6 +33,7 @@ pub trait InitDevice { impl InitDevice for DeviceManager { fn init_devices( &mut self, + monitor_server_builder: &mut MonitorServerBuilder, mm: Arc>, devices: Vec, irq_chip: Arc, @@ -97,31 +98,9 @@ impl InitDevice for DeviceManager { mm.clone(), )); - if false { - std::thread::spawn({ - let dev = dev.clone(); - move || { - let mut i = 0; - loop { - i += 1; - - { - sleep(Duration::from_secs(5)); - - let mut dev = dev.lock().unwrap(); - dev.update_num_pages(i); - } - - { - sleep(Duration::from_secs(5)); - - let mut dev = dev.lock().unwrap(); - dev.update_num_pages(0); - } - } - } - }); - } + let monitor = VirtioBalloonMonitor::new(dev.clone()); + monitor_server_builder + .register_command_handler("balloon", Box::new(monitor))?; // TODO: use mmio allocator? let virtio_mmio_balloon = VirtioMmioBalloonDevice::new( diff --git a/crates/vm-machine/src/error.rs b/crates/vm-machine/src/error.rs index 0041345..3f766c1 100644 --- a/crates/vm-machine/src/error.rs +++ b/crates/vm-machine/src/error.rs @@ -23,6 +23,9 @@ pub enum Error { #[error("gdb_stub failed, error: {0}")] GdbStub(String), + + #[error("monitor error: {0}")] + Monitor(#[from] vm_core::monitor::Error), } pub type Result = core::result::Result; diff --git a/crates/vm-machine/src/vm.rs b/crates/vm-machine/src/vm.rs index 6989ef6..032e097 100644 --- a/crates/vm-machine/src/vm.rs +++ b/crates/vm-machine/src/vm.rs @@ -4,6 +4,7 @@ use vm_bootloader::boot_loader::BootLoader; use vm_core::arch::irq::InterruptController; use vm_core::debug::gdbstub::GdbStub; use vm_core::device::device_manager::DeviceManager; +use vm_core::monitor::MonitorServer; use vm_core::virt::Virt; use vm_mm::manager::MemoryAddressSpace; @@ -16,6 +17,7 @@ pub struct Vm { pub(crate) irq_chip: Arc, pub(crate) device_manager: Arc, pub(crate) gdb_stub: Option, + pub(crate) monitor: MonitorServer, } impl Vm @@ -30,6 +32,8 @@ where self.device_manager.mmio_devices(), )?; + self.monitor.start(); + if let Some(gdb_stub) = &self.gdb_stub { gdb_stub .wait_for_connection() diff --git a/crates/vm-machine/src/vm_builder.rs b/crates/vm-machine/src/vm_builder.rs index 46ab716..5f57340 100644 --- a/crates/vm-machine/src/vm_builder.rs +++ b/crates/vm-machine/src/vm_builder.rs @@ -4,6 +4,7 @@ use vm_core::arch::layout::MemoryLayout; use vm_core::debug::gdbstub::GdbStub; use vm_core::device::device_manager::DeviceManager; use vm_core::device::mmio::MmioLayout; +use vm_core::monitor::MonitorServerBuilder; use vm_core::virt::Virt; use vm_device::device::Device; use vm_mm::manager::MemoryAddressSpace; @@ -38,6 +39,8 @@ impl VmBuilder { where V: Virt, { + let mut monitor_server_builder = MonitorServerBuilder::default(); + let mut virt = V::new(self.vcpus)?; let mut memory_regions = MemoryAddressSpace::default(); @@ -54,7 +57,12 @@ impl VmBuilder { }; let mut device_manager = DeviceManager::new(mmio_layout); - device_manager.init_devices(memory.clone(), self.devices, irq_chip.clone())?; + device_manager.init_devices( + &mut monitor_server_builder, + memory.clone(), + self.devices, + irq_chip.clone(), + )?; let vm = Vm { memory, @@ -62,6 +70,7 @@ impl VmBuilder { irq_chip, device_manager: Arc::new(device_manager), gdb_stub: self.gdb_port.map(GdbStub::new), + monitor: monitor_server_builder.build(), }; Ok(vm) diff --git a/crates/vm-monitor/src/main.rs b/crates/vm-monitor/src/main.rs index a35400f..8698742 100644 --- a/crates/vm-monitor/src/main.rs +++ b/crates/vm-monitor/src/main.rs @@ -1,9 +1,11 @@ +#![deny(warnings)] + use rustyline::DefaultEditor; use rustyline::error::ReadlineError; use tokio::io::AsyncReadExt; use tokio::net::UnixStream; -const PATH: &'static str = "/tmp/vm.sock"; +const PATH: &str = "/tmp/vm.sock"; #[tokio::main] async fn main() -> anyhow::Result<()> { @@ -27,8 +29,10 @@ async fn main() -> anyhow::Result<()> { stream.try_write(line.as_bytes())?; stream.readable().await?; - let mut buf = vec![0u8; 1024]; + + let mut buf = vec![]; let n = stream.read_buf(&mut buf).await?; + println!("{}", str::from_utf8(&buf[..n])?); } Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => break, diff --git a/crates/vm-virtio/src/transport/mmio.rs b/crates/vm-virtio/src/transport/mmio.rs index 63155ef..8004e63 100644 --- a/crates/vm-virtio/src/transport/mmio.rs +++ b/crates/vm-virtio/src/transport/mmio.rs @@ -36,6 +36,10 @@ where pub fn new(dev: Arc>>, mmio_range: MmioRange) -> Self { VirtioMmioTransport { mmio_range, dev } } + + pub fn dev(&self) -> Arc>> { + self.dev.clone() + } } impl Device for VirtioMmioTransport