-
Notifications
You must be signed in to change notification settings - Fork 72
First box to call NVIC owns the interrupt #403
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
Conversation
Patater
left a comment
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.
Use command tense in commit messages.
Consider replacing First box to call NVIC owns the interrupt with Make vIRQ functions claim unclaimed IRQs
core/system/src/unvic.c
Outdated
| * table is not 0 */ | ||
| is_irqn_registered = uv->hdlr ? 1 : 0; | ||
| /* an IRQn slot is considered as registered if the vector id is not unused. */ | ||
| is_irqn_registered = (uv->id != UNVIC_VECTOR_UNUSED); |
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 wouldn't need this check if, instead of UNVIC_VECTOR_UNUSED, we use an invalid box ID define (which we may need to create). Simply checking uv->id != g_active_box would be good enough.
core/system/src/unvic.c
Outdated
| NVIC_SetPriorityGrouping(0); | ||
|
|
||
| /* Make sure the UNVIC_VECTOR_UNUSED is definitely NOT a valid box id. */ | ||
| if (vmpu_is_box_id_valid(UNVIC_VECTOR_UNUSED)) { |
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 wouldn't have to do this check if UNVIC_VECTOR_UNUSED were replaced with an invalid box ID define.
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.
Yup, this same mechanism is used in page allocator. I'll make it generic then.
| g_unvic_vector[ii].id = UNVIC_VECTOR_UNUSED; | ||
| g_unvic_vector[ii].hdlr = (TIsrVector) user_vtor[ii + NVIC_OFFSET]; | ||
| /* set default priority (SVC must always be higher) */ | ||
| NVIC_SetPriority(ii, __UVISOR_NVIC_MIN_PRIORITY); |
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 registering the user interrupt table interrupts with the public box really necessary? It sounds like a nice thing to do at first glance, but given that interrupts must be enabled before use, isn't just having enable (and the other vIRQ functions) register for the interrupt good enough? Is there some case I'm missing where an interrupt in the user interrupt table can be used without calling one of the NVIC or vIRQ functions?
If it is possible for a user to use an interrupt without calling any NVIC (or vIRQ) functions, how easy is it for a user to change the user interrupt table? Does a user need to change mbed OS code?
On the surface, I agree with this change. Interrupts in the user interrupt table are "legacy" interrupts and should be removed in favor of using the "non-legacy" vIRQ API. This fits with the uVisor story well of: the public box is for backwards compatibility; to do things with secure boxes you need to use uVisor APIs. However, if it is too hard to remove some interrupt from the user interrupt table (and I'd argue that having to change a local copy of mbed OS is too hard), then I think usability goes out the window if there is no other way to unregister the interrupt from the public box. We may need to rethink including the ability for a box to unregister interrupts.
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.
Nevermind. You are only setting the priority, not claiming the IRQ. Good job.
| if(uv->id || uv->hdlr) | ||
| if(uv->id != UNVIC_VECTOR_UNUSED) | ||
| { | ||
| HALT_ERROR(PERMISSION_DENIED, |
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.
Instead of halting, which shuts down the entire system even on release builds, consider debug asserting so we can catch programmer mistakes, but silently not fulfill the request in release builds. This will allow us to write tests that make sure we handle interrupt ownership contention. Note that this will require changes to all functions that call unvic_acl_check, as the function can now return in the case where the IRQ is registered, but not to the caller. All functions using unvic_acl_check will have to deal with: IRQ unclaimed, IRQ claimed by current box, IRQ claimed by other box.
f11a3d8 to
f0dac5b
Compare
|
Updated for review. |
core/vmpu/inc/vmpu.h
Outdated
| extern uint32_t vmpu_round_up_region(uint32_t addr, uint32_t size); | ||
|
|
||
| /** Invalid box id for use in marking objects with invalid ownership. */ | ||
| #define UVISOR_BOX_ID_INVALID ((uint8_t) -1) |
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.
If this were in vmpu_exports.h, we wouldn't have to redefine it as -1 in the unsupported files.
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.
Good point.
core/system/src/unvic.c
Outdated
| HALT_ERROR(NOT_ALLOWED, "IRQ %d is unregistered; state cannot be changed", irqn); | ||
| } | ||
| g_unvic_vector[irqn].id = g_active_box; | ||
| DPRINTF("IRQ %d registered to box %d\n\r", irqn, g_active_box); |
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 registration of an irqn to a box is something we do often in these functions. Consider placing this into an itty bitty static function, so we don't have to repeat the code and DPRINTF string so much.
Heck, even the call and check of unvic_acl_check is done very often. Maybe a new function like unvic_irq_register would be good. You'd use it like so.
/* Attempt to register for the IRQ. */
if (!unvic_irq_register(irqn)) {
/* We couldn't claim the interrupt. */
return 0;
}This is just a suggestion. Do what you think makes sense.
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/system/src/unvic.c
Outdated
| for (uint32_t ii=0; ii < NVIC_VECTORS; ii++) { | ||
| g_unvic_vector[ii].id = UVISOR_BOX_ID_INVALID; | ||
| g_unvic_vector[ii].hdlr = (TIsrVector) user_vtor[ii + NVIC_OFFSET]; | ||
| /* set default priority (SVC must always be higher) */ |
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.
Use complete sentences (with capital letter and correct punctuation) for new comments.
6fb95f7 to
3f5ed64
Compare
core/system/src/unvic.c
Outdated
| static int unvic_isr_register(uint32_t irqn) | ||
| { | ||
| int isr_status = unvic_acl_check(irqn); | ||
| switch(isr_status) |
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.
Add space before (
3f5ed64 to
85cf97a
Compare
Patater
left a comment
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.
Other than a little missing space, LGTM
|
|
||
| /* check if IRQ entry is populated */ | ||
| if(uv->id || uv->hdlr) | ||
| if(uv->id != UVISOR_BOX_ID_INVALID) |
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.
Add space before (
85cf97a to
0cea838
Compare
|
@niklas-arm As part of this PR you should also quickly update the docs. Please scan the docs folder for occurrences of the NVIC APIs. While the APIs haven't changed, the underlying behavior has. This includes at least:
Please specify the new behavior (no need to set an ISR first; first box touching an IRQ gets the IRQ; you cannot release ownership of an IRQ; if you don't set an IRQ you get the box 0 default one). |
core/system/src/unvic.c
Outdated
| NVIC_DisableIRQ(irqn); | ||
| g_unvic_vector[irqn].id = 0; | ||
| if (!unvic_isr_register(irqn)) { | ||
| 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.
Here and in other similar instances return should be replaced with an HALT_ERROR, so the user gets a notification, the NVIC API does not silently fail, and the system halts exactly at that point.
The backward compatibility introduced by this PR still allows legacy code to work as expected.
As @Patater points out this is a source of DoS, and it will be solved when we will support individually-killable boxes.
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 "fixed" this by halting in the unvic_acl_check function. This prevents duplicate code and restores the original behavior.
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.
Yup! The return gives the wrong expectation, though. Even if we use hardcoded IRQ ACLs, the API will never be meant to return if the IRQ is not registered to the calling box.
Consider replacing occurrences of:
if (!unvic_isr_register(irqn)) {
return;
}with:
/* This function halts if the IRQ is owned by another box or by uVisor. */
unvic_isr_register(irqn)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.
(If instead you want to keep the semantics by which unvic_isr_register(irqn) returns the ownership status of irqn, you will have to halt from every function that calls that function. I like this semantics so I'd rather duplicate the HALT_ERROR. @Patater ?)
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.
-1 for duplication, I'll call the function without if.
core/system/src/unvic.c
Outdated
| NVIC_SetPriorityGrouping(0); | ||
|
|
||
| /* Copy the user interrupt table into the handlers. */ | ||
| for (uint32_t ii=0; ii < NVIC_VECTORS; ii++) { |
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.
-uint32_t ii=0;
+uint32_t ii = 0;The ii is very MATLAB-y 😄
core/system/src/unvic.c
Outdated
| * the same priority level. */ | ||
| NVIC_SetPriorityGrouping(0); | ||
|
|
||
| /* Copy the user interrupt table into the handlers. */ |
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.
Could you add a comment here specifying the reason why this is needed and the consequences? In particular, it's important to specify that if a secure box code enables an IRQ before setting its ISR, the IRQ might be immediately served by the static ISR, which is out of the secure box control (because it's usually set by box 0/the host OS).
Related to this: #403 (comment).
0cea838 to
7fce949
Compare
core/system/src/unvic.c
Outdated
| NVIC_DisableIRQ(irqn); | ||
| g_unvic_vector[irqn].id = 0; | ||
| if (!unvic_isr_register(irqn)) { | ||
| 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.
Yup! The return gives the wrong expectation, though. Even if we use hardcoded IRQ ACLs, the API will never be meant to return if the IRQ is not registered to the calling box.
Consider replacing occurrences of:
if (!unvic_isr_register(irqn)) {
return;
}with:
/* This function halts if the IRQ is owned by another box or by uVisor. */
unvic_isr_register(irqn)7fce949 to
7713290
Compare
core/system/src/unvic.c
Outdated
| if (uv->id == UVISOR_BOX_ID_INVALID) { | ||
| return UNVIC_ISR_OWNER_NONE; | ||
| } | ||
| HALT_ERROR(PERMISSION_DENIED, "Permission denied: IRQ %d is owned by box %d\n\r", irqn, uv->id) |
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.
Now the return values from both unvic_acl_check and unvic_isr_register do not make sense anymore. I'd rather have unvic_acl_check return the ownership status and unvic_isr_register be a void function that either registers the IRQ or halts.
a097bd3 to
de4ec48
Compare
|
Updated. |
AlessandroA
left a comment
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.
Thanks a lot for the changes, it's a very neat PR! I left a couple of comments for minor fixes (remember to fix the style when you happen to modify a line with a broken style).
I think this doc still needs an update: https://github.com/ARMmbed/uvisor/blob/master/docs/api/API.md#low-level-apis (the vIRQ_SetVector API in particular).
LGTM after those fixes 👍
core/system/src/unvic.c
Outdated
| } | ||
|
|
||
| return is_irqn_registered; | ||
| HALT_ERROR(PERMISSION_DENIED, "Permission denied: IRQ %d is owned by another box!\n\r", irqn); |
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.
\n\r ---> \r\n
core/system/src/unvic.c
Outdated
| else { | ||
| HALT_ERROR(NOT_ALLOWED, "IRQ %d is unregistered; state cannot be changed", irqn); | ||
| } | ||
| /* clear pending IRQ */ |
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.
/* Clear pending IRQ. */
core/system/src/unvic.c
Outdated
| else { | ||
| HALT_ERROR(NOT_ALLOWED, "IRQ %d is unregistered; state cannot be changed", irqn); | ||
| } | ||
| /* set pending IRQ */ |
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.
/* Set pending IRQ. */
core/system/src/unvic.c
Outdated
| irqn, | ||
| vector ? "registered to" : "released by", | ||
| g_active_box); | ||
| /* save unprivileged 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.
/* Save the unprivileged handler. */
11cc71a to
ae6c8d7
Compare
|
retest uvisor |
1 similar comment
|
retest uvisor |
|
retest uvisor |
1 similar comment
|
retest uvisor |
This also copies the initial VTOR table from flash to ram and sets correct priority. That way, statically allocated interrupts are preserved and need only be enabled.
cc @AlessandroA @Patater