Search code examples
rustkernelriscv

Qemu RiscV bare metal set SATP register failed


OS: Ubuntu 24.04 LTS on Windows 10 x8

QEMU: qemu-system-riscv64 version 8.2.2

gdb: GNU gdb (GDB) 15.2

I'm study write a kernel for Risc-V in Rust, running in QEMU, but it's crashed when setting Page Table. I have no idea.

This is the Source Code, it's pretty simple, just setup the Page Table.

I launched and debug it by: make debug, and used gdb debug it: bash debug.sh,

The app would be frozen while executing at src/lib.rs line:109:

    pub fn use_sv39(&self) {
        // let satp: usize = self.page_table.satp_token().into();
        // riscv::register::satp::write(satp);
        // let ppn = self.page_table.address.into();
        unsafe {
            let tok: usize = self.page_table.satp_token().into();
109: --->   asm!(concat!("csrw satp, {0}"), in(reg) tok);
            // let res = riscv::register::satp::try_set(Mode::Sv39, 0, ppn);
            println!("set satp");
            asm!("SFENCE.VMA");
        }
    }

I've tried debug the assembly, but while it run at csrw satp, a0 and be frozen.

I've checked the satp value, it's 0x0 before.

I don't have any idea, Thank you. If you need more message, let me know.

NEW, Debug Message:

I've breakpoint at symbol __alltraps as in file src/trap/trap.S/ line:12, that is trap_handler entry, have been set to stvec register. and when executing at src/lib.rs line:109 above, gdb stopped at symbol __alltraps:

debug message

That's indicate rise a exception, and I checked sstatus register, the SPP bit was 1, that indicated it was S-Mode before.

And the value of scause was 0xc, means Instruction page fault

BUT! if I type si to execute next instruction, it was reenter the __alltraps, the stvec! the breakpoint already hit 2 times! The $sstatus still was 0x200000100 and $scause was 0xc

debug message 2

That's very weird. That's Why? How to solve it?


Solution

  • You just misunderstood the meaning of the PPN field. To generate a physical address, the PPN needs to be shifted 12 bits to the left and the VPN needs to be concatenated.

    Here:

    impl PTE {
        pub(crate) fn new(frame: Frame, flags: Flags) -> Self {
            // Here: >> 12
            let pte = ((frame.value() >> 12) & ((1 << 44) - 1)) << 10 | flags.bits() as usize;
            PTE(pte)
        }
    
        pub fn is_valid(&self) -> bool {
            self.0 as u8 & Flags::V.bits() == Flags::V.bits()
        }
    
        /// get the physical page number,
        /// if this is not a leaf page table, the return is [PhysicalAddress](crate::mm::PhysicalAddress), or a [Frame]
        pub fn ppn(&self) -> Frame {
            // Here: << 12
            (((self.0 >> 10) & ((1 << 44) - 1)) << 12).into()
        }
    }
    
    

    And:

    // page_tables.rs:79 map
    frame = match block.map_type {
        MapType::Identical => (block.vpn.value() << 12).into(),
        MapType::UseFrame(f) => f,
    };
    

    By the way, the BSS address is incorrect. Modify it as follows:

        . = ALIGN(4K);
        edata = .;
        .bss : {
            sbss = .;
            *(.bss.stack)
            ;- sbss = .;
            *(.bss .bss.*)
            *(.sbss .sbss.*)
        }
    

    Now it should execute the following code correctly.