PMP Addressing

I’m trying to program the PMP address registers and I can’t write any value in the ram space. I believe it extends from 0x8000_0000 to 0x8000_3FFF. If I try to write 0x8000_1000 to the address then read the contents, it only shows up as 0x1000. Can the RAM not be protected by PMP?

EDIT
Reading some more content, I believe I am misunderstanding how the addressing range works. Still don’t under stand it yet though.

Found the answer, for NAPOT range 0x8000_0000 to 0x8000_4000
range = 0x4000
[0x8000_0000 + ((0x4000/2) - 1)] >> 2
pmpaddrx = 0x200007FF

Something still isn’t right with the pmp address configuration

         L         A   XWR
pmp0cfg: 0  00    01   111  (unlocked, TOR, RWX)
pmp1cfg: 0  00    11   011  (unlocked, NAPOT, RW)
pmpaddr0: 0x20040000
pmpaddr1: 0x20000EA7

pmpaddr1 should prevent stack overflows
bottom of stack = 0x80003920
top of stack = 0x80003C20

0x80003920 + ((0x768/2) - 1)] >> 2 = 0x20000EA7

Stack overflow occurs without an Instruction Fault, meaning the pmp did not catch the overflow.
anyone know what I missed?

You’re trying to use pmpaddr1 to encode a NAPOT range from 0x80003920 to 0x80003C20?

That is size 0x300 which is not a POT (Power Of Two) so NAPOT can not be used.

0x20000EA7 is
0010 0000 0000 0000 0000 1110 1010 0111

Which makes the first 0 bit from the right bit 3, so this value encodes a region of size 2^(3+3) = 64 = 0x40 bytes, at address
1000 0000 0000 0000 0011 1010 1000 0000

i.e. 0x800003c80 to (0x800003c80 + 0x40) = 0x800003cc0

The calculation in the previous message for NAPOT range 0x8000_0000 to 0x8000_4000 is correct.

Note that pmpaddr0 equal to 0x20040000 as a TOR is enabling RWX for all addresses from 0x0 to 0x80100000. Any address in this range will match this entry and succeed, so in particular if later entries cover your desired range 0x80003920 to 0x80003C20 those entries will never be used.

1 Like

I misunderstood how TOR works, I thought you put the actual address in. Now I see that that value is also bit-shifted. Yeah, after I posted I realized my mistake on the NAPOT not being a power of two size. This makes it much clearer. Thanks

I’m trying to protect my u-mode application’s stack frame from overflow. It is working sort of. I initialize an array of size “x” bytes to zeros. I set the stack pointer to the address of the top of the array etc, then mret. pmpaddr1 is set to the address of the bottom of the stack frame, and pmpaddr2 is top of stack fram, both TOR.

 _____   <- 0x80003fff Top of RAM
|     |
|     | <- 0x80003ED8 MTVAL after StoreFault Trap
|     |
|     | <- 0x80003CD0 Top of array for u-mode stack frame, sp set here on MRET and pmpaddr2 (TOR)
|     |
|     | <- 0x80003AD0 Bottom of array for u-mode stack frame and pmpaddr1 
|_____|

When User mode is entered and I begin calling functions, a store fault is incurred. Why is the stack pointer higher than it is when I manually set it? Shouldn’t the stack grow from higher addresses to lower ones? My array for testing was 512 bytes, the difference between the stack pointer and the mtval was 520 bytes. If I change the array size, the difference is always the array size plus 8 bytes. What am I not understanding?

It is absolutely impossible to tell what is happening from such an informal English prose description.

Presumably you are in some way not doing what you describe. Or you have left out some important detail but there is no way to know what it is.

I think I figured it out anyways.
Following the SiFive user-mode-example on github, they create a stack frame for user mode to use before they drop privilege.

1. create array with 16 byte alignment and initialize with zeros
2. determine the address of the end of the array (my_stack + sizeof(my_stack) in C)
3. setup mstatus appropriately
4. set the stack pointer to the address calculated in #2
5. MRET

Before I MRET into user mode, I added pmp configurations to encapsulate the user stack frame to prevent stack overflows. However, I was never able to enter user mode because it always trapped with a StoreFault. Reading the stack pointer showed that it was outside of the user stack frame by a lot.

It turns out, that when I was setting the stack pointer before the MRET, I was using &stack_ptr instead of just stack_ptr. This fixes my problem, I probably should have posted some code before, but it is Rust code so I wan’t sure. A lot of my issues I encounter are borderline between Rust and RISC-V which makes it hard to know which forum to post my question on, plus I’m new to embedded development in general.