xref: /cloud-hypervisor/arch/src/x86_64/regs.rs (revision 4ad44caa5217cf55eed512fc10fd68416a37d31c)
1 // Copyright © 2020, Oracle and/or its affiliates.
2 //
3 // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 // SPDX-License-Identifier: Apache-2.0
5 //
6 // Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style license that can be
8 // found in the LICENSE-BSD-3-Clause file.
9 use crate::layout::{
10     BOOT_GDT_START, BOOT_IDT_START, BOOT_STACK_POINTER, PVH_INFO_START, ZERO_PAGE_START,
11 };
12 use crate::{EntryPoint, GuestMemoryMmap};
13 use hypervisor::arch::x86::gdt::{gdt_entry, segment_from_gdt};
14 use hypervisor::arch::x86::regs::CR0_PE;
15 use hypervisor::arch::x86::{FpuState, SpecialRegisters};
16 use std::sync::Arc;
17 use std::{mem, result};
18 use thiserror::Error;
19 use vm_memory::{Address, Bytes, GuestMemory, GuestMemoryError};
20 
21 #[derive(Debug, Error)]
22 pub enum Error {
23     /// Failed to get SREGs for this CPU.
24     #[error("Failed to get SREGs for this CPU: {0}")]
25     GetStatusRegisters(hypervisor::HypervisorCpuError),
26     /// Failed to set base registers for this CPU.
27     #[error("Failed to set base registers for this CPU: {0}")]
28     SetBaseRegisters(hypervisor::HypervisorCpuError),
29     /// Failed to configure the FPU.
30     #[error("Failed to configure the FPU: {0}")]
31     SetFpuRegisters(hypervisor::HypervisorCpuError),
32     /// Setting up MSRs failed.
33     #[error("Setting up MSRs failed: {0}")]
34     SetModelSpecificRegisters(hypervisor::HypervisorCpuError),
35     /// Failed to set SREGs for this CPU.
36     #[error("Failed to set SREGs for this CPU: {0}")]
37     SetStatusRegisters(hypervisor::HypervisorCpuError),
38     /// Checking the GDT address failed.
39     #[error("Checking the GDT address failed")]
40     CheckGdtAddr,
41     /// Writing the GDT to RAM failed.
42     #[error("Writing the GDT to RAM failed: {0}")]
43     WriteGdt(GuestMemoryError),
44     /// Writing the IDT to RAM failed.
45     #[error("Writing the IDT to RAM failed: {0}")]
46     WriteIdt(GuestMemoryError),
47     /// Writing PDPTE to RAM failed.
48     #[error("Writing PDPTE to RAM failed: {0}")]
49     WritePdpteAddress(GuestMemoryError),
50     /// Writing PDE to RAM failed.
51     #[error("Writing PDE to RAM failed: {0}")]
52     WritePdeAddress(GuestMemoryError),
53     /// Writing PML4 to RAM failed.
54     #[error("Writing PML4 to RAM failed: {0}")]
55     WritePml4Address(GuestMemoryError),
56     /// Writing PML5 to RAM failed.
57     #[error("Writing PML5 to RAM failed: {0}")]
58     WritePml5Address(GuestMemoryError),
59 }
60 
61 pub type Result<T> = result::Result<T, Error>;
62 
63 /// Configure Floating-Point Unit (FPU) registers for a given CPU.
64 ///
65 /// # Arguments
66 ///
67 /// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
68 pub fn setup_fpu(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
69     let fpu: FpuState = FpuState {
70         fcw: 0x37f,
71         mxcsr: 0x1f80,
72         ..Default::default()
73     };
74 
75     vcpu.set_fpu(&fpu).map_err(Error::SetFpuRegisters)
76 }
77 
78 /// Configure Model Specific Registers (MSRs) for a given CPU.
79 ///
80 /// # Arguments
81 ///
82 /// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
83 pub fn setup_msrs(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
84     vcpu.set_msrs(&vcpu.boot_msr_entries())
85         .map_err(Error::SetModelSpecificRegisters)?;
86 
87     Ok(())
88 }
89 
90 /// Configure base registers for a given CPU.
91 ///
92 /// # Arguments
93 ///
94 /// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
95 /// * `entry_point` - Description of the boot entry to set up.
96 pub fn setup_regs(vcpu: &Arc<dyn hypervisor::Vcpu>, entry_point: EntryPoint) -> Result<()> {
97     let mut regs = vcpu.create_standard_regs();
98     match entry_point.setup_header {
99         None => {
100             regs.set_rflags(0x0000000000000002u64);
101             regs.set_rip(entry_point.entry_addr.raw_value());
102             regs.set_rbx(PVH_INFO_START.raw_value());
103         }
104         Some(_) => {
105             regs.set_rflags(0x0000000000000002u64);
106             regs.set_rip(entry_point.entry_addr.raw_value());
107             regs.set_rsp(BOOT_STACK_POINTER.raw_value());
108             regs.set_rsi(ZERO_PAGE_START.raw_value());
109         }
110     };
111     vcpu.set_regs(&regs).map_err(Error::SetBaseRegisters)
112 }
113 
114 /// Configures the segment registers and system page tables for a given CPU.
115 ///
116 /// # Arguments
117 ///
118 /// * `mem` - The memory that will be passed to the guest.
119 /// * `vcpu` - Structure for the VCPU that holds the VCPU's fd.
120 pub fn setup_sregs(mem: &GuestMemoryMmap, vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> {
121     let mut sregs: SpecialRegisters = vcpu.get_sregs().map_err(Error::GetStatusRegisters)?;
122     configure_segments_and_sregs(mem, &mut sregs)?;
123     vcpu.set_sregs(&sregs).map_err(Error::SetStatusRegisters)
124 }
125 
126 const BOOT_GDT_MAX: usize = 4;
127 
128 fn write_gdt_table(table: &[u64], guest_mem: &GuestMemoryMmap) -> Result<()> {
129     let boot_gdt_addr = BOOT_GDT_START;
130     for (index, entry) in table.iter().enumerate() {
131         let addr = guest_mem
132             .checked_offset(boot_gdt_addr, index * mem::size_of::<u64>())
133             .ok_or(Error::CheckGdtAddr)?;
134         guest_mem.write_obj(*entry, addr).map_err(Error::WriteGdt)?;
135     }
136     Ok(())
137 }
138 
139 fn write_idt_value(val: u64, guest_mem: &GuestMemoryMmap) -> Result<()> {
140     let boot_idt_addr = BOOT_IDT_START;
141     guest_mem
142         .write_obj(val, boot_idt_addr)
143         .map_err(Error::WriteIdt)
144 }
145 
146 pub fn configure_segments_and_sregs(
147     mem: &GuestMemoryMmap,
148     sregs: &mut SpecialRegisters,
149 ) -> Result<()> {
150     let gdt_table: [u64; BOOT_GDT_MAX] = {
151         // Configure GDT entries as specified by PVH boot protocol
152         [
153             gdt_entry(0, 0, 0),               // NULL
154             gdt_entry(0xc09b, 0, 0xffffffff), // CODE
155             gdt_entry(0xc093, 0, 0xffffffff), // DATA
156             gdt_entry(0x008b, 0, 0x67),       // TSS
157         ]
158     };
159 
160     let code_seg = segment_from_gdt(gdt_table[1], 1);
161     let data_seg = segment_from_gdt(gdt_table[2], 2);
162     let tss_seg = segment_from_gdt(gdt_table[3], 3);
163 
164     // Write segments
165     write_gdt_table(&gdt_table[..], mem)?;
166     sregs.gdt.base = BOOT_GDT_START.raw_value();
167     sregs.gdt.limit = mem::size_of_val(&gdt_table) as u16 - 1;
168 
169     write_idt_value(0, mem)?;
170     sregs.idt.base = BOOT_IDT_START.raw_value();
171     sregs.idt.limit = mem::size_of::<u64>() as u16 - 1;
172 
173     sregs.cs = code_seg;
174     sregs.ds = data_seg;
175     sregs.es = data_seg;
176     sregs.fs = data_seg;
177     sregs.gs = data_seg;
178     sregs.ss = data_seg;
179     sregs.tr = tss_seg;
180 
181     sregs.cr0 = CR0_PE;
182     sregs.cr4 = 0;
183 
184     Ok(())
185 }
186 
187 #[cfg(test)]
188 mod tests {
189     use super::*;
190     use vm_memory::GuestAddress;
191 
192     fn create_guest_mem() -> GuestMemoryMmap {
193         GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap()
194     }
195 
196     fn read_u64(gm: &GuestMemoryMmap, offset: GuestAddress) -> u64 {
197         gm.read_obj(offset).unwrap()
198     }
199 
200     #[test]
201     fn segments_and_sregs() {
202         let mut sregs: SpecialRegisters = Default::default();
203         let gm = create_guest_mem();
204         configure_segments_and_sregs(&gm, &mut sregs).unwrap();
205         assert_eq!(0x0, read_u64(&gm, BOOT_GDT_START));
206         assert_eq!(
207             0xcf9b000000ffff,
208             read_u64(&gm, BOOT_GDT_START.unchecked_add(8))
209         );
210         assert_eq!(
211             0xcf93000000ffff,
212             read_u64(&gm, BOOT_GDT_START.unchecked_add(16))
213         );
214         assert_eq!(
215             0x8b0000000067,
216             read_u64(&gm, BOOT_GDT_START.unchecked_add(24))
217         );
218         assert_eq!(0x0, read_u64(&gm, BOOT_IDT_START));
219 
220         assert_eq!(0, sregs.cs.base);
221         assert_eq!(0xffffffff, sregs.ds.limit);
222         assert_eq!(0x10, sregs.es.selector);
223         assert_eq!(1, sregs.fs.present);
224         assert_eq!(1, sregs.gs.g);
225         assert_eq!(0, sregs.ss.avl);
226         assert_eq!(0, sregs.tr.base);
227         assert_eq!(0, sregs.tr.g);
228         assert_eq!(0x67, sregs.tr.limit);
229         assert_eq!(0xb, sregs.tr.type_);
230         assert_eq!(0, sregs.tr.avl);
231         assert_eq!(CR0_PE, sregs.cr0);
232         assert_eq!(0, sregs.cr4);
233     }
234 }
235