Re: [PATCH 3/9] KVM: x86: SVM: Pass through shadow stack MSRs

From: Maxim Levitsky
Date: Thu Nov 02 2023 - 14:11:04 EST


On Wed, 2023-10-18 at 16:57 +0530, Nikunj A. Dadhania wrote:
> On 10/17/2023 11:47 PM, John Allen wrote:
> > On Thu, Oct 12, 2023 at 02:31:19PM +0530, Nikunj A. Dadhania wrote:
> > > On 10/11/2023 1:32 AM, John Allen wrote:
> > > > If kvm supports shadow stack, pass through shadow stack MSRs to improve
> > > > guest performance.
> > > >
> > > > Signed-off-by: John Allen <john.allen@xxxxxxx>
> > > > ---
> > > > arch/x86/kvm/svm/svm.c | 26 ++++++++++++++++++++++++++
> > > > arch/x86/kvm/svm/svm.h | 2 +-
> > > > 2 files changed, 27 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
> > > > index e435e4fbadda..984e89d7a734 100644
> > > > --- a/arch/x86/kvm/svm/svm.c
> > > > +++ b/arch/x86/kvm/svm/svm.c
> > > > @@ -139,6 +139,13 @@ static const struct svm_direct_access_msrs {
> > > > { .index = X2APIC_MSR(APIC_TMICT), .always = false },
> > > > { .index = X2APIC_MSR(APIC_TMCCT), .always = false },
> > > > { .index = X2APIC_MSR(APIC_TDCR), .always = false },
> > > > + { .index = MSR_IA32_U_CET, .always = false },
> > > > + { .index = MSR_IA32_S_CET, .always = false },
> > > > + { .index = MSR_IA32_INT_SSP_TAB, .always = false },
> > > > + { .index = MSR_IA32_PL0_SSP, .always = false },
> > > > + { .index = MSR_IA32_PL1_SSP, .always = false },
> > > > + { .index = MSR_IA32_PL2_SSP, .always = false },
> > > > + { .index = MSR_IA32_PL3_SSP, .always = false },
> > >
> > > First three MSRs are emulated in the patch 1, any specific reason for skipping MSR_IA32_PL[0-3]_SSP ?
> >
> > I'm not sure what you mean.
>
> MSR_IA32_U_CET, MSR_IA32_S_CET and MSR_IA32_INT_SSP_TAB are selectively emulated and there is no good explanation why MSR_IA32_PL[0-3]_SSP do not need emulation. Moreover, MSR interception is initially set (i.e. always=false) for MSR_IA32_PL[0-3]_SSP.
>

Let me explain:

Passing through an MSR and having code for reading/writing it in svm_set_msr/svm_get_msr are two different things:

Passing through an MSR means that the guest can read/write the msr freely (that assumes that this can't cause harm to the host),
but either KVM or the hardware usually swaps the guest MSR value with host msr value on vm entry/exit.

Therefore the function of svm_set_msr/svm_get_msr is to obtain the saved guest msr value while the guest is not running for various use cases (for example for migration).

In case of MSR_IA32_U_CET, MSR_IA32_S_CET and MSR_IA32_INT_SSP_TAB the hardware itself loads the guest values from the vmcb on VM entry and saves the modified guest values
to the vmcb on vm exit, thus correct way of reading/writing the guest value is to read/write it from/to the vmcb field.


Now why other CET msrs are not handled by svm_set_msr/svm_get_msr?
The answer is that those msrs are not saved/restored by SVM microcode, and instead their guest values remain
during the VMexit.

The CET common code which I finally finished reviewing last night, context switches them together with the rest of guest FPU state using xsaves/xrstors instruction,
and if the KVM wants to read these msrs, the common code will first load the guest FPU state and then read/write the msrs using regular rdmsr/wrmsr.


> > The PLx_SSP MSRS should be getting passed
> > through here unless I'm misunderstanding something.
>
> In that case, intercept should be cleared from the very beginning.
>
> + { .index = MSR_IA32_PL0_SSP, .always = true },
> + { .index = MSR_IA32_PL1_SSP, .always = true },
> + { .index = MSR_IA32_PL2_SSP, .always = true },
> + { .index = MSR_IA32_PL3_SSP, .always = true },

.always is only true when a MSR is *always* passed through. CET msrs are only passed through when CET is supported.

Therefore I don't expect that we ever add another msr to this list which has .always = true.

In fact the .always = True for X86_64 arch msrs like MSR_GS_BASE/MSR_FS_BASE and such is not 100% correct too -
when we start a VM which doesn't have cpuid bit X86_FEATURE_LM, these msrs should not exist and I think that we have a
kvm unit test that fails because of this on 32 bit but I didn't bother yet to fix it.

.always probably needs to be dropped completely.


So IMHO this patch is correct (I might have missed something though):

Reviewed-by: Maxim Levitsky <mlevitsk@xxxxxxxxxx>

Best regards,
Maxim Levitsky

>
> Regards
> Nikunj
>
>