Skip to content

Supporting User Mode on ARM Cortex-M Baseline #19689

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Oct 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion arch/arm/core/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ config CPU_CORTEX_M
select HAS_FLASH_LOAD_OFFSET
select ARCH_HAS_THREAD_ABORT
select ARCH_HAS_TRUSTED_EXECUTION if ARM_TRUSTZONE_M
select ARCH_HAS_STACK_PROTECTION if ARM_MPU || CPU_CORTEX_M_HAS_SPLIM
select ARCH_HAS_STACK_PROTECTION if (ARM_MPU && !ARMV6_M_ARMV8_M_BASELINE) || CPU_CORTEX_M_HAS_SPLIM
select ARCH_HAS_USERSPACE if ARM_MPU
select ARCH_HAS_NOCACHE_MEMORY_SUPPORT if ARM_MPU && CPU_HAS_ARM_MPU && CPU_CORTEX_M7
select ARCH_HAS_RAMFUNC_SUPPORT
Expand Down
1 change: 1 addition & 0 deletions arch/arm/core/cortex_m/mpu/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ config ARM_MPU

config ARM_MPU_REGION_MIN_ALIGN_AND_SIZE
int
default 256 if ARM_MPU && ARMV6_M_ARMV8_M_BASELINE && !ARMV8_M_BASELINE
default 32 if ARM_MPU
default 4
help
Expand Down
146 changes: 87 additions & 59 deletions arch/arm/core/swap_helper.S
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,34 @@ out_fp_endif:
cpsie i
_thread_irq_disabled:

#if defined(CONFIG_ARM_MPU)
/* Re-program dynamic memory map */
push {r2,lr}
mov r0, r2
bl z_arm_configure_dynamic_mpu_regions
pop {r2,r3}
mov lr, r3
#endif

#ifdef CONFIG_USERSPACE
/* restore mode */
ldr r3, =_thread_offset_to_mode
adds r3, r2, r3
ldr r0, [r3]
mrs r3, CONTROL
movs.n r1, #1
bics r3, r1
orrs r3, r0
msr CONTROL, r3

/* ISB is not strictly necessary here (stack pointer is not being
* touched), but it's recommended to avoid executing pre-fetched
* instructions with the previous privilege.
*/
isb

#endif

ldr r4, =_thread_offset_to_callee_saved
adds r0, r2, r4

Expand Down Expand Up @@ -342,65 +370,8 @@ _thread_irq_disabled:
*/
bx lr

#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)

/**
*
* @brief Service call handler
*
* The service call (svc) is used in the following occasions:
* - IRQ offloading
* - Kernel run-time exceptions
*
* @return N/A
*/
SECTION_FUNC(TEXT, z_arm_svc)
/* Use EXC_RETURN state to find out if stack frame is on the
* MSP or PSP
*/
ldr r0, =0x4
mov r1, lr
tst r1, r0
beq _stack_frame_msp
mrs r0, PSP
bne _stack_frame_endif
_stack_frame_msp:
mrs r0, MSP
_stack_frame_endif:

/* Figure out what SVC call number was invoked */
ldr r1, [r0, #24] /* grab address of PC from stack frame */
/* SVC is a two-byte instruction, point to it and read encoding */
subs r1, r1, #2
ldrb r1, [r1, #0]

/*
* grab service call number:
* 1: irq_offload (if configured)
* 2: kernel panic or oops (software generated fatal exception)
* Planned implementation of system calls for memory protection will
* expand this case.
*/

cmp r1, #2
beq _oops

#if defined(CONFIG_IRQ_OFFLOAD)
push {r0, lr}
bl z_irq_do_offload /* call C routine which executes the offload */
pop {r0, r1}
mov lr, r1
#endif /* CONFIG_IRQ_OFFLOAD */

/* exception return is done in z_arm_int_exit() */
b z_arm_int_exit

_oops:
push {r0, lr}
bl z_do_kernel_oops
pop {r0, pc}

#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) || \
defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)

/**
*
Expand All @@ -417,10 +388,22 @@ SECTION_FUNC(TEXT, z_arm_svc)
/* Use EXC_RETURN state to find out if stack frame is on the
* MSP or PSP
*/
#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
movs r0, #0x4
mov r1, lr
tst r1, r0
beq _stack_frame_msp
mrs r0, PSP
bne _stack_frame_endif
_stack_frame_msp:
mrs r0, MSP
_stack_frame_endif:
#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
tst lr, #0x4 /* did we come from thread mode ? */
ite eq /* if zero (equal), came from handler mode */
mrseq r0, MSP /* handler mode, stack frame is on MSP */
mrsne r0, PSP /* thread mode, stack frame is on PSP */
#endif


/* Figure out what SVC call number was invoked */
Expand All @@ -429,7 +412,12 @@ SECTION_FUNC(TEXT, z_arm_svc)
/* SVC is a two-byte instruction, point to it and read the
* SVC number (lower byte of SCV instruction)
*/
#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
subs r1, r1, #2
ldrb r1, [r1]
#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
ldrb r1, [r1, #-2]
#endif

/*
* grab service call number:
Expand All @@ -448,7 +436,12 @@ SECTION_FUNC(TEXT, z_arm_svc)
* check that we are privileged before invoking other SVCs
* oops if we are unprivileged
*/
#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
movs r3, #0x1
tst r2, r3
#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
tst r2, #0x1
#endif
bne _oops

#endif /* CONFIG_USERSPACE */
Expand All @@ -459,7 +452,12 @@ SECTION_FUNC(TEXT, z_arm_svc)
#if defined(CONFIG_IRQ_OFFLOAD)
push {r0, lr}
bl z_irq_do_offload /* call C routine which executes the offload */
#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
pop {r0, r3}
mov lr, r3
#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
pop {r0, lr}
#endif

/* exception return is done in z_arm_int_exit() */
b z_arm_int_exit
Expand Down Expand Up @@ -493,13 +491,28 @@ _oops:
* r8 - saved link register
*/
_do_syscall:
#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
movs r3, #24
ldr r1, [r0, r3] /* grab address of PC from stack frame */
mov r8, r1
#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
ldr r8, [r0, #24] /* grab address of PC from stack frame */
#endif
ldr r1, =z_arm_do_syscall
#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
str r1, [r0, r3] /* overwrite the PC to point to z_arm_do_syscall */
#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
str r1, [r0, #24] /* overwrite the PC to point to z_arm_do_syscall */
#endif

#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
ldr r3, =K_SYSCALL_LIMIT
cmp r6, r3
#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
/* validate syscall limit */
ldr ip, =K_SYSCALL_LIMIT
cmp r6, ip
#endif
blt valid_syscall_id

/* bad syscall id. Set arg1 to bad id and set call_id to SYSCALL_BAD */
Expand All @@ -511,13 +524,28 @@ _do_syscall:
valid_syscall_id:
ldr r0, =_kernel
ldr r0, [r0, #_kernel_offset_to_current]
#if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
mov ip, r2
ldr r1, =_thread_offset_to_mode
ldr r3, [r0, r1]
movs r2, #1
bics r3, r2
/* Store (privileged) mode in thread's mode state variable */
str r3, [r0, r1]
mov r2, ip
dsb
/* set mode to privileged, r2 still contains value from CONTROL */
movs r3, #1
bics r2, r3
#elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE)
ldr r1, [r0, #_thread_offset_to_mode]
bic r1, #1
/* Store (privileged) mode in thread's mode state variable */
str r1, [r0, #_thread_offset_to_mode]
dsb
/* set mode to privileged, r2 still contains value from CONTROL */
bic r2, #1
#endif
msr CONTROL, r2

/* ISB is not strictly necessary here (stack pointer is not being
Expand Down
Loading