ostd/arch/x86/mod.rs
1// SPDX-License-Identifier: MPL-2.0
2
3//! Platform-specific code for the x86 platform.
4
5pub(crate) mod boot;
6pub mod cpu;
7pub mod device;
8pub(crate) mod io;
9pub(crate) mod iommu;
10pub mod irq;
11pub mod kernel;
12pub(crate) mod mm;
13mod power;
14pub(crate) mod serial;
15pub(crate) mod task;
16mod timer;
17pub mod trap;
18
19#[cfg(feature = "cvm_guest")]
20pub(crate) mod tdx_guest;
21
22#[cfg(feature = "cvm_guest")]
23pub(crate) fn init_cvm_guest() {
24 use ::tdx_guest::{
25 disable_sept_ve, init_tdx, metadata, reduce_unnecessary_ve,
26 tdcall::{InitError, write_td_metadata},
27 };
28 match init_tdx() {
29 Ok(td_info) => {
30 reduce_unnecessary_ve().unwrap();
31 disable_sept_ve(td_info.attributes).unwrap();
32 // Enable notification for zero step attack detection.
33 write_td_metadata(metadata::NOTIFY_ENABLES, 1, 1).unwrap();
34
35 crate::early_println!(
36 "[kernel] Intel TDX initialized\n[kernel] td gpaw: {}, td attributes: {:?}",
37 td_info.gpaw,
38 td_info.attributes
39 );
40 }
41 Err(InitError::TdxGetVpInfoError(td_call_error)) => {
42 panic!(
43 "[kernel] Intel TDX not initialized, Failed to get TD info: {:?}",
44 td_call_error
45 );
46 }
47 // The machine has no TDX support.
48 Err(_) => {}
49 }
50}
51
52/// Architecture-specific initialization on the bootstrapping processor.
53///
54/// It should be called when the heap and frame allocators are available.
55///
56/// # Safety
57///
58/// 1. This function must be called only once in the boot context of the
59/// bootstrapping processor.
60/// 2. This function must be called after the kernel page table is activated on
61/// the bootstrapping processor.
62pub(crate) unsafe fn late_init_on_bsp() {
63 // SAFETY: This is only called once on this BSP in the boot context.
64 unsafe { trap::init_on_cpu() };
65
66 // SAFETY: The caller ensures that this function is only called once on BSP,
67 // after the kernel page table is activated.
68 let io_mem_builder = unsafe { io::construct_io_mem_allocator_builder() };
69
70 kernel::apic::init(&io_mem_builder).expect("APIC doesn't exist");
71 irq::chip::init(&io_mem_builder);
72 irq::ipi::init();
73
74 kernel::tsc::init_tsc_freq();
75 timer::init_on_bsp();
76
77 // SAFETY: We're on the BSP and we're ready to boot all APs.
78 unsafe { crate::boot::smp::boot_all_aps() };
79
80 if_tdx_enabled!({
81 } else {
82 match iommu::init(&io_mem_builder) {
83 Ok(_) => {}
84 Err(err) => log::warn!("IOMMU initialization error:{:?}", err),
85 }
86 });
87
88 // SAFETY:
89 // 1. All the system device memory have been removed from the builder.
90 // 2. All the port I/O regions belonging to the system device are defined using the macros.
91 // 3. `MAX_IO_PORT` defined in `crate::arch::io` is the maximum value specified by x86-64.
92 unsafe { crate::io::init(io_mem_builder) };
93
94 kernel::acpi::init();
95 power::init();
96}
97
98/// Initializes application-processor-specific state.
99///
100/// # Safety
101///
102/// 1. This function must be called only once on each application processor.
103/// 2. This function must be called after the BSP's call to [`late_init_on_bsp`]
104/// and before any other architecture-specific code in this module is called
105/// on this AP.
106pub(crate) unsafe fn init_on_ap() {
107 timer::init_on_ap();
108}
109
110/// Returns the frequency of TSC. The unit is Hz.
111pub fn tsc_freq() -> u64 {
112 use core::sync::atomic::Ordering;
113
114 kernel::tsc::TSC_FREQ.load(Ordering::Acquire)
115}
116
117/// Reads the current value of the processor's time-stamp counter (TSC).
118pub fn read_tsc() -> u64 {
119 use core::arch::x86_64::_rdtsc;
120
121 // SAFETY: It is safe to read a time-related counter.
122 unsafe { _rdtsc() }
123}
124
125/// Reads a hardware generated 64-bit random value.
126///
127/// Returns `None` if no random value was generated.
128pub fn read_random() -> Option<u64> {
129 use core::arch::x86_64::_rdrand64_step;
130
131 use cpu::extension::{IsaExtensions, has_extensions};
132
133 if !has_extensions(IsaExtensions::RDRAND) {
134 return None;
135 }
136
137 // Recommendation from "Intel(R) Digital Random Number Generator (DRNG) Software
138 // Implementation Guide" - Section 5.2.1 and "Intel(R) 64 and IA-32 Architectures
139 // Software Developer's Manual" - Volume 1 - Section 7.3.17.1.
140 const RETRY_LIMIT: usize = 10;
141
142 for _ in 0..RETRY_LIMIT {
143 let mut val = 0;
144 let generated = unsafe { _rdrand64_step(&mut val) };
145 if generated == 1 {
146 return Some(val);
147 }
148 }
149 None
150}
151
152pub(crate) fn enable_cpu_features() {
153 use cpu::extension::{IsaExtensions, has_extensions};
154 use x86_64::registers::{
155 control::{Cr0Flags, Cr4Flags},
156 xcontrol::XCr0Flags,
157 };
158
159 cpu::extension::init();
160
161 let mut cr0 = x86_64::registers::control::Cr0::read();
162 cr0 |= Cr0Flags::WRITE_PROTECT;
163 // These FPU control bits should be set for new CPUs (e.g., all CPUs with 64-bit support) and
164 // modern OSes. See recommendation from "Intel(R) 64 and IA-32 Architectures Software
165 // Developer's Manual" - Volume 3 - Section 10.2.1, Configuring the x87 FPU Environment.
166 cr0 |= Cr0Flags::NUMERIC_ERROR | Cr0Flags::MONITOR_COPROCESSOR;
167 unsafe { x86_64::registers::control::Cr0::write(cr0) };
168
169 let mut cr4 = x86_64::registers::control::Cr4::read();
170 cr4 |= Cr4Flags::OSFXSR | Cr4Flags::OSXMMEXCPT_ENABLE | Cr4Flags::PAGE_GLOBAL;
171 if has_extensions(IsaExtensions::XSAVE) {
172 cr4 |= Cr4Flags::OSXSAVE;
173 }
174 // For now, we unconditionally require the `rdfsbase`, `wrfsbase`, `rdgsbase`, and `wrgsbase`
175 // instructions because they are used when switching contexts, getting the address of a
176 // CPU-local variable, e.t.c. Meanwhile, this is at a very early stage of the boot process, so
177 // we want to avoid failing immediately even if we cannot enable these instructions (though the
178 // kernel will certainly fail later when they are absent).
179 //
180 // Note that this also enables the userspace to control their own FS/GS bases, which requires
181 // the kernel to properly deal with the arbitrary base values set by the userspace program.
182 if has_extensions(IsaExtensions::FSGSBASE) {
183 cr4 |= Cr4Flags::FSGSBASE;
184 }
185 unsafe { x86_64::registers::control::Cr4::write(cr4) };
186
187 if has_extensions(IsaExtensions::XSAVE) {
188 let mut xcr0 = x86_64::registers::xcontrol::XCr0::read();
189 xcr0 |= XCr0Flags::SSE;
190 if has_extensions(IsaExtensions::AVX) {
191 xcr0 |= XCr0Flags::AVX;
192 }
193 if has_extensions(IsaExtensions::AVX512F) {
194 xcr0 |= XCr0Flags::OPMASK | XCr0Flags::ZMM_HI256 | XCr0Flags::HI16_ZMM;
195 }
196 unsafe { x86_64::registers::xcontrol::XCr0::write(xcr0) };
197 }
198
199 cpu::context::enable_essential_features();
200
201 mm::enable_essential_features();
202}
203
204/// Inserts a TDX-specific code block.
205///
206/// This macro conditionally executes a TDX-specific code block based on the following conditions:
207/// (1) The `cvm_guest` feature is enabled at compile time.
208/// (2) The TDX feature is detected at runtime via `::tdx_guest::tdx_is_enabled()`.
209///
210/// If both conditions are met, the `if_block` is executed. If an `else_block` is provided, it will be executed
211/// when either the `cvm_guest` feature is not enabled or the TDX feature is not detected at runtime.
212#[macro_export]
213macro_rules! if_tdx_enabled {
214 // Match when there is an else block
215 ($if_block:block else $else_block:block) => {{
216 #[cfg(feature = "cvm_guest")]
217 {
218 if ::tdx_guest::tdx_is_enabled() {
219 $if_block
220 } else {
221 $else_block
222 }
223 }
224 #[cfg(not(feature = "cvm_guest"))]
225 {
226 $else_block
227 }
228 }};
229 // Match when there is no else block
230 ($if_block:block) => {{
231 #[cfg(feature = "cvm_guest")]
232 {
233 if ::tdx_guest::tdx_is_enabled() {
234 $if_block
235 }
236 }
237 }};
238}
239
240pub use if_tdx_enabled;