Skip to content

Commit e16c098

Browse files
authored
PCID support instructions (#169)
1 parent 8a54bca commit e16c098

File tree

4 files changed

+138
-5
lines changed

4 files changed

+138
-5
lines changed

src/asm/asm.s

+6
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ _x86_64_asm_invlpg:
9494
invlpg (%rdi)
9595
retq
9696

97+
.global _x86_64_asm_invpcid
98+
.p2align 4
99+
_x86_64_asm_invpcid:
100+
invpcid (%rsi), %rdi
101+
retq
102+
97103
.global _x86_64_asm_ltr
98104
.p2align 4
99105
_x86_64_asm_ltr:

src/asm/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,12 @@ extern "C" {
144144
)]
145145
pub(crate) fn x86_64_asm_invlpg(addr: u64);
146146

147+
#[cfg_attr(
148+
any(target_env = "gnu", target_env = "musl"),
149+
link_name = "_x86_64_asm_invpcid"
150+
)]
151+
pub(crate) fn x86_64_asm_invpcid(kind: u64, desc: u64);
152+
147153
#[cfg_attr(
148154
any(target_env = "gnu", target_env = "musl"),
149155
link_name = "_x86_64_asm_read_cr0"

src/instructions/tlb.rs

+85
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,88 @@ pub fn flush_all() {
2323
let (frame, flags) = Cr3::read();
2424
unsafe { Cr3::write(frame, flags) }
2525
}
26+
27+
/// The Invalidate PCID Command to execute.
28+
#[derive(Debug)]
29+
pub enum InvPicdCommand {
30+
/// The logical processor invalidates mappings—except global translations—for the linear address and PCID specified.
31+
Address(VirtAddr, Pcid),
32+
33+
/// The logical processor invalidates all mappings—except global translations—associated with the PCID.
34+
Single(Pcid),
35+
36+
/// The logical processor invalidates all mappings—including global translations—associated with any PCID.
37+
All,
38+
39+
/// The logical processor invalidates all mappings—except global translations—associated with any PCID.
40+
AllExceptGlobal,
41+
}
42+
43+
/// The INVPCID descriptor comprises 128 bits and consists of a PCID and a linear address.
44+
/// For INVPCID type 0, the processor uses the full 64 bits of the linear address even outside 64-bit mode; the linear address is not used for other INVPCID types.
45+
#[repr(C)]
46+
#[derive(Debug)]
47+
struct InvpcidDescriptor {
48+
address: u64,
49+
pcid: u64,
50+
}
51+
52+
/// Structure of a PCID. A PCID has to be <= 4096 for x86_64.
53+
#[repr(transparent)]
54+
#[derive(Debug)]
55+
pub struct Pcid(u16);
56+
57+
impl Pcid {
58+
/// Create a new PCID. Will result in a failure if the value of
59+
/// PCID is out of expected bounds.
60+
pub const fn new(pcid: u16) -> Result<Pcid, &'static str> {
61+
if pcid >= 4096 {
62+
Err("PCID should be < 4096.")
63+
} else {
64+
Ok(Pcid(pcid))
65+
}
66+
}
67+
68+
/// Get the value of the current PCID.
69+
pub const fn value(&self) -> u16 {
70+
self.0
71+
}
72+
}
73+
74+
/// Invalidate the given address in the TLB using the `invpcid` instruction.
75+
///
76+
/// ## Safety
77+
/// This function is unsafe as it requires CPUID.(EAX=07H, ECX=0H):EBX.INVPCID to be 1.
78+
#[inline]
79+
pub unsafe fn flush_pcid(command: InvPicdCommand) {
80+
let mut desc = InvpcidDescriptor {
81+
address: 0,
82+
pcid: 0,
83+
};
84+
85+
let kind: u64;
86+
match command {
87+
InvPicdCommand::Address(addr, pcid) => {
88+
kind = 0;
89+
desc.pcid = pcid.value().into();
90+
desc.address = addr.as_u64()
91+
}
92+
InvPicdCommand::Single(pcid) => {
93+
kind = 1;
94+
desc.pcid = pcid.0.into()
95+
}
96+
InvPicdCommand::All => kind = 2,
97+
InvPicdCommand::AllExceptGlobal => kind = 3,
98+
}
99+
100+
#[cfg(feature = "inline_asm")]
101+
{
102+
let desc_value = &desc as *const InvpcidDescriptor as u64;
103+
asm!("invpcid {1}, [{0}]", in(reg) desc_value, in(reg) kind);
104+
};
105+
106+
#[cfg(not(feature = "inline_asm"))]
107+
{
108+
crate::asm::x86_64_asm_invpcid(kind, &desc as *const InvpcidDescriptor as u64)
109+
};
110+
}

src/registers/control.rs

+41-5
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,7 @@ bitflags! {
128128
#[cfg(feature = "instructions")]
129129
mod x86_64 {
130130
use super::*;
131-
use crate::structures::paging::PhysFrame;
132-
use crate::{PhysAddr, VirtAddr};
131+
use crate::{instructions::tlb::Pcid, structures::paging::PhysFrame, PhysAddr, VirtAddr};
133132

134133
impl Cr0 {
135134
/// Read the current set of CR0 flags.
@@ -233,6 +232,14 @@ mod x86_64 {
233232
/// Read the current P4 table address from the CR3 register.
234233
#[inline]
235234
pub fn read() -> (PhysFrame, Cr3Flags) {
235+
let (frame, value) = Cr3::read_raw();
236+
let flags = Cr3Flags::from_bits_truncate(value.into());
237+
(frame, flags)
238+
}
239+
240+
/// Read the current P4 table address from the CR3 register
241+
#[inline]
242+
pub fn read_raw() -> (PhysFrame, u16) {
236243
let value: u64;
237244

238245
#[cfg(feature = "inline_asm")]
@@ -245,10 +252,18 @@ mod x86_64 {
245252
value = crate::asm::x86_64_asm_read_cr3();
246253
}
247254

248-
let flags = Cr3Flags::from_bits_truncate(value);
249255
let addr = PhysAddr::new(value & 0x_000f_ffff_ffff_f000);
250256
let frame = PhysFrame::containing_address(addr);
251-
(frame, flags)
257+
(frame, (value & 0xFFF) as u16)
258+
}
259+
260+
/// Read the current P4 table address from the CR3 register along with PCID.
261+
/// The correct functioning of this requires CR4.PCIDE = 1.
262+
/// See [`Cr4Flags::PCID`]
263+
#[inline]
264+
pub fn read_pcid() -> (PhysFrame, Pcid) {
265+
let (frame, value) = Cr3::read_raw();
266+
(frame, Pcid::new(value as u16).unwrap())
252267
}
253268

254269
/// Write a new P4 table address into the CR3 register.
@@ -258,8 +273,29 @@ mod x86_64 {
258273
/// changing the page mapping.
259274
#[inline]
260275
pub unsafe fn write(frame: PhysFrame, flags: Cr3Flags) {
276+
Cr3::write_raw(frame, flags.bits() as u16);
277+
}
278+
279+
/// Write a new P4 table address into the CR3 register.
280+
///
281+
/// ## Safety
282+
/// Changing the level 4 page table is unsafe, because it's possible to violate memory safety by
283+
/// changing the page mapping.
284+
/// [`Cr4Flags::PCID`] must be set before calling this method.
285+
#[inline]
286+
pub unsafe fn write_pcid(frame: PhysFrame, pcid: Pcid) {
287+
Cr3::write_raw(frame, pcid.value());
288+
}
289+
290+
/// Write a new P4 table address into the CR3 register.
291+
///
292+
/// ## Safety
293+
/// Changing the level 4 page table is unsafe, because it's possible to violate memory safety by
294+
/// changing the page mapping.
295+
#[inline]
296+
unsafe fn write_raw(frame: PhysFrame, val: u16) {
261297
let addr = frame.start_address();
262-
let value = addr.as_u64() | flags.bits();
298+
let value = addr.as_u64() | val as u64;
263299

264300
#[cfg(feature = "inline_asm")]
265301
asm!("mov cr3, {}", in(reg) value, options(nostack));

0 commit comments

Comments
 (0)