1 use system_error::SystemError;
2
3 use super::{vcpu::Vcpu, vm};
4 use crate::{
5 kdebug,
6 mm::{kernel_mapper::KernelMapper, page::PageFlags, VirtAddr},
7 };
8
9 /*
10 * Address types:
11 *
12 * gva - guest virtual address
13 * gpa - guest physical address
14 * gfn - guest frame number
15 * hva - host virtual address
16 * hpa - host physical address
17 * hfn - host frame number
18 */
19 pub const KVM_USER_MEM_SLOTS: u32 = 16;
20 pub const KVM_PRIVATE_MEM_SLOTS: u32 = 3;
21 pub const KVM_MEM_SLOTS_NUM: u32 = KVM_USER_MEM_SLOTS + KVM_PRIVATE_MEM_SLOTS;
22 pub const KVM_ADDRESS_SPACE_NUM: usize = 2;
23
24 pub const KVM_MEM_LOG_DIRTY_PAGES: u32 = 1 << 0;
25 pub const KVM_MEM_READONLY: u32 = 1 << 1;
26 pub const KVM_MEM_MAX_NR_PAGES: u32 = (1 << 31) - 1;
27
28 /*
29 * The bit 16 ~ bit 31 of kvm_memory_region::flags are internally used
30 * in kvm, other bits are visible for userspace which are defined in
31 * include/linux/kvm_h.
32 */
33 pub const KVM_MEMSLOT_INVALID: u32 = 1 << 16;
34 // pub const KVM_MEMSLOT_INCOHERENT:u32 = 1 << 17;
35
36 // pub const KVM_PERMILLE_MMU_PAGES: u32 = 20; // the proportion of MMU pages required per thousand (out of 1000) memory pages.
37 // pub const KVM_MIN_ALLOC_MMU_PAGES: u32 = 64;
38
39 pub const PAGE_SHIFT: u32 = 12;
40 pub const PAGE_SIZE: u32 = 1 << PAGE_SHIFT;
41 pub const PAGE_MASK: u32 = !(PAGE_SIZE - 1);
42
43 /// 通过这个结构可以将虚拟机的物理地址对应到用户进程的虚拟地址
44 /// 用来表示虚拟机的一段物理内存
45 #[repr(C)]
46 #[derive(Default)]
47 pub struct KvmUserspaceMemoryRegion {
48 pub slot: u32, // 要在哪个slot上注册内存区间
49 // flags有两个取值,KVM_MEM_LOG_DIRTY_PAGES和KVM_MEM_READONLY,用来指示kvm针对这段内存应该做的事情。
50 // KVM_MEM_LOG_DIRTY_PAGES用来开启内存脏页,KVM_MEM_READONLY用来开启内存只读。
51 pub flags: u32,
52 pub guest_phys_addr: u64, // 虚机内存区间起始物理地址
53 pub memory_size: u64, // 虚机内存区间大小
54 pub userspace_addr: u64, // 虚机内存区间对应的主机虚拟地址
55 }
56
57 #[derive(Default, Clone, Copy, Debug)]
58 pub struct KvmMemorySlot {
59 pub base_gfn: u64, // 虚机内存区间起始物理页框号
60 pub npages: u64, // 虚机内存区间页数,即内存区间的大小
61 pub userspace_addr: u64, // 虚机内存区间对应的主机虚拟地址
62 pub flags: u32, // 虚机内存区间属性
63 pub id: u16, // 虚机内存区间id
64 // 用来记录虚机内存区间的脏页信息,每个bit对应一个页,如果bit为1,表示对应的页是脏页,如果bit为0,表示对应的页是干净页。
65 // pub dirty_bitmap: *mut u8,
66 // unsigned long *rmap[KVM_NR_PAGE_SIZES]; 反向映射相关的结构, 创建EPT页表项时就记录GPA对应的页表项地址(GPA-->页表项地址),暂时不需要
67 }
68
69 #[derive(Default, Clone, Copy, Debug)]
70 pub struct KvmMemorySlots {
71 pub memslots: [KvmMemorySlot; KVM_MEM_SLOTS_NUM as usize], // 虚机内存区间数组
72 pub used_slots: u32, // 已经使用的slot数量
73 }
74
75 #[derive(PartialEq, Eq, Debug)]
76 pub enum KvmMemoryChange {
77 Create,
78 Delete,
79 Move,
80 FlagsOnly,
81 }
82
kvm_vcpu_memslots(_vcpu: &mut dyn Vcpu) -> KvmMemorySlots83 pub fn kvm_vcpu_memslots(_vcpu: &mut dyn Vcpu) -> KvmMemorySlots {
84 let kvm = vm(0).unwrap();
85 let as_id = 0;
86 return kvm.memslots[as_id];
87 }
88
__gfn_to_memslot(slots: KvmMemorySlots, gfn: u64) -> Option<KvmMemorySlot>89 fn __gfn_to_memslot(slots: KvmMemorySlots, gfn: u64) -> Option<KvmMemorySlot> {
90 kdebug!("__gfn_to_memslot");
91 // TODO: 使用二分查找的方式优化
92 for i in 0..slots.used_slots {
93 let memslot = slots.memslots[i as usize];
94 if gfn >= memslot.base_gfn && gfn < memslot.base_gfn + memslot.npages {
95 return Some(memslot);
96 }
97 }
98 return None;
99 }
100
__gfn_to_hva(slot: KvmMemorySlot, gfn: u64) -> u64101 fn __gfn_to_hva(slot: KvmMemorySlot, gfn: u64) -> u64 {
102 return slot.userspace_addr + (gfn - slot.base_gfn) * (PAGE_SIZE as u64);
103 }
__gfn_to_hva_many( slot: Option<KvmMemorySlot>, gfn: u64, nr_pages: Option<&mut u64>, write: bool, ) -> Result<u64, SystemError>104 fn __gfn_to_hva_many(
105 slot: Option<KvmMemorySlot>,
106 gfn: u64,
107 nr_pages: Option<&mut u64>,
108 write: bool,
109 ) -> Result<u64, SystemError> {
110 kdebug!("__gfn_to_hva_many");
111 if slot.is_none() {
112 return Err(SystemError::KVM_HVA_ERR_BAD);
113 }
114 let slot = slot.unwrap();
115 if slot.flags & KVM_MEMSLOT_INVALID != 0 || (slot.flags & KVM_MEM_READONLY != 0) && write {
116 return Err(SystemError::KVM_HVA_ERR_BAD);
117 }
118
119 if let Some(nr_pages) = nr_pages {
120 *nr_pages = slot.npages - (gfn - slot.base_gfn);
121 }
122
123 return Ok(__gfn_to_hva(slot, gfn));
124 }
125
126 /* From Linux kernel
127 * Pin guest page in memory and return its pfn.
128 * @addr: host virtual address which maps memory to the guest
129 * @atomic: whether this function can sleep
130 * @async: whether this function need to wait IO complete if the
131 * host page is not in the memory
132 * @write_fault: whether we should get a writable host page
133 * @writable: whether it allows to map a writable host page for !@write_fault
134 *
135 * The function will map a writable host page for these two cases:
136 * 1): @write_fault = true
137 * 2): @write_fault = false && @writable, @writable will tell the caller
138 * whether the mapping is writable.
139 */
140 // 计算 HVA 对应的 pfn,同时确保该物理页在内存中
141 // host端虚拟地址到物理地址的转换,有两种方式,hva_to_pfn_fast、hva_to_pfn_slow
142 // 正确性待验证
hva_to_pfn(addr: u64, _atomic: bool, _writable: &mut bool) -> Result<u64, SystemError>143 fn hva_to_pfn(addr: u64, _atomic: bool, _writable: &mut bool) -> Result<u64, SystemError> {
144 kdebug!("hva_to_pfn");
145 unsafe {
146 let raw = addr as *const i32;
147 kdebug!("raw={:x}", *raw);
148 }
149 // let hpa = MMArch::virt_2_phys(VirtAddr::new(addr)).unwrap().data() as u64;
150 let hva = VirtAddr::new(addr as usize);
151 let mut mapper = KernelMapper::lock();
152 let mapper = mapper.as_mut().unwrap();
153 if let Some((hpa, _)) = mapper.translate(hva) {
154 return Ok(hpa.data() as u64 >> PAGE_SHIFT);
155 }
156 unsafe {
157 mapper.map(hva, PageFlags::mmio_flags());
158 }
159 let (hpa, _) = mapper.translate(hva).unwrap();
160 return Ok(hpa.data() as u64 >> PAGE_SHIFT);
161 }
162
__gfn_to_pfn( slot: Option<KvmMemorySlot>, gfn: u64, atomic: bool, write: bool, writable: &mut bool, ) -> Result<u64, SystemError>163 pub fn __gfn_to_pfn(
164 slot: Option<KvmMemorySlot>,
165 gfn: u64,
166 atomic: bool,
167 write: bool,
168 writable: &mut bool,
169 ) -> Result<u64, SystemError> {
170 kdebug!("__gfn_to_pfn");
171 let mut nr_pages = 0;
172 let addr = __gfn_to_hva_many(slot, gfn, Some(&mut nr_pages), write)?;
173 let pfn = hva_to_pfn(addr, atomic, writable)?;
174 kdebug!("hva={}, pfn={}", addr, pfn);
175 return Ok(pfn);
176 }
177
kvm_vcpu_gfn_to_memslot(vcpu: &mut dyn Vcpu, gfn: u64) -> Option<KvmMemorySlot>178 pub fn kvm_vcpu_gfn_to_memslot(vcpu: &mut dyn Vcpu, gfn: u64) -> Option<KvmMemorySlot> {
179 return __gfn_to_memslot(kvm_vcpu_memslots(vcpu), gfn);
180 }
181