-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add no console option #40
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,8 @@ | |
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause | ||
|
||
use std::convert::TryInto; | ||
use std::io; | ||
use std::io::Write; | ||
use std::os::unix::net::UnixStream; | ||
use std::sync::{Arc, Mutex}; | ||
use std::{result, u64}; | ||
|
||
|
@@ -11,7 +12,6 @@ use kvm_ioctls::{VcpuExit, VcpuFd, VmFd}; | |
use vm_device::bus::MmioAddress; | ||
use vm_device::device_manager::{IoManager, MmioManager}; | ||
use vm_memory::{Address, Bytes, GuestAddress, GuestMemoryError, GuestMemoryMmap}; | ||
use vmm_sys_util::terminal::Terminal; | ||
|
||
use crate::devices::serial::{LumperSerial, SERIAL_PORT_BASE, SERIAL_PORT_LAST_REGISTER}; | ||
|
||
|
@@ -224,84 +224,85 @@ impl Vcpu { | |
} | ||
|
||
/// vCPU emulation loop. | ||
pub fn run(&mut self) { | ||
pub fn run(&mut self, socket_name: String) { | ||
let mut unix_socket = UnixStream::connect(socket_name).unwrap(); | ||
// Call into KVM to launch (VMLAUNCH) or resume (VMRESUME) the virtual CPU. | ||
// This is a blocking function, it only returns for either an error or a | ||
// VM-Exit. In the latter case, we can inspect the exit reason. | ||
match self.vcpu_fd.run() { | ||
Ok(exit_reason) => match exit_reason { | ||
// The VM stopped (Shutdown ot HLT). | ||
VcpuExit::Shutdown | VcpuExit::Hlt => { | ||
println!("Guest shutdown: {:?}. Bye!", exit_reason); | ||
let stdin = io::stdin(); | ||
let stdin_lock = stdin.lock(); | ||
stdin_lock.set_canon_mode().unwrap(); | ||
|
||
unsafe { libc::exit(0) }; | ||
} | ||
loop { | ||
let run = self.vcpu_fd.run(); | ||
|
||
match run { | ||
Ok(exit_reason) => match exit_reason { | ||
// The VM stopped (Shutdown ot HLT). | ||
VcpuExit::Shutdown | VcpuExit::Hlt => { | ||
println!("Guest shutdown: {:?}. Bye!", exit_reason); | ||
unix_socket.write_all(b"1").unwrap(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We notify the main thread and then continue running? |
||
} | ||
|
||
// This is a PIO write, i.e. the guest is trying to write | ||
// something to an I/O port. | ||
VcpuExit::IoOut(addr, data) => match addr { | ||
SERIAL_PORT_BASE..=SERIAL_PORT_LAST_REGISTER => { | ||
self.serial | ||
.lock() | ||
.unwrap() | ||
.serial | ||
.write( | ||
// This is a PIO write, i.e. the guest is trying to write | ||
// something to an I/O port. | ||
VcpuExit::IoOut(addr, data) => match addr { | ||
SERIAL_PORT_BASE..=SERIAL_PORT_LAST_REGISTER => { | ||
self.serial | ||
.lock() | ||
.unwrap() | ||
.serial | ||
.write( | ||
(addr - SERIAL_PORT_BASE) | ||
.try_into() | ||
.expect("Invalid serial register offset"), | ||
data[0], | ||
) | ||
.unwrap(); | ||
} | ||
_ => { | ||
println!("Unsupported device write at {:x?}", addr); | ||
} | ||
}, | ||
|
||
// This is a PIO read, i.e. the guest is trying to read | ||
// from an I/O port. | ||
VcpuExit::IoIn(addr, data) => match addr { | ||
SERIAL_PORT_BASE..=SERIAL_PORT_LAST_REGISTER => { | ||
data[0] = self.serial.lock().unwrap().serial.read( | ||
(addr - SERIAL_PORT_BASE) | ||
.try_into() | ||
.expect("Invalid serial register offset"), | ||
data[0], | ||
) | ||
); | ||
} | ||
_ => { | ||
println!("Unsupported device read at {:x?}", addr); | ||
} | ||
}, | ||
|
||
// This is a MMIO write, i.e. the guest is trying to write | ||
// something to a memory-mapped I/O region. | ||
VcpuExit::MmioWrite(addr, data) => { | ||
self.virtio_manager | ||
.lock() | ||
.unwrap() | ||
.mmio_write(MmioAddress(addr), data) | ||
.unwrap(); | ||
} | ||
_ => { | ||
println!("Unsupported device write at {:x?}", addr); | ||
} | ||
}, | ||
|
||
// This is a PIO read, i.e. the guest is trying to read | ||
// from an I/O port. | ||
VcpuExit::IoIn(addr, data) => match addr { | ||
SERIAL_PORT_BASE..=SERIAL_PORT_LAST_REGISTER => { | ||
data[0] = self.serial.lock().unwrap().serial.read( | ||
(addr - SERIAL_PORT_BASE) | ||
.try_into() | ||
.expect("Invalid serial register offset"), | ||
); | ||
// This is a MMIO read, i.e. the guest is trying to read | ||
// from a memory-mapped I/O region. | ||
VcpuExit::MmioRead(addr, data) => { | ||
self.virtio_manager | ||
.lock() | ||
.unwrap() | ||
.mmio_read(MmioAddress(addr), data) | ||
.unwrap(); | ||
} | ||
|
||
_ => { | ||
println!("Unsupported device read at {:x?}", addr); | ||
eprintln!("Unhandled VM-Exit: {:?}", exit_reason); | ||
} | ||
}, | ||
|
||
// This is a MMIO write, i.e. the guest is trying to write | ||
// something to a memory-mapped I/O region. | ||
VcpuExit::MmioWrite(addr, data) => { | ||
self.virtio_manager | ||
.lock() | ||
.unwrap() | ||
.mmio_write(MmioAddress(addr), data) | ||
.unwrap(); | ||
} | ||
|
||
// This is a MMIO read, i.e. the guest is trying to read | ||
// from a memory-mapped I/O region. | ||
VcpuExit::MmioRead(addr, data) => { | ||
self.virtio_manager | ||
.lock() | ||
.unwrap() | ||
.mmio_read(MmioAddress(addr), data) | ||
.unwrap(); | ||
} | ||
|
||
_ => { | ||
eprintln!("Unhandled VM-Exit: {:?}", exit_reason); | ||
} | ||
}, | ||
|
||
Err(e) => eprintln!("Emulation error: {}", e), | ||
Err(e) => eprintln!("Emulation error: {}", e), | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,10 +8,13 @@ extern crate linux_loader; | |
extern crate vm_memory; | ||
extern crate vm_superio; | ||
|
||
use std::any::Any; | ||
use std::fs::File; | ||
use std::io::stdout; | ||
use std::os::unix::io::AsRawFd; | ||
use std::os::unix::net::UnixListener; | ||
use std::os::unix::prelude::RawFd; | ||
use std::path::Path; | ||
use std::sync::{Arc, Mutex}; | ||
use std::thread; | ||
use std::{io, path::PathBuf}; | ||
|
@@ -25,6 +28,7 @@ use vm_device::device_manager::IoManager; | |
use vm_device::resources::Resource; | ||
use vm_memory::{Address, GuestAddress, GuestMemory, GuestMemoryMmap, GuestMemoryRegion}; | ||
use vmm_sys_util::eventfd::EventFd; | ||
use vmm_sys_util::rand; | ||
use vmm_sys_util::terminal::Terminal; | ||
mod cpu; | ||
use cpu::{cpuid, mptable, Vcpu}; | ||
|
@@ -86,6 +90,10 @@ pub enum Error { | |
VirtioNet(devices::net::VirtioNetError), | ||
/// Error related to IOManager. | ||
IoManager(vm_device::device_manager::Error), | ||
/// Access thread handler error | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does that mean? |
||
AccessThreadHandlerError, | ||
/// Join thread error | ||
JoinThreadError(Box<dyn Any + Send>), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is unused. |
||
} | ||
|
||
/// Dedicated [`Result`](https://doc.rust-lang.org/std/result/) type. | ||
|
@@ -262,6 +270,14 @@ impl VMM { | |
} | ||
|
||
pub fn configure_console(&mut self, console_path: Option<String>) -> Result<()> { | ||
if console_path.is_none() { | ||
return Ok(()); | ||
} | ||
|
||
self.cmdline | ||
.insert_str("console=ttyS0") | ||
.map_err(Error::Cmdline)?; | ||
|
||
if let Some(console_path) = console_path { | ||
// We create the file if it does not exist, else we open | ||
let file = File::create(&console_path).map_err(Error::ConsoleError)?; | ||
|
@@ -326,13 +342,41 @@ impl VMM { | |
|
||
// Run all virtual CPUs. | ||
pub fn run(&mut self) -> Result<()> { | ||
let mut unix_socket_name = String::from("/tmp/vmm.sock"); | ||
while Path::new(&unix_socket_name).exists() { | ||
let rng = rand::rand_alphanumerics(8); | ||
unix_socket_name = format!("/tmp/vmm-{}.sock", rng.to_str().unwrap()); | ||
} | ||
Comment on lines
+345
to
+349
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. iiuc you're passing a named unix socket to the vcpu for it to check in when it get a shutdown/halt? |
||
|
||
let mut handlers: Vec<thread::JoinHandle<_>> = Vec::new(); | ||
let listener = UnixListener::bind(unix_socket_name.as_str()).unwrap(); | ||
let total_cpus = self.vcpus.len(); | ||
|
||
for mut vcpu in self.vcpus.drain(..) { | ||
println!("Starting vCPU {:?}", vcpu.index); | ||
let _ = thread::Builder::new().spawn(move || loop { | ||
vcpu.run(); | ||
let socket_name = unix_socket_name.clone(); | ||
let handler = thread::Builder::new().spawn(move || { | ||
vcpu.run(socket_name.clone()); | ||
}); | ||
|
||
match handler { | ||
Ok(handler) => handlers.push(handler), | ||
Err(_) => { | ||
println!("Failed to start vCPU"); | ||
return Err(Error::AccessThreadHandlerError); | ||
} | ||
} | ||
} | ||
|
||
let mut connections: Vec<_> = Vec::new(); | ||
|
||
while connections.len() < total_cpus { | ||
let connection = listener.accept().unwrap().0; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please handle errors properly, by propagating them. |
||
self.epoll.add_fd(connection.as_raw_fd()).unwrap(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto. |
||
connections.push(connection); | ||
Comment on lines
+372
to
+375
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would be a good place to explain what you're trying to do here: You're going to listen on a socket for any vcpu to halt and then do something about that information. |
||
} | ||
|
||
self.epoll.add_fd(listener.as_raw_fd()).unwrap(); | ||
|
||
let stdin = io::stdin(); | ||
let stdin_lock = stdin.lock(); | ||
stdin_lock | ||
|
@@ -383,6 +427,14 @@ impl VMM { | |
.process_tap() | ||
.map_err(Error::VirtioNet)?; | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are we still listening to stdin when the VM has no console? |
||
if connections.iter().any(|c| c.as_raw_fd() == event_data) { | ||
use vmm_sys_util::signal::Killable; | ||
handlers.iter().for_each(|handler| { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So we're killing each thread whenever ones is shutdown/halted, right? |
||
handler.kill(9).unwrap(); | ||
}); | ||
return Ok(()); | ||
} | ||
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did we move the loop here to avoid passing the socket_name after each vm exit?