-
Notifications
You must be signed in to change notification settings - Fork 104
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
SNP Guest VSM: Start VP hypercall handling #634
base: main
Are you sure you want to change the base?
Conversation
899e334
to
37d1d1c
Compare
/// doesn't handle EnableVpVtl, there's no obvious place to set this. | ||
hcvm_vtl1_enabled: Mutex<bool>, | ||
/// Only modified for hardware CVMs. | ||
hcvm_vtl1_state: Mutex<UhVpCvmVtl1State>, |
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 we move this off of VpInner so non-CVMs don't have it anymore? Now that we have partition-wide per-backing state we could put a Vec on UhCvmPartitionState to hold these instead. I was already adding vp_count to BackingSharedParams in #704, so you could copy that bit to size the Vec.
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 feel like there's a pattern here, Steven, where you add/suggest adding a bunch of per-X things and I come by and say "hey, just make a single per-X structure and put everything in that." In this case, should we have a Vec<VpInnerCvm>
somewhere instead of a bunch of per-VP Vec<Whatever>
s?
Sometimes separate Vec
s do make sense, for memory locality reasons, but we should be intentional when we do that.
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.
Usually I hold off on making that per-thing struct until there's going to be more than one field in it, but yes it does seem like we're heading in that direction.
@@ -254,6 +255,8 @@ mod private { | |||
|
|||
fn untrusted_synic(&self) -> Option<&ProcessorSynic>; | |||
fn untrusted_synic_mut(&mut self) -> Option<&mut ProcessorSynic>; | |||
|
|||
fn set_exit_vtl(this: &mut UhProcessor<'_, Self>, vtl: GuestVtl); |
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 there ever a case where we're setting the exit vtl but not switching vtl state? I think this should just be handled inside of those two impls.
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.
Ah I see, thats how startvp works. Hmmmm, I really don't like having this here since it's only used for hardware isolated backings, but we need it in handle wake....
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.
@jstarks any thoughts?
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 think maybe the way we could achieve it is to "flip" start_virtual_processor so that it's not in the common processor\mod.rs, but has individual implementations per-backing, and then they can call into a common validation code in processor\mod.rs, and for CVMs set the exit_vtl only in the CVM backing. Is that preferable? The main thing I'm not sure about is the ordering of when the exit vtl is set vs the initial state, but I think technically it doesn't matter.
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 think we need to 1. restrict the VP start code to be CVM only, since the hypervisor handles it for non-CVM (I think?? I see that we have it in our hypercall table, but I would expect the hypervisor to just handle it, just like it handles INIT) and 2. update the wake code to call out to backing for "extended" wakes, and then have the CVM backings call into some common "handle_cvm_wake" call or something. Then we'll have access to all the CVM stuff in start VP, without needing an extra trait method.
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.
So even with startup suspend, we have no reason to believe that openhcl needs to handle startvp for non-cvms? Can I safely remove it from the hypercall table as well?
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.
Maybe it's worth digging into the closed source history to see why we originally added it for non-cvm.
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 suspect we just need it in the APIC emulation case. We also originally had it when the hypervisor didn't have precise VP startup support and we had to emulate INIT/SIPI/startup suspend as well... but that never worked well, as you recall.
I think if you try to remove it from the hypercall table, you also need to make sure we don't report StartVp support to the guest in the case where OpenHCL is providing the mshv privileges/features via cpuid. So there will be a little plumbing through hv1_emulator to make this conditional (we really need to make a full builder pattern or something for that...)
It might be cleanest to make this removal/refactor a separate PR, then layer this one on top. Up to you.
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 so make sure I understand what should happen, it would be:
- Remove startvp hypercall handling for non-CVMs, and don't report startvp support to the guest.
- Create an "extended wakes" backing, and move the startvp wake handling onto the backings for CVMs.
Is that correct? I'm not sure on item 2 based on this comment
I suspect we just need it in the APIC emulation case.
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.
// vtls and with the DenyLowerVtlStartup, and in practice, it's not clear | ||
// whether any guest OS does this. For now, if guest vsm is enabled, | ||
// simplify by disallowing repeated vp startup. Revisit this later if it | ||
// becomes a problem. Note that this will not apply to non-hardware cvms |
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 just be non-cvm
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 decided to scope disallowing repeated vp startup to guestvsm + cvm because the edge cases seem to really come in with guest vsm.
@@ -1183,7 +1192,7 @@ impl<B: HardwareIsolatedBacking> UhProcessor<'_, B> { | |||
// Check for VTL preemption - which ignores RFLAGS.IF | |||
if is_interrupt_pending(self, GuestVtl::Vtl1, false) { | |||
B::switch_vtl_state(self, GuestVtl::Vtl0, GuestVtl::Vtl1); |
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 think switch_vtl_state should be responsible for setting exit vtl, so that callers don't have to always call both. Is there ever a case where we're switching state but not exiting?
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 thought you had intentionally removed setting the exit vtl from switch_vtl_state in a previous PR, possibly because it makes it more obvious what is happening. I don't have a preference either way, at the moment I don't think we switch the state without changing the exit vtl.
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 remember doing that but it's certainly possible. Maybe that was when this was only being called in one place instead of multiple? But yeah, I think I'm leaning towards doing it in switch_vtl_state now, so that callers can't mess it up.
@@ -424,6 +424,10 @@ impl BackingPrivate for HypervisorBackedX86 { | |||
fn untrusted_synic_mut(&mut self) -> Option<&mut ProcessorSynic> { | |||
None | |||
} | |||
|
|||
fn set_exit_vtl(_this: &mut UhProcessor<'_, Self>, _vtl: GuestVtl) { |
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.
Don't we have a CVM-specific trait we can hang this off of?
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.
see the thread where I pinged you. This is currently needed in handle_wake, which is implemented on all backings.
@@ -1354,8 +1389,60 @@ impl<T, B: Backing> hv1_hypercall::StartVirtualProcessor<hvdef::hypercall::Initi | |||
let target_vtl = self.target_vtl_no_higher(target_vtl)?; | |||
let target_vp = &self.vp.partition.vps[target_vp as usize]; | |||
|
|||
// TODO CVM GUEST VSM: probably some validation on vtl1_enabled | |||
*target_vp.hv_start_enable_vtl_vp[target_vtl].lock() = Some(Box::new(*vp_context)); | |||
if self.vp.partition.isolation.is_hardware_isolated() { |
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.
@smalis-msft , I think we just need to remove support for doing this kind of stuff in the non-CVM case. We're accumulating too much complexity.
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 agree, but I think we want to wait until we have CVM CI coverage, right?
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.
In this case I don't know if it's worth it, even in the short run. We should be able to remove this for non-CVM without breaking guest compatibility, as long as we don't report to the guest that we support the hypercall.
// whether any guest OS does this. For now, if guest vsm is enabled, | ||
// simplify by disallowing repeated vp startup. Revisit this later if it | ||
// becomes a problem. Note that this will not apply to non-hardware cvms | ||
// as this may regress existing VMs. |
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.
Given our goals around guest compatibility within CVMs, this might become a problem. What would it take to support this correctly?
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.
There was speculation over whether any guest actually does this or will want to do this vs an INIT+SIPI. Do you have a concrete example?
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 have any examples. But the claim here is that 1. we don't need to support it but 2. it's important to support it for non-CVM cases. Given our compatibility goals, these can't both be true.
Does legacy HCL support this? Presumably the hypervisor supports it. Why is it difficult to support here?
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.
My rudimentary understanding is that the desired approach would be to mimic the startup suspend changes that were made in the hypervisor so that start vp only takes effect when the higher VTL does a VTL return. Then handling the shared vtl registers and reasoning about higher vtl state is more straightforward. I agree that claims 1 and 2 can't both be true, but at least for this PR I think I'm willing to take the compromise for CVMs given that we haven't shipped guest vsm support for OpenHCL.
We can't take the legacy HCL as an example because it doesn't properly support it.
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. I think if you make the other suggested changes (in particular, making this whole code path CVM only), then it's certainly OK for this PR to only support what guests actually do.
Start VP hypercall handling for SNP Guest VSM support.
Tested: