Skip to content
Open
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
4 changes: 4 additions & 0 deletions src/arch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ use std::result;

#[derive(Default)]
pub struct ArchMemoryInfo {
#[cfg(target_arch = "x86_64")]
pub ram_below_gap: u64,
#[cfg(target_arch = "x86_64")]
pub ram_above_gap: u64,
pub ram_last_addr: u64,
pub shm_start_addr: u64,
pub page_size: usize,
Expand Down
213 changes: 118 additions & 95 deletions src/arch/src/x86_64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,78 +71,94 @@ pub fn arch_memory_regions(

// It's safe to cast MMIO_MEM_START to usize because it fits in a u32 variable
// (It points to an address in the 32 bit space).
let (ram_last_addr, shm_start_addr, regions, firmware_addr) = match size
.checked_sub(MMIO_MEM_START as usize)
{
// case1: guest memory fits before the gap
None | Some(0) => {
let shm_start_addr = FIRST_ADDR_PAST_32BITS;

let (ram_last_addr, mut regions) = if let Some(kernel_load_addr) = kernel_load_addr {
if size < (kernel_load_addr + kernel_size as u64) as usize {
panic!("Kernel doesn't fit in RAM");
}

let ram_last_addr = kernel_load_addr + kernel_size as u64 + size as u64;
let (ram_below_gap, ram_above_gap, ram_last_addr, shm_start_addr, regions, firmware_addr) =
match size.checked_sub(MMIO_MEM_START as usize) {
// case1: guest memory fits before the gap
None | Some(0) => {
let shm_start_addr = FIRST_ADDR_PAST_32BITS;

let (ram_last_addr, mut regions) = if let Some(kernel_load_addr) = kernel_load_addr
{
if size < (kernel_load_addr + kernel_size as u64) as usize {
panic!("Kernel doesn't fit in RAM");
}

let ram_last_addr = kernel_load_addr + kernel_size as u64 + size as u64;
(
ram_last_addr,
vec![
(GuestAddress(0), kernel_load_addr as usize),
(GuestAddress(kernel_load_addr + kernel_size as u64), size),
],
)
} else {
let ram_last_addr = size as u64;
(ram_last_addr, vec![(GuestAddress(0), size)])
};

let firmware_addr = if let Some(firmware_size) = firmware_size {
let firmware_start = layout::FIRST_ADDR_PAST_32BITS - firmware_size as u64;
regions.push((GuestAddress(firmware_start), firmware_size));
firmware_start
} else {
0
};

(
size as u64,
0,
ram_last_addr,
vec![
(GuestAddress(0), kernel_load_addr as usize),
(GuestAddress(kernel_load_addr + kernel_size as u64), size),
],
shm_start_addr,
regions,
firmware_addr,
)
} else {
let ram_last_addr = size as u64;
(ram_last_addr, vec![(GuestAddress(0), size)])
};

let firmware_addr = if let Some(firmware_size) = firmware_size {
let firmware_start = layout::FIRST_ADDR_PAST_32BITS - firmware_size as u64;
regions.push((GuestAddress(firmware_start), firmware_size));
firmware_start
} else {
0
};

(ram_last_addr, shm_start_addr, regions, firmware_addr)
}
}

// case2: guest memory extends beyond the gap
Some(remaining) => {
let ram_last_addr = FIRST_ADDR_PAST_32BITS + remaining as u64;
let shm_start_addr = ((ram_last_addr / 0x4000_0000) + 1) * 0x4000_0000;
// case2: guest memory extends beyond the gap
Some(remaining) => {
let ram_last_addr = FIRST_ADDR_PAST_32BITS + remaining as u64;
let shm_start_addr = ((ram_last_addr / 0x4000_0000) + 1) * 0x4000_0000;

let mut regions = if let Some(kernel_load_addr) = kernel_load_addr {
vec![
(GuestAddress(0), kernel_load_addr as usize),
(
GuestAddress(kernel_load_addr + kernel_size as u64),
(MMIO_MEM_START - (kernel_load_addr + kernel_size as u64)) as usize,
),
(GuestAddress(FIRST_ADDR_PAST_32BITS), remaining),
]
} else {
vec![
(GuestAddress(0), MMIO_MEM_START as usize),
(GuestAddress(FIRST_ADDR_PAST_32BITS), remaining),
]
};

let firmware_addr = if let Some(firmware_size) = firmware_size {
let firmware_start = layout::FIRST_ADDR_PAST_32BITS - firmware_size as u64;
regions.insert(
regions.len() - 2,
(GuestAddress(firmware_start), firmware_size),
);
firmware_start
} else {
0
};

(ram_last_addr, shm_start_addr, regions, firmware_addr)
}
};
let mut regions = if let Some(kernel_load_addr) = kernel_load_addr {
vec![
(GuestAddress(0), kernel_load_addr as usize),
(
GuestAddress(kernel_load_addr + kernel_size as u64),
(MMIO_MEM_START - (kernel_load_addr + kernel_size as u64)) as usize,
),
(GuestAddress(FIRST_ADDR_PAST_32BITS), remaining),
]
} else {
vec![
(GuestAddress(0), MMIO_MEM_START as usize),
(GuestAddress(FIRST_ADDR_PAST_32BITS), remaining),
]
};

let firmware_addr = if let Some(firmware_size) = firmware_size {
let firmware_start = layout::FIRST_ADDR_PAST_32BITS - firmware_size as u64;
regions.insert(
regions.len() - 1,
(GuestAddress(firmware_start), firmware_size),
);
firmware_start
} else {
0
};

(
MMIO_MEM_START,
remaining as u64,
ram_last_addr,
shm_start_addr,
regions,
firmware_addr,
)
}
};
let info = ArchMemoryInfo {
ram_below_gap,
ram_above_gap,
ram_last_addr,
shm_start_addr,
page_size,
Expand Down Expand Up @@ -176,36 +192,43 @@ pub fn arch_memory_regions(

// It's safe to cast MMIO_MEM_START to usize because it fits in a u32 variable
// (It points to an address in the 32 bit space).
let (ram_last_addr, shm_start_addr, regions) = match size.checked_sub(MMIO_MEM_START as usize) {
// case1: guest memory fits before the gap
None | Some(0) => {
let ram_last_addr = size as u64;
let shm_start_addr = 0u64;
(
ram_last_addr,
shm_start_addr,
vec![
(GuestAddress(0), size),
(GuestAddress(FIRMWARE_START), FIRMWARE_SIZE as usize),
],
)
}
// case2: guest memory extends beyond the gap
Some(remaining) => {
let ram_last_addr = FIRST_ADDR_PAST_32BITS + remaining as u64;
let shm_start_addr = 0u64;
(
ram_last_addr,
shm_start_addr,
vec![
(GuestAddress(0), MMIO_MEM_START as usize),
(GuestAddress(FIRMWARE_START), FIRMWARE_SIZE as usize),
(GuestAddress(FIRST_ADDR_PAST_32BITS), remaining),
],
)
}
};
let (ram_below_gap, ram_above_gap, ram_last_addr, shm_start_addr, regions) =
match size.checked_sub(MMIO_MEM_START as usize) {
// case1: guest memory fits before the gap
None | Some(0) => {
let ram_last_addr = size as u64;
let shm_start_addr = 0u64;
(
size as u64,
0,
ram_last_addr,
shm_start_addr,
vec![
(GuestAddress(0), size),
(GuestAddress(FIRMWARE_START), FIRMWARE_SIZE as usize),
],
)
}
// case2: guest memory extends beyond the gap
Some(remaining) => {
let ram_last_addr = FIRST_ADDR_PAST_32BITS + remaining as u64;
let shm_start_addr = 0u64;
(
MMIO_MEM_START,
remaining as u64,
ram_last_addr,
shm_start_addr,
vec![
(GuestAddress(0), MMIO_MEM_START as usize),
(GuestAddress(FIRMWARE_START), FIRMWARE_SIZE as usize),
(GuestAddress(FIRST_ADDR_PAST_32BITS), remaining),
],
)
}
};
let info = ArchMemoryInfo {
ram_below_gap,
ram_above_gap,
ram_last_addr,
shm_start_addr,
page_size,
Expand Down
4 changes: 4 additions & 0 deletions src/devices/src/legacy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ mod vcpu;
#[cfg(target_arch = "x86_64")]
mod x86_64;
#[cfg(target_arch = "x86_64")]
use x86_64::cmos;
#[cfg(target_arch = "x86_64")]
use x86_64::serial;
#[cfg(target_arch = "aarch64")]
mod aarch64;
Expand All @@ -40,6 +42,8 @@ mod riscv64;
#[cfg(target_arch = "riscv64")]
use riscv64::serial;

#[cfg(target_arch = "x86_64")]
pub use self::cmos::Cmos;
#[cfg(target_os = "macos")]
pub use self::gicv3::GicV3;
#[cfg(target_arch = "aarch64")]
Expand Down
79 changes: 79 additions & 0 deletions src/devices/src/legacy/x86_64/cmos.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright 2025 Red Hat, Inc.
// SPDX-License-Identifier: Apache-2.0

use std::cmp::min;

use crate::bus::BusDevice;

const INDEX_MASK: u8 = 0x7f;
const INDEX_OFFSET: u64 = 0x0;
const DATA_OFFSET: u64 = 0x1;
const DATA_LEN: usize = 128;

pub struct Cmos {
index: u8,
data: [u8; DATA_LEN],
}

impl Cmos {
pub fn new(mem_below_4g: u64, mem_above_4g: u64) -> Cmos {
debug!("cmos: mem_below_4g={mem_below_4g} mem_above_4g={mem_above_4g}");

let mut data = [0u8; DATA_LEN];

// Extended memory from 16 MB to 4 GB in units of 64 KB
let ext_mem = min(
0xFFFF,
mem_below_4g.saturating_sub(16 * 1024 * 1024) / (64 * 1024),
);
data[0x34] = ext_mem as u8;
data[0x35] = (ext_mem >> 8) as u8;

// High memory (> 4GB) in units of 64 KB
let high_mem = min(0xFFFFFF, mem_above_4g / (64 * 1024));
data[0x5b] = high_mem as u8;
data[0x5c] = (high_mem >> 8) as u8;
data[0x5d] = (high_mem >> 16) as u8;

Cmos { index: 0, data }
}
}

impl BusDevice for Cmos {
fn read(&mut self, _vcpuid: u64, offset: u64, data: &mut [u8]) {
if data.len() != 1 {
error!("cmos: unsupported read length");
return;
}

data[0] = match offset {
INDEX_OFFSET => {
debug!("cmos: read index offset");
self.index
}
DATA_OFFSET => {
debug!("cmos: read data offset from index={:x}", self.index);
self.data[(self.index & INDEX_MASK) as usize]
}
_ => {
debug!("cmos: unsupported read offset");
0
}
};
}

fn write(&mut self, _vcpuid: u64, offset: u64, data: &[u8]) {
if data.len() != 1 {
error!("cmos: unsupported write length");
return;
}

match offset {
INDEX_OFFSET => {
debug!("cmos: update index");
self.index = data[0] & INDEX_MASK;
}
_ => error!("cmos: unsupported write to CMOS"),
}
}
}
1 change: 1 addition & 0 deletions src/devices/src/legacy/x86_64/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod cmos;
pub mod serial;
6 changes: 6 additions & 0 deletions src/vmm/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ use crate::resources::{ConsoleType, VmResources};
use crate::vmm_config::external_kernel::{ExternalKernel, KernelFormat};
#[cfg(feature = "net")]
use crate::vmm_config::net::NetBuilder;
#[cfg(target_arch = "x86_64")]
use devices::legacy::Cmos;
#[cfg(all(target_os = "linux", target_arch = "riscv64"))]
use devices::legacy::KvmAia;
#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
Expand Down Expand Up @@ -750,6 +752,10 @@ pub fn build_microvm(
// Safe to unwrap 'serial_device' as it's always 'Some' on x86_64.
// x86_64 uses the i8042 reset event as the Vmm exit event.
let mut pio_device_manager = PortIODeviceManager::new(
Arc::new(Mutex::new(Cmos::new(
arch_memory_info.ram_below_gap,
arch_memory_info.ram_above_gap,
))),
serial_devices,
exit_evt
.try_clone()
Expand Down
Loading