Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/agent/machine/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,6 @@ impl Machine {
.collect(),
logs_telemetry_config: config.logs_telemetry_config.clone(),
};
let takeoff_args_str = takeoff_args.encode()?;
kernel_cmd.insert_str(format!("takeoff={}", takeoff_args_str))?;

let mut io_manager = IoManager::new();
let mut irq_allocator = IrqAllocator::new(SERIAL_IRQ)?;
Expand All @@ -408,6 +406,7 @@ impl Machine {
&config,
&kvm,
vm_fd.clone(),
&takeoff_args,
&guest_memory,
&mut irq_allocator,
&mut mmio_allocator,
Expand Down
20 changes: 10 additions & 10 deletions src/agent/machine/state_machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,18 +293,18 @@ impl MachineStateMachine {
async fn handle_user_start(&mut self) -> Result<()> {
let is_first_start = self.current_state == MachineState::Idle;

// Reset guest manager for non-first starts
if !is_first_start {
self.resources
.devices
.guest_manager
.lock()
.expect("Failed to lock guest manager")
.set_snapshot_strategy(None);
}

match self.current_state {
MachineState::Idle | MachineState::Suspended => {
// Reset guest manager for non-first starts
if !is_first_start {
self.resources
.devices
.guest_manager
.lock()
.expect("Failed to lock guest manager")
.set_snapshot_strategy(None);
}

// Set state to Booting and start VCPUs
// For first start: SystemDeviceReady will transition to Ready
// For resume from suspend: SystemVcpuRestarted will transition to Ready
Expand Down
1 change: 1 addition & 0 deletions src/agent/machine/vm/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub const BLK_SHIFT: u32 = 12;
pub const BLK_SIZE: usize = 1 << BLK_SHIFT;

pub const CMDLINE_CAPACITY: usize = 4096;
pub const CMDLINE_SAFE_LIMIT: usize = 2048;

pub const X86_CR0_PE: u64 = 0x1;
pub const X86_CR0_PG: u64 = 0x8000_0000;
Expand Down
49 changes: 42 additions & 7 deletions src/agent/machine/vm/devices/meta/guest_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::{
};

use tracing::warn;
use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap};

use crate::agent::machine::{
machine::SnapshotStrategy,
Expand Down Expand Up @@ -35,6 +36,11 @@ const CMD_FLASH_UNLOCK: u8 = CMD_OFFSET + 1;

const READ_OFFSET_LAST_BOOT_TIME: u64 = 0;
const READ_OFFSET_FIRST_BOOT_TIME: u64 = 8;
const READ_OFFSET_TAKEOFF_ARGS_LEN: u64 = 16;

const WRITE_OFFSET_TRIGGER: u64 = 0;
const WRITE_OFFSET_CMD: u64 = 8;
const WRITE_OFFSET_TAKEOFF_ARGS: u64 = 16;

#[allow(unused)]
#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -131,6 +137,8 @@ impl Cmd {
}

pub struct GuestManagerDevice {
memory: GuestMemoryMmap,
takeoff_args: Vec<u8>,
listen_trigger_count: u32,
device_event_tx: async_broadcast::Sender<DeviceEvent>,
first_boot_duration: Option<Duration>,
Expand All @@ -148,10 +156,14 @@ impl GuestManagerDevice {
}

pub fn new(
memory: GuestMemoryMmap,
takeoff_args: Vec<u8>,
device_event_tx: async_broadcast::Sender<DeviceEvent>,
snapshot_strategy: Option<SnapshotStrategy>,
) -> Arc<Mutex<Self>> {
let guest_manager = Self {
memory,
takeoff_args,
snapshot_strategy,
listen_trigger_count: 0,
first_boot_duration: None,
Expand Down Expand Up @@ -185,7 +197,8 @@ impl GuestManagerDevice {
.map(|duration| duration.as_micros() as u64),
READ_OFFSET_FIRST_BOOT_TIME => self
.first_boot_duration
.map(|duration| duration.as_micros() as u64),
.map(|duration: Duration| duration.as_micros() as u64),
READ_OFFSET_TAKEOFF_ARGS_LEN => self.process_args_read(),
_ => {
warn!("unhandled read offset {}", offset);
return;
Expand All @@ -197,13 +210,15 @@ impl GuestManagerDevice {
}

pub fn mmio_write(&mut self, offset: vm_device::bus::MmioAddressOffset, data: &[u8]) -> bool {
if offset == 0 {
return self.process_trigger(data);
} else if offset == 8 {
return self.process_cmd(data);
match offset {
WRITE_OFFSET_TRIGGER => self.process_trigger(data),
WRITE_OFFSET_CMD => self.process_cmd(data),
WRITE_OFFSET_TAKEOFF_ARGS => self.process_args_write(data),
_ => {
warn!("unhandled write offset {}", offset);
false
}
}

false
}

fn process_trigger(&mut self, data: &[u8]) -> bool {
Expand Down Expand Up @@ -275,4 +290,24 @@ impl GuestManagerDevice {

return false;
}

fn process_args_read(&mut self) -> Option<u64> {
let len = self.takeoff_args.len();
return Some(len as u64);
}

fn process_args_write(&mut self, data: &[u8]) -> bool {
let ptr = u64::from_le_bytes([
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
]);

if let Err(e) = self
.memory
.write_slice(&self.takeoff_args, GuestAddress(ptr))
{
warn!("Failed to write hello world! to ptr {}: {:?}", ptr, e);
}

return false;
}
}
13 changes: 12 additions & 1 deletion src/agent/machine/vm/devices/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use event_manager::{EventManager, MutEventSubscriber};
use kvm_bindings::{KVM_PIT_SPEAKER_DUMMY, kvm_pit_config, kvm_userspace_memory_region};
use kvm_ioctls::{Kvm, VmFd};
use linux_loader::loader::Cmdline;
use takeoff_proto::proto::TakeoffInitArgs;
use vm_allocator::{AddressAllocator, AllocPolicy};
use vm_device::{
bus::{BusRange, MmioAddress, PioAddress, PioRange},
Expand Down Expand Up @@ -57,6 +58,7 @@ pub async fn setup_devices(
machine_config: &MachineConfig,
kvm: &Kvm,
vm_fd: Arc<VmFd>,
takeoff_args: &TakeoffInitArgs,
memory: &GuestMemoryMmap,
irq_allocator: &mut IrqAllocator,
mmio_allocator: &mut AddressAllocator,
Expand All @@ -79,7 +81,16 @@ pub async fn setup_devices(
snapshot_strategy, ..
} => Some(snapshot_strategy.clone()),
};
let guest_manager = GuestManagerDevice::new(device_event_tx.clone(), snapshot_strategy);

let takeoff_args_str = takeoff_args.encode()?;
let takeoff_args_bytes = takeoff_args_str.as_bytes().to_vec();

let guest_manager = GuestManagerDevice::new(
memory.clone(),
takeoff_args_bytes,
device_event_tx.clone(),
snapshot_strategy,
);

let net = setup_network_device(
vm_fd.clone(),
Expand Down
15 changes: 13 additions & 2 deletions src/agent/machine/vm/kernel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use vm_memory::{
use crate::agent::machine::{
machine::MachineConfig,
vm::constants::{
CMDLINE_CAPACITY, CMDLINE_START, E820_RAM, EBDA_START, HIGH_RAM_START,
CMDLINE_CAPACITY, CMDLINE_SAFE_LIMIT, CMDLINE_START, E820_RAM, EBDA_START, HIGH_RAM_START,
KERNEL_BOOT_FLAG_MAGIC, KERNEL_HDR_MAGIC, KERNEL_LOADER_OTHER, KERNEL_MIN_ALIGNMENT_BYTES,
PAGE_SIZE, ZERO_PAGE_START,
},
Expand Down Expand Up @@ -64,7 +64,18 @@ pub async fn load_kernel(
boot_params.hdr.ramdisk_size = initrd_size as u32;

boot_params.hdr.cmd_line_ptr = CMDLINE_START as u32;
boot_params.hdr.cmdline_size = kernel_cmd.as_cstring()?.as_bytes().len() as u32;
let cmdline_cstring = kernel_cmd.as_cstring()?;
let cmdline_bytes = cmdline_cstring.as_bytes();
boot_params.hdr.cmdline_size = cmdline_bytes.len() as u32;

// Validate command line size against safe kernel limit
if cmdline_bytes.len() > CMDLINE_SAFE_LIMIT {
bail!(
"Command line too large: {} bytes exceeds safe kernel limit of {} bytes.",
cmdline_bytes.len(),
CMDLINE_SAFE_LIMIT
);
}

linux_loader::loader::load_cmdline(memory, GuestAddress(CMDLINE_START), kernel_cmd)?;

Expand Down
45 changes: 43 additions & 2 deletions src/takeoff/src/guest.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{
ffi::c_void,
fs::File,
io::{Read, Seek, SeekFrom},
os::fd::{AsRawFd, FromRawFd},
};

Expand All @@ -12,6 +13,7 @@ use nix::{
stat::Mode,
},
};
use takeoff_proto::proto::TakeoffInitArgs;

const PAGE_SIZE: usize = 4096;
const MAGIC_MMIO_ADDR: i64 = 0xd0000000;
Expand Down Expand Up @@ -56,8 +58,6 @@ impl GuestManager {
pub fn set_exit_code(&self, code: i32) {
unsafe {
let ptr = self.map_base.as_ptr() as *mut u64;
// first 4 bytes are the trigger code
// last byte is 0x04

let mut cmd = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04];
cmd[..4].copy_from_slice(&code.to_le_bytes());
Expand All @@ -68,6 +68,26 @@ impl GuestManager {
}
}

pub fn read_takeoff_args(&self) -> Result<TakeoffInitArgs> {
let len = unsafe {
let ptr = self.map_base.as_ptr().add(16) as *const u64;
ptr.read_volatile()
};

let mut buffer = vec![0u8; len as usize];
let buff_slice = buffer.as_mut_slice();
let val = self.virt_to_phys(buff_slice.as_ptr() as u64)?;

unsafe {
let ptr: *mut u64 = self.map_base.as_ptr().add(16) as *mut u64;
ptr.write_volatile(val);
}

let str = String::from_utf8_lossy(&buff_slice[0..len as usize]).to_string();

TakeoffInitArgs::decode(&str)
}

#[allow(dead_code)]
pub fn trigger_manual_snapshot(&self) {
unsafe {
Expand Down Expand Up @@ -95,4 +115,25 @@ impl GuestManager {
time_us
}
}

pub fn virt_to_phys(&self, virt_addr: u64) -> Result<u64> {
let page_size = 4096;
let virt_pfn = virt_addr / page_size;

let mut pagemap = File::open("/proc/self/pagemap")?;
pagemap.seek(SeekFrom::Start((virt_pfn * 8) as u64))?;

let mut page_info = [0u8; 8];
pagemap.read_exact(&mut page_info)?;
let page_info = u64::from_le_bytes(page_info);

if (page_info & (1u64 << 63)) == 0 {
anyhow::bail!("Page not present");
}

let phys_pfn = page_info & ((1u64 << 55) - 1);
let phys_addr = (phys_pfn * page_size) + (virt_addr % page_size);

Ok(phys_addr)
}
}
7 changes: 5 additions & 2 deletions src/takeoff/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use nix::{
};
use oci_config::{EnvVar, OciConfig};
use serial::SerialWriter;
use takeoff_proto::proto::{LogsTelemetryConfig, TakeoffInitArgs};
use takeoff_proto::proto::LogsTelemetryConfig;

use tokio::{
fs,
Expand Down Expand Up @@ -45,10 +45,13 @@ async fn takeoff() -> Result<()> {

let guest_manager = Arc::new(GuestManager::new().expect("create guest manager"));

let Ok(args) = guest_manager.read_takeoff_args() else {
bail!("failed to read takeoff init args");
};

let cmdline = fs::read_to_string("/proc/cmdline")
.await
.expect("read cmdline");
let args = TakeoffInitArgs::try_parse_from_kernel_cmdline(&cmdline)?;

configure_dns(&cmdline).await?;

Expand Down