OSDev.org

The Place to Start for Operating System Developers
It is currently Sun Apr 28, 2024 5:22 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 11 posts ] 
Author Message
 Post subject: General Protection Fault when Setting WC via MTRR
PostPosted: Mon Oct 30, 2023 12:34 pm 
Offline

Joined: Mon Oct 30, 2023 12:04 pm
Posts: 3
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?) :

Code:
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(())
}

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


Top
 Profile  
 
 Post subject: Re: General Protection Fault when Setting WC via MTRR
PostPosted: Mon Oct 30, 2023 9:02 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5146
Code:
        writemsr(mask_reg, 12..=51, mask >> 12).unwrap(); // Set the mask                   // <-- THIS IS THE LINE CAUSING THE GPF

When a processor implements less than the architecturally-defined address size of 52 bits, the unused bits are reserved, and setting reserved bits in any MSR causes a general protection fault.


Top
 Profile  
 
 Post subject: Re: General Protection Fault when Setting WC via MTRR
PostPosted: Tue Oct 31, 2023 2:09 am 
Offline

Joined: Mon Oct 30, 2023 12:04 pm
Posts: 3
Used :
Code:
cat /proc/cpuinfo | grep "address" 

And got :
Code:
address sizes   : 43 bits physical, 48 bits virtual

Changed it and now it works.

I feel kinda dumb but not too dumb either so it's fine. And I found a lot of bugs by searching to solve this one so yeah, worth it I guess

Now my framebuffer is quicker, instead of having one line every two seconds, I have two new lines every second. There must be some other problem elsewhere but well


Top
 Profile  
 
 Post subject: Re: General Protection Fault when Setting WC via MTRR
PostPosted: Tue Oct 31, 2023 11:24 am 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5146
Absobel wrote:
There must be some other problem elsewhere but well

You're not reading the framebuffer, are you? That's going to be extremely slow no matter how you do it.

How close together (spatially and temporally) are your writes? The CPU has a limited number of write-combining buffers, and anything that spreads out your writes can cause the buffers to be emptied before you've really filled them.

Which CPU instructions are you using to write the framebuffer? Some instructions that are fast with WB memory are not so fast with WC memory.

What kind of display adapter are you using? Some of them are known to be pretty slow when using the MMIO framebuffer set up by the firmware.


Top
 Profile  
 
 Post subject: Re: General Protection Fault when Setting WC via MTRR
PostPosted: Tue Oct 31, 2023 12:26 pm 
Offline

Joined: Mon Oct 30, 2023 12:04 pm
Posts: 3
Ah, I begin to see the problem. I am indeed both reading from the framebuffer and doing read write quickly in time instead of doing everything once, this is what double buffering is for I suppose ?

I don't actually know what instructions my rust code uses but I'm essentially just interpreting the whole framebuffer as a big slice and reading/writing from/into it.

I'm using GOP provided by UEFI


Top
 Profile  
 
 Post subject: Re: General Protection Fault when Setting WC via MTRR
PostPosted: Tue Oct 31, 2023 1:27 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5146
Absobel wrote:
Ah, I begin to see the problem. I am indeed both reading from the framebuffer and doing read write quickly in time instead of doing everything once, this is what double buffering is for I suppose ?

Yep, you need to double buffer to avoid expensive framebuffer reads.

Absobel wrote:
I don't actually know what instructions my rust code uses but I'm essentially just interpreting the whole framebuffer as a big slice and reading/writing from/into it.

Replace the framebuffer reads with backbuffer reads, add backbuffer writes to the framebuffer writes, and you're good to go. It might not give you the best performance, but it's the simplest way to avoid copying the entire backbuffer when the changes are small, and it should still be pretty fast in most cases.

Absobel wrote:
I'm using GOP provided by UEFI

That's a firmware API. I'm asking about the hardware.


Top
 Profile  
 
 Post subject: Re: General Protection Fault when Setting WC via MTRR
PostPosted: Tue Oct 31, 2023 1:29 pm 
Offline
Member
Member

Joined: Wed Oct 01, 2008 1:55 pm
Posts: 3195
My GOP buffer (Intel with HDMI) is behaving very strangely. Sometimes it is really fast and sometimes really slow. I'm starting to suspect that it might be slow (or fast) on BSP and the other way with AP cores. Since the stress test app is switched between cores that could explain this anomaly.

Maybe EFI sets up some MTRRs on BSP and then they are not setup on AP cores? I do setup the pages to WC, but there seems to be something else going on too. I also use double buffering. With AMD, GOP LFB is a lot faster, even on dated hardware.


Top
 Profile  
 
 Post subject: Re: General Protection Fault when Setting WC via MTRR
PostPosted: Tue Oct 31, 2023 1:54 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5146
rdos wrote:
Maybe EFI sets up some MTRRs on BSP and then they are not setup on AP cores?

That's possible. Firmware is supposed to set things up the same on every core, but sometimes that doesn't happen.


Top
 Profile  
 
 Post subject: Re: General Protection Fault when Setting WC via MTRR
PostPosted: Tue Oct 31, 2023 1:59 pm 
Offline
Member
Member

Joined: Wed Aug 30, 2017 8:24 am
Posts: 1605
rdos wrote:
Maybe EFI sets up some MTRRs on BSP and then they are not setup on AP cores?
On one hand I want to say that this would be pretty poor firmware. On the other hand, there certainly are examples of poor firmware in the world. Can you dump the MTRRs on the different cores?

I typically assume the firmware set up the MTRRs correctly. If that is false, then I actually need code to replicate the MTRRs to the APs. OK, that would be simple, as I already have code to replicate other MSRs to the APs, but I thought this was one area where it wasn't needed. I also found code to set up the MTRRs on all of the APs in Coreboot, and I thought if the Coreboot people thought of it, the professionals must have also. But maybe not.

_________________
Carpe diem!


Top
 Profile  
 
 Post subject: Re: General Protection Fault when Setting WC via MTRR
PostPosted: Tue Oct 31, 2023 2:21 pm 
Offline
Member
Member

Joined: Wed Oct 01, 2008 1:55 pm
Posts: 3195
I think I discovered the problem. I reprogram the MSR_PAT on BSP so entry 1 is WC, but then I fail to do the same on AP processors. I think that might explain this anomaly. I probably assumed the MSRs were global, but they apparently are not.


Top
 Profile  
 
 Post subject: Re: General Protection Fault when Setting WC via MTRR
PostPosted: Tue Oct 31, 2023 2:44 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5146
MSRs are never global, although per-socket MSRs are effectively global on single-socket systems. It's best to assume MSRs are unique for each logical thread unless the manuals specify otherwise.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot], DotBot [Bot], SemrushBot [Bot] and 31 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group