-
Notifications
You must be signed in to change notification settings - Fork 132
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added MTRRs and other missing registers #311
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for the PR! I did not have time to review the MTRR code (it's a lot), but I left some comments on the other changes.
I don't know when I'll have time for a more detailed review, but I would be fine with merging this behind a feature gate. In #266 (review) I proposed a cargo feature named experimental
for this, to indicate new code that hasn't been tested much yet. What do you think about this approach?
Yeah sounds like a good idea! Right now I'm a bit busy so I will implement the fixups you mentioned in one or two weeks |
bitflags! { | ||
/// Configuration flags of the [`Cr8`] register. | ||
pub struct Cr8Flags: u64 { | ||
/// A value corresponding to the highest-priority interrupt that is to be blocked | ||
const TASK_PRIORITY = 0xf; | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure bitflags is the right abstraction for this. The register doesn't contain any flags, it contains the task priority as a 4 bit number.
/// interrupting a high-priority task. This is accomplished by loading TPR with a value corresponding to | ||
/// the highest-priority interrupt that is to be blocked. | ||
#[derive(Debug)] | ||
pub struct Cr8; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The other registers appear in order of their number. We should keep it that way.
@@ -162,6 +176,57 @@ mod x86_64 { | |||
use super::*; | |||
use crate::{instructions::tlb::Pcid, structures::paging::PhysFrame, PhysAddr, VirtAddr}; | |||
|
|||
impl Cr8 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same thing about the order here.
/// MtrrFixDramEn | ||
const MFDE = 1 << 18; | ||
/// MtrrFixDramModEn | ||
const MFDM = 1 << 19; | ||
/// MtrrVarDramEn | ||
const MVDM = 1 << 20; | ||
/// MtrrTom2En | ||
const TOM2 = 1 << 21; | ||
/// Tom2ForceMemTypeWB | ||
const FWB = 1 << 22; | ||
/// MemEncryptionModeEn | ||
const MEME = 1 << 23; | ||
/// SecureNestPagingEn | ||
const SNPE = 1 << 24; | ||
/// VMPLEn | ||
const VMPLE = 1 << 25; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation for these could be improved.
#[derive(Debug)] | ||
pub struct MTRRcap; | ||
|
||
#[allow(dead_code)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not needed.
/// Flags for the MTRRphysBase[n] registers | ||
pub struct MTRRphysBaseFlags: u64 { | ||
/// The memory range base-address in physical-address space | ||
const PHYS_BASE = 0xffff_ffff_ff << 12; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same thing here with PHYS_BASE
as with PHYS_MASK
.
impl MTRRphysBase0 { | ||
/// Read the current raw MTRRphysBase flags. | ||
#[inline] | ||
pub fn read_raw() -> u64 { | ||
unsafe { Self::MSR.read() } | ||
} | ||
|
||
/// Write the MTRRphysBase flags. | ||
/// | ||
/// Does not preserve any bits, including reserved fields. | ||
/// | ||
/// ## Safety | ||
/// | ||
/// Unsafe because it's possible to | ||
/// break memory safety with wrong flags | ||
#[inline] | ||
pub unsafe fn write_raw(flags: u64) { | ||
let mut msr = Self::MSR; | ||
msr.write(flags); | ||
} | ||
|
||
/// Write the MTRRphysBase flags, preserving reserved values. | ||
/// | ||
/// Preserves the value of reserved fields. | ||
/// | ||
/// ## Safety | ||
/// | ||
/// Unsafe because it's possible to break memory | ||
/// safety with wrong flags | ||
#[inline] | ||
pub unsafe fn write(flags: MTRRphysBaseFlags) { | ||
let old_value = Self::read_raw(); | ||
let reserved = old_value & !(MTRRphysBaseFlags::all().bits()); | ||
let new_value = reserved | flags.bits(); | ||
|
||
Self::write_raw(new_value); | ||
} | ||
|
||
/// Read the current MTRRphysBase flags. | ||
#[inline] | ||
pub fn read() -> MTRRphysBaseFlags { | ||
MTRRphysBaseFlags::from_bits_truncate(Self::read_raw()) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be worth considering putting this in a macro instead of repeating it 8 times. Also applies to MTRRphysMaskn
and MTRRfix64K00000
.
Alternatively you could consider making those registers enumerations. This would also have the advantage that it allows choosing the register at runtime.
let two = FixMemRange::new( | ||
0x10000, | ||
0x1FFFF, | ||
((r & (0xff << 8)) >> 8).try_into().unwrap(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use r.get_bits(8..16)
instead of ((r & (0xff << 8)) >> 8)
.
@@ -44,7 +44,7 @@ use crate::registers::segmentation::{Segment, CS, SS}; | |||
/// // Add entry for TSS, call gdt.load() then update segment registers | |||
/// ``` | |||
|
|||
#[derive(Debug, Clone)] | |||
#[derive(Debug, Clone, Copy)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was proposed and rejected in #166.
bitflags! { | ||
/// Configuration flags of the [`Cr8`] register. | ||
pub struct Cr8Flags: u64 { | ||
/// A value corresponding to the highest-priority interrupt that is to be blocked |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We usually end all sentences in comments with a dot. This should be fixed in a others comments too.
Out of curiosity, is there a reason that you're using MTTRs instead of the PAT? This seems like a lot of code for something that has already been superseded. |
Thanks for the throughout review! The problem is that MTTRs are still relevant in x64 and active and can override PAT. Which means it's very important to know how they are configured by default to guard against very hard to pin down bugs. An example: My AMD Bulldozer FX-8600 for example has the default mtrr memory type of uncached. Which means every memory region that is not covered by variable length mtrrs is set to uncached even if the PAT says something different. |
Very interesting! How can this cause bugs? AFAICT the MTRRs can only overwrite the PAT in that they can disable caching that would have been allowed by the PAT. Is this not true or am I missing something else? |
@Freax13 Ah very nice! I must admit I didn't look too closely into the table because I just assumed that a different effective caching behavior then what the developer thought would be there can lead to bugs, know I know for real ^^ |
If someone wants to open up a new reworked pull request go ahead :-) I sadly won't be able to work on this for at least a couple of months sadly (maybe @Freax13 ?) |
Added new registers:
Whats missing?
Implementing the
#[cfg(not(feature = "inline_asm"))]
handles.A write() function for
mtrrfix4ke0000
and the like