Conversation
e20000a to
57f874b
Compare
|
🤖 SemverChecks 🤖 No breaking API changes detected Note: this does not mean API is unchanged, or even that there are no breaking changes; simply, none of the detections triggered. |
| // Placeholder for now | ||
| let upcall = crate::UPCALL.get().expect("OP-TEE upcall not registered"); | ||
| let mut ctx = litebox_common_linux::PtRegs::default(); | ||
| let _ = upcall.execute(&mut ctx); |
There was a problem hiding this comment.
Did you intend to ignore the return the value here?
Seems like this should be properly handled/propagated.
There was a problem hiding this comment.
As wrote in the comment, this is just a placeholder. we should handle the return value.
There was a problem hiding this comment.
Minor nit: keeping a TODO:/XXX:/... or such signifier makes it easier to grep in the future quickly
There was a problem hiding this comment.
Didn't put TODO here because the actual implementation already exists in another PR.
https://github.com/microsoft/litebox/pull/564/changes#diff-3eeb171b29f4643a455abd0bf670819afc33087f009eb4f6300fff0364f19d6bR127
| type Parameter = litebox_common_linux::PtRegs; | ||
| type Return = litebox_common_linux::PtRegs; |
There was a problem hiding this comment.
Curious to know why you need the registers here?
Do you intend to run a thread during upcall.execute?
There was a problem hiding this comment.
This is just for defining parameter and return types. We don't specify any particular data types here yet. The reason I use PtRegs here is because it is the most common data type used in many places in the LiteBox code base.
|
@jaybosamiya-ms It would be good to have you take a look at this PR since it changes litebox. |
There was a problem hiding this comment.
Minor comments throughout, but I have a more general comment about the design/idea itself: this is roughly equivalent to the functionality that already exists in litebox::shim::... right? This feels like adding a parallel way of handling roughly similar things, which seems suboptimal. Until we can clearly articulate when that should be used and when this should be used, I think this PR should not be merged, which is why I am marking this as "Request changes".
I am happy to discuss this IRL more if it would help tweak the design of the existing litebox::shim::... to support the use case (or maybe it already can support the use case?). Currently, this PR is only introducing the new functionality without the use case, which makes it a little hard to understand why the required use case cannot be handled by existing litebox::shim::EnterShim and related bits.
CC: @jstarks I expect you'll have some useful insights on this PR
| //! Examples of such messages or requests include HVCI/Heki requests from | ||
| //! VTL0 and OP-TEE SMC calls from the normal world. |
There was a problem hiding this comment.
Would signals from the outside world be a similar situation (i.e., should they be handled through this mechanism)?
There was a problem hiding this comment.
Unlike the Linux shim case that all signals will be handled by the shim, certain exceptions/interrupts will be handled by the LVBS platform whereas some other ones are handled by the OP-TEE shim.
There was a problem hiding this comment.
That's fine right? The platform can just decide not to forward along things to the shim. There is nothing in the design that says that the platform must forward everything over, right? Maybe I am misunderstanding why the signals world and the upcall world are drastically different. Might be easier to chat in-person?
There was a problem hiding this comment.
Yes, I think the current explanation is misleading. It is more about there are something the platform doesn't know how to handle and delegates them to the others if applicable.
| //! calls within LiteBox. However, care must be taken to ensure that the upcall's | ||
| //! parameters and return values are properly validated and sanitized to prevent | ||
| //! potential security vulnerabilities. This is because the parameters might be |
There was a problem hiding this comment.
"care must be taken" -> by whom?
There was a problem hiding this comment.
the upcall handler. will clarify this.
| //! does not have semantics to validate them). We can specify a function for early | ||
| //! validation at the platform side if needed but its advantages are not clear at | ||
| //! this moment (since there is no costly context switch within LiteBox). |
There was a problem hiding this comment.
Nit: this sentence is surprising to see as part of the doc string. Might be better just as a comment, rather than being in the doc string.
| /// Initialize the upcall handler. Must be called by the platform exactly once. | ||
| /// Per-thread initialization is possible but all threads must share the same | ||
| /// upcall handler. |
There was a problem hiding this comment.
This comment is confusing. What happens if the platform does not invoke this? (I might have missed something but I didn't see the platform invoke this here). I also don't understand the "per-thread initialization is possible but ..." comment here. Should each thread invoke this? Or should different threads within the platform coordinate to make sure they have not invoked this more than once?
A slightly easier-to-use interface is likely something like "get singleton", so that the platform doesn't need to store/manage this, and the runner side can choose to make it more/less performant as it needs. Alternatively, one can just eliminate initialization and set up the execute to initialize on first use?
There was a problem hiding this comment.
Basically, this is a callback registration. I agree that the comment is confusing. I'll fix this.
| fn init( | ||
| &self, | ||
| ) -> alloc::boxed::Box< | ||
| dyn crate::upcall::Upcall<Parameter = Self::Parameter, Return = Self::Return>, | ||
| >; |
There was a problem hiding this comment.
You need self to get essentially a Box<dyn itself>? This is a confusing signature. Where do you get teh original Upcall to even invoke this initialization? Why is it returning a boxed version of itself?
There was a problem hiding this comment.
the runner has a function and registers it to the platform through this (Platform::register_upcall).
| /// platform validates the parameters, the implementation of `execute` must validate | ||
| /// parameters to avoid potential security vulnerabilities. Also, it must sanitize | ||
| /// the return values before returning them to the platform. | ||
| fn execute(&self, ctx: &mut Self::Parameter) -> Result<Self::Return, UpcallError>; |
There was a problem hiding this comment.
I'm mildly surprised by seeing the &mut here for the parameter. Due to lifetime elision and such, Self::Return's lifetime cannot depend on that mutable reference anyways. A slightly cleaner design might be to just pass the parameter as a (consumed/owned) value? Alternatively, the Return should have a generic lifetime that gets bound to the lifetime of the parameter (or even more generally, it would get bound to a new lifetime that is itself bounded by the lifetimes of &self and ctx).
There was a problem hiding this comment.
Yes. Technically, we don't need to struggle with lifetime. let me use values.
| #[error("Upcall failed")] | ||
| Failure, |
There was a problem hiding this comment.
Failed due to? It becomes impossible in the current design for the upcall to give more useful information to the place below. Would be good to make this generic in some way (can do it generically, or can do a Failure(Box<dyn Error>) might be enough).
| #[error("Upcall needs to be retried")] | ||
| Retry, |
There was a problem hiding this comment.
If the upcall needs to be retried, it needs to mention when it needs to be retried, right? Otherwise, if "retry immediately" is allowed, might as well do the retrying internally. The failure case is that "it cannot be handled right now, please try later", but yeah, something else feels necessary here in the output so that a platform can know when to retry.
There was a problem hiding this comment.
Yes. Technically, there is no easy way to know when the platform can try it again. Let me think.
| // Placeholder for now | ||
| let upcall = crate::UPCALL.get().expect("OP-TEE upcall not registered"); | ||
| let mut ctx = litebox_common_linux::PtRegs::default(); | ||
| let _ = upcall.execute(&mut ctx); |
There was a problem hiding this comment.
Minor nit: keeping a TODO:/XXX:/... or such signifier makes it easier to grep in the future quickly
| upcall: &'static ( | ||
| dyn litebox::upcall::Upcall< | ||
| Parameter = litebox_common_linux::PtRegs, | ||
| Return = litebox_common_linux::PtRegs, | ||
| > + Send | ||
| + Sync | ||
| ), |
There was a problem hiding this comment.
oof, this looks like rustfmt did a horrifyingly bad job here. Consider putting the type into a type alias (type Upcall = ...) to make this be nicer maintained?
We can definitely discuss this later. This design is based on some discussion between John, Weidong, and me. Long story short, we need a runner or something below the shim to support this stuff. For example, two of this upcall's main usages are loading ELF and invoking Anyhow, what we need is there is a function in the runner which should be invoked by the platform through a simple interface. |
|
Discussion result: Instead of defining a new interface, we will add one function to |
|
Turns that using |
|
We decided to move |
This PR adds the
upcalltrait to let platforms delegate handling of unknownmessages/requests (e.g., OP-TEE messages from the normal-world/VTL0
kernel) to other layers of LiteBox (i.e., runner or shim).