Skip to content

Commit 5d0f159

Browse files
committed
Recover from stacking errors
We hard fault if we try to read from the stack if the mem manage fault is a stacking or unstacking fault. Check the fault status before reading the pc from the stack to avoid the hard fault.
1 parent 13f269f commit 5d0f159

File tree

6 files changed

+141
-67
lines changed

6 files changed

+141
-67
lines changed

core/debug/inc/debug.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ uint32_t debug_get_version(void);
4242
void debug_halt_error(THaltError reason);
4343
void debug_reboot(TResetReason reason);
4444

45+
/* Enter the debug box from a privileged mode exception handler. This function
46+
* requires the caller to have already switched the PSP to the debug box stack.
47+
* We currently only call this on MPU faults and Hard Faults in
48+
* vmpu_sys_mux_handler. If called from outside a privileged mode exception
49+
* handler, this function does nothing. */
50+
uint32_t debug_box_enter_from_priv(uint32_t lr);
51+
4552
#ifdef NDEBUG
4653

4754
#define DEBUG_INIT(...) {}

core/debug/src/debug_box.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,31 @@ void debug_register_driver(const TUvisorDebugDriver * const driver)
142142
g_debug_box.box_id = g_active_box;
143143
g_debug_box.initialized = 1;
144144
}
145+
146+
/* FIXME This is a bit platform specific. Consider moving to a platform
147+
* specific location. */
148+
uint32_t debug_box_enter_from_priv(uint32_t lr) {
149+
uint32_t shcsr;
150+
uint32_t from_priv = !(lr & 0x4);
151+
152+
/* If we are not handling an exception caused from privileged mode, return
153+
* the original lr. */
154+
if (!from_priv) {
155+
return lr;
156+
}
157+
158+
shcsr = SCB->SHCSR;
159+
160+
/* Make sure SVC is active. */
161+
assert(shcsr & SCB_SHCSR_SVCALLACT_Msk);
162+
163+
/* We had a fault (from SVC), so clear the SVC fault before returning. SVC
164+
* and all other exceptions must be no longer active after the EXC RETURN,
165+
* or else we cause usage faults when doing SVCs later (for example, to
166+
* reboot via the debug_reboot SVC). */
167+
SCB->SHCSR = shcsr & ~SCB_SHCSR_SVCALLACT_Msk;
168+
169+
/* Return to Thread mode and use the Process Stack for return. The PSP will
170+
* have been changed already. */
171+
return 0xFFFFFFFD;
172+
}

core/system/src/system.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,8 @@ void UVISOR_NAKED UVISOR_NORETURN isr_default_sys_handler(void)
9191
asm volatile(
9292
"mov r0, lr\n"
9393
"mrs r1, MSP\n"
94-
"push {lr}\n"
95-
"blx vmpu_sys_mux_handler\n"
96-
"pop {pc}\n"
94+
"bl vmpu_sys_mux_handler\n"
95+
"bx r0\n"
9796
);
9897
}
9998

core/vmpu/inc/vmpu.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,9 @@ extern void vmpu_arch_init_hw(void);
155155
extern int vmpu_init_pre(void);
156156
extern void vmpu_init_post(void);
157157

158-
extern void vmpu_sys_mux_handler(uint32_t lr, uint32_t msp);
158+
/* Handle system exceptions and interrupts. Return the EXC_RETURN desired for
159+
* returning from exception mode. */
160+
extern uint32_t vmpu_sys_mux_handler(uint32_t lr, uint32_t msp);
159161

160162
/* contains the total number of boxes
161163
* boxes are enumerated from 0 to (g_vmpu_box_count - 1) and the following

core/vmpu/src/armv7m/vmpu_armv7m.c

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,11 @@ static int vmpu_fault_recovery_mpu(uint32_t pc, uint32_t sp, uint32_t fault_addr
9090
const MpuRegion *region;
9191
uint8_t mask, index, page;
9292

93-
/* No recovery is possible if the MPU syndrome register is not valid. */
94-
if (fault_status == (SCB_CFSR_MMARVALID_Msk | SCB_CFSR_DACCVIOL_Msk)) {
93+
/* No recovery is possible if the MPU syndrome register is not valid or
94+
* this is not a stacking fault (where the MPU syndrome register would not
95+
* be valid, but we can still recover). */
96+
if (!((fault_status == (SCB_CFSR_MMARVALID_Msk | SCB_CFSR_DACCVIOL_Msk)) ||
97+
(fault_status & (SCB_CFSR_MSTKERR_Msk | SCB_CFSR_MUNSTKERR_Msk)))) {
9598
return 0;
9699
}
97100

@@ -112,7 +115,7 @@ static int vmpu_fault_recovery_mpu(uint32_t pc, uint32_t sp, uint32_t fault_addr
112115
return 1;
113116
}
114117

115-
void vmpu_sys_mux_handler(uint32_t lr, uint32_t msp)
118+
uint32_t vmpu_sys_mux_handler(uint32_t lr, uint32_t msp)
116119
{
117120
uint32_t psp, pc;
118121
uint32_t fault_addr, fault_status;
@@ -127,29 +130,45 @@ void vmpu_sys_mux_handler(uint32_t lr, uint32_t msp)
127130

128131
switch (ipsr) {
129132
case MemoryManagement_IRQn:
130-
/* Currently we only support recovery from unprivileged mode. */
131-
if (lr & 0x4) {
133+
fault_status = VMPU_SCB_MMFSR;
134+
135+
/* If we are having an unstacking fault, we can't read the pc
136+
* at fault. */
137+
if (fault_status & (SCB_CFSR_MSTKERR_Msk | SCB_CFSR_MUNSTKERR_Msk)) {
138+
/* Fake pc */
139+
pc = 0x0;
140+
141+
/* The stack pointer is at fault. MMFAR doesn't contain a
142+
* valid fault address. */
143+
fault_addr = lr & 0x4 ? psp : msp;
144+
} else {
132145
/* pc at fault */
133-
pc = vmpu_unpriv_uint32_read(psp + (6 * 4));
146+
if (lr & 0x4) {
147+
pc = vmpu_unpriv_uint32_read(psp + (6 * 4));
148+
} else {
149+
/* We can be privileged here if we tried doing an ldrt or
150+
* strt to a region not currently loaded in the MPU. In
151+
* such cases, we are reading from the msp and shouldn't go
152+
* through vmpu_unpriv_uint32_read. A box wouldn't have
153+
* access to our stack. */
154+
pc = *(uint32_t *) (msp + (6 * 4));
155+
}
134156

135157
/* Backup fault address and status */
136158
fault_addr = SCB->MMFAR;
137-
fault_status = VMPU_SCB_MMFSR;
138-
139-
/* Check if the fault is an MPU fault. */
140-
if (vmpu_fault_recovery_mpu(pc, psp, fault_addr, fault_status)) {
141-
VMPU_SCB_MMFSR = fault_status;
142-
return;
143-
}
159+
}
144160

145-
/* If recovery was not successful, throw an error and halt. */
146-
DEBUG_FAULT(FAULT_MEMMANAGE, lr, psp);
161+
/* Check if the fault is an MPU fault. */
162+
if (vmpu_fault_recovery_mpu(pc, psp, fault_addr, fault_status)) {
147163
VMPU_SCB_MMFSR = fault_status;
148-
HALT_ERROR(PERMISSION_DENIED, "Access to restricted resource denied");
149-
} else {
150-
DEBUG_FAULT(FAULT_MEMMANAGE, lr, msp);
151-
HALT_ERROR(FAULT_MEMMANAGE, "Cannot recover from privileged MemManage fault");
164+
return lr;
152165
}
166+
167+
/* If recovery was not successful, throw an error and halt. */
168+
DEBUG_FAULT(FAULT_MEMMANAGE, lr, lr & 0x4 ? psp : msp);
169+
VMPU_SCB_MMFSR = fault_status;
170+
HALT_ERROR(PERMISSION_DENIED, "Access to restricted resource denied");
171+
lr = debug_box_enter_from_priv(lr);
153172
break;
154173

155174
case BusFault_IRQn:
@@ -173,16 +192,13 @@ void vmpu_sys_mux_handler(uint32_t lr, uint32_t msp)
173192
/* Check if the fault is the special register corner case. */
174193
if (!vmpu_fault_recovery_bus(pc, psp, fault_addr, fault_status)) {
175194
VMPU_SCB_BFSR = fault_status;
176-
return;
195+
return lr;
177196
}
178-
179-
/* If recovery was not successful, throw an error and halt. */
180-
DEBUG_FAULT(FAULT_BUS, lr, psp);
181-
HALT_ERROR(PERMISSION_DENIED, "Access to restricted resource denied");
182-
} else {
183-
DEBUG_FAULT(FAULT_BUS, lr, msp);
184-
HALT_ERROR(FAULT_BUS, "Cannot recover from privileged bus fault");
185197
}
198+
199+
/* If recovery was not successful, throw an error and halt. */
200+
DEBUG_FAULT(FAULT_BUS, lr, lr & 0x4 ? psp : msp);
201+
HALT_ERROR(PERMISSION_DENIED, "Access to restricted resource denied");
186202
break;
187203

188204
case UsageFault_IRQn:
@@ -193,6 +209,7 @@ void vmpu_sys_mux_handler(uint32_t lr, uint32_t msp)
193209
case HardFault_IRQn:
194210
DEBUG_FAULT(FAULT_HARD, lr, lr & 0x4 ? psp : msp);
195211
HALT_ERROR(FAULT_HARD, "Cannot recover from a hard fault.");
212+
lr = debug_box_enter_from_priv(lr);
196213
break;
197214

198215
case DebugMonitor_IRQn:
@@ -212,6 +229,8 @@ void vmpu_sys_mux_handler(uint32_t lr, uint32_t msp)
212229
HALT_ERROR(NOT_ALLOWED, "Active IRQn(%i) is not a system interrupt", ipsr);
213230
break;
214231
}
232+
233+
return lr;
215234
}
216235

217236
static int vmpu_mem_push_page_acl_iterator(uint8_t mask, uint8_t index)

core/vmpu/src/kinetis/vmpu_kinetis.c

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ static int vmpu_fault_recovery_mpu(uint32_t pc, uint32_t sp, uint32_t fault_addr
5050
return -1;
5151
}
5252

53-
void vmpu_sys_mux_handler(uint32_t lr, uint32_t msp)
53+
uint32_t vmpu_sys_mux_handler(uint32_t lr, uint32_t msp)
5454
{
5555
uint32_t psp, pc;
5656
uint32_t fault_addr, fault_status;
@@ -82,51 +82,67 @@ void vmpu_sys_mux_handler(uint32_t lr, uint32_t msp)
8282
/* Note: All recovery functions update the stacked stack pointer so
8383
* that exception return points to the correct instruction. */
8484

85-
/* Currently we only support recovery from unprivileged mode. */
86-
if (lr & 0x4) {
85+
fault_status = VMPU_SCB_BFSR;
86+
87+
/* If we are having an unstacking fault, we can't read the pc
88+
* at fault. */
89+
if (fault_status & (SCB_CFSR_MSTKERR_Msk | SCB_CFSR_MUNSTKERR_Msk)) {
90+
/* fake pc */
91+
pc = 0x0;
92+
93+
/* The stack pointer is at fault. BFAR doesn't contain a
94+
* valid fault address. */
95+
fault_addr = psp;
96+
} else {
8797
/* pc at fault */
88-
pc = vmpu_unpriv_uint32_read(psp + (6 * 4));
98+
if (lr & 0x4) {
99+
pc = vmpu_unpriv_uint32_read(psp + (6 * 4));
100+
} else {
101+
/* We can be privileged here if we tried doing an ldrt or
102+
* strt to a region not currently loaded in the MPU. In
103+
* such cases, we are reading from the msp and shouldn't go
104+
* through vmpu_unpriv_uint32_read. A box wouldn't have
105+
* access to our stack. */
106+
pc = *(uint32_t *) (msp + (6 * 4));
107+
}
89108

90109
/* backup fault address and status */
91110
fault_addr = SCB->BFAR;
92-
fault_status = VMPU_SCB_BFSR;
93-
94-
/* Check if the fault is an MPU fault. */
95-
int slave_port = vmpu_fault_get_slave_port();
96-
if (slave_port >= 0) {
97-
/* If the fault comes from the MPU module, we don't use the
98-
* bus fault syndrome register, but the MPU one. */
99-
fault_addr = MPU->SP[slave_port].EAR;
100-
101-
/* Check if we can recover from the MPU fault. */
102-
if (!vmpu_fault_recovery_mpu(pc, psp, fault_addr)) {
103-
/* We clear the bus fault status anyway. */
104-
VMPU_SCB_BFSR = fault_status;
105-
106-
/* We also clear the MPU fault status bit. */
107-
vmpu_fault_clear_slave_port(slave_port);
108-
109-
/* Recover from the exception. */
110-
return;
111-
}
112-
} else if (slave_port == VMPU_FAULT_MULTIPLE) {
113-
DPRINTF("Multiple MPU violations found.\r\n");
114-
}
111+
}
112+
113+
/* Check if the fault is an MPU fault. */
114+
int slave_port = vmpu_fault_get_slave_port();
115+
if (slave_port >= 0) {
116+
/* If the fault comes from the MPU module, we don't use the
117+
* bus fault syndrome register, but the MPU one. */
118+
fault_addr = MPU->SP[slave_port].EAR;
115119

116-
/* Check if the fault is the special register corner case. */
117-
if (!vmpu_fault_recovery_bus(pc, psp, fault_addr, fault_status)) {
120+
/* Check if we can recover from the MPU fault. */
121+
if (!vmpu_fault_recovery_mpu(pc, psp, fault_addr)) {
122+
/* We clear the bus fault status anyway. */
118123
VMPU_SCB_BFSR = fault_status;
119-
return;
124+
125+
/* We also clear the MPU fault status bit. */
126+
vmpu_fault_clear_slave_port(slave_port);
127+
128+
/* Recover from the exception. */
129+
return lr;
120130
}
131+
} else if (slave_port == VMPU_FAULT_MULTIPLE) {
132+
DPRINTF("Multiple MPU violations found.\r\n");
133+
}
121134

122-
/* If recovery was not successful, throw an error and halt. */
123-
DEBUG_FAULT(FAULT_BUS, lr, psp);
135+
/* Check if the fault is the special register corner case. */
136+
if (!vmpu_fault_recovery_bus(pc, psp, fault_addr, fault_status)) {
124137
VMPU_SCB_BFSR = fault_status;
125-
HALT_ERROR(PERMISSION_DENIED, "Access to restricted resource denied");
126-
} else {
127-
DEBUG_FAULT(FAULT_BUS, lr, msp);
128-
HALT_ERROR(FAULT_BUS, "Cannot recover from privileged bus fault");
138+
return lr;
129139
}
140+
141+
/* If recovery was not successful, throw an error and halt. */
142+
DEBUG_FAULT(FAULT_BUS, lr, lr & 0x4 ? psp : msp);
143+
VMPU_SCB_BFSR = fault_status;
144+
HALT_ERROR(PERMISSION_DENIED, "Access to restricted resource denied");
145+
lr = debug_box_enter_from_priv(lr);
130146
break;
131147

132148
case UsageFault_IRQn:
@@ -137,6 +153,7 @@ void vmpu_sys_mux_handler(uint32_t lr, uint32_t msp)
137153
case HardFault_IRQn:
138154
DEBUG_FAULT(FAULT_HARD, lr, lr & 0x4 ? psp : msp);
139155
HALT_ERROR(FAULT_HARD, "Cannot recover from a hard fault.");
156+
lr = debug_box_enter_from_priv(lr);
140157
break;
141158

142159
case DebugMonitor_IRQn:
@@ -156,6 +173,8 @@ void vmpu_sys_mux_handler(uint32_t lr, uint32_t msp)
156173
HALT_ERROR(NOT_ALLOWED, "Active IRQn(%i) is not a system interrupt", ipsr);
157174
break;
158175
}
176+
177+
return lr;
159178
}
160179

161180
void vmpu_acl_stack(uint8_t box_id, uint32_t bss_size, uint32_t stack_size)

0 commit comments

Comments
 (0)