use crate::cpuid::HV_PSP_CPUID_PAGE;
use crate::importer::Aarch64Register;
use crate::importer::BootPageAcceptance;
use crate::importer::IgvmParameterType;
use crate::importer::ImageLoad;
use crate::importer::IsolationConfig;
use crate::importer::IsolationType;
use crate::importer::SegmentRegister;
use crate::importer::StartupMemoryType;
use crate::importer::TableRegister;
use crate::importer::X86Register;
use crate::linux::InitrdAddressType;
use crate::linux::InitrdConfig;
use crate::linux::InitrdInfo;
use crate::linux::KernelInfo;
use crate::linux::load_kernel_and_initrd_arm64;
use aarch64defs::Cpsr64;
use aarch64defs::IntermPhysAddrSize;
use aarch64defs::SctlrEl1;
use aarch64defs::TranslationBaseEl1;
use aarch64defs::TranslationControlEl1;
use aarch64defs::TranslationGranule0;
use aarch64defs::TranslationGranule1;
use hvdef::HV_PAGE_SIZE;
use hvdef::Vtl;
use igvm::registers::AArch64Register;
use loader_defs::paravisor::*;
use loader_defs::shim::ShimParamsRaw;
use memory_range::MemoryRange;
use page_table::aarch64::Arm64PageSize;
use page_table::aarch64::MemoryAttributeEl1;
use page_table::aarch64::MemoryAttributeIndirectionEl1;
use page_table::x64::PageTableBuilder;
use page_table::x64::X64_LARGE_PAGE_SIZE;
use page_table::x64::align_up_to_large_page_size;
use page_table::x64::align_up_to_page_size;
use page_table::x64::calculate_pde_table_count;
use thiserror::Error;
use x86defs::GdtEntry;
use x86defs::X64_BUSY_TSS_SEGMENT_ATTRIBUTES;
use x86defs::X64_DEFAULT_CODE_SEGMENT_ATTRIBUTES;
use x86defs::X64_DEFAULT_DATA_SEGMENT_ATTRIBUTES;
use x86defs::cpuid::CpuidFunction;
use zerocopy::FromZeros;
use zerocopy::IntoBytes;
#[derive(Debug)]
pub struct Vtl0Linux<'a> {
pub command_line: &'a std::ffi::CString,
pub load_info: crate::linux::LoadInfo,
}
#[derive(Debug)]
pub struct Vtl0Config<'a> {
pub supports_pcat: bool,
pub supports_uefi: Option<(crate::uefi::LoadInfo, Vec<u8>)>,
pub supports_linux: Option<Vtl0Linux<'a>>,
}
pub const HCL_SECURE_VTL: Vtl = Vtl::Vtl2;
#[derive(Debug, Error)]
pub enum Error {
#[error("memory is unaligned: {0}")]
MemoryUnaligned(u64),
#[error("command line too large: {0}")]
CommandLineSize(usize),
#[error("kernel load error")]
Kernel(#[source] crate::linux::Error),
#[error("shim load error")]
Shim(#[source] crate::elf::Error),
#[error("invalid initrd size: {0}")]
InvalidInitrdSize(u64),
#[error("memory used: {0} is greater than available")]
NotEnoughMemory(u64),
#[error("importer error")]
Importer(#[from] anyhow::Error),
}
pub enum CommandLineType<'a> {
Static(&'a str),
HostAppendable(&'a str),
}
pub fn load_openhcl_x64<F>(
importer: &mut dyn ImageLoad<X86Register>,
kernel_image: &mut F,
shim: &mut F,
sidecar: Option<&mut F>,
command_line: CommandLineType<'_>,
initrd: Option<&[u8]>,
memory_page_base: Option<u64>,
memory_page_count: u64,
vtl0_config: Vtl0Config<'_>,
) -> Result<(), Error>
where
F: std::io::Read + std::io::Seek,
{
let IsolationConfig {
isolation_type,
paravisor_present,
shared_gpa_boundary_bits,
} = importer.isolation_config();
let with_relocation = memory_page_base.is_none() && isolation_type == IsolationType::None;
let memory_start_address = memory_page_base
.map(|page_number| page_number * HV_PAGE_SIZE)
.unwrap_or(PARAVISOR_DEFAULT_MEMORY_BASE_ADDRESS);
let memory_size = memory_page_count * HV_PAGE_SIZE;
if memory_start_address % X64_LARGE_PAGE_SIZE != 0 {
return Err(Error::MemoryUnaligned(memory_start_address));
}
if memory_size % X64_LARGE_PAGE_SIZE != 0 {
return Err(Error::MemoryUnaligned(memory_size));
}
importer.verify_startup_memory_available(
memory_start_address / HV_PAGE_SIZE,
memory_page_count,
if paravisor_present {
StartupMemoryType::Vtl2ProtectableRam
} else {
StartupMemoryType::Ram
},
)?;
let kernel_acceptance = match isolation_type {
IsolationType::Snp | IsolationType::Tdx => BootPageAcceptance::Shared,
_ => BootPageAcceptance::Exclusive,
};
let mut offset = memory_start_address;
let bounce_buffer = if matches!(isolation_type, IsolationType::Snp | IsolationType::Tdx) {
let bounce_buffer_gpa = offset;
assert_eq!(bounce_buffer_gpa % X64_LARGE_PAGE_SIZE, 0);
let range = MemoryRange::new(bounce_buffer_gpa..bounce_buffer_gpa + X64_LARGE_PAGE_SIZE);
offset += range.len();
Some(range)
} else {
None
};
tracing::trace!(offset, "loading the kernel");
let load_info = crate::elf::load_static_elf(
importer,
kernel_image,
offset,
0,
true,
kernel_acceptance,
"underhill-kernel",
)
.map_err(|e| Error::Kernel(crate::linux::Error::ElfLoader(e)))?;
tracing::trace!("Kernel loaded at {load_info:x?}");
let crate::elf::LoadInfo {
minimum_address_used: _min_addr,
next_available_address: mut offset,
entrypoint: kernel_entrypoint,
} = load_info;
assert_eq!(offset & (HV_PAGE_SIZE - 1), 0);
let (sidecar_size, sidecar_entrypoint) = if let Some(sidecar) = sidecar {
offset = align_up_to_large_page_size(offset);
let load_info = crate::elf::load_static_elf(
importer,
sidecar,
0,
offset,
false,
BootPageAcceptance::Exclusive,
"sidecar-kernel",
)
.map_err(|e| Error::Kernel(crate::linux::Error::ElfLoader(e)))?;
(
load_info.next_available_address - offset,
load_info.entrypoint,
)
} else {
(0, 0)
};
let sidecar_base = offset;
offset += sidecar_size;
let load_info = crate::elf::load_static_elf(
importer,
shim,
0,
offset,
false,
BootPageAcceptance::Exclusive,
"underhill-boot-shim",
)
.map_err(Error::Shim)?;
tracing::trace!("The boot shim loaded at {load_info:x?}");
let crate::elf::LoadInfo {
minimum_address_used: shim_base_addr,
next_available_address: mut offset,
entrypoint: shim_entry_address,
} = load_info;
let ramdisk = if let Some(initrd) = initrd {
let initrd_base = offset;
let initrd_size = align_up_to_page_size(initrd.len() as u64);
importer.import_pages(
initrd_base / HV_PAGE_SIZE,
initrd_size / HV_PAGE_SIZE,
"underhill-initrd",
kernel_acceptance,
initrd,
)?;
offset += initrd_size;
Some((initrd_base, initrd.len() as u64))
} else {
None
};
let gdt_base_address = offset;
let gdt_size = HV_PAGE_SIZE;
offset += gdt_size;
let boot_params_base = offset;
let boot_params_size = HV_PAGE_SIZE;
offset += boot_params_size;
let cmdline_base = offset;
let (cmdline, policy) = match command_line {
CommandLineType::Static(val) => (val, CommandLinePolicy::STATIC),
CommandLineType::HostAppendable(val) => (val, CommandLinePolicy::APPEND_CHOSEN),
};
if cmdline.len() > COMMAND_LINE_SIZE {
return Err(Error::CommandLineSize(cmdline.len()));
}
let mut static_command_line = [0; COMMAND_LINE_SIZE];
static_command_line[..cmdline.len()].copy_from_slice(cmdline.as_bytes());
let paravisor_command_line = ParavisorCommandLine {
policy,
static_command_line_len: cmdline.len() as u16,
static_command_line,
};
importer.import_pages(
cmdline_base / HV_PAGE_SIZE,
1,
"underhill-command-line",
BootPageAcceptance::Exclusive,
paravisor_command_line.as_bytes(),
)?;
offset += HV_PAGE_SIZE;
let reserved_region_size = PARAVISOR_RESERVED_VTL2_PAGE_COUNT_MAX * HV_PAGE_SIZE;
let reserved_region_start = offset;
offset += reserved_region_size;
tracing::debug!(reserved_region_start);
let parameter_region_size = PARAVISOR_VTL2_CONFIG_REGION_PAGE_COUNT_MAX * HV_PAGE_SIZE;
let parameter_region_start = offset;
offset += parameter_region_size;
tracing::debug!(parameter_region_start);
let end_of_underhill_mem = offset;
let local_map = match isolation_type {
IsolationType::Snp | IsolationType::Tdx => {
Some((PARAVISOR_LOCAL_MAP_VA, PARAVISOR_LOCAL_MAP_SIZE))
}
_ => None,
};
let page_table_base_page_count = 5;
let page_table_dynamic_page_count = {
calculate_pde_table_count(memory_start_address, memory_size) * 2
+ local_map.map_or(0, |v| calculate_pde_table_count(v.0, v.1))
};
let page_table_isolation_page_count = match isolation_type {
IsolationType::Tdx => {
3
}
_ => 0,
};
let page_table_page_count = page_table_base_page_count
+ page_table_dynamic_page_count
+ page_table_isolation_page_count;
let page_table_region_size = HV_PAGE_SIZE * page_table_page_count;
let page_table_region_start = offset;
offset += page_table_region_size;
tracing::debug!(page_table_region_start, page_table_region_size);
let mut page_table_builder = PageTableBuilder::new(page_table_region_start)
.with_mapped_region(memory_start_address, memory_size);
if let Some((local_map_start, size)) = local_map {
page_table_builder = page_table_builder.with_local_map(local_map_start, size);
}
match isolation_type {
IsolationType::Snp => {
page_table_builder = page_table_builder.with_confidential_bit(51);
}
IsolationType::Tdx => {
page_table_builder = page_table_builder.with_reset_vector(true);
}
_ => {}
}
let page_table = page_table_builder.build();
assert!(page_table.len() as u64 % HV_PAGE_SIZE == 0);
let page_table_page_base = page_table_region_start / HV_PAGE_SIZE;
assert!(page_table.len() as u64 <= page_table_region_size);
let offset = offset;
if with_relocation {
importer.relocation_region(
memory_start_address,
end_of_underhill_mem - memory_start_address,
X64_LARGE_PAGE_SIZE,
PARAVISOR_DEFAULT_MEMORY_BASE_ADDRESS,
1 << 48,
true,
true,
0, )?;
importer.page_table_relocation(
page_table_region_start,
page_table_region_size / HV_PAGE_SIZE,
page_table.len() as u64 / HV_PAGE_SIZE,
0,
)?;
}
if offset > memory_start_address + memory_size {
return Err(Error::NotEnoughMemory(offset - memory_start_address));
}
let (initrd_base, initrd_size) = ramdisk.unwrap_or((0, 0));
let calculate_shim_offset = |addr: u64| addr.wrapping_sub(shim_base_addr) as i64;
let initrd_crc = crc32fast::hash(initrd.unwrap_or(&[]));
let shim_params = ShimParamsRaw {
kernel_entry_offset: calculate_shim_offset(kernel_entrypoint),
cmdline_offset: calculate_shim_offset(cmdline_base),
initrd_offset: calculate_shim_offset(initrd_base),
initrd_size,
initrd_crc,
supported_isolation_type: match isolation_type {
IsolationType::None | IsolationType::Vbs => {
loader_defs::shim::SupportedIsolationType::VBS
}
IsolationType::Snp => loader_defs::shim::SupportedIsolationType::SNP,
IsolationType::Tdx => loader_defs::shim::SupportedIsolationType::TDX,
},
memory_start_offset: calculate_shim_offset(memory_start_address),
memory_size,
parameter_region_offset: calculate_shim_offset(parameter_region_start),
parameter_region_size,
vtl2_reserved_region_offset: calculate_shim_offset(reserved_region_start),
vtl2_reserved_region_size: reserved_region_size,
sidecar_offset: calculate_shim_offset(sidecar_base),
sidecar_size,
sidecar_entry_offset: calculate_shim_offset(sidecar_entrypoint),
used_start: calculate_shim_offset(memory_start_address),
used_end: calculate_shim_offset(offset),
bounce_buffer_start: bounce_buffer.map_or(0, |r| calculate_shim_offset(r.start())),
bounce_buffer_size: bounce_buffer.map_or(0, |r| r.len()),
};
tracing::debug!(boot_params_base, "shim gpa");
importer
.import_pages(
boot_params_base / HV_PAGE_SIZE,
boot_params_size / HV_PAGE_SIZE,
"underhill-shim-params",
BootPageAcceptance::Exclusive,
shim_params.as_bytes(),
)
.map_err(Error::Importer)?;
importer.import_pages(
page_table_page_base,
page_table_page_count,
"underhill-page-tables",
BootPageAcceptance::Exclusive,
&page_table,
)?;
let default_data_attributes: u16 = X64_DEFAULT_DATA_SEGMENT_ATTRIBUTES.into();
let default_code_attributes: u16 = X64_DEFAULT_CODE_SEGMENT_ATTRIBUTES.into();
let gdt = [
GdtEntry::new_zeroed(),
GdtEntry::new_zeroed(),
GdtEntry {
limit_low: 0xffff,
attr_low: default_code_attributes as u8,
attr_high: (default_code_attributes >> 8) as u8,
..GdtEntry::new_zeroed()
},
GdtEntry {
limit_low: 0xffff,
attr_low: default_data_attributes as u8,
attr_high: (default_data_attributes >> 8) as u8,
..GdtEntry::new_zeroed()
},
];
let gdt_entry_size = size_of::<GdtEntry>();
let linear_selector_offset = 3 * gdt_entry_size;
let linear_code64_selector_offset = 2 * gdt_entry_size;
importer.import_pages(
gdt_base_address / HV_PAGE_SIZE,
gdt_size / HV_PAGE_SIZE,
"underhill-gdt",
BootPageAcceptance::Exclusive,
gdt.as_bytes(),
)?;
let mut import_reg = |register| {
importer
.import_vp_register(register)
.map_err(Error::Importer)
};
import_reg(X86Register::Gdtr(TableRegister {
base: gdt_base_address,
limit: (size_of::<GdtEntry>() * 4 - 1) as u16,
}))?;
let ds = SegmentRegister {
selector: linear_selector_offset as u16,
base: 0,
limit: 0xffffffff,
attributes: default_data_attributes,
};
import_reg(X86Register::Ds(ds))?;
import_reg(X86Register::Es(ds))?;
import_reg(X86Register::Fs(ds))?;
import_reg(X86Register::Gs(ds))?;
import_reg(X86Register::Ss(ds))?;
let cs = SegmentRegister {
selector: linear_code64_selector_offset as u16,
base: 0,
limit: 0xffffffff,
attributes: default_code_attributes,
};
import_reg(X86Register::Cs(cs))?;
import_reg(X86Register::Tr(SegmentRegister {
selector: 0x0000,
base: 0x00000000,
limit: 0x0000FFFF,
attributes: X64_BUSY_TSS_SEGMENT_ATTRIBUTES.into(),
}))?;
import_reg(X86Register::Cr0(
x86defs::X64_CR0_PG | x86defs::X64_CR0_PE | x86defs::X64_CR0_NE,
))?;
import_reg(X86Register::Cr3(page_table_region_start))?;
import_reg(X86Register::Cr4(
x86defs::X64_CR4_PAE | x86defs::X64_CR4_MCE | x86defs::X64_CR4_OSXSAVE,
))?;
import_reg(X86Register::Efer(
x86defs::X64_EFER_LMA | x86defs::X64_EFER_LME | x86defs::X64_EFER_NXE,
))?;
import_reg(X86Register::Pat(x86defs::X86X_MSR_DEFAULT_PAT))?;
let relative_boot_params_base = boot_params_base - shim_base_addr;
import_reg(X86Register::Rsi(relative_boot_params_base))?;
import_reg(X86Register::Rip(shim_entry_address))?;
let config_region_page_base = parameter_region_start / HV_PAGE_SIZE;
let slit_page_base = config_region_page_base + PARAVISOR_CONFIG_SLIT_PAGE_INDEX;
let slit_parameter_area = importer.create_parameter_area(
slit_page_base,
PARAVISOR_CONFIG_SLIT_SIZE_PAGES as u32,
"underhill-slit",
)?;
importer.import_parameter(slit_parameter_area, 0, IgvmParameterType::Slit)?;
let pptt_page_base = config_region_page_base + PARAVISOR_CONFIG_PPTT_PAGE_INDEX;
let pptt_parameter_area = importer.create_parameter_area(
pptt_page_base,
PARAVISOR_CONFIG_PPTT_SIZE_PAGES as u32,
"underhill-pptt",
)?;
importer.import_parameter(pptt_parameter_area, 0, IgvmParameterType::Pptt)?;
let dt_page_base = config_region_page_base + PARAVISOR_CONFIG_DEVICE_TREE_PAGE_INDEX;
let dt_parameter_area = importer.create_parameter_area(
dt_page_base,
PARAVISOR_CONFIG_DEVICE_TREE_SIZE_PAGES as u32,
"underhill-device-tree",
)?;
importer.import_parameter(dt_parameter_area, 0, IgvmParameterType::DeviceTree)?;
if isolation_type == IsolationType::Snp {
let reserved_region_page_base = reserved_region_start / HV_PAGE_SIZE;
let secrets_page_base: u64 =
reserved_region_page_base + PARAVISOR_RESERVED_VTL2_SNP_SECRETS_PAGE_INDEX;
importer.import_pages(
secrets_page_base,
PARAVISOR_RESERVED_VTL2_SNP_SECRETS_SIZE_PAGES,
"underhill-snp-secrets-page",
BootPageAcceptance::SecretsPage,
&[],
)?;
let cpuid_page = create_snp_cpuid_page();
let cpuid_page_base =
reserved_region_page_base + PARAVISOR_RESERVED_VTL2_SNP_CPUID_PAGE_INDEX;
importer.import_pages(
cpuid_page_base,
1,
"underhill-snp-cpuid-page",
BootPageAcceptance::CpuidPage,
cpuid_page.as_bytes(),
)?;
importer.import_pages(
cpuid_page_base + 1,
1,
"underhill-snp-cpuid-extended-state-page",
BootPageAcceptance::CpuidExtendedStatePage,
&[],
)?;
let vmsa_page_base =
reserved_region_page_base + PARAVISOR_RESERVED_VTL2_SNP_VMSA_PAGE_INDEX;
importer.set_vp_context_page(vmsa_page_base)?;
}
let mut free_page = 1;
let mut measured_config = ParavisorMeasuredVtl0Config {
magic: ParavisorMeasuredVtl0Config::MAGIC,
..FromZeros::new_zeroed()
};
let Vtl0Config {
supports_pcat,
supports_uefi,
supports_linux,
} = vtl0_config;
if supports_pcat {
measured_config.supported_vtl0.set_pcat_supported(true);
}
if let Some((uefi, vp_context)) = &supports_uefi {
measured_config.supported_vtl0.set_uefi_supported(true);
let vp_context_page = free_page;
free_page += 1;
measured_config.uefi_info = UefiInfo {
firmware: PageRegionDescriptor {
base_page_number: uefi.firmware_base / HV_PAGE_SIZE,
page_count: uefi.total_size / HV_PAGE_SIZE,
},
vtl0_vp_context: PageRegionDescriptor {
base_page_number: vp_context_page,
page_count: 1,
},
};
importer.import_pages(
vp_context_page,
1,
"openhcl-uefi-vp-context",
BootPageAcceptance::Exclusive,
vp_context,
)?;
}
if let Some(linux) = supports_linux {
measured_config
.supported_vtl0
.set_linux_direct_supported(true);
let kernel_region = PageRegionDescriptor::new(
linux.load_info.kernel.gpa / HV_PAGE_SIZE,
align_up_to_page_size(linux.load_info.kernel.size) / HV_PAGE_SIZE,
);
let (initrd_region, initrd_size) = match linux.load_info.initrd {
Some(info) => {
if info.gpa % HV_PAGE_SIZE != 0 {
return Err(Error::MemoryUnaligned(info.gpa));
}
(
PageRegionDescriptor::new(
info.gpa / HV_PAGE_SIZE,
align_up_to_page_size(info.size) / HV_PAGE_SIZE,
),
info.size,
)
}
None => (PageRegionDescriptor::EMPTY, 0),
};
let command_line_page = free_page;
importer
.import_pages(
command_line_page,
1,
"underhill-vtl0-linux-command-line",
BootPageAcceptance::Exclusive,
linux.command_line.as_bytes_with_nul(),
)
.map_err(Error::Importer)?;
let command_line = PageRegionDescriptor::new(command_line_page, 1);
measured_config.linux_info = LinuxInfo {
kernel_region,
kernel_entrypoint: linux.load_info.kernel.entrypoint,
initrd_region,
initrd_size,
command_line,
};
}
importer
.import_pages(
PARAVISOR_VTL0_MEASURED_CONFIG_BASE_PAGE_X64,
1,
"underhill-measured-config",
BootPageAcceptance::Exclusive,
measured_config.as_bytes(),
)
.map_err(Error::Importer)?;
let vtl2_measured_config = ParavisorMeasuredVtl2Config {
magic: ParavisorMeasuredVtl2Config::MAGIC,
vtom_offset_bit: shared_gpa_boundary_bits.unwrap_or(0),
padding: [0; 7],
};
importer
.import_pages(
config_region_page_base + PARAVISOR_MEASURED_VTL2_CONFIG_PAGE_INDEX,
PARAVISOR_MEASURED_VTL2_CONFIG_SIZE_PAGES,
"underhill-vtl2-measured-config",
BootPageAcceptance::Exclusive,
vtl2_measured_config.as_bytes(),
)
.map_err(Error::Importer)?;
let imported_region_base =
config_region_page_base + PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_PAGE_INDEX;
importer.set_imported_regions_config_page(imported_region_base);
Ok(())
}
fn create_snp_cpuid_page() -> HV_PSP_CPUID_PAGE {
let mut cpuid_page = HV_PSP_CPUID_PAGE::default();
for (i, required_leaf) in crate::cpuid::SNP_REQUIRED_CPUID_LEAF_LIST_PARAVISOR
.iter()
.enumerate()
{
let entry = &mut cpuid_page.cpuid_leaf_info[i];
entry.eax_in = required_leaf.eax;
entry.ecx_in = required_leaf.ecx;
if required_leaf.eax == CpuidFunction::ExtendedStateEnumeration.0 {
entry.xfem_in = 1;
}
cpuid_page.count += 1;
}
cpuid_page
}
pub fn load_openhcl_arm64<F>(
importer: &mut dyn ImageLoad<Aarch64Register>,
kernel_image: &mut F,
shim: &mut F,
command_line: CommandLineType<'_>,
initrd: Option<&[u8]>,
memory_page_base: Option<u64>,
memory_page_count: u64,
vtl0_config: Vtl0Config<'_>,
) -> Result<(), Error>
where
F: std::io::Read + std::io::Seek,
{
let Vtl0Config {
supports_pcat,
supports_uefi,
supports_linux,
} = vtl0_config;
assert!(!supports_pcat);
assert!(supports_uefi.is_some() || supports_linux.is_some());
let paravisor_present = importer.isolation_config().paravisor_present;
let with_relocation = memory_page_base.is_none();
let memory_start_address = memory_page_base
.map(|page_number| page_number * HV_PAGE_SIZE)
.unwrap_or(PARAVISOR_DEFAULT_MEMORY_BASE_ADDRESS);
let memory_size = memory_page_count * HV_PAGE_SIZE;
if memory_start_address % u64::from(Arm64PageSize::Large) != 0 {
return Err(Error::MemoryUnaligned(memory_start_address));
}
if memory_size % u64::from(Arm64PageSize::Large) != 0 {
return Err(Error::MemoryUnaligned(memory_size));
}
importer.verify_startup_memory_available(
memory_start_address / HV_PAGE_SIZE,
memory_page_count,
if paravisor_present {
StartupMemoryType::Vtl2ProtectableRam
} else {
StartupMemoryType::Ram
},
)?;
tracing::trace!(memory_start_address, "loading the kernel");
let initrd_address_type = InitrdAddressType::AfterKernel;
let initrd_config = InitrdConfig {
initrd_address: initrd_address_type,
initrd: initrd.unwrap_or_default(),
};
let device_tree_blob = None;
let crate::linux::LoadInfo {
kernel:
KernelInfo {
gpa: kernel_base,
size: kernel_size,
entrypoint: kernel_entry_point,
},
initrd: initrd_info,
dtb,
} = load_kernel_and_initrd_arm64(
importer,
kernel_image,
memory_start_address,
Some(initrd_config),
device_tree_blob,
)
.map_err(Error::Kernel)?;
assert!(
dtb.is_none(),
"DeviceTree is generated dynamically by the boot shim."
);
tracing::trace!(kernel_base, "kernel loaded");
let mut next_addr;
let InitrdInfo {
gpa: initrd_gpa,
size: initrd_size,
} = if let Some(initrd_info) = initrd_info {
assert!(initrd_address_type == InitrdAddressType::AfterKernel);
next_addr = initrd_info.gpa + initrd_info.size;
initrd_info
} else {
next_addr = kernel_base + kernel_size;
InitrdInfo { gpa: 0, size: 0 }
};
next_addr = align_up_to_page_size(next_addr);
tracing::trace!(next_addr, "loading the boot shim");
let crate::elf::LoadInfo {
minimum_address_used: shim_base_addr,
next_available_address: mut next_addr,
entrypoint: shim_entry_point,
} = crate::elf::load_static_elf(
importer,
shim,
0,
next_addr,
false,
BootPageAcceptance::Exclusive,
"underhill-boot-shim",
)
.map_err(Error::Shim)?;
tracing::trace!(shim_base_addr, "boot shim loaded");
tracing::trace!(next_addr, "loading the command line");
let cmdline_base = next_addr;
let (cmdline, policy) = match command_line {
CommandLineType::Static(val) => (val, CommandLinePolicy::STATIC),
CommandLineType::HostAppendable(val) => (val, CommandLinePolicy::APPEND_CHOSEN),
};
if cmdline.len() > COMMAND_LINE_SIZE {
return Err(Error::CommandLineSize(cmdline.len()));
}
let mut static_command_line = [0; COMMAND_LINE_SIZE];
static_command_line[..cmdline.len()].copy_from_slice(cmdline.as_bytes());
let paravisor_command_line = ParavisorCommandLine {
policy,
static_command_line_len: cmdline.len() as u16,
static_command_line,
};
importer.import_pages(
cmdline_base / HV_PAGE_SIZE,
1,
"underhill-command-line",
BootPageAcceptance::Exclusive,
paravisor_command_line.as_bytes(),
)?;
next_addr += HV_PAGE_SIZE;
tracing::trace!(next_addr, "loading the boot shim parameters");
let shim_params_base = next_addr;
let shim_params_size = HV_PAGE_SIZE;
next_addr += shim_params_size;
let parameter_region_size = PARAVISOR_VTL2_CONFIG_REGION_PAGE_COUNT_MAX * HV_PAGE_SIZE;
let parameter_region_start = next_addr;
next_addr += parameter_region_size;
tracing::debug!(parameter_region_start);
let end_of_underhill_mem = next_addr;
let page_table_base_page_count = 5;
let page_table_dynamic_page_count = 2 * page_table_base_page_count;
let page_table_page_count = page_table_base_page_count + page_table_dynamic_page_count;
let page_table_region_size = HV_PAGE_SIZE * page_table_page_count;
let page_table_region_start = next_addr;
next_addr += page_table_region_size;
tracing::debug!(page_table_region_start, page_table_region_size);
let next_addr = next_addr;
if next_addr > memory_start_address + memory_size {
return Err(Error::NotEnoughMemory(next_addr - memory_start_address));
}
let calculate_shim_offset = |addr: u64| -> i64 { addr.wrapping_sub(shim_base_addr) as i64 };
let initrd_crc = crc32fast::hash(initrd.unwrap_or(&[]));
let shim_params = ShimParamsRaw {
kernel_entry_offset: calculate_shim_offset(kernel_entry_point),
cmdline_offset: calculate_shim_offset(cmdline_base),
initrd_offset: calculate_shim_offset(initrd_gpa),
initrd_size,
initrd_crc,
supported_isolation_type: match importer.isolation_config().isolation_type {
IsolationType::None | IsolationType::Vbs => {
loader_defs::shim::SupportedIsolationType::VBS
}
_ => panic!("only None and VBS are supported for ARM64"),
},
memory_start_offset: calculate_shim_offset(memory_start_address),
memory_size,
parameter_region_offset: calculate_shim_offset(parameter_region_start),
parameter_region_size,
vtl2_reserved_region_offset: 0,
vtl2_reserved_region_size: 0,
sidecar_offset: 0,
sidecar_size: 0,
sidecar_entry_offset: 0,
used_start: calculate_shim_offset(memory_start_address),
used_end: calculate_shim_offset(next_addr),
bounce_buffer_start: 0,
bounce_buffer_size: 0,
};
importer
.import_pages(
shim_params_base / HV_PAGE_SIZE,
shim_params_size / HV_PAGE_SIZE,
"underhill-shim-params",
BootPageAcceptance::Exclusive,
shim_params.as_bytes(),
)
.map_err(Error::Importer)?;
let mut measured_config = ParavisorMeasuredVtl0Config {
magic: ParavisorMeasuredVtl0Config::MAGIC,
..FromZeros::new_zeroed()
};
if let Some((uefi, vp_context)) = &supports_uefi {
measured_config.supported_vtl0.set_uefi_supported(true);
let vp_context_page = PARAVISOR_VTL0_MEASURED_CONFIG_BASE_PAGE_AARCH64 + 1;
measured_config.uefi_info = UefiInfo {
firmware: PageRegionDescriptor {
base_page_number: uefi.firmware_base / HV_PAGE_SIZE,
page_count: uefi.total_size / HV_PAGE_SIZE,
},
vtl0_vp_context: PageRegionDescriptor {
base_page_number: vp_context_page,
page_count: 1,
},
};
importer.import_pages(
vp_context_page,
1,
"openhcl-uefi-vp-context",
BootPageAcceptance::Exclusive,
vp_context,
)?;
}
importer
.import_pages(
PARAVISOR_VTL0_MEASURED_CONFIG_BASE_PAGE_AARCH64,
1,
"underhill-measured-config",
BootPageAcceptance::Exclusive,
measured_config.as_bytes(),
)
.map_err(Error::Importer)?;
tracing::trace!(page_table_region_start, "loading the page tables");
let memory_attribute_indirection = MemoryAttributeIndirectionEl1([
MemoryAttributeEl1::Device_nGnRnE,
MemoryAttributeEl1::Normal_NonCacheable,
MemoryAttributeEl1::Normal_WriteThrough,
MemoryAttributeEl1::Normal_WriteBack,
MemoryAttributeEl1::Device_nGnRnE,
MemoryAttributeEl1::Device_nGnRnE,
MemoryAttributeEl1::Device_nGnRnE,
MemoryAttributeEl1::Device_nGnRnE,
]);
let page_tables = page_table::aarch64::build_identity_page_tables_aarch64(
page_table_region_start,
memory_start_address,
memory_size,
memory_attribute_indirection,
page_table_region_size as usize,
);
assert!(page_tables.len() as u64 % HV_PAGE_SIZE == 0);
let page_table_page_base = page_table_region_start / HV_PAGE_SIZE;
assert!(page_tables.len() as u64 <= page_table_region_size);
assert!(page_table_region_size as usize > page_tables.len());
if with_relocation {
importer.relocation_region(
memory_start_address,
end_of_underhill_mem - memory_start_address,
Arm64PageSize::Large.into(),
PARAVISOR_DEFAULT_MEMORY_BASE_ADDRESS,
1 << 48,
true,
false,
0, )?;
importer.page_table_relocation(
page_table_region_start,
page_table_region_size / HV_PAGE_SIZE,
page_tables.len() as u64 / HV_PAGE_SIZE,
0,
)?;
}
importer.import_pages(
page_table_page_base,
page_table_page_count,
"underhill-page-tables",
BootPageAcceptance::Exclusive,
&page_tables,
)?;
tracing::trace!("Importing register state");
let mut import_reg = |register| {
importer
.import_vp_register(register)
.map_err(Error::Importer)
};
let relative_boot_params_base = shim_params_base - shim_base_addr;
import_reg(AArch64Register::X0(relative_boot_params_base).into())?;
import_reg(AArch64Register::Pc(shim_entry_point).into())?;
import_reg(AArch64Register::Cpsr(Cpsr64::new().with_sp(true).with_el(1).into()).into())?;
import_reg(
AArch64Register::SctlrEl1(
SctlrEl1::new()
.with_m(true)
.with_c(true)
.with_i(true)
.with_eos(true)
.with_tscxt(true)
.with_eis(true)
.with_span(true)
.with_n_tlsmd(true)
.with_lsmaoe(true)
.into(),
)
.into(),
)?;
import_reg(
AArch64Register::TcrEl1(
TranslationControlEl1::new()
.with_t0sz(0x11)
.with_irgn0(1)
.with_orgn0(1)
.with_sh0(3)
.with_tg0(TranslationGranule0::TG_4KB)
.with_epd1(1)
.with_tg1(TranslationGranule1::TG_4KB)
.with_ips(IntermPhysAddrSize::IPA_48_BITS_256_TB)
.into(),
)
.into(),
)?;
import_reg(AArch64Register::MairEl1(memory_attribute_indirection.into()).into())?;
import_reg(
AArch64Register::Ttbr0El1(
TranslationBaseEl1::new()
.with_baddr(page_table_region_start)
.into(),
)
.into(),
)?;
import_reg(AArch64Register::VbarEl1(0).into())?;
let config_region_page_base = parameter_region_start / HV_PAGE_SIZE;
let slit_page_base = config_region_page_base + PARAVISOR_CONFIG_SLIT_PAGE_INDEX;
let slit_parameter_area = importer.create_parameter_area(
slit_page_base,
PARAVISOR_CONFIG_SLIT_SIZE_PAGES as u32,
"underhill-slit",
)?;
importer.import_parameter(slit_parameter_area, 0, IgvmParameterType::Slit)?;
let pptt_page_base = config_region_page_base + PARAVISOR_CONFIG_PPTT_PAGE_INDEX;
let pptt_parameter_area = importer.create_parameter_area(
pptt_page_base,
PARAVISOR_CONFIG_PPTT_SIZE_PAGES as u32,
"underhill-pptt",
)?;
importer.import_parameter(pptt_parameter_area, 0, IgvmParameterType::Pptt)?;
let dt_page_base = config_region_page_base + PARAVISOR_CONFIG_DEVICE_TREE_PAGE_INDEX;
let dt_parameter_area = importer.create_parameter_area(
dt_page_base,
PARAVISOR_CONFIG_DEVICE_TREE_SIZE_PAGES as u32,
"underhill-device-tree",
)?;
importer.import_parameter(dt_parameter_area, 0, IgvmParameterType::DeviceTree)?;
let vtl2_measured_config = ParavisorMeasuredVtl2Config {
magic: ParavisorMeasuredVtl2Config::MAGIC,
vtom_offset_bit: 0,
padding: [0; 7],
};
importer
.import_pages(
config_region_page_base + PARAVISOR_MEASURED_VTL2_CONFIG_PAGE_INDEX,
PARAVISOR_MEASURED_VTL2_CONFIG_SIZE_PAGES,
"underhill-vtl2-measured-config",
BootPageAcceptance::Exclusive,
vtl2_measured_config.as_bytes(),
)
.map_err(Error::Importer)?;
let imported_region_base =
config_region_page_base + PARAVISOR_MEASURED_VTL2_CONFIG_ACCEPTED_MEMORY_PAGE_INDEX;
importer.set_imported_regions_config_page(imported_region_base);
Ok(())
}