Skip to content
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

Draft
wants to merge 15 commits into
base: master
Choose a base branch
from

Conversation

Qubasa
Copy link
Contributor

@Qubasa Qubasa commented Sep 26, 2021

Added new registers:

  • mtrrcap
  • mtrrdeftype
  • mtrrfix64k00000
  • mtrrfix16k80000
  • mtrrfix16ka0000
  • mtrrfix4kc0000
  • mtrrfix4kc8000
  • mtrrfix4kd0000
  • mtrrfix4kd8000
  • mtrrfix4ke0000
  • mtrrfix4ke8000
  • mtrrfix4kf8000
  • mtrrphysmask0
  • mtrrphysmask1
  • mtrrphysmask2
  • mtrrphysmask3
  • mtrrphysmask4
  • mtrrphysmask5
  • mtrrphysmask6
  • mtrrphysmask7
  • mtrrphysbase0
  • mtrrphysbase1
  • mtrrphysbase2
  • mtrrphysbase3
  • mtrrphysbase4
  • mtrrphysbase5
  • mtrrphysbase6
  • mtrrphysbase7
  • cstar
  • syscfg
  • ldtr
  • cr8

Whats missing?
Implementing the #[cfg(not(feature = "inline_asm"))] handles.
A write() function for mtrrfix4ke0000 and the like

@phil-opp phil-opp added the waiting-for-review Waiting for a review from the maintainers. label Nov 6, 2021
Copy link
Member

@phil-opp phil-opp left a 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?

src/instructions/tables.rs Outdated Show resolved Hide resolved
src/instructions/tables.rs Outdated Show resolved Hide resolved
src/registers/control.rs Outdated Show resolved Hide resolved
src/registers/control.rs Outdated Show resolved Hide resolved
src/registers/control.rs Outdated Show resolved Hide resolved
src/registers/control.rs Outdated Show resolved Hide resolved
@phil-opp phil-opp added waiting-on-author Waiting for the author to act on review feedback. and removed waiting-for-review Waiting for a review from the maintainers. labels Nov 6, 2021
@Qubasa
Copy link
Contributor Author

Qubasa commented Nov 6, 2021

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

@Qubasa Qubasa requested a review from phil-opp November 17, 2021 23:14
@phil-opp phil-opp added waiting-for-review Waiting for a review from the maintainers. and removed waiting-on-author Waiting for the author to act on review feedback. labels Nov 20, 2021
Comment on lines +12 to +19
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;
}
}

Copy link
Member

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;
Copy link
Member

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 {
Copy link
Member

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.

Comment on lines +114 to +129
/// 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;
Copy link
Member

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)]
Copy link
Member

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;
Copy link
Member

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.

Comment on lines +426 to +469
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())
}
}
Copy link
Member

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(),
Copy link
Member

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)]
Copy link
Member

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
Copy link
Member

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.

@Freax13
Copy link
Member

Freax13 commented Dec 1, 2021

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.

@Qubasa
Copy link
Contributor Author

Qubasa commented Dec 2, 2021

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.

@Freax13
Copy link
Member

Freax13 commented Dec 2, 2021

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.

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?

@Qubasa
Copy link
Contributor Author

Qubasa commented Dec 3, 2021

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.

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?

Not only no, here is a table which illustrates what side effects can be caused (it's from the AMD manual vol 2 page 263):
image

@Freax13
Copy link
Member

Freax13 commented Dec 3, 2021

Not only no, here is a table which illustrates what side effects can be caused (it's from the AMD manual vol 2 page 263): image

Which one's of these show that the MTRR's can allow caching that isn't allowed by the PAT? The only one I see is the when the PAT is set to UC- and the MTRR is set to WC, but given that that is the sole purpose of UC- I don't think that counts.

I think I found another case where the MTRR's can allow more caching:
image
A MTRR can change the effective memory type from write-through to write-combine, this matters because write-combine can do out-of-order writes which aren't allowed by write-through.

@Qubasa
Copy link
Contributor Author

Qubasa commented Dec 3, 2021

@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 ^^

@Freax13 Freax13 added waiting-on-author Waiting for the author to act on review feedback. and removed waiting-for-review Waiting for a review from the maintainers. labels Dec 13, 2021
@Qubasa
Copy link
Contributor Author

Qubasa commented Mar 7, 2022

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 ?)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
waiting-on-author Waiting for the author to act on review feedback.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants