General Protection Fault when Setting WC via MTRR
Posted: Mon Oct 30, 2023 12:34 pm
I've been stuck on this problem for more than a week. I read the wiki page on MTRR and the relevant AMD64 manual section (volume 2, 7.7), i searched on almost every random forum/reddit post and it still doesn't work. I'm trying to make my framebuffer WC to make graphics faster, but I keep getting a General Protection Fault. Here is my code (it's in rust but I don't think there is anything too complicated to understand?) :
In theory my writemsr and readmsr functions work correctly, I tested them. Maybe somehow I missed something but I don't think so?
I checked all the values and the mask and everything so many times, I calculated the values I should have in the register by hand and compared them with what I'm writing in the registers and everything seems fine. I really can't find why it doesn't work
(I swear if it's something dumb I'll remove my hippocampus with a table spoon)
Random informations that might or might not be relevant :
- I boot on UEFI using grub (the BootServices already are exited by the time my code is ran)
- I just have basic temporary paging yet, just enough huge pages to reach the framebuffer address given to me
- Here is my GitHub repo
Code: Select all
fn free_mtrr_pair() -> Option<(usize, usize)> {
let mttr_pair_reg_nb = mtrr_pair_reg_nb();
let mut mttr_pair_reg = None;
for i in 0..mttr_pair_reg_nb {
let mask_reg = 0x201 + i * 2;
let valid_bit = unsafe { readmsr(mask_reg, 11..=11) };
if valid_bit == 0 {
mttr_pair_reg = Some((mask_reg - 1, mask_reg));
break;
}
}
mttr_pair_reg
}
pub fn set_mtrr_wc(addr: usize, size: usize) -> Result<(), MsrError> {
if !has_msr_support() {
return Err(MsrError::NoMsrSupport);
}
if !has_wc_type_support() {
return Err(MsrError::NoWCTypeSupport);
}
enable_mtrr(); // It's already enabled by default but well
// Use the MTTR pair to set the WC memory type to the given address range
let (base_reg, mask_reg) = free_mtrr_pair().ok_or(MsrError::NoFreeMtrPair)?;
// size must be aligned to a boundary of a power of two and not be bigger than 52 bits
let mask = !(size.next_power_of_two() - 1) & ((1 << 52) - 1);
x86_64::instructions::interrupts::without_interrupts(move || unsafe {
writemsr(base_reg, 12..=51, addr >> 12).unwrap(); // Set the base address
writemsr(base_reg, 0..=7, WC_MEMORY_TYPE).unwrap(); // Set the memory type (WC_MEMORY_TYPE = 1)
writemsr(mask_reg, 12..=51, mask >> 12).unwrap(); // Set the mask // <-- THIS IS THE LINE CAUSING THE GPF
writemsr(mask_reg, 11..=11, 1).unwrap(); // Set the valid bit
});
Ok(())
}
I checked all the values and the mask and everything so many times, I calculated the values I should have in the register by hand and compared them with what I'm writing in the registers and everything seems fine. I really can't find why it doesn't work
(I swear if it's something dumb I'll remove my hippocampus with a table spoon)
Random informations that might or might not be relevant :
- I boot on UEFI using grub (the BootServices already are exited by the time my code is ran)
- I just have basic temporary paging yet, just enough huge pages to reach the framebuffer address given to me
- Here is my GitHub repo