Add deprivilege/print/reprivilege flow#497
Conversation
alzix
left a comment
There was a problem hiding this comment.
i have not yet had time to review the whole thing - these are my initial thoughts
| DPRINTF("\n***********************************************************\n");\ | ||
| DPRINTF(" "x"\n");\ | ||
| DPRINTF("***********************************************************\n\n");\ | ||
| DPRINTF("\r\n***********************************************************\r\n");\ |
There was a problem hiding this comment.
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.
| void (*spin_unlock)(UvisorSpinlock * spinlock); | ||
|
|
||
| void (*debug_semihosting_enable)(void); | ||
| void (*debug_print_enable)(bool); |
There was a problem hiding this comment.
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;
...
}
|
|
||
| UVISOR_WEAK void default_putc(uint8_t data) | ||
| { | ||
| if (DEBUG_SEMIHOSTING_MAGIC == g_semihosting_magic) { |
There was a problem hiding this comment.
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.
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
Patater
left a comment
There was a problem hiding this comment.
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.
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.
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.
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.
Do we also need to include lr?
{r4-r11, lr}
There was a problem hiding this comment.
No need since LR is automatically saved when executing SVC after this PUSH.
Also R0-R3, R12 and xPSR.
|
|
||
| /* 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 |
| ((void (*)(void)) return_handler)(); | ||
| } | ||
|
|
||
| /* FIXME: replace these stubs by the actual implementation. */ |
There was a problem hiding this comment.
Is the plan to implement these in this PR or a later one?
There was a problem hiding this comment.
Probably it's better to do it in a separate PR.
This one is already big enough.
There was a problem hiding this comment.
Can we remove these empty stubs or does that break the build?
| { | ||
| /* Abort printing if debug box wasn't initialized yet. */ | ||
| if (!g_debug_box.initialized) { | ||
| return; |
There was a problem hiding this comment.
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.
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.
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.
I'll think about the best way to do it.
| ".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.
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.
Removed the conditional compilation.
| } | ||
|
|
||
| /* 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.
This has nothing to do with virq_gateway. Could we give it a better name, please?
There was a problem hiding this comment.
Renamed deprivilege and return functions to:
debug_uvisor_deprivilege
debug_uvisor_return
and moved them to debug_box.c
| /* 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.
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.
Made it a separate commit in this PR.
There was a problem hiding this comment.
Changed the design to reset active bits, so reverted this change.
| } | ||
|
|
||
| /* 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.
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.
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.
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.
Please follow style guidelines. else and if should be on same line here.
| { | ||
| /* Return to handler mode with MSP using a basic stack frame. */ | ||
| asm volatile( | ||
| "ldr lr, =0xfffffff1\n" |
There was a problem hiding this comment.
- Would anything bad happen if another uVisor SVC was called before the
virq_gateway_returnSVC?- 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_returnSVC be used to leak information stored on the MSP to its caller?
There was a problem hiding this comment.
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 |
Patater
left a comment
There was a problem hiding this comment.
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);
}