summary refs log tree commit diff
path: root/x86_64/src
diff options
context:
space:
mode:
Diffstat (limited to 'x86_64/src')
-rw-r--r--x86_64/src/cpuid.rs35
-rw-r--r--x86_64/src/lib.rs3
-rw-r--r--x86_64/src/regs.rs107
3 files changed, 140 insertions, 5 deletions
diff --git a/x86_64/src/cpuid.rs b/x86_64/src/cpuid.rs
index 9f76550..5710120 100644
--- a/x86_64/src/cpuid.rs
+++ b/x86_64/src/cpuid.rs
@@ -121,6 +121,41 @@ pub fn setup_cpuid(kvm: &kvm::Kvm, vcpu: &kvm::Vcpu, cpu_id: u64, nrcpus: u64) -
         .map_err(Error::SetSupportedCpusFailed)
 }
 
+/// get host cpu max physical address bits
+pub fn phy_max_address_bits() -> u32 {
+    let mut eax: u32 = 0;
+    let mut ebx: u32 = 0;
+    let mut ecx: u32 = 0;
+    let mut edx: u32 = 0;
+    let mut phys_bits: u32 = 36;
+
+    unsafe {
+        host_cpuid(
+            0x80000000,
+            0,
+            &mut eax as *mut u32,
+            &mut ebx as *mut u32,
+            &mut ecx as *mut u32,
+            &mut edx as *mut u32,
+        );
+    }
+    if eax >= 0x80000008 {
+        unsafe {
+            host_cpuid(
+                0x80000008,
+                0,
+                &mut eax as *mut u32,
+                &mut ebx as *mut u32,
+                &mut ecx as *mut u32,
+                &mut edx as *mut u32,
+            );
+        }
+        phys_bits = eax & 0xff;
+    }
+
+    phys_bits
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs
index 158a11c..172a504 100644
--- a/x86_64/src/lib.rs
+++ b/x86_64/src/lib.rs
@@ -157,6 +157,7 @@ impl std::error::Error for Error {}
 pub struct X8664arch;
 
 const BOOT_STACK_POINTER: u64 = 0x8000;
+// Make sure it align to 256MB for MTRR convenient
 const MEM_32BIT_GAP_SIZE: u64 = (768 << 20);
 const FIRST_ADDR_PAST_32BITS: u64 = (1 << 32);
 const END_ADDR_BEFORE_32BITS: u64 = FIRST_ADDR_PAST_32BITS - MEM_32BIT_GAP_SIZE;
@@ -758,7 +759,7 @@ impl X8664arch {
     ) -> Result<()> {
         let kernel_load_addr = GuestAddress(KERNEL_START_OFFSET);
         cpuid::setup_cpuid(kvm, vcpu, cpu_id, num_cpus).map_err(Error::SetupCpuid)?;
-        regs::setup_msrs(vcpu).map_err(Error::SetupMsrs)?;
+        regs::setup_msrs(vcpu, END_ADDR_BEFORE_32BITS).map_err(Error::SetupMsrs)?;
         let kernel_end = guest_mem
             .checked_offset(kernel_load_addr, KERNEL_64BIT_ENTRY_OFFSET)
             .ok_or(Error::KernelOffsetPastEnd)?;
diff --git a/x86_64/src/regs.rs b/x86_64/src/regs.rs
index 8879a0c..344878a 100644
--- a/x86_64/src/regs.rs
+++ b/x86_64/src/regs.rs
@@ -13,7 +13,7 @@ use kvm_sys::kvm_msr_entry;
 use kvm_sys::kvm_msrs;
 use kvm_sys::kvm_regs;
 use kvm_sys::kvm_sregs;
-use sys_util::{self, GuestAddress, GuestMemory, LayoutAllocation};
+use sys_util::{self, warn, GuestAddress, GuestMemory, LayoutAllocation};
 
 use crate::gdt;
 
@@ -65,7 +65,102 @@ impl Display for Error {
     }
 }
 
-fn create_msr_entries() -> Vec<kvm_msr_entry> {
+const MTRR_MEMTYPE_UC: u8 = 0x0;
+const MTRR_MEMTYPE_WB: u8 = 0x6;
+const MTRR_VAR_VALID: u64 = 0x800;
+const MTRR_ENABLE: u64 = 0x800;
+const MTRR_PHYS_BASE_MSR: u32 = 0x200;
+const MTRR_PHYS_MASK_MSR: u32 = 0x201;
+const VAR_MTRR_NUM_MASK: u64 = 0xFF;
+
+// Returns the value of the highest bit in a 64-bit value. Equivalent to
+// 1 << HighBitSet(x)
+fn get_power_of_two(data: u64) -> u64 {
+    1 << (64 - data.leading_zeros() - 1)
+}
+
+// Returns the max length which suitable for mtrr setting based on the
+// specified (base, len)
+fn get_max_len(base: u64, len: u64) -> u64 {
+    let mut ret = get_power_of_two(len);
+
+    while base % ret != 0 {
+        ret >>= 1;
+    }
+
+    ret
+}
+
+// For the specified (Base, Len), returns (base, len) pair which could be
+// set into mtrr register. mtrr requires: the base-address alignment value can't be
+// less than its length
+fn get_mtrr_pairs(base: u64, len: u64) -> Vec<(u64, u64)> {
+    let mut vecs = Vec::new();
+
+    let mut remains = len;
+    let mut new = base;
+    while remains != 0 {
+        let max = get_max_len(new, remains);
+        vecs.push((new, max));
+        remains -= max;
+        new += max;
+    }
+
+    vecs
+}
+
+fn create_mtrr_entries(vpu: &kvm::Vcpu, pci_start: u64) -> Vec<kvm_msr_entry> {
+    let mut entries = Vec::<kvm_msr_entry>::new();
+
+    // Get VAR MTRR num from MSR_MTRRcap
+    let mut msrs = vec![kvm_msr_entry {
+        index: crate::msr_index::MSR_MTRRcap,
+        ..Default::default()
+    }];
+    if vpu.get_msrs(&mut msrs).is_err() {
+        warn!("get msrs fail, guest with pass through device may be very slow");
+        return entries;
+    }
+    let var_num = msrs[0].data & VAR_MTRR_NUM_MASK;
+
+    // Set pci_start .. 4G as UC
+    // all others are set to default WB
+    let pci_len = (1 << 32) - pci_start;
+    let vecs = get_mtrr_pairs(pci_start, pci_len);
+    if vecs.len() as u64 > var_num {
+        warn!(
+            "mtrr fail for pci mmio, please check pci_start addr,
+              guest with pass through device may be very slow"
+        );
+        return entries;
+    }
+
+    let phys_mask: u64 = (1 << crate::cpuid::phy_max_address_bits()) - 1;
+    for (idx, (base, len)) in vecs.iter().enumerate() {
+        let reg_idx = idx as u32 * 2;
+        entries.push(kvm_msr_entry {
+            index: MTRR_PHYS_BASE_MSR + reg_idx,
+            data: base | MTRR_MEMTYPE_UC as u64,
+            ..Default::default()
+        });
+        let mask: u64 = len.wrapping_neg() & phys_mask | MTRR_VAR_VALID;
+        entries.push(kvm_msr_entry {
+            index: MTRR_PHYS_MASK_MSR + reg_idx,
+            data: mask,
+            ..Default::default()
+        });
+    }
+    // Disable fixed MTRRs and enable variable MTRRs, set default type as WB
+    entries.push(kvm_msr_entry {
+        index: crate::msr_index::MSR_MTRRdefType,
+        data: MTRR_ENABLE | MTRR_MEMTYPE_WB as u64,
+        ..Default::default()
+    });
+
+    entries
+}
+
+fn create_msr_entries(vcpu: &kvm::Vcpu, pci_start: u64) -> Vec<kvm_msr_entry> {
     let mut entries = Vec::<kvm_msr_entry>::new();
 
     entries.push(kvm_msr_entry {
@@ -121,6 +216,10 @@ fn create_msr_entries() -> Vec<kvm_msr_entry> {
         ..Default::default()
     });
 
+    let mut mtrr_entries = create_mtrr_entries(vcpu, pci_start);
+
+    entries.append(&mut mtrr_entries);
+
     entries
 }
 
@@ -129,14 +228,14 @@ fn create_msr_entries() -> Vec<kvm_msr_entry> {
 /// # Arguments
 ///
 /// * `vcpu` - Structure for the vcpu that holds the vcpu fd.
-pub fn setup_msrs(vcpu: &kvm::Vcpu) -> Result<()> {
+pub fn setup_msrs(vcpu: &kvm::Vcpu, pci_start: u64) -> Result<()> {
     const SIZE_OF_MSRS: usize = mem::size_of::<kvm_msrs>();
     const SIZE_OF_ENTRY: usize = mem::size_of::<kvm_msr_entry>();
     const ALIGN_OF_MSRS: usize = mem::align_of::<kvm_msrs>();
     const ALIGN_OF_ENTRY: usize = mem::align_of::<kvm_msr_entry>();
     const_assert!(ALIGN_OF_MSRS >= ALIGN_OF_ENTRY);
 
-    let entry_vec = create_msr_entries();
+    let entry_vec = create_msr_entries(vcpu, pci_start);
     let size = SIZE_OF_MSRS + entry_vec.len() * SIZE_OF_ENTRY;
     let layout = Layout::from_size_align(size, ALIGN_OF_MSRS).expect("impossible layout");
     let mut allocation = LayoutAllocation::zeroed(layout);