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 let cpuid = unsafe { core::arch::x86_64::__cpuid(0x8000_0008) };
164 if !cpuid.ebx.get_bit(3) {
165 return None;
166 }
167
168 let tlb_flush_nested = cpuid.ebx.get_bit(21);
169 let invlpgb_count_max = cpuid.edx.get_bits(0..=15) as u16;
170
171 let cpuid = unsafe { core::arch::x86_64::__cpuid(0x8000_000a) };
173 let nasid = cpuid.ebx;
174
175 Some(Self {
176 tlb_flush_nested,
177 invlpgb_count_max,
178 nasid,
179 })
180 }
181
182 #[inline]
184 pub fn invlpgb_count_max(&self) -> u16 {
185 self.invlpgb_count_max
186 }
187
188 #[inline]
190 pub fn tlb_flush_nested(&self) -> bool {
191 self.tlb_flush_nested
192 }
193
194 #[inline]
196 pub fn nasid(&self) -> u32 {
197 self.nasid
198 }
199
200 pub fn build(&self) -> InvlpgbFlushBuilder<'_> {
202 InvlpgbFlushBuilder {
203 invlpgb: self,
204 page_range: None,
205 pcid: None,
206 asid: None,
207 include_global: false,
208 final_translation_only: false,
209 include_nested_translations: false,
210 }
211 }
212
213 #[inline]
216 pub fn tlbsync(&self) {
217 unsafe {
218 asm!("tlbsync", options(nomem, preserves_flags));
219 }
220 }
221}
222
223#[derive(Debug, Clone)]
225#[must_use]
226pub struct InvlpgbFlushBuilder<'a, S = Size4KiB>
227where
228 S: NotGiantPageSize,
229{
230 invlpgb: &'a Invlpgb,
231 page_range: Option<PageRange<S>>,
232 pcid: Option<Pcid>,
233 asid: Option<u16>,
234 include_global: bool,
235 final_translation_only: bool,
236 include_nested_translations: bool,
237}
238
239impl<'a, S> InvlpgbFlushBuilder<'a, S>
240where
241 S: NotGiantPageSize,
242{
243 pub fn pages<T>(self, page_range: PageRange<T>) -> InvlpgbFlushBuilder<'a, T>
248 where
249 T: NotGiantPageSize,
250 {
251 InvlpgbFlushBuilder {
252 invlpgb: self.invlpgb,
253 page_range: Some(page_range),
254 pcid: self.pcid,
255 asid: self.asid,
256 include_global: self.include_global,
257 final_translation_only: self.final_translation_only,
258 include_nested_translations: self.include_nested_translations,
259 }
260 }
261
262 pub unsafe fn pcid(&mut self, pcid: Pcid) -> &mut Self {
268 self.pcid = Some(pcid);
269 self
270 }
271
272 pub unsafe fn asid(&mut self, asid: u16) -> Result<&mut Self, AsidOutOfRangeError> {
279 if u32::from(asid) >= self.invlpgb.nasid {
280 return Err(AsidOutOfRangeError {
281 asid,
282 nasid: self.invlpgb.nasid,
283 });
284 }
285
286 self.asid = Some(asid);
287 Ok(self)
288 }
289
290 pub fn include_global(&mut self) -> &mut Self {
292 self.include_global = true;
293 self
294 }
295
296 pub fn final_translation_only(&mut self) -> &mut Self {
298 self.final_translation_only = true;
299 self
300 }
301
302 pub fn include_nested_translations(mut self) -> Self {
304 assert!(
305 self.invlpgb.tlb_flush_nested,
306 "flushing all nested translations is not supported"
307 );
308
309 self.include_nested_translations = true;
310 self
311 }
312
313 pub fn flush(&self) {
315 if let Some(mut pages) = self.page_range {
316 while !pages.is_empty() {
317 let count = Page::<S>::steps_between_impl(&pages.start, &pages.end).0;
319
320 let second_half_start =
322 Page::<S>::containing_address(VirtAddr::new(0xffff_8000_0000_0000));
323 let count = if pages.start < second_half_start {
324 let count_to_second_half =
325 Page::steps_between_impl(&pages.start, &second_half_start).0;
326 cmp::min(count, count_to_second_half)
327 } else {
328 count
329 };
330
331 let count = u16::try_from(count).unwrap_or(u16::MAX);
333
334 let count = cmp::min(count, self.invlpgb.invlpgb_count_max);
336
337 unsafe {
338 flush_broadcast(
339 Some((pages.start, count)),
340 self.pcid,
341 self.asid,
342 self.include_global,
343 self.final_translation_only,
344 self.include_nested_translations,
345 );
346 }
347
348 let inc_count = cmp::max(count, 1);
351 pages.start =
352 Page::forward_checked_impl(pages.start, usize::from(inc_count)).unwrap();
353 }
354 } else {
355 unsafe {
356 flush_broadcast::<S>(
357 None,
358 self.pcid,
359 self.asid,
360 self.include_global,
361 self.final_translation_only,
362 self.include_nested_translations,
363 );
364 }
365 }
366 }
367}
368
369#[derive(Debug)]
371pub struct AsidOutOfRangeError {
372 pub asid: u16,
374 pub nasid: u32,
376}
377
378impl fmt::Display for AsidOutOfRangeError {
379 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380 write!(
381 f,
382 "{} is out of the range of available ASIDS ({})",
383 self.asid, self.nasid
384 )
385 }
386}
387
388#[inline]
390unsafe fn flush_broadcast<S>(
391 va_and_count: Option<(Page<S>, u16)>,
392 pcid: Option<Pcid>,
393 asid: Option<u16>,
394 include_global: bool,
395 final_translation_only: bool,
396 include_nested_translations: bool,
397) where
398 S: NotGiantPageSize,
399{
400 let mut rax = 0;
401 let mut ecx = 0;
402 let mut edx = 0;
403
404 if let Some((va, count)) = va_and_count {
405 rax.set_bit(0, true);
406 rax.set_bits(12.., va.start_address().as_u64().get_bits(12..));
407
408 ecx.set_bits(0..=15, u32::from(count));
409 ecx.set_bit(31, S::SIZE == Size2MiB::SIZE);
410 }
411
412 if let Some(pcid) = pcid {
413 rax.set_bit(1, true);
414 edx.set_bits(16..=27, u32::from(pcid.value()));
415 }
416
417 if let Some(asid) = asid {
418 rax.set_bit(2, true);
419 edx.set_bits(0..=15, u32::from(asid));
420 }
421
422 rax.set_bit(3, include_global);
423 rax.set_bit(4, final_translation_only);
424 rax.set_bit(5, include_nested_translations);
425
426 unsafe {
427 asm!(
428 "invlpgb",
429 in("rax") rax,
430 in("ecx") ecx,
431 in("edx") edx,
432 options(nostack, preserves_flags),
433 );
434 }
435}