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
40 changes: 40 additions & 0 deletions .github/workflows/integration_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,29 @@ jobs:
- name: Setup build environment
uses: ./.github/actions/setup-build-env

- name: Install cross-compilation dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends lld

- name: Add musl target
run: rustup target add x86_64-unknown-linux-musl

- name: Add FreeBSD x86_64 target
run: rustup target add x86_64-unknown-freebsd

- name: Install FreeBSD test prerequisites
run: sudo apt-get install -y --no-install-recommends libarchive-tools

- name: Build FreeBSD sysroot and init
run: make BUILD_BSD_INIT=1 -- init/init-freebsd

- name: Install gvproxy
run: |
curl -fL -o /tmp/gvproxy https://github.com/containers/gvisor-tap-vsock/releases/download/v0.8.9/gvproxy-linux-amd64
chmod +x /tmp/gvproxy
sudo mv /tmp/gvproxy /usr/local/bin/gvproxy

- name: Build and install libkrun to test prefix
run: make test-prefix NET=1

Expand Down Expand Up @@ -80,9 +100,29 @@ jobs:
- name: Setup build environment
uses: ./.github/actions/setup-build-env

- name: Install cross-compilation dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends clang lld

- name: Add musl target
run: rustup target add aarch64-unknown-linux-musl

- name: Install nightly toolchain with rust-src for FreeBSD aarch64
run: rustup +nightly-2026-01-25 component add rust-src

- name: Install FreeBSD test prerequisites
run: sudo apt-get install -y --no-install-recommends libarchive-tools

- name: Build FreeBSD sysroot and init
run: make BUILD_BSD_INIT=1 -- init/init-freebsd

- name: Install gvproxy
run: |
curl -fL -o /tmp/gvproxy https://github.com/containers/gvisor-tap-vsock/releases/download/v0.8.9/gvproxy-linux-arm64
chmod +x /tmp/gvproxy
sudo mv /tmp/gvproxy /usr/local/bin/gvproxy

- name: Build and install libkrun to test prefix
run: make test-prefix NET=1

Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,19 @@ endif
ifeq ($(VIRGL_RESOURCE_MAP2),1)
FEATURE_FLAGS += --features virgl_resource_map2
endif
# Test targets require the block device (BLK) feature for FreeBSD disk tests
# and the NET feature for gvproxy-based network tests.
# Enable automatically unless the user explicitly set them to a value.
ifeq ($(BLK),)
ifneq ($(filter test test-prefix,$(MAKECMDGOALS)),)
BLK := 1
endif
endif
ifeq ($(NET),)
ifneq ($(filter test test-prefix,$(MAKECMDGOALS)),)
NET := 1
endif
endif
ifeq ($(BLK),1)
FEATURE_FLAGS += --features blk
endif
Expand Down
3 changes: 3 additions & 0 deletions src/arch/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,8 @@ kvm-bindings = { version = "0.12", features = ["fam-wrappers"] }
kvm-ioctls = "0.22"
tdx = { version = "0.1.0", optional = true }

[target.'cfg(target_arch = "x86_64")'.dependencies]
linux-loader = { version = "0.13.2", features = ["elf"] }

[dev-dependencies]
utils = { package = "krun-utils", version = "=0.1.0-1.18.0", path = "../utils" }
11 changes: 9 additions & 2 deletions src/arch/src/x86_64/gdt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,14 @@ fn get_base(entry: u64) -> u64 {
}

fn get_limit(entry: u64) -> u32 {
((((entry) & 0x000F_0000_0000_0000) >> 32) | ((entry) & 0x0000_0000_0000_FFFF)) as u32
let limit =
((((entry) & 0x000F_0000_0000_0000) >> 32) | ((entry) & 0x0000_0000_0000_FFFF)) as u32;

if get_g(entry) == 1 {
(limit << 12) | 0xFFF
} else {
limit
}
}

fn get_g(entry: u64) -> u8 {
Expand Down Expand Up @@ -109,7 +116,7 @@ mod tests {
assert_eq!(0xB, seg.type_);
// base and limit
assert_eq!(0x10_0000, seg.base);
assert_eq!(0xfffff, seg.limit);
assert_eq!(0xffffffff, seg.limit);
assert_eq!(0x0, seg.unusable);
}
}
15 changes: 15 additions & 0 deletions src/arch/src/x86_64/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@ pub const IRQ_MAX: u32 = 15;
/// Address for the TSS setup.
pub const KVM_TSS_ADDRESS: u64 = 0xfffb_d000;

/// Address of the hvm_start_info struct used in PVH boot.
/// Mutually exclusive with SNP_CPUID_START (TEE only).
pub const PVH_INFO_START: u64 = 0x6000;

/// Starting address of array of modules of hvm_modlist_entry type.
/// Used to enable initrd support using the PVH boot ABI.
pub const MODLIST_START: u64 = 0x6040;

/// Address of memory map table used in PVH boot. Can overlap
/// with the zero page address since they are mutually exclusive.
pub const MEMMAP_START: u64 = 0x7000;

/// Location of RSDP pointer in x86 machines.
pub const RSDP_ADDR: u64 = 0x000e_0000;

/// The 'zero page', a.k.a linux kernel bootparams.
pub const ZERO_PAGE_START: u64 = 0x7000;

Expand Down
162 changes: 154 additions & 8 deletions src/arch/src/x86_64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,20 @@ use crate::x86_64::layout::{EBDA_START, FIRST_ADDR_PAST_32BITS, MMIO_MEM_START};
#[cfg(feature = "tee")]
use crate::x86_64::layout::{FIRMWARE_SIZE, FIRMWARE_START};
use crate::{ArchMemoryInfo, InitrdConfig};
#[cfg(not(feature = "tee"))]
use arch_gen::x86::bootparam::E820_RESERVED;
use arch_gen::x86::bootparam::{boot_params, E820_RAM};
use vm_memory::Bytes;
use vm_memory::{Address, ByteValued, GuestAddress, GuestMemoryMmap};
use vmm_sys_util::align_upwards;

#[cfg(not(feature = "tee"))]
use linux_loader::configurator::{pvh::PvhBootConfigurator, BootConfigurator, BootParams};
#[cfg(not(feature = "tee"))]
use linux_loader::loader::elf::start_info::{
hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info,
};

// This is a workaround to the Rust enforcement specifying that any implementation of a foreign
// trait (in this case `ByteValued`) where:
// * the type that is implementing the trait is foreign or
Expand All @@ -45,6 +54,9 @@ pub enum Error {
/// Error writing MP table to memory.
#[cfg(not(feature = "tee"))]
MpTableSetup(mptable::Error),
/// Error writing hvm_start_info to guest memory.
#[cfg(not(feature = "tee"))]
StartInfoSetup,
/// Error writing the zero page of guest memory.
ZeroPageSetup,
/// Failed to compute initrd address.
Expand Down Expand Up @@ -245,6 +257,7 @@ pub fn arch_memory_regions(
/// * `cmdline_size` - Size of the kernel command line in bytes including the null terminator.
/// * `initrd` - Information about where the ramdisk image was loaded in the `guest_mem`.
/// * `num_cpus` - Number of virtual CPUs the guest will have.
/// * `pvh` - Whether to use the PVH boot protocol.
#[allow(unused_variables)]
pub fn configure_system(
guest_mem: &GuestMemoryMmap,
Expand All @@ -253,6 +266,116 @@ pub fn configure_system(
cmdline_size: usize,
initrd: &Option<InitrdConfig>,
num_cpus: u8,
pvh: bool,
) -> super::Result<()> {
// Note that this puts the mptable at the last 1k of Linux's 640k base RAM
#[cfg(not(feature = "tee"))]
mptable::setup_mptable(guest_mem, num_cpus).map_err(Error::MpTableSetup)?;

if pvh {
#[cfg(not(feature = "tee"))]
configure_pvh(guest_mem, arch_memory_info, cmdline_addr, initrd)?;
} else {
configure_64bit_boot(
guest_mem,
arch_memory_info,
cmdline_addr,
cmdline_size,
initrd,
num_cpus,
)?;
}
Ok(())
}

#[cfg(not(feature = "tee"))]
fn configure_pvh(
guest_mem: &GuestMemoryMmap,
arch_memory_info: &ArchMemoryInfo,
cmdline_addr: GuestAddress,
initrd: &Option<InitrdConfig>,
) -> Result<(), Error> {
const XEN_HVM_START_MAGIC_VALUE: u32 = 0x336e_c578;
let first_addr_past_32bits = GuestAddress(FIRST_ADDR_PAST_32BITS);
let end_32bit_gap_start = GuestAddress(MMIO_MEM_START);
let himem_start = GuestAddress(layout::HIMEM_START);
let mut modules: Vec<hvm_modlist_entry> = Vec::new();
if let Some(initrd_config) = initrd {
modules.push(hvm_modlist_entry {
paddr: initrd_config.address.raw_value(),
size: initrd_config.size as u64,
..Default::default()
});
}
let mut memmap: Vec<hvm_memmap_table_entry> = Vec::new();
add_memmap_entry(&mut memmap, 0, mptable::MPTABLE_START, E820_RAM);
add_memmap_entry(
&mut memmap,
mptable::MPTABLE_START,
layout::RSDP_ADDR - mptable::MPTABLE_START,
E820_RESERVED,
);
let last_addr = GuestAddress(arch_memory_info.ram_last_addr);
if last_addr < end_32bit_gap_start {
add_memmap_entry(
&mut memmap,
himem_start.raw_value(),
last_addr.unchecked_offset_from(himem_start) + 1,
E820_RAM,
);
} else {
add_memmap_entry(
&mut memmap,
himem_start.raw_value(),
end_32bit_gap_start.unchecked_offset_from(himem_start),
E820_RAM,
);
if last_addr > first_addr_past_32bits {
add_memmap_entry(
&mut memmap,
first_addr_past_32bits.raw_value(),
last_addr.unchecked_offset_from(first_addr_past_32bits) + 1,
E820_RAM,
);
}
}
let mut start_info = hvm_start_info {
magic: XEN_HVM_START_MAGIC_VALUE,
version: 1,
cmdline_paddr: cmdline_addr.raw_value(),
memmap_paddr: layout::MEMMAP_START,
memmap_entries: memmap.len() as u32,
nr_modules: modules.len() as u32,
..Default::default()
};
if !modules.is_empty() {
start_info.modlist_paddr = layout::MODLIST_START;
}
let mut boot_params =
BootParams::new::<hvm_start_info>(&start_info, GuestAddress(layout::PVH_INFO_START));
boot_params.set_sections::<hvm_memmap_table_entry>(&memmap, GuestAddress(layout::MEMMAP_START));
boot_params.set_modules::<hvm_modlist_entry>(&modules, GuestAddress(layout::MODLIST_START));
PvhBootConfigurator::write_bootparams(&boot_params, guest_mem)
.map_err(|_| Error::StartInfoSetup)
}

#[cfg(not(feature = "tee"))]
fn add_memmap_entry(memmap: &mut Vec<hvm_memmap_table_entry>, addr: u64, size: u64, mem_type: u32) {
memmap.push(hvm_memmap_table_entry {
addr,
size,
type_: mem_type,
reserved: 0,
});
}

fn configure_64bit_boot(
guest_mem: &GuestMemoryMmap,
arch_memory_info: &ArchMemoryInfo,
cmdline_addr: GuestAddress,
cmdline_size: usize,
initrd: &Option<InitrdConfig>,
#[allow(unused_variables)] num_cpus: u8,
) -> super::Result<()> {
const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
const KERNEL_HDR_MAGIC: u32 = 0x5372_6448;
Expand All @@ -263,10 +386,6 @@ pub fn configure_system(

let himem_start = GuestAddress(layout::HIMEM_START);

// Note that this puts the mptable at the last 1k of Linux's 640k base RAM
#[cfg(not(feature = "tee"))]
mptable::setup_mptable(guest_mem, num_cpus).map_err(Error::MpTableSetup)?;

let mut params: BootParamsWrapper = BootParamsWrapper(boot_params::default());

params.0.hdr.type_of_loader = KERNEL_LOADER_OTHER;
Expand Down Expand Up @@ -401,7 +520,7 @@ mod tests {
let no_vcpus = 4;
let gm = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
let info = ArchMemoryInfo::default();
let config_err = configure_system(&gm, &info, GuestAddress(0), 0, &None, 1);
let config_err = configure_system(&gm, &info, GuestAddress(0), 0, &None, 1, false);
assert!(config_err.is_err());
#[cfg(not(feature = "tee"))]
assert_eq!(
Expand All @@ -414,21 +533,48 @@ mod tests {
let (arch_mem_info, arch_mem_regions) =
arch_memory_regions(mem_size, Some(KERNEL_LOAD_ADDR), KERNEL_SIZE, 0, None);
let gm = GuestMemoryMmap::from_ranges(&arch_mem_regions).unwrap();
configure_system(&gm, &arch_mem_info, GuestAddress(0), 0, &None, no_vcpus).unwrap();
configure_system(
&gm,
&arch_mem_info,
GuestAddress(0),
0,
&None,
no_vcpus,
false,
)
.unwrap();

// Now assigning some memory that is equal to the start of the 32bit memory hole.
let mem_size = 3328 << 20;
let (arch_mem_info, arch_mem_regions) =
arch_memory_regions(mem_size, Some(KERNEL_LOAD_ADDR), KERNEL_SIZE, 0, None);
let gm = GuestMemoryMmap::from_ranges(&arch_mem_regions).unwrap();
configure_system(&gm, &arch_mem_info, GuestAddress(0), 0, &None, no_vcpus).unwrap();
configure_system(
&gm,
&arch_mem_info,
GuestAddress(0),
0,
&None,
no_vcpus,
false,
)
.unwrap();

// Now assigning some memory that falls after the 32bit memory hole.
let mem_size = 3330 << 20;
let (arch_mem_info, arch_mem_regions) =
arch_memory_regions(mem_size, Some(KERNEL_LOAD_ADDR), KERNEL_SIZE, 0, None);
let gm = GuestMemoryMmap::from_ranges(&arch_mem_regions).unwrap();
configure_system(&gm, &arch_mem_info, GuestAddress(0), 0, &None, no_vcpus).unwrap();
configure_system(
&gm,
&arch_mem_info,
GuestAddress(0),
0,
&None,
no_vcpus,
false,
)
.unwrap();
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion src/arch/src/x86_64/mptable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ unsafe impl ByteValued for MpcLintsrcWrapper {}
unsafe impl ByteValued for MpfIntelWrapper {}

// MPTABLE, describing VCPUS.
const MPTABLE_START: u64 = 0x9fc00;
pub const MPTABLE_START: u64 = 0x9fc00;

#[derive(Debug, Eq, PartialEq)]
pub enum Error {
Expand Down
Loading
Loading