From 62abc117abe3249795521f63784f6ee00a085e57 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Mon, 8 Feb 2021 17:27:47 +0000 Subject: [PATCH] tdx: Configure TDX state for the VM Load the sections backed from the file into their required addresses in memory and populate the HOB with details of the memory. Using the HOB address initialize the TDX state in the vCPUs and finalize the TDX configuration. Signed-off-by: Rob Bradford --- vmm/src/vm.rs | 195 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 193 insertions(+), 2 deletions(-) diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 1b0586b20..657a8ae8d 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -245,6 +245,34 @@ pub enum Error { /// Error triggering power button PowerButton(device_manager::DeviceManagerError), + + /// Error doing I/O on TDX firmware file + #[cfg(feature = "tdx")] + LoadTdvf(std::io::Error), + + /// Error parsing TDVF + #[cfg(feature = "tdx")] + ParseTdvf(arch::x86_64::tdx::TdvfError), + + /// Error populating HOB + #[cfg(feature = "tdx")] + PopulateHob(arch::x86_64::tdx::TdvfError), + + /// Error allocating TDVF memory + #[cfg(feature = "tdx")] + AllocatingTdvfMemory(crate::memory_manager::Error), + + /// Error enabling TDX VM + #[cfg(feature = "tdx")] + InitializeTDXVM(hypervisor::HypervisorVmError), + + /// Error enabling TDX memory region + #[cfg(feature = "tdx")] + InitializeTDXMemoryRegion(hypervisor::HypervisorVmError), + + /// Error finalizing TDX setup + #[cfg(feature = "tdx")] + FinalizeTDX(hypervisor::HypervisorVmError), } pub type Result = result::Result; @@ -669,14 +697,24 @@ impl Vm { serial_pty: Option, console_pty: Option, ) -> Result { + #[cfg(feature = "tdx")] + let tdx_enabled = config.lock().unwrap().tdx.is_some(); #[cfg(all(feature = "kvm", target_arch = "x86_64"))] hypervisor.check_required_extensions().unwrap(); + #[cfg(feature = "tdx")] + let vm = hypervisor + .create_vm_with_type(if tdx_enabled { + 2 // KVM_X86_TDX_VM + } else { + 0 // KVM_X86_LEGACY_VM + }) + .unwrap(); + #[cfg(not(feature = "tdx"))] let vm = hypervisor.create_vm().unwrap(); + #[cfg(target_arch = "x86_64")] vm.enable_split_irq().unwrap(); let phys_bits = physical_bits(config.lock().unwrap().cpus.max_phys_bits); - #[cfg(feature = "tdx")] - let tdx_enabled = config.lock().unwrap().tdx.is_some(); let memory_manager = MemoryManager::new( vm.clone(), &config.lock().unwrap().memory.clone(), @@ -1509,6 +1547,131 @@ impl Vm { } } + #[cfg(feature = "tdx")] + fn init_tdx(&mut self) -> Result<()> { + let cpuid = self.cpu_manager.lock().unwrap().common_cpuid(); + let max_vcpus = self.cpu_manager.lock().unwrap().max_vcpus() as u32; + self.vm + .tdx_init(&cpuid, max_vcpus) + .map_err(Error::InitializeTDXVM)?; + Ok(()) + } + + #[cfg(feature = "tdx")] + fn init_tdx_memory(&mut self) -> Result> { + use arch::x86_64::tdx::*; + // Get the memory end *before* we start adding TDVF ram regions + let mem_end = { + let guest_memory = self.memory_manager.lock().as_ref().unwrap().guest_memory(); + let mem = guest_memory.memory(); + mem.last_addr() + }; + + // The TDVF file contains a table of section as well as code + let mut firmware_file = + File::open(&self.config.lock().unwrap().tdx.as_ref().unwrap().firmware) + .map_err(Error::LoadTdvf)?; + + // For all the sections allocate some RAM backing them + let sections = parse_tdvf_sections(&mut firmware_file).map_err(Error::ParseTdvf)?; + for section in §ions { + info!("Allocating TDVF Section: {:?}", section); + self.memory_manager + .lock() + .unwrap() + .add_ram_region(GuestAddress(section.address), section.size as usize) + .map_err(Error::AllocatingTdvfMemory)?; + } + + // The guest memory at this point now has all the required regions so it + // is safe to copy from the TDVF file into it. + let guest_memory = self.memory_manager.lock().as_ref().unwrap().guest_memory(); + let mem = guest_memory.memory(); + let mut hob_offset = None; + for section in §ions { + info!("Populating TDVF Section: {:?}", section); + match section.r#type { + TdvfSectionType::Bfv | TdvfSectionType::Cfv => { + info!("Copying section to guest memory"); + firmware_file + .seek(SeekFrom::Start(section.data_offset as u64)) + .map_err(Error::LoadTdvf)?; + mem.read_from( + GuestAddress(section.address), + &mut firmware_file, + section.data_size as usize, + ) + .unwrap(); + } + TdvfSectionType::TdHob => { + hob_offset = Some(section.address); + } + _ => {} + } + } + + // Generate HOB + let mut hob = TdHob::start(hob_offset.unwrap()); + + // RAM regions (all below 3GiB case) + if mem_end < arch::layout::MEM_32BIT_RESERVED_START { + hob.add_memory_resource(&mem, 0, mem_end.0 + 1, true) + .map_err(Error::PopulateHob)?; + } else { + // Otherwise split into two + hob.add_memory_resource(&mem, 0, arch::layout::MEM_32BIT_RESERVED_START.0, true) + .map_err(Error::PopulateHob)?; + if mem_end > arch::layout::RAM_64BIT_START { + hob.add_memory_resource( + &mem, + arch::layout::RAM_64BIT_START.raw_value(), + mem_end.unchecked_offset_from(arch::layout::RAM_64BIT_START) + 1, + true, + ) + .map_err(Error::PopulateHob)?; + } + } + + // MMIO regions + hob.add_mmio_resource( + &mem, + arch::layout::MEM_32BIT_DEVICES_START.raw_value(), + arch::layout::APIC_START.raw_value() + - arch::layout::MEM_32BIT_DEVICES_START.raw_value(), + ) + .map_err(Error::PopulateHob)?; + let start_of_device_area = self + .memory_manager + .lock() + .unwrap() + .start_of_device_area() + .raw_value(); + let end_of_device_area = self + .memory_manager + .lock() + .unwrap() + .end_of_device_area() + .raw_value(); + hob.add_mmio_resource(&mem, start_of_device_area, end_of_device_area) + .map_err(Error::PopulateHob)?; + + hob.finish(&mem).map_err(Error::PopulateHob)?; + + for section in §ions { + self.vm + .tdx_init_memory_region( + mem.get_host_address(GuestAddress(section.address)).unwrap() as u64, + section.address, + section.size, + /* TDVF_SECTION_ATTRIBUTES_EXTENDMR */ + section.attributes == 1, + ) + .map_err(Error::InitializeTDXMemoryRegion)?; + } + + Ok(hob_offset) + } + pub fn boot(&mut self) -> Result<()> { event!("vm", "booting"); let current_state = self.get_state()?; @@ -1526,6 +1689,13 @@ impl Vm { None }; + // The initial TDX configuration must be done before the vCPUs are + // created + #[cfg(feature = "tdx")] + if self.config.lock().unwrap().tdx.is_some() { + self.init_tdx()?; + } + // Create and configure vcpus self.cpu_manager .lock() @@ -1533,11 +1703,32 @@ impl Vm { .create_boot_vcpus(entry_point) .map_err(Error::CpuManager)?; + // Configuring the TDX regions requires that the vCPUs are created + #[cfg(feature = "tdx")] + let hob_address = if self.config.lock().unwrap().tdx.is_some() { + self.init_tdx_memory()? + } else { + None + }; + // Configure shared state based on loaded kernel entry_point .map(|entry_point| self.configure_system(entry_point)) .transpose()?; + #[cfg(feature = "tdx")] + if let Some(hob_address) = hob_address { + // With the HOB address extracted the vCPUs can have + // their TDX state configured. + self.cpu_manager + .lock() + .unwrap() + .initialize_tdx(hob_address) + .map_err(Error::CpuManager)?; + // With TDX memory and CPU state configured TDX setup is complete + self.vm.tdx_finalize().map_err(Error::FinalizeTDX)?; + } + self.cpu_manager .lock() .unwrap()