riscv: clarify stack size and alignment parameters

The StackGuard area is used to save the esf and run the exception code
resulting from a StackGuard trap. Size it appropriately.

Remove redundancy, clarify documentation, etc.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
This commit is contained in:
Nicolas Pitre 2022-04-30 15:12:31 -04:00 committed by Carles Cufí
parent 3997f7bed2
commit 6051ea7d3c
5 changed files with 63 additions and 111 deletions

View file

@ -169,10 +169,13 @@ config PMP_STACK_GUARD
config PMP_STACK_GUARD_MIN_SIZE
int "Guard size"
depends on PMP_STACK_GUARD
default 64
default 1024 if 64BIT
default 512
help
Size of the stack guard area. This should be large enough to
accommodate the stack overflow exception stack usage.
catch any sudden jump in stack pointer decrement, plus some
wiggle room to accommodate the eventual overflow exception
stack usage.
config PMP_POWER_OF_TWO_ALIGNMENT
bool "Enforce power-of-two alignment on PMP memory areas"
@ -190,6 +193,7 @@ endmenu
config MAIN_STACK_SIZE
default 4096 if 64BIT
default 2048 if PMP_STACK_GUARD
config TEST_EXTRA_STACK_SIZE
default 1024

View file

@ -338,15 +338,18 @@ void z_riscv_pmp_init(void)
void z_riscv_pmp_stackguard_prepare(struct k_thread *thread)
{
unsigned int index = global_pmp_end_index;
uintptr_t stack_bottom = thread->stack_info.start;
uintptr_t stack_bottom;
/* Retrieve pmpcfg0 partial content from global entries */
thread->arch.m_mode_pmpcfg_regs[0] = global_pmp_cfg[0];
/* make the bottom addresses of our stack inaccessible */
stack_bottom = thread->stack_info.start - K_KERNEL_STACK_RESERVED;
#ifdef CONFIG_USERSPACE
if (thread->arch.priv_stack_start != 0) {
stack_bottom = thread->arch.priv_stack_start;
} else if (z_stack_is_user_capable(thread->stack_obj)) {
stack_bottom = thread->stack_info.start - K_THREAD_STACK_RESERVED;
}
#endif
set_pmp_entry(&index, PMP_NONE,

View file

@ -217,12 +217,11 @@ FUNC_NORETURN void arch_user_mode_enter(k_thread_entry_t user_entry,
_current->arch.priv_stack_start =
(ulong_t)z_priv_stack_find(_current->stack_obj);
#else
_current->arch.priv_stack_start =
(ulong_t)(_current->stack_obj) +
Z_RISCV_STACK_GUARD_SIZE;
_current->arch.priv_stack_start = (ulong_t)_current->stack_obj;
#endif /* CONFIG_GEN_PRIV_STACKS */
top_of_priv_stack = Z_STACK_PTR_ALIGN(_current->arch.priv_stack_start
+ CONFIG_PRIVILEGED_STACK_SIZE);
top_of_priv_stack = Z_STACK_PTR_ALIGN(_current->arch.priv_stack_start +
K_KERNEL_STACK_RESERVED +
CONFIG_PRIVILEGED_STACK_SIZE);
top_of_user_stack = Z_STACK_PTR_ALIGN(
_current->stack_info.start +

View file

@ -28,17 +28,26 @@
#include <soc.h>
#include <zephyr/devicetree.h>
#include <zephyr/arch/riscv/csr.h>
#include <zephyr/arch/riscv/exp.h>
/* stacks, for RISCV architecture stack should be 16byte-aligned */
#define ARCH_STACK_PTR_ALIGN 16
#ifdef CONFIG_PMP_STACK_GUARD
#define Z_RISCV_PMP_ALIGN CONFIG_PMP_STACK_GUARD_MIN_SIZE
#define Z_RISCV_STACK_GUARD_SIZE Z_RISCV_PMP_ALIGN
#else
#define Z_RISCV_PMP_ALIGN 4
#define Z_RISCV_STACK_GUARD_SIZE 0
#endif
/*
* The StackGuard is an area at the bottom of the kernel-mode stack made to
* fault when accessed. It is _not_ faulting when in exception mode as we rely
* on that area to save the exception stack frame and to process said fault.
* Therefore the guard area must be large enough to hold the esf, plus some
* configurable stack wiggle room to execute the fault handling code off of,
* as well as some guard size to cover possible sudden stack pointer
* displacement before the fault.
*
* The m-mode PMP set is not overly used so no need to force NAPOT.
*/
#define Z_RISCV_STACK_GUARD_SIZE \
ROUND_UP(sizeof(z_arch_esf_t) + CONFIG_PMP_STACK_GUARD_MIN_SIZE, \
ARCH_STACK_PTR_ALIGN)
/* Kernel-only stacks have the following layout if a stack guard is enabled:
*
@ -52,31 +61,40 @@
* | TLS | } thread.stack_info.delta
* +------------+ <- thread.stack_info.start + thread.stack_info.size
*/
#ifdef CONFIG_PMP_STACK_GUARD
#define ARCH_KERNEL_STACK_RESERVED Z_RISCV_STACK_GUARD_SIZE
#define ARCH_KERNEL_STACK_OBJ_ALIGN \
MAX(Z_RISCV_PMP_ALIGN, ARCH_STACK_PTR_ALIGN)
#else /* !CONFIG_PMP_STACK_GUARD */
#define Z_RISCV_STACK_GUARD_SIZE 0
#endif
#ifdef CONFIG_USERSPACE
/* Any thread running In user mode will have full access to the region denoted
* by thread.stack_info.
*
* Thread-local storage is at the very highest memory locations of this area.
* Memory for TLS and any initial random stack pointer offset is captured
* in thread.stack_info.delta.
*/
#ifdef CONFIG_PMP_STACK_GUARD
#ifdef CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT
/* Use defaults for everything. The privilege elevation stack is located
* in another area of memory generated at build time by gen_kobject_list.py
/* The privilege elevation stack is located in another area of memory
* generated at build time by gen_kobject_list.py
*
* +------------+ <- thread.arch.priv_stack_start
* | Guard | } Z_RISCV_STACK_GUARD_SIZE
* +------------+
* | Priv Stack | } CONFIG_PRIVILEGED_STACK_SIZE - Z_RISCV_STACK_GUARD_SIZE
* | Priv Stack | } CONFIG_PRIVILEGED_STACK_SIZE
* +------------+ <- thread.arch.priv_stack_start +
* CONFIG_PRIVILEGED_STACK_SIZE
* CONFIG_PRIVILEGED_STACK_SIZE +
* Z_RISCV_STACK_GUARD_SIZE
*
* The main stack will be initially (or potentially only) used by kernel
* mode so we need to make room for a possible stack guard area when enabled:
*
* +------------+ <- thread.stack_obj
* | Guard | } Z_RISCV_STACK_GUARD_SIZE
* +............| <- thread.stack_info.start
* | Thread |
* | stack |
* | |
* +............|
* | TLS | } thread.stack_info.delta
* +------------+ <- thread.stack_info.start + thread.stack_info.size
*
* When transitioning to user space, the guard area will be removed from
* the main stack. Any thread running in user mode will have full access
* to the region denoted by thread.stack_info. Make it PMP-NAPOT compatible.
*
* +------------+ <- thread.stack_obj = thread.stack_info.start
* | Thread |
@ -86,65 +104,20 @@
* | TLS | } thread.stack_info.delta
* +------------+ <- thread.stack_info.start + thread.stack_info.size
*/
#define ARCH_THREAD_STACK_RESERVED Z_RISCV_STACK_GUARD_SIZE
#define ARCH_THREAD_STACK_SIZE_ADJUST(size) \
Z_POW2_CEIL(ROUND_UP((size), Z_RISCV_PMP_ALIGN))
Z_POW2_CEIL(MAX(size, CONFIG_PRIVILEGED_STACK_SIZE))
#define ARCH_THREAD_STACK_OBJ_ALIGN(size) \
ARCH_THREAD_STACK_SIZE_ADJUST(size)
#define ARCH_THREAD_STACK_RESERVED 0
#else /* !CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT */
/* The stack object will contain the PMP guard, the privilege stack, and then
* the stack buffer in that order:
* the usermode stack buffer in that order:
*
* +------------+ <- thread.stack_obj
* | Guard | } Z_RISCV_STACK_GUARD_SIZE
* +------------+ <- thread.arch.priv_stack_start
* | Priv Stack | } CONFIG_PRIVILEGED_STACK_SIZE
* +------------+ <- thread.stack_info.start
* | Thread |
* | stack |
* | |
* +............|
* | TLS | } thread.stack_info.delta
* +------------+ <- thread.stack_info.start + thread.stack_info.size
*/
#define ARCH_THREAD_STACK_RESERVED (Z_RISCV_STACK_GUARD_SIZE + \
CONFIG_PRIVILEGED_STACK_SIZE)
#define ARCH_THREAD_STACK_OBJ_ALIGN(size) Z_RISCV_PMP_ALIGN
/* We need to be able to exactly cover the stack buffer with an PMP region,
* so round its size up to the required granularity of the PMP
*/
#define ARCH_THREAD_STACK_SIZE_ADJUST(size) \
(ROUND_UP((size), Z_RISCV_PMP_ALIGN))
#endif /* CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT */
#else /* !CONFIG_PMP_STACK_GUARD */
#ifdef CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT
/* Use defaults for everything. The privilege elevation stack is located
* in another area of memory generated at build time by gen_kobject_list.py
*
* +------------+ <- thread.arch.priv_stack_start
* | Priv Stack | } Z_KERNEL_STACK_LEN(CONFIG_PRIVILEGED_STACK_SIZE)
* +------------+
*
* +------------+ <- thread.stack_obj = thread.stack_info.start
* | Thread |
* | stack |
* | |
* +............|
* | TLS | } thread.stack_info.delta
* +------------+ <- thread.stack_info.start + thread.stack_info.size
*/
#define ARCH_THREAD_STACK_SIZE_ADJUST(size) \
Z_POW2_CEIL(ROUND_UP((size), Z_RISCV_PMP_ALIGN))
#define ARCH_THREAD_STACK_OBJ_ALIGN(size) \
ARCH_THREAD_STACK_SIZE_ADJUST(size)
#define ARCH_THREAD_STACK_RESERVED 0
#else /* !CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT */
/* Userspace enabled, but supervisor stack guards are not in use */
/* Reserved area of the thread object just contains the privilege stack:
*
* +------------+ <- thread.stack_obj = thread.arch.priv_stack_start
* | Priv Stack | } CONFIG_PRIVILEGED_STACK_SIZE
* +------------+ <- thread.stack_info.start
* | Thread |
@ -154,38 +127,11 @@
* | TLS | } thread.stack_info.delta
* +------------+ <- thread.stack_info.start + thread.stack_info.size
*/
#define ARCH_THREAD_STACK_RESERVED CONFIG_PRIVILEGED_STACK_SIZE
#define ARCH_THREAD_STACK_SIZE_ADJUST(size) \
(ROUND_UP((size), Z_RISCV_PMP_ALIGN))
#define ARCH_THREAD_STACK_OBJ_ALIGN(size) Z_RISCV_PMP_ALIGN
#define ARCH_THREAD_STACK_RESERVED \
ROUND_UP(Z_RISCV_STACK_GUARD_SIZE + CONFIG_PRIVILEGED_STACK_SIZE, \
ARCH_STACK_PTR_ALIGN)
#endif /* CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT */
#endif /* CONFIG_PMP_STACK_GUARD */
#else /* !CONFIG_USERSPACE */
#ifdef CONFIG_PMP_STACK_GUARD
/* Reserve some memory for the stack guard.
* This is just a minimally-sized region at the beginning of the stack
* object, which is programmed to produce an exception if written to.
*
* +------------+ <- thread.stack_obj
* | Guard | } Z_RISCV_STACK_GUARD_SIZE
* +------------+ <- thread.stack_info.start
* | Thread |
* | stack |
* | |
* +............|
* | TLS | } thread.stack_info.delta
* +------------+ <- thread.stack_info.start + thread.stack_info.size
*/
#define ARCH_THREAD_STACK_RESERVED Z_RISCV_STACK_GUARD_SIZE
#define ARCH_THREAD_STACK_OBJ_ALIGN(size) Z_RISCV_PMP_ALIGN
/* Default for ARCH_THREAD_STACK_SIZE_ADJUST */
#else /* !CONFIG_PMP_STACK_GUARD */
/* No stack guard, no userspace, Use defaults for everything. */
#endif /* CONFIG_PMP_STACK_GUARD */
#endif /* CONFIG_USERSPACE */
#ifdef CONFIG_64BIT
#define RV_REGSIZE 8

View file

@ -125,7 +125,7 @@ static inline void set_fault_valid(bool valid)
#elif defined(CONFIG_ARM)
#define MEM_REGION_ALLOC (Z_THREAD_MIN_STACK_ALIGN)
#elif defined(CONFIG_RISCV)
#define MEM_REGION_ALLOC (Z_RISCV_PMP_ALIGN)
#define MEM_REGION_ALLOC (4)
#else
#error "Test suite not compatible for the given architecture"
#endif