Skip to content

Commit 7a70ab5

Browse files
committed
Added the module user-mode entry, the only way a module reaches the kernel
A module runs in User mode and cannot execute kernel code or touch kernel memory. When it calls a ThreadX service the call arrives here: SVC 1 raises privilege, the dispatch function performs the service, SVC 2 drops back to User mode, and the module continues. Everything the module is allowed to ask for is decided inside _txm_module_manager_kernel_dispatch, in memory the module cannot reach. Derived from the Cortex-R4 module port rather than the Armv8-M one, which is the right parent for this file specifically. Privilege on Armv8-M is a bit in CONTROL and the transition rides an exception return; in AArch32 it is the processor mode, and SVC is how a module asks to change it. The R4 sequence transfers directly even though its MPU does not. One improvement over the R4 version, and it is about isolation rather than memory. That port aligns this function to 4 KB because PMSAv7 regions must be a power of two in size and aligned to their own size, so the smallest region that covers the entry without covering its neighbours is a page -- and whatever else shares that page is then also executable by the module. A base and limit pair has no such constraint. The function is 64-byte aligned, and the measured privileged surface a module can execute is 24 bytes. The kernel entry region's limit is taken from a symbol at the end of the function rather than from its base. One granule covers 24 bytes so the base would work today, but if the entry ever grows past 64 bytes, sizing from the base would leave its tail outside the region and a module would fault on a call it is entitled to make. SVC 2 sits between the dispatch and every return path rather than on one of them. Skipping it would return the module to its own code still privileged, which is the entire protection gone, so it is unconditional. The supervisor call vector that recognises SVC 1 and SVC 2 is the next piece and does not exist yet. Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
1 parent d734a67 commit 7a70ab5

2 files changed

Lines changed: 122 additions & 1 deletion

File tree

‎ports_module/cortex_r52/gnu/module_manager/src/txm_module_manager_mm_register_setup.c‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@
7272
configuration would need to revisit this, and would need to revisit far more
7373
than this. */
7474

75+
/* Marks the end of the user-mode entry function, so the kernel entry region can
76+
be sized to it. */
77+
78+
extern VOID _txm_module_manager_user_mode_entry_end(VOID);
79+
7580
VOID _txm_module_manager_mm_register_setup(TXM_MODULE_INSTANCE *module_instance)
7681
{
7782

@@ -84,7 +89,14 @@ ULONG callback_stack_size;
8489
Mask address to proper range, inner shareable, read only. */
8590
module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MPU_KERNEL_ENTRY_INDEX].txm_module_mpu_region_base_address = ((ULONG) _txm_module_manager_user_mode_entry & TXM_MODULE_MPU_ADDRESS_MASK) | TXM_MODULE_ATTRIBUTE_NON_SHAREABLE | TXM_MODULE_ATTRIBUTE_READ_ONLY;
8691
/* Set the limit address, attribute index, and enable bit. */
87-
module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MPU_KERNEL_ENTRY_INDEX].txm_module_mpu_region_limit_address = ((ULONG) _txm_module_manager_user_mode_entry & TXM_MODULE_MPU_ADDRESS_MASK) | TXM_MODULE_ATTRIBUTE_INDEX | TXM_MODULE_ATTRIBUTE_REGION_ENABLE;
92+
/* Limit taken from the end of the function rather than from its base. The
93+
whole privileged surface a module can execute is 24 bytes, so one granule
94+
covers it today and the base would have done -- but if the entry ever grows
95+
past 64 bytes, sizing from the base would silently leave the tail of it
96+
outside the region and the module would fault on a call it is entitled to
97+
make. */
98+
99+
module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MPU_KERNEL_ENTRY_INDEX].txm_module_mpu_region_limit_address = (((ULONG) _txm_module_manager_user_mode_entry_end - 1) & TXM_MODULE_MPU_ADDRESS_MASK) | TXM_MODULE_ATTRIBUTE_INDEX | TXM_MODULE_ATTRIBUTE_REGION_ENABLE;
88100
/* End of kernel mode entry setup. */
89101

90102

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
@/***************************************************************************
2+
@ * Copyright (c) 2026 Eclipse ThreadX contributors
3+
@ *
4+
@ * This program and the accompanying materials are made available under the
5+
@ * terms of the MIT License which is available at
6+
@ * https://opensource.org/licenses/MIT.
7+
@ *
8+
@ * AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
9+
@ * The AI-generated portions may be considered public domain (CC0-1.0)
10+
@ * and not subject to the project's licence. The human contributor has
11+
@ * reviewed and verified that the code is correct.
12+
@ *
13+
@ * SPDX-License-Identifier: MIT and CC0-1.0
14+
@ **************************************************************************/
15+
@
16+
@/**************************************************************************/
17+
@/* */
18+
@/* MODULE MANAGER RELEASE */
19+
@/* */
20+
@/* txm_module_manager_user_mode_entry.S Cortex-R52/GNU */
21+
@/* 6.5.2 */
22+
@/* AUTHOR */
23+
@/* */
24+
@/* Frédéric Desbiens, Eclipse Foundation */
25+
@/* */
26+
@/* DESCRIPTION */
27+
@/* */
28+
@/* The only way a module reaches the kernel. */
29+
@/* */
30+
@/* A module runs in User mode and cannot execute kernel code or touch */
31+
@/* kernel memory. When it calls a ThreadX service, the call arrives */
32+
@/* here: SVC 1 raises privilege, the dispatch function performs the */
33+
@/* service, SVC 2 drops back to User mode, and the module continues. */
34+
@/* */
35+
@/* This function is the entire privileged surface a module can see. */
36+
@/* It gets an MPU region of its own -- the one at */
37+
@/* TXM_MODULE_MPU_KERNEL_ENTRY_INDEX -- because a module must be able */
38+
@/* to execute these few instructions and nothing else on that side of */
39+
@/* the boundary. Everything the module is allowed to ask for is */
40+
@/* decided inside _txm_module_manager_kernel_dispatch, in kernel */
41+
@/* memory the module cannot reach. */
42+
@/* */
43+
@/* SVC 1 and SVC 2 are handled by the port's supervisor call vector. */
44+
@/* */
45+
@/* CALLS */
46+
@/* */
47+
@/* SVC 1 Leave User mode */
48+
@/* _txm_module_manager_kernel_dispatch Perform the service */
49+
@/* SVC 2 Return to User mode */
50+
@/* */
51+
@/* CALLED BY */
52+
@/* */
53+
@/* Modules in User mode */
54+
@/* */
55+
@/**************************************************************************/
56+
57+
.arm
58+
.text
59+
60+
@ 64-byte aligned, which is the PMSAv8-R granule and all that is needed here.
61+
@
62+
@ The Cortex-R4 module port aligns this to 4 KB, and has to: PMSAv7 regions must
63+
@ be a power of two in size and aligned to their own size, so the smallest
64+
@ region that can cover this function without also covering its neighbours is a
65+
@ page. A base and limit pair has no such constraint, so the kernel entry
66+
@ region can be sized to these instructions and stop. That matters for
67+
@ isolation rather than for memory: on PMSAv7 whatever else shares the page is
68+
@ inside the one region a module is allowed to execute.
69+
70+
.align 6
71+
72+
.global _txm_module_manager_user_mode_entry
73+
.global _txm_module_manager_user_mode_entry_end
74+
.extern _txm_module_manager_kernel_dispatch
75+
76+
.type _txm_module_manager_user_mode_entry, %function
77+
_txm_module_manager_user_mode_entry:
78+
79+
_txm_system_mode_enter:
80+
81+
@ Leave User mode. The supervisor call vector recognises 1 and raises the
82+
@ thread to privileged mode on its kernel stack.
83+
84+
SVC 1
85+
86+
_txm_module_priv:
87+
88+
@ Privileged now. r3 is pushed alongside lr only to keep the stack eight-byte
89+
@ aligned, which the ABI requires at a public interface.
90+
91+
PUSH {r3, lr}
92+
BL _txm_module_manager_kernel_dispatch
93+
POP {r3, lr}
94+
95+
_txm_system_mode_exit:
96+
97+
@ Back to User mode before returning to the module. If this were skipped the
98+
@ module would resume privileged, which is the whole protection gone -- so it is
99+
@ unconditional and sits between the dispatch and every return path.
100+
101+
SVC 2
102+
103+
BX lr
104+
105+
@ Marks the end of the region a module is allowed to execute, so the region can
106+
@ be sized to this function rather than rounded up to something larger.
107+
108+
_txm_module_manager_user_mode_entry_end:
109+
NOP

0 commit comments

Comments
 (0)