|
24 | 24 | //! # Example - Running a VM on x86_64
|
25 | 25 | //!
|
26 | 26 | //! In this example we are creating a Virtual Machine (VM) with one vCPU.
|
27 |
| -//! On the vCPU we are running x86_64 specific code. This example is based on |
| 27 | +//! On the vCPU we are running machine specific code. This example is based on |
28 | 28 | //! the [LWN article](https://lwn.net/Articles/658511/) on using the KVM API.
|
| 29 | +//! The aarch64 example was modfied accordingly. |
29 | 30 | //!
|
30 | 31 | //! To get code running on the vCPU we are going through the following steps:
|
31 | 32 | //!
|
|
37 | 38 | //! are adding only one memory region and write the code in one memory page.
|
38 | 39 | //! 4. Create a vCPU using the VM object. The vCPU is used for running
|
39 | 40 | //! [vCPU specific ioctls](struct.VcpuFd.html).
|
40 |
| -//! 5. Setup x86 specific general purpose registers and special registers. For |
| 41 | +//! 5. Setup architectural specific general purpose registers and special registers. For |
41 | 42 | //! details about how and why these registers are set, please check the
|
42 | 43 | //! [LWN article](https://lwn.net/Articles/658511/) on which this example is
|
43 | 44 | //! built.
|
|
52 | 53 | //! use kvm_ioctls::{Kvm, VmFd, VcpuFd};
|
53 | 54 | //! use kvm_ioctls::VcpuExit;
|
54 | 55 | //!
|
55 |
| -//! #[cfg(target_arch = "x86_64")] |
56 | 56 | //! fn main(){
|
57 | 57 | //! use std::io::Write;
|
58 | 58 | //! use std::slice;
|
|
61 | 61 | //! use kvm_bindings::KVM_MEM_LOG_DIRTY_PAGES;
|
62 | 62 | //! use kvm_bindings::kvm_userspace_memory_region;
|
63 | 63 | //!
|
| 64 | +//! let mem_size = 0x4000; |
| 65 | +//! let guest_addr = 0x1000; |
| 66 | +//! let asm_code: &[u8]; |
| 67 | +//! |
| 68 | +//! // Setting up architectural dependent values. |
| 69 | +//! #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
| 70 | +//! { |
| 71 | +//! asm_code = &[ |
| 72 | +//! 0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */ |
| 73 | +//! 0x00, 0xd8, /* add %bl, %al */ |
| 74 | +//! 0x04, b'0', /* add $'0', %al */ |
| 75 | +//! 0xee, /* out %al, %dx */ |
| 76 | +//! 0xec, /* in %dx, %al */ |
| 77 | +//! 0xc6, 0x06, 0x00, 0x80, 0x00, /* movl $0, (0x8000); This generates a MMIO Write.*/ |
| 78 | +//! 0x8a, 0x16, 0x00, 0x80, /* movl (0x8000), %dl; This generates a MMIO Read.*/ |
| 79 | +//! 0xf4, /* hlt */ |
| 80 | +//! ]; |
| 81 | +//! } |
| 82 | +//! #[cfg(target_arch = "aarch64")] |
| 83 | +//! { |
| 84 | +//! asm_code = &[ |
| 85 | +//! 0x01, 0x00, 0x00, 0x10, /* adr x1, <this address> */ |
| 86 | +//! 0x22, 0x10, 0x00, 0xb9, /* str w2, [x1, #16]; write to this page */ |
| 87 | +//! 0x02, 0x00, 0x00, 0xb9, /* str w2, [x0]; This generates a MMIO Write.*/ |
| 88 | +//! 0x00, 0x00, 0x00, 0x14, /* b <this address>; shouldn't get here, but if so loop forever */ |
| 89 | +//! ]; |
| 90 | +//! } |
| 91 | +//! |
64 | 92 | //! // 1. Instantiate KVM.
|
65 | 93 | //! let kvm = Kvm::new().unwrap();
|
66 | 94 | //!
|
67 | 95 | //! // 2. Create a VM.
|
68 | 96 | //! let vm = kvm.create_vm().unwrap();
|
69 | 97 | //!
|
70 | 98 | //! // 3. Initialize Guest Memory.
|
71 |
| -//! let mem_size = 0x4000; |
72 |
| -//! let guest_addr: u64 = 0x1000; |
73 | 99 | //! let load_addr: *mut u8 = unsafe {
|
74 | 100 | //! libc::mmap(
|
75 | 101 | //! null_mut(),
|
|
93 | 119 | //! };
|
94 | 120 | //! unsafe { vm.set_user_memory_region(mem_region).unwrap() };
|
95 | 121 | //!
|
96 |
| -//! |
97 |
| -//! let x86_code = [ |
98 |
| -//! 0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */ |
99 |
| -//! 0x00, 0xd8, /* add %bl, %al */ |
100 |
| -//! 0x04, b'0', /* add $'0', %al */ |
101 |
| -//! 0xee, /* out %al, %dx */ |
102 |
| -//! 0xec, /* in %dx, %al */ |
103 |
| -//! 0xc6, 0x06, 0x00, 0x80, 0x00, /* movl $0, (0x8000); This generates a MMIO Write.*/ |
104 |
| -//! 0x8a, 0x16, 0x00, 0x80, /* movl (0x8000), %dl; This generates a MMIO Read.*/ |
105 |
| -//! 0xf4, /* hlt */ |
106 |
| -//! ]; |
107 |
| -//! |
108 | 122 | //! // Write the code in the guest memory. This will generate a dirty page.
|
109 | 123 | //! unsafe {
|
110 | 124 | //! let mut slice = slice::from_raw_parts_mut(load_addr, mem_size);
|
111 |
| -//! slice.write(&x86_code).unwrap(); |
| 125 | +//! slice.write(&asm_code).unwrap(); |
112 | 126 | //! }
|
113 | 127 | //!
|
114 | 128 | //! // 4. Create one vCPU.
|
115 | 129 | //! let vcpu_fd = vm.create_vcpu(0).unwrap();
|
116 | 130 | //!
|
117 | 131 | //! // 5. Initialize general purpose and special registers.
|
118 |
| -//! let mut vcpu_sregs = vcpu_fd.get_sregs().unwrap(); |
119 |
| -//! vcpu_sregs.cs.base = 0; |
120 |
| -//! vcpu_sregs.cs.selector = 0; |
121 |
| -//! vcpu_fd.set_sregs(&vcpu_sregs).unwrap(); |
122 |
| -//! |
123 |
| -//! let mut vcpu_regs = vcpu_fd.get_regs().unwrap(); |
124 |
| -//! vcpu_regs.rip = guest_addr; |
125 |
| -//! vcpu_regs.rax = 2; |
126 |
| -//! vcpu_regs.rbx = 3; |
127 |
| -//! vcpu_regs.rflags = 2; |
128 |
| -//! vcpu_fd.set_regs(&vcpu_regs).unwrap(); |
| 132 | +//! #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
| 133 | +//! { |
| 134 | +//! // x86_64 specific registry setup. |
| 135 | +//! let mut vcpu_sregs = vcpu_fd.get_sregs().unwrap(); |
| 136 | +//! vcpu_sregs.cs.base = 0; |
| 137 | +//! vcpu_sregs.cs.selector = 0; |
| 138 | +//! vcpu_fd.set_sregs(&vcpu_sregs).unwrap(); |
| 139 | +//! |
| 140 | +//! let mut vcpu_regs = vcpu_fd.get_regs().unwrap(); |
| 141 | +//! vcpu_regs.rip = guest_addr; |
| 142 | +//! vcpu_regs.rax = 2; |
| 143 | +//! vcpu_regs.rbx = 3; |
| 144 | +//! vcpu_regs.rflags = 2; |
| 145 | +//! vcpu_fd.set_regs(&vcpu_regs).unwrap(); |
| 146 | +//! } |
| 147 | +//! |
| 148 | +//! #[cfg(target_arch = "aarch64")] |
| 149 | +//! { |
| 150 | +//! // aarch64 specific registry setup. |
| 151 | +//! let mut kvi = kvm_bindings::kvm_vcpu_init::default(); |
| 152 | +//! vm.get_preferred_target(&mut kvi).unwrap(); |
| 153 | +//! vcpu_fd.vcpu_init(&kvi).unwrap(); |
| 154 | +//! |
| 155 | +//! let core_reg_base: u64 = 0x6030_0000_0010_0000; |
| 156 | +//! let mmio_addr: u64 = guest_addr + mem_size as u64; |
| 157 | +//! vcpu_fd.set_one_reg(core_reg_base + 2 * 32, guest_addr); // set PC |
| 158 | +//! vcpu_fd.set_one_reg(core_reg_base + 2 * 0, mmio_addr); // set X0 |
| 159 | +//! } |
129 | 160 | //!
|
130 | 161 | //! // 6. Run code on the vCPU.
|
131 | 162 | //! loop {
|
|
155 | 186 | //! "Received an MMIO Write Request to the address {:#x}.",
|
156 | 187 | //! addr,
|
157 | 188 | //! );
|
158 |
| -//! } |
159 |
| -//! VcpuExit::Hlt => { |
160 | 189 | //! // The code snippet dirties 1 page when it is loaded in memory
|
161 | 190 | //! let dirty_pages_bitmap = vm.get_dirty_log(slot, mem_size).unwrap();
|
162 | 191 | //! let dirty_pages = dirty_pages_bitmap
|
163 | 192 | //! .into_iter()
|
164 | 193 | //! .map(|page| page.count_ones())
|
165 | 194 | //! .fold(0, |dirty_page_count, i| dirty_page_count + i);
|
166 | 195 | //! assert_eq!(dirty_pages, 1);
|
| 196 | +//! // Since on aarch64 there is not halt instruction, |
| 197 | +//! // we break immediately after the last known instruction |
| 198 | +//! // of the asm code example so that we avoid an infinite loop. |
| 199 | +//! #[cfg(target_arch = "aarch64")] |
| 200 | +//! break; |
| 201 | +//! } |
| 202 | +//! VcpuExit::Hlt => { |
167 | 203 | //! break;
|
168 | 204 | //! }
|
169 | 205 | //! r => panic!("Unexpected exit reason: {:?}", r),
|
170 | 206 | //! }
|
171 | 207 | //! }
|
172 | 208 | //! }
|
173 |
| -//! |
174 |
| -//! #[cfg(not(target_arch = "x86_64"))] |
175 |
| -//! fn main() { |
176 |
| -//! println!("This code example only works on x86_64."); |
177 |
| -//! } |
178 | 209 | //! ```
|
179 | 210 |
|
180 | 211 | extern crate kvm_bindings;
|
|
0 commit comments