Skip to content

Commit a0a1c4d

Browse files
committed
[M2351] Override NS interface by locking kernel scheduler
Lock kernel scheduler rather than mutex to guarantee serialization of NS secure calls
1 parent dec84ed commit a0a1c4d

File tree

1 file changed

+157
-0
lines changed

1 file changed

+157
-0
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/*
2+
* Copyright (c) 2019, Nuvoton Technology Corporation
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#include <stdint.h>
20+
#include <stdbool.h>
21+
#include "cmsis.h"
22+
#include "cmsis_os2.h"
23+
#include "tfm_ns_lock.h"
24+
#include "mbed_critical.h"
25+
#include "mbed_assert.h"
26+
#include "mbed_error.h"
27+
28+
/* Approach to serialization of NS secure calls required by TF-M secure world
29+
*
30+
* Default implementation of NS interface uses mutex to meet the requirement,
31+
* but it cannot support NS secure call in interrupt-disabled context. Instead,
32+
* in this override, NS secure call is guaranteed to be non-preemptive during
33+
* this period by locking kernel scheduler. Apparently, this approach has one
34+
* weakness: all other threads are also locked during this period. Until there's
35+
* a better approach coming out, we can just use this expedient one.
36+
*
37+
* For the 'lock kernel scheduler' approach to work thoroughly, we must also
38+
* address some side issues:
39+
*
40+
* - Prohibit NS secure call from ISR except SVC, so non-preemptive doesn't break.
41+
* - Allow NS secure call in SVC context because it is synchronous. Here, we lock
42+
* interrupt instead of kernel scheduler because svcRtxKernelLock()/svcRtxKernelRestoreLock(...)
43+
* are inaccessible outside rtx_kernel.c. Currently, this is rare case and would cause
44+
* little trouble (see known paths below).
45+
* - Call into secure world straight in interrupt-disabled context. When in
46+
* interrupt-disabled context, NS secure call is guaranteed to be non-preemptive
47+
* naturally.
48+
* - Call into secure world straight at pre-rtos stage. When at pre-rtos stage,
49+
* NS secure call is guaranteed to be non-preemptive naturally.
50+
* - osKernelLock() will error when kernel state is 'osKernelSuspended'. Address
51+
* it separately. Known path of NS secure call when kernel state is 'osKernelSuspended':
52+
* - default idle thread > osKernelSuspend() > lp_ticker_init > SYS_ResetModule_S/
53+
* CLK_SetModuleClock_S/CLK_EnableModuleClock_S
54+
*
55+
* Known paths of NS secure call in interrupt-disabled context:
56+
* - mbed-os/platform/mbed_sleep_manager.c > sleep_manager_sleep_auto >
57+
* hal_sleep/hal_deepsleep > nu_idle_s/nu_powerdown_s
58+
* - mbed-os/hal/LowPowerTickerWrapper.cpp > LowPowerTickerWrapper::init >
59+
* lp_ticker_init > SYS_ResetModule_S/CLK_SetModuleClock_S/CLK_EnableModuleClock_S
60+
* - mbed-os/platform/mbed_board.c > mbed_die > pin_function_s
61+
* - mbed-os-tests-mbed_hal-rtc > rtc_write_read_test > rtc_write >
62+
* CLK_IsRTCClockEnabled_S
63+
*
64+
* Known paths of NS secure call in SVC context:
65+
* - In tickless mode, osKernelStart > svcRtxKernelStart > OS_Tick_Enable >
66+
* us_ticker_init/lp_ticker_init > SYS_ResetModule_S/CLK_SetModuleClock_S/
67+
* CLK_EnableModuleClock_S
68+
*/
69+
70+
struct ns_interface_state
71+
{
72+
bool init; // Flag if kernel has initialized (and then scheduler
73+
// has started)
74+
};
75+
76+
static struct ns_interface_state ns_interface = {
77+
.init = false
78+
};
79+
80+
/* Override tfm_ns_lock_init()
81+
*
82+
* On Mbed OS, we expect this function is called before kernel scheduler is
83+
* started so that we can distinguish pre-rtos and rtos stage to meet the
84+
* requirement of serialization of NS secure calls.
85+
*/
86+
enum tfm_status_e tfm_ns_lock_init()
87+
{
88+
if (!ns_interface.init) {
89+
osKernelState_t kernel_state = osKernelGetState();
90+
MBED_ASSERT(kernel_state == osKernelInactive || kernel_state == osKernelReady);
91+
92+
ns_interface.init = true;
93+
}
94+
95+
return TFM_SUCCESS;
96+
}
97+
98+
/* Override tfm_ns_lock_dispatch(...) */
99+
uint32_t tfm_ns_lock_dispatch(veneer_fn fn,
100+
uint32_t arg0, uint32_t arg1,
101+
uint32_t arg2, uint32_t arg3)
102+
{
103+
/* Prohibit NS secure call from ISR except SVC, so non-preemptive doesn't break */
104+
uint32_t ipsr = __get_IPSR();
105+
if (ipsr == 11U) {
106+
/* Allow NS secure call in SVC context because it is synchronous. Here,
107+
* we lock interrupt instead of kernel scheduler because svcRtxKernelLock()/
108+
* svcRtxKernelRestoreLock(...) are inaccessible outside rtx_kernel.c. */
109+
core_util_critical_section_enter();
110+
uint32_t result = fn(arg0, arg1, arg2, arg3);
111+
core_util_critical_section_exit();
112+
113+
return result;
114+
} else if (ipsr) {
115+
MBED_ERROR1(MBED_MAKE_ERROR(MBED_MODULE_KERNEL, MBED_ERROR_CODE_PROHIBITED_IN_ISR_CONTEXT), "Prohibited in ISR context", (uintptr_t) fn);
116+
}
117+
118+
/* Call into secure world straight in interrupt-disabled context because
119+
* NS secure call is non-preemptive naturally */
120+
if (!core_util_are_interrupts_enabled()) {
121+
return fn(arg0, arg1, arg2, arg3);
122+
}
123+
124+
/* Call into secure world straight at pre-rtos stage because NS secure
125+
* call is non-preemptive naturally */
126+
if (!ns_interface.init) {
127+
return fn(arg0, arg1, arg2, arg3);
128+
}
129+
130+
/* osKernelLock() will error when kernel state is 'osKernelSuspended'. Address
131+
* it separately. */
132+
osKernelState_t kernel_state = osKernelGetState();
133+
if (kernel_state == osKernelSuspended) {
134+
return fn(arg0, arg1, arg2, arg3);
135+
} else if (kernel_state == osKernelError) {
136+
MBED_ERROR1(MBED_MAKE_ERROR(MBED_MODULE_KERNEL, MBED_ERROR_CODE_UNKNOWN), "RTX kernel state error", (uintptr_t) fn);
137+
}
138+
139+
/* Lock kernel scheduler and save previous lock state for restore */
140+
int32_t lock_state = osKernelLock();
141+
if (lock_state == osError) {
142+
MBED_ERROR1(MBED_MAKE_ERROR(MBED_MODULE_KERNEL, MBED_ERROR_CODE_UNKNOWN), "Unknown RTX error", (uintptr_t) fn);
143+
}
144+
MBED_ASSERT(lock_state >= 0);
145+
146+
/* NS secure call is non-preemptive because kernel scheduler is locked */
147+
uint32_t result = fn(arg0, arg1, arg2, arg3);
148+
149+
/* Restore previous lock state */
150+
lock_state = osKernelRestoreLock(lock_state);
151+
if (lock_state == osError) {
152+
MBED_ERROR1(MBED_MAKE_ERROR(MBED_MODULE_KERNEL, MBED_ERROR_CODE_UNKNOWN), "Unknown RTX error", (uintptr_t) fn);
153+
}
154+
MBED_ASSERT(lock_state >= 0);
155+
156+
return result;
157+
}

0 commit comments

Comments
 (0)