-
Notifications
You must be signed in to change notification settings - Fork 71
Add deprivilege/print/reprivilege flow #497
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
Add deprivilege/print/reprivilege flow #497
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i have not yet had time to review the whole thing - these are my initial thoughts
core/debug/src/debug.c
Outdated
DPRINTF("\n***********************************************************\n");\ | ||
DPRINTF(" "x"\n");\ | ||
DPRINTF("***********************************************************\n\n");\ | ||
DPRINTF("\r\n***********************************************************\r\n");\ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please remove all \r and configure your serial terminal to force CR on each LF - it is a terminal configuration and should not be treated in code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
api/inc/api.h
Outdated
@@ -90,6 +90,7 @@ typedef struct { | |||
void (*spin_unlock)(UvisorSpinlock * spinlock); | |||
|
|||
void (*debug_semihosting_enable)(void); | |||
void (*debug_print_enable)(bool); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function comes to solve initialization order problems when using debug driver.
I believe this logic should reside on application side. An alternative is to look for any falg/callback from mbed-os - for checking such a condition.
Application can define a global bool debug_print_enable = false;
variable, and avoid printing if it is disabled.
as i see it:
bool debug_print_enable = false;
void debug_print_driver(THaltError error_code, const THaltInfo *bsod)
{
if (debug_print_enable){
printf("oops\n");
}
}
int main(void)
{
debug_print_enable = true;
...
}
@@ -50,25 +50,31 @@ void debug_semihosting_enable(void) | |||
|
|||
UVISOR_WEAK void default_putc(uint8_t data) | |||
{ | |||
if (DEBUG_SEMIHOSTING_MAGIC == g_semihosting_magic) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i suggest not to remove semi-hosting print logic - we still need it for debugging stuff during early boot stages.
we can duplicate print to two sinks - that is what all decent loggers do :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not removed, divided into two separate checks.
This better reflects the nature of this function. The real debug_deprivilege_and_return() will be added later.
Since we're not switching back to the same point this copying makes no sense. Moreover, it can create problematic behavior if ICI/IT field is set to non-zero state on the target stack frame during the copying.
Different line endings were used throughout the project - \n, \r\n and \n\r. All of them were set to be \n. Also dprintf("\n\r") and dprintf("\n") were replaced by default_putc('\n'). The implications are that any COM port monitors must be configured to treat \n as \r\n to maintain correct printout layout.
c8ee9f3
to
ea32d0f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I quite like calling SVC to enter the debug box. Excellent approach! Nice work.
I have a few questions about the implementation. Please find the comments inline with the source code.
"push {r4 - r11}\n" | ||
|
||
/* Execute SVC that will perform the deprivileging. */ | ||
"svc %[depriv]\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we ever call debug_deprivilege_and_return
from while an SVC is active? I suspect not, but if we did, we'd have to clear SVC active bit before calling the SVC here, and restore it after return.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now we can't perform deprivileging from SVC handler since executing SVC in this case will result in Hard Fault.
We need either disable deprivileging from SVC like it's done for Hard Fault/NMI or manually play with active bit. I'll check this option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added active bit cleaning, so now deprivileging from SVC handler is possible.
/* Save the current context on the stack, deprivilege and restore the context upon return. */ | ||
asm volatile( | ||
/* Save general purpose registers that won't be saved by the following SVC. */ | ||
"push {r4 - r11}\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we also need to include lr
?
{r4-r11, lr}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need since LR is automatically saved when executing SVC after this PUSH.
Also R0-R3, R12 and xPSR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
@@ -37,7 +37,21 @@ void debug_deprivilege_and_die(void * debug_handler, void * return_handler, | |||
|
|||
/* De-privilege, call the debug box handler, re-privilege, call the return | |||
* handler. */ | |||
/* FIXME: the below way of deprivileging may be problematic when executed from an exception handler |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed.
uint32_t caller = UVISOR_GET_NS_ALIAS(UVISOR_GET_NS_ADDRESS((uint32_t) debug_handler)); | ||
SECURE_TRANSITION_S_TO_NS(caller, a0, a1, a2, a3); | ||
((void (*)(void)) return_handler)(); | ||
} | ||
|
||
/* FIXME: replace these stubs by the actual implementation. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the plan to implement these in this PR or a later one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably it's better to do it in a separate PR.
This one is already big enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove these empty stubs or does that break the build?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Breaks the build.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
core/debug/src/debug_box.c
Outdated
{ | ||
/* Abort printing if debug box wasn't initialized yet. */ | ||
if (!g_debug_box.initialized) { | ||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How often do you run into this case in practice? I'm curious what prints we might be missing out on. With the statically initialized debug box, I'd hope we could get prints even very early on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently the initialization header isn't printed out:
uVisor mode: 2
uvisor_ram : @0x1FFF0400 (7168 bytes) [config]
@0x1FFF0400 (7168 bytes) [linker]
bss_boxes : @0x1FFF2000 (128 bytes) [config]
Box 0 ACLs:
- Peripheral: 0x40047000 - 0x40048064 (permissions: 0x0AB6)
- Peripheral: 0x40065000 - 0x40065001 (permissions: 0x0AB6)
- Peripheral: 0x40064000 - 0x4006400E (permissions: 0x0AB6)
- Peripheral: 0x40049000 - 0x400490CC (permissions: 0x0AB6)
- Peripheral: 0x4004A000 - 0x4004A0CC (permissions: 0x0AB6)
- Peripheral: 0x4004B000 - 0x4004B0CC (permissions: 0x0AB6)
- Peripheral: 0x4004C000 - 0x4004C0CC (permissions: 0x0AB6)
- Peripheral: 0x4004D000 - 0x4004D0CC (permissions: 0x0AB6)
- Peripheral: 0x4003D000 - 0x4003D808 (permissions: 0x0AB6)
- Peripheral: 0x40040000 - 0x40040010 (permissions: 0x0AB6)
- Peripheral: 0x40037000 - 0x40037140 (permissions: 0x0AB6)
- Peripheral: 0x4007E000 - 0x4007E004 (permissions: 0x0AB6)
- Peripheral: 0x4006A000 - 0x4006A020 (permissions: 0x0AB6)
- Peripheral: 0x40066000 - 0x4006600C (permissions: 0x0AB6)
- Peripheral: 0x4002C000 - 0x4002C08C (permissions: 0x0AB6)
vmpu_enumerate_boxes [DONE]
page heap: [0x20000000, 0x2000a000] 40kB -> 5 8kB pages
uvisor initialized
Debug box becomes initialized right before "vmpu_enumerate_boxes [DONE]":
...
g_debug_box.initialized = 1;
}
/* Load box 0. */
context_switch_in(CONTEXT_SWITCH_UNBOUND_FIRST, 0, 0, 0);
DPRINTF("vmpu_enumerate_boxes [DONE]\n");
However interrupts are still disabled at this point which is problematic for deprivilege/reprivilege flow.
The next two lines are still printed with the interrupts disabled, therefore deprivileging isn't performed for them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We currently share buffers for semihosting and debug box. If we made the two buffers separate, could we cache the debug box buffer output until the debug box is usable? That way, we could get these early prints. Not sure if we want two buffers, though. I'm open to other ideas to get these early prints.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll think about the best way to do it.
core/system/src/core_armv7m/svc.c
Outdated
@@ -135,7 +135,11 @@ void UVISOR_NAKED SVCall_IRQn_Handler(void) | |||
".align 4\n" // the jump table must be aligned | |||
"jump_table_unpriv:\n" | |||
".word virq_gateway_out\n" | |||
#ifdef NDEBUG |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like this. We should have the same SVC interface regardless of debug or release build.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the conditional compilation.
core/system/src/core_armv7m/virq.c
Outdated
@@ -324,6 +324,27 @@ int virq_irq_level_get(void) | |||
return (int) NVIC_GetPriority(irqn) - __UVISOR_NVIC_MIN_PRIORITY; | |||
} | |||
|
|||
/* Jump to unprivileged thread mode, the stack frame is already prepared on PSP. */ | |||
void UVISOR_NAKED virq_gateway_deprivilege(uint32_t svc_sp, uint32_t svc_pc) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has nothing to do with virq_gateway
. Could we give it a better name, please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed deprivilege and return functions to:
debug_uvisor_deprivilege
debug_uvisor_return
and moved them to debug_box.c
core/system/src/core_armv7m/virq.c
Outdated
/* Set the priority of each exception. SVC is lower priority than | ||
* MemManage, BusFault, UsageFault, and SecureFault_IRQn, so that we can | ||
* recover from security violations more simply. */ | ||
/* Set the priority of each exception. SVC is set to higher priority than |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please change the priority of exceptions in its own commit, so we can test that changing the priority doesn't cause us to fail tests, independently of other changes you've made.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made it a separate commit in this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed the design to reset active bits, so reverted this change.
@@ -137,6 +137,17 @@ uint32_t vmpu_sys_mux_handler(uint32_t lr, uint32_t msp) | |||
halt_info = &info; | |||
} | |||
|
|||
/* If we have a Hard Fault due to the Bus or MemManage Fault escalation we'll try to treat | |||
* it as the original fault. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The handling of escalated hard faults should also be in its own commit, so we can test it independently of other changes.
What happens if we hard fault a second time as a result of trying to handle the escalated fault? Do we enter a hard fault forever loop? Such a situation seems not so great.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made it a separate commit in this PR.
Still need to think whether we can stuck in a Hard Fault loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed the design to reset active bits, so reverted this change.
if (VMPU_SCB_BFSR) { | ||
ipsr = BusFault_IRQn; | ||
} else | ||
if (VMPU_SCB_MMFSR) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please follow style guidelines. else
and if
should be on same line here.
core/system/src/core_armv7m/virq.c
Outdated
{ | ||
/* Return to handler mode with MSP using a basic stack frame. */ | ||
asm volatile( | ||
"ldr lr, =0xfffffff1\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Would anything bad happen if another uVisor SVC was called before the
virq_gateway_return
SVC?- What would happen to MSP?
- Are we taking any steps to prevent calls to other SVC while in the debug box?
- Can the
virq_gateway_return
SVC be used to leak information stored on the MSP to its caller?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's possible to execute SVC during deprivileging since deprivileged code operates at lower exception level.
No information is supposed to leak during return - we're using the previously created on MSP stack frame.
ea32d0f
to
3b69672
Compare
3b69672
to
c112576
Compare
retest uvisor |
e4c52f8
to
e5f6c44
Compare
Changes: - Add a debug_deprivilege_and_return() which performs deprivilege and return within the function itself - Add debug_print() handler to TUvisorDebugDriver structure - Add two SVC handlers to assist deprivilege and return flow
e5f6c44
to
d452560
Compare
@mikisch81 @orenc17A @theamirocohen |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we consider using a separate stack for the debug context, so that we don't overwrite any used state? For instance, when an interrupt is being actively serviced by a box that is also acting as the debug box, we might overwrite the oldest stack data, preventing exit from the interrupt from working correctly after having done a debug print.
Merged debug box printing to dev/romkuz01/debug-box-print-experimental due to high risk change and lack of thorough testing Merged ready to go commits to master |
Major changes:
which better reflects its nature
performs deprivilege and return within the function itself
be used from the user's application to enable
deprivilege/print/reprivilege flow
Note:
Ideally it would be better to break this commit into a few smaller ones
Usage example in the user's application:
...
static void halt_error(THaltError reason, const THaltInfo * halt_info)
{
...
}
static void debug_print(const char * message)
{
printf("%s", message);
}
UVISOR_PUBLIC_BOX_DEBUG_DRIVER(halt_error, debug_print);
#define BAD_BAD_ADDR (*((volatile unsigned long *) (0xFFFFFFFF)))
int main(void)
{
uvisor_debug_print_enable(true);
}