1use bit_field::BitField;
4
5use crate::{
6 instructions::segmentation::{Segment, CS},
7 structures::paging::{
8 page::{NotGiantPageSize, PageRange},
9 Page, PageSize, Size2MiB, Size4KiB,
10 },
11 PrivilegeLevel, VirtAddr,
12};
13use core::{arch::asm, cmp, convert::TryFrom, fmt};
14
15#[inline]
17pub fn flush(addr: VirtAddr) {
18 unsafe {
19 asm!("invlpg [{}]", in(reg) addr.as_u64(), options(nostack, preserves_flags));
20 }
21}
22
23#[inline]
25pub fn flush_all() {
26 use crate::registers::control::Cr3;
27 let (frame, flags) = Cr3::read();
28 unsafe { Cr3::write(frame, flags) }
29}
30
31#[derive(Debug)]
33pub enum InvPcidCommand {
34 Address(VirtAddr, Pcid),
36
37 Single(Pcid),
39
40 All,
42
43 AllExceptGlobal,
45}
46
47#[deprecated = "please use `InvPcidCommand` instead"]
49#[doc(hidden)]
50pub type InvPicdCommand = InvPcidCommand;
51
52#[repr(C)]
55#[derive(Debug)]
56struct InvpcidDescriptor {
57 pcid: u64,
58 address: u64,
59}
60
61#[repr(transparent)]
63#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
64pub struct Pcid(u16);
65
66impl Pcid {
67 pub const fn new(pcid: u16) -> Result<Pcid, PcidTooBig> {
70 if pcid >= 4096 {
71 Err(PcidTooBig(pcid))
72 } else {
73 Ok(Pcid(pcid))
74 }
75 }
76
77 pub const fn value(&self) -> u16 {
79 self.0
80 }
81}
82
83#[derive(Debug)]
87pub struct PcidTooBig(u16);
88
89impl fmt::Display for PcidTooBig {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 write!(f, "PCID should be < 4096, got {}", self.0)
92 }
93}
94
95#[inline]
101pub unsafe fn flush_pcid(command: InvPcidCommand) {
102 let mut desc = InvpcidDescriptor {
103 pcid: 0,
104 address: 0,
105 };
106
107 let kind: u64;
108 match command {
109 InvPcidCommand::Address(addr, pcid) => {
110 kind = 0;
111 desc.pcid = pcid.value().into();
112 desc.address = addr.as_u64()
113 }
114 InvPcidCommand::Single(pcid) => {
115 kind = 1;
116 desc.pcid = pcid.0.into()
117 }
118 InvPcidCommand::All => kind = 2,
119 InvPcidCommand::AllExceptGlobal => kind = 3,
120 }
121
122 unsafe {
123 asm!("invpcid {0}, [{1}]", in(reg) kind, in(reg) &desc, options(nostack, preserves_flags));
124 }
125}
126
127#[derive(Debug, Clone, Copy)]
146pub struct Invlpgb {
147 invlpgb_count_max: u16,
148 tlb_flush_nested: bool,
149 nasid: u32,
150}
151
152impl Invlpgb {
153 pub fn new() -> Option<Self> {
159 let cs = CS::get_reg();
160 assert_eq!(cs.rpl(), PrivilegeLevel::Ring0);
161
162 #[allow(unused_unsafe)]
164 let cpuid = unsafe { core::arch::x86_64::__cpuid(0x8000_0008) };
165 if !cpuid.ebx.get_bit(3) {
166 return None;
167 }
168
169 let tlb_flush_nested = cpuid.ebx.get_bit(21);
170 let invlpgb_count_max = cpuid.edx.get_bits(0..=15) as u16;
171
172 #[allow(unused_unsafe)]
174 let cpuid = unsafe { core::arch::x86_64::__cpuid(0x8000_000a) };
175 let nasid = cpuid.ebx;
176
177 Some(Self {
178 tlb_flush_nested,
179 invlpgb_count_max,
180 nasid,
181 })
182 }
183
184 #[inline]
186 pub fn invlpgb_count_max(&self) -> u16 {
187 self.invlpgb_count_max
188 }
189
190 #[inline]
192 pub fn tlb_flush_nested(&self) -> bool {
193 self.tlb_flush_nested
194 }
195
196 #[inline]
198 pub fn nasid(&self) -> u32 {
199 self.nasid
200 }
201
202 pub fn build(&self) -> InvlpgbFlushBuilder<'_> {
204 InvlpgbFlushBuilder {
205 invlpgb: self,
206 page_range: None,
207 pcid: None,
208 asid: None,
209 include_global: false,
210 final_translation_only: false,
211 include_nested_translations: false,
212 }
213 }
214
215 #[inline]
218 pub fn tlbsync(&self) {
219 unsafe {
220 asm!("tlbsync", options(nomem, preserves_flags));
221 }
222 }
223}
224
225#[derive(Debug, Clone)]
227#[must_use]
228pub struct InvlpgbFlushBuilder<'a, S = Size4KiB>
229where
230 S: NotGiantPageSize,
231{
232 invlpgb: &'a Invlpgb,
233 page_range: Option<PageRange<S>>,
234 pcid: Option<Pcid>,
235 asid: Option<u16>,
236 include_global: bool,
237 final_translation_only: bool,
238 include_nested_translations: bool,
239}
240
241impl<'a, S> InvlpgbFlushBuilder<'a, S>
242where
243 S: NotGiantPageSize,
244{
245 pub fn pages<T>(self, page_range: PageRange<T>) -> InvlpgbFlushBuilder<'a, T>
250 where
251 T: NotGiantPageSize,
252 {
253 InvlpgbFlushBuilder {
254 invlpgb: self.invlpgb,
255 page_range: Some(page_range),
256 pcid: self.pcid,
257 asid: self.asid,
258 include_global: self.include_global,
259 final_translation_only: self.final_translation_only,
260 include_nested_translations: self.include_nested_translations,
261 }
262 }
263
264 pub unsafe fn pcid(&mut self, pcid: Pcid) -> &mut Self {
270 self.pcid = Some(pcid);
271 self
272 }
273
274 pub unsafe fn asid(&mut self, asid: u16) -> Result<&mut Self, AsidOutOfRangeError> {
281 if u32::from(asid) >= self.invlpgb.nasid {
282 return Err(AsidOutOfRangeError {
283 asid,
284 nasid: self.invlpgb.nasid,
285 });
286 }
287
288 self.asid = Some(asid);
289 Ok(self)
290 }
291
292 pub fn include_global(&mut self) -> &mut Self {
294 self.include_global = true;
295 self
296 }
297
298 pub fn final_translation_only(&mut self) -> &mut Self {
300 self.final_translation_only = true;
301 self
302 }
303
304 pub fn include_nested_translations(mut self) -> Self {
306 assert!(
307 self.invlpgb.tlb_flush_nested,
308 "flushing all nested translations is not supported"
309 );
310
311 self.include_nested_translations = true;
312 self
313 }
314
315 pub fn flush(&self) {
317 if let Some(mut pages) = self.page_range {
318 while !pages.is_empty() {
319 let count = Page::<S>::steps_between_impl(&pages.start, &pages.end).0;
321
322 let second_half_start =
324 Page::<S>::containing_address(VirtAddr::new(0xffff_8000_0000_0000));
325 let count = if pages.start < second_half_start {
326 let count_to_second_half =
327 Page::steps_between_impl(&pages.start, &second_half_start).0;
328 cmp::min(count, count_to_second_half)
329 } else {
330 count
331 };
332
333 let count = u16::try_from(count).unwrap_or(u16::MAX);
335
336 let count = cmp::min(count, self.invlpgb.invlpgb_count_max);
338
339 unsafe {
340 flush_broadcast(
341 Some((pages.start, count)),
342 self.pcid,
343 self.asid,
344 self.include_global,
345 self.final_translation_only,
346 self.include_nested_translations,
347 );
348 }
349
350 let inc_count = cmp::max(count, 1);
353 pages.start =
354 Page::forward_checked_impl(pages.start, usize::from(inc_count)).unwrap();
355 }
356 } else {
357 unsafe {
358 flush_broadcast::<S>(
359 None,
360 self.pcid,
361 self.asid,
362 self.include_global,
363 self.final_translation_only,
364 self.include_nested_translations,
365 );
366 }
367 }
368 }
369}
370
371#[derive(Debug)]
373pub struct AsidOutOfRangeError {
374 pub asid: u16,
376 pub nasid: u32,
378}
379
380impl fmt::Display for AsidOutOfRangeError {
381 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
382 write!(
383 f,
384 "{} is out of the range of available ASIDS ({})",
385 self.asid, self.nasid
386 )
387 }
388}
389
390#[inline]
392unsafe fn flush_broadcast<S>(
393 va_and_count: Option<(Page<S>, u16)>,
394 pcid: Option<Pcid>,
395 asid: Option<u16>,
396 include_global: bool,
397 final_translation_only: bool,
398 include_nested_translations: bool,
399) where
400 S: NotGiantPageSize,
401{
402 let mut rax = 0;
403 let mut ecx = 0;
404 let mut edx = 0;
405
406 if let Some((va, count)) = va_and_count {
407 rax.set_bit(0, true);
408 rax.set_bits(12.., va.start_address().as_u64().get_bits(12..));
409
410 ecx.set_bits(0..=15, u32::from(count));
411 ecx.set_bit(31, S::SIZE == Size2MiB::SIZE);
412 }
413
414 if let Some(pcid) = pcid {
415 rax.set_bit(1, true);
416 edx.set_bits(16..=27, u32::from(pcid.value()));
417 }
418
419 if let Some(asid) = asid {
420 rax.set_bit(2, true);
421 edx.set_bits(0..=15, u32::from(asid));
422 }
423
424 rax.set_bit(3, include_global);
425 rax.set_bit(4, final_translation_only);
426 rax.set_bit(5, include_nested_translations);
427
428 unsafe {
429 asm!(
430 "invlpgb",
431 in("rax") rax,
432 in("ecx") ecx,
433 in("edx") edx,
434 options(nostack, preserves_flags),
435 );
436 }
437}