* [kvm-unit-tests PATCH 0/3] nSVM: Add testing for routing L2 exceptions
@ 2022-02-07 5:11 Manali Shukla
2022-02-07 5:12 ` [kvm-unit-tests PATCH 1/3] x86: Add routines to set/clear PT_USER_MASK for all pages Manali Shukla
` (2 more replies)
0 siblings, 3 replies; 13+ messages in thread
From: Manali Shukla @ 2022-02-07 5:11 UTC (permalink / raw)
To: pbonzini; +Cc: kvm, seanjc, aaronlewis
Series is inspired by vmx exception test framework series[1].
Set up a test framework that verifies an exception occurring in L2 is
forwarded to the right place (L1 or L2).
Tests two conditions for each exception.
1) Exception generated in L2, is handled by L2 when L2 exception handler
is registered.
2) Exception generated in L2, is handled by L1 when intercept exception
bit map is set in L1.
Above tests were added to verify 8 different exceptions
#GP, #UD, #DE, #BP, #NM, #OF, #DB, #AC.
There are 3 patches in this series
1) Added routines to set/clear PT_USER_MASK to make #AC test work for nSVM.
2) exception_mnemonic patch is taken from the Aaron's vmx series[1].
3) Added test infrastructure and exception tests.
[1] https://lore.kernel.org/all/20220125203127.1161838-1-aaronlewis@google.com/
Aaron Lewis (1):
x86: Make exception_mnemonic() visible to the tests
Manali Shukla (2):
x86: Add routines to set/clear PT_USER_MASK for all pages
x86: nSVM: Add an exception test framework and tests
lib/x86/desc.c | 2 +-
lib/x86/desc.h | 1 +
lib/x86/vm.c | 54 ++++++++++++++
lib/x86/vm.h | 3 +
x86/svm_tests.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 244 insertions(+), 1 deletion(-)
--
2.30.2
^ permalink raw reply [flat|nested] 13+ messages in thread* [kvm-unit-tests PATCH 1/3] x86: Add routines to set/clear PT_USER_MASK for all pages 2022-02-07 5:11 [kvm-unit-tests PATCH 0/3] nSVM: Add testing for routing L2 exceptions Manali Shukla @ 2022-02-07 5:12 ` Manali Shukla 2022-02-14 19:30 ` Sean Christopherson 2022-02-07 5:12 ` [kvm-unit-tests PATCH 2/3] x86: Make exception_mnemonic() visible to the tests Manali Shukla 2022-02-07 5:12 ` [kvm-unit-tests PATCH 3/3] x86: nSVM: Add an exception test framework and tests Manali Shukla 2 siblings, 1 reply; 13+ messages in thread From: Manali Shukla @ 2022-02-07 5:12 UTC (permalink / raw) To: pbonzini; +Cc: kvm, seanjc, aaronlewis Add following 2 routines : 1) set_user_mask_all() - set PT_USER_MASK for all the levels of page tables 2) clear_user_mask_all - clear PT_USER_MASK for all the levels of page tables commit 916635a813e975600335c6c47250881b7a328971 (nSVM: Add test for NPT reserved bit and #NPF error code behavior) clears PT_USER_MASK for all svm testcases. Any tests that requires usermode access will fail after this commit. Usermode function needs to be called from L2 guest to generate AC exception and calling usermode function with supervisor pages generates #PF exception with error code 0004 Add solution to above mentioned problem which is to set PT_USER_MASK for all pages in the initialization of #AC exception test and clear them in uninitialization of #AC exception at last. AC exception test works fine without hampering other test cases with this solution. Signed-off-by: Manali Shukla <manali.shukla@amd.com> --- lib/x86/vm.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/x86/vm.h | 3 +++ 2 files changed, 57 insertions(+) diff --git a/lib/x86/vm.c b/lib/x86/vm.c index 56be57b..f3a7ae8 100644 --- a/lib/x86/vm.c +++ b/lib/x86/vm.c @@ -37,6 +37,60 @@ pteval_t *install_pte(pgd_t *cr3, return &pt[offset]; } +/* + * set PT_USER_MASK bit for all levels of page tables + */ + +void set_user_mask_all(pteval_t *pt, int level) +{ + pteval_t pte, *ptep; + int i; + + if (level == PAGE_LEVEL) + pte_opt_mask |= PT_USER_MASK; + + for (i = 0; i < 512; i++) { + ptep = &pt[i]; + pte = *ptep; + + if ((pte & PT_PRESENT_MASK) && !(pte & PT_USER_MASK)) { + *ptep |= PT_USER_MASK; + + if (level == 1 || pte & PT_PAGE_SIZE_MASK) + continue; + + set_user_mask_all(phys_to_virt(pte & 0xffffffffff000ull), level - 1); + } + } +} + + +/* + * clear PT_USER_MASK bit for all levels of page tables + */ + +void clear_user_mask_all(pteval_t *pt, int level) +{ + pteval_t pte, *ptep; + int i; + + for (i = 0; i < 512; i++) { + ptep = &pt[i]; + pte = *ptep; + + if ((pte & PT_PRESENT_MASK) && (pte & PT_USER_MASK)) { + *ptep &= ~PT_USER_MASK; + + if (level == 1 || pte & PT_PAGE_SIZE_MASK) + continue; + + clear_user_mask_all(phys_to_virt(pte & 0xffffffffff000ull), level - 1); + } + } + if (level == PAGE_LEVEL) + pte_opt_mask &= ~PT_USER_MASK; +} + /* * Finds last PTE in the mapping of @virt that's at or above @lowest_level. The * returned PTE isn't necessarily present, but its parent is. diff --git a/lib/x86/vm.h b/lib/x86/vm.h index 4c6dff9..75715e5 100644 --- a/lib/x86/vm.h +++ b/lib/x86/vm.h @@ -38,6 +38,9 @@ pteval_t *install_large_page(pgd_t *cr3, phys_addr_t phys, void *virt); void install_pages(pgd_t *cr3, phys_addr_t phys, size_t len, void *virt); bool any_present_pages(pgd_t *cr3, void *virt, size_t len); +void set_user_mask_all(pteval_t *pt, int level); +void clear_user_mask_all(pteval_t *pt, int level); + static inline void *current_page_table(void) { return phys_to_virt(read_cr3()); -- 2.30.2 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [kvm-unit-tests PATCH 1/3] x86: Add routines to set/clear PT_USER_MASK for all pages 2022-02-07 5:12 ` [kvm-unit-tests PATCH 1/3] x86: Add routines to set/clear PT_USER_MASK for all pages Manali Shukla @ 2022-02-14 19:30 ` Sean Christopherson 2022-02-17 3:55 ` Shukla, Manali 0 siblings, 1 reply; 13+ messages in thread From: Sean Christopherson @ 2022-02-14 19:30 UTC (permalink / raw) To: Manali Shukla; +Cc: pbonzini, kvm, aaronlewis On Mon, Feb 07, 2022, Manali Shukla wrote: > Add following 2 routines : > 1) set_user_mask_all() - set PT_USER_MASK for all the levels of page tables > 2) clear_user_mask_all - clear PT_USER_MASK for all the levels of page tables > > commit 916635a813e975600335c6c47250881b7a328971 > (nSVM: Add test for NPT reserved bit and #NPF error code behavior) > clears PT_USER_MASK for all svm testcases. Any tests that requires > usermode access will fail after this commit. Gah, I took the easy route and it burned us. I would rather we start breaking up the nSVM and nVMX monoliths, e.g. add a separate NPT test and clear the USER flag only in that test, not the "common" nSVM test. If we don't do that, this should really use walk_pte() instead of adding yet another PTE walker. ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [kvm-unit-tests PATCH 1/3] x86: Add routines to set/clear PT_USER_MASK for all pages 2022-02-14 19:30 ` Sean Christopherson @ 2022-02-17 3:55 ` Shukla, Manali 2022-02-17 14:34 ` Aaron Lewis 2022-02-17 15:20 ` Sean Christopherson 0 siblings, 2 replies; 13+ messages in thread From: Shukla, Manali @ 2022-02-17 3:55 UTC (permalink / raw) To: Sean Christopherson, Manali Shukla; +Cc: pbonzini, kvm, aaronlewis On 2/15/2022 1:00 AM, Sean Christopherson wrote: > On Mon, Feb 07, 2022, Manali Shukla wrote: >> Add following 2 routines : >> 1) set_user_mask_all() - set PT_USER_MASK for all the levels of page tables >> 2) clear_user_mask_all - clear PT_USER_MASK for all the levels of page tables >> >> commit 916635a813e975600335c6c47250881b7a328971 >> (nSVM: Add test for NPT reserved bit and #NPF error code behavior) >> clears PT_USER_MASK for all svm testcases. Any tests that requires >> usermode access will fail after this commit. > > Gah, I took the easy route and it burned us. I would rather we start breaking up > the nSVM and nVMX monoliths, e.g. add a separate NPT test and clear the USER flag > only in that test, not the "common" nSVM test. Yeah. I agree. I will try to set/clear User flag in svm_npt_rsvd_bits_test() and set User flag by default for all the test cases by calling setup_vm() and use walk_pte() to set/clear User flag in svm_npt_rsvd_bits_test(). Walk_pte() routine uses start address and length to walk over the memory region where flag needs to be set/clear. So start address and length need to be figured out to use this routine. I will work on this. > > If we don't do that, this should really use walk_pte() instead of adding yet > another PTE walker. -Manali ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [kvm-unit-tests PATCH 1/3] x86: Add routines to set/clear PT_USER_MASK for all pages 2022-02-17 3:55 ` Shukla, Manali @ 2022-02-17 14:34 ` Aaron Lewis 2022-02-20 4:42 ` Shukla, Manali 2022-02-17 15:20 ` Sean Christopherson 1 sibling, 1 reply; 13+ messages in thread From: Aaron Lewis @ 2022-02-17 14:34 UTC (permalink / raw) To: Shukla, Manali; +Cc: Sean Christopherson, Manali Shukla, pbonzini, kvm On Thu, Feb 17, 2022 at 3:55 AM Shukla, Manali <mashukla@amd.com> wrote: > > > > On 2/15/2022 1:00 AM, Sean Christopherson wrote: > > On Mon, Feb 07, 2022, Manali Shukla wrote: > >> Add following 2 routines : > >> 1) set_user_mask_all() - set PT_USER_MASK for all the levels of page tables > >> 2) clear_user_mask_all - clear PT_USER_MASK for all the levels of page tables > >> > >> commit 916635a813e975600335c6c47250881b7a328971 > >> (nSVM: Add test for NPT reserved bit and #NPF error code behavior) > >> clears PT_USER_MASK for all svm testcases. Any tests that requires > >> usermode access will fail after this commit. > > > > Gah, I took the easy route and it burned us. I would rather we start breaking up > > the nSVM and nVMX monoliths, e.g. add a separate NPT test and clear the USER flag > > only in that test, not the "common" nSVM test. > > Yeah. I agree. I will try to set/clear User flag in svm_npt_rsvd_bits_test() and > set User flag by default for all the test cases by calling setup_vm() > and use walk_pte() to set/clear User flag in svm_npt_rsvd_bits_test(). > > Walk_pte() routine uses start address and length to walk over the memory > region where flag needs to be set/clear. So start address and length need to be > figured out to use this routine. For the start address and length you can use 'stext' and 'etext' to compute those. There's an example in access.c set_cr4_smep(), however, it uses walk_ptes() as opposed to walk_pte() (similar, but different). > > I will work on this. > > > > > If we don't do that, this should really use walk_pte() instead of adding yet > > another PTE walker. > > -Manali ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [kvm-unit-tests PATCH 1/3] x86: Add routines to set/clear PT_USER_MASK for all pages 2022-02-17 14:34 ` Aaron Lewis @ 2022-02-20 4:42 ` Shukla, Manali 0 siblings, 0 replies; 13+ messages in thread From: Shukla, Manali @ 2022-02-20 4:42 UTC (permalink / raw) To: Aaron Lewis; +Cc: Sean Christopherson, Manali Shukla, pbonzini, kvm On 2/17/2022 8:04 PM, Aaron Lewis wrote: > On Thu, Feb 17, 2022 at 3:55 AM Shukla, Manali <mashukla@amd.com> wrote: >> >> >> >> On 2/15/2022 1:00 AM, Sean Christopherson wrote: >>> On Mon, Feb 07, 2022, Manali Shukla wrote: >>>> Add following 2 routines : >>>> 1) set_user_mask_all() - set PT_USER_MASK for all the levels of page tables >>>> 2) clear_user_mask_all - clear PT_USER_MASK for all the levels of page tables >>>> >>>> commit 916635a813e975600335c6c47250881b7a328971 >>>> (nSVM: Add test for NPT reserved bit and #NPF error code behavior) >>>> clears PT_USER_MASK for all svm testcases. Any tests that requires >>>> usermode access will fail after this commit. >>> >>> Gah, I took the easy route and it burned us. I would rather we start breaking up >>> the nSVM and nVMX monoliths, e.g. add a separate NPT test and clear the USER flag >>> only in that test, not the "common" nSVM test. >> >> Yeah. I agree. I will try to set/clear User flag in svm_npt_rsvd_bits_test() and >> set User flag by default for all the test cases by calling setup_vm() >> and use walk_pte() to set/clear User flag in svm_npt_rsvd_bits_test(). >> >> Walk_pte() routine uses start address and length to walk over the memory >> region where flag needs to be set/clear. So start address and length need to be >> figured out to use this routine. > > For the start address and length you can use 'stext' and 'etext' to > compute those. There's an example in access.c set_cr4_smep(), > however, it uses walk_ptes() as opposed to walk_pte() (similar, but > different). > Yeah PT_USER_MASK is only set for text segment in set_cr4_smep(). In #AC exception test, PT_USER_MASK needs to be set for user stack pages. So, setting PT_USER_MASK for length('etext' - 'stext') of memory will not resolve the problem. Please let me know if I am missing something. >> >> I will work on this. >> >>> >>> If we don't do that, this should really use walk_pte() instead of adding yet >>> another PTE walker. >> >> -Manali ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [kvm-unit-tests PATCH 1/3] x86: Add routines to set/clear PT_USER_MASK for all pages 2022-02-17 3:55 ` Shukla, Manali 2022-02-17 14:34 ` Aaron Lewis @ 2022-02-17 15:20 ` Sean Christopherson 2022-02-20 5:35 ` Shukla, Manali 1 sibling, 1 reply; 13+ messages in thread From: Sean Christopherson @ 2022-02-17 15:20 UTC (permalink / raw) To: Shukla, Manali; +Cc: Manali Shukla, pbonzini, kvm, aaronlewis On Thu, Feb 17, 2022, Shukla, Manali wrote: > > On 2/15/2022 1:00 AM, Sean Christopherson wrote: > > On Mon, Feb 07, 2022, Manali Shukla wrote: > >> Add following 2 routines : > >> 1) set_user_mask_all() - set PT_USER_MASK for all the levels of page tables > >> 2) clear_user_mask_all - clear PT_USER_MASK for all the levels of page tables > >> > >> commit 916635a813e975600335c6c47250881b7a328971 > >> (nSVM: Add test for NPT reserved bit and #NPF error code behavior) > >> clears PT_USER_MASK for all svm testcases. Any tests that requires > >> usermode access will fail after this commit. > > > > Gah, I took the easy route and it burned us. I would rather we start breaking up > > the nSVM and nVMX monoliths, e.g. add a separate NPT test and clear the USER flag > > only in that test, not the "common" nSVM test. > > Yeah. I agree. I will try to set/clear User flag in svm_npt_rsvd_bits_test() and > set User flag by default for all the test cases by calling setup_vm() > and use walk_pte() to set/clear User flag in svm_npt_rsvd_bits_test(). I was thinking of something more drastic. The only reason the nSVM tests are "incompatible" with usermode is this snippet in main(): int main(int ac, char **av) { /* Omit PT_USER_MASK to allow tested host.CR4.SMEP=1. */ pteval_t opt_mask = 0; int i = 0; ac--; av++; __setup_vm(&opt_mask); ... } Change that to setup_vm() and KUT will build the test with PT_USER_MASK set on all PTEs. My thought (might be a bad one) is to move the nNPT tests to their own file/test so that the tests don't need to fiddle with page tables midway through. The quick and dirty approach would be to turn the current main() into a small helper, minus its call to __setup_vm(). Longer term, I think it'd make sense to add svm/ + vmx/ subdirectories, and turn much of the common code into proper libraries, e.g. test_wanted() can and should be common helper, probably with more glue code to allow declaring a set of subtests. But for now I think we can just add svm_npt.c or whatever. ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [kvm-unit-tests PATCH 1/3] x86: Add routines to set/clear PT_USER_MASK for all pages 2022-02-17 15:20 ` Sean Christopherson @ 2022-02-20 5:35 ` Shukla, Manali 0 siblings, 0 replies; 13+ messages in thread From: Shukla, Manali @ 2022-02-20 5:35 UTC (permalink / raw) To: Sean Christopherson; +Cc: Manali Shukla, pbonzini, kvm, aaronlewis On 2/17/2022 8:50 PM, Sean Christopherson wrote: > On Thu, Feb 17, 2022, Shukla, Manali wrote: >> >> On 2/15/2022 1:00 AM, Sean Christopherson wrote: >>> On Mon, Feb 07, 2022, Manali Shukla wrote: >>>> Add following 2 routines : >>>> 1) set_user_mask_all() - set PT_USER_MASK for all the levels of page tables >>>> 2) clear_user_mask_all - clear PT_USER_MASK for all the levels of page tables >>>> >>>> commit 916635a813e975600335c6c47250881b7a328971 >>>> (nSVM: Add test for NPT reserved bit and #NPF error code behavior) >>>> clears PT_USER_MASK for all svm testcases. Any tests that requires >>>> usermode access will fail after this commit. >>> >>> Gah, I took the easy route and it burned us. I would rather we start breaking up >>> the nSVM and nVMX monoliths, e.g. add a separate NPT test and clear the USER flag >>> only in that test, not the "common" nSVM test. >> >> Yeah. I agree. I will try to set/clear User flag in svm_npt_rsvd_bits_test() and >> set User flag by default for all the test cases by calling setup_vm() >> and use walk_pte() to set/clear User flag in svm_npt_rsvd_bits_test(). > > I was thinking of something more drastic. The only reason the nSVM tests are > "incompatible" with usermode is this snippet in main(): > > int main(int ac, char **av) > { > /* Omit PT_USER_MASK to allow tested host.CR4.SMEP=1. */ > pteval_t opt_mask = 0; > int i = 0; > > ac--; > av++; > > __setup_vm(&opt_mask); > > ... > } > > Change that to setup_vm() and KUT will build the test with PT_USER_MASK set on > all PTEs. My thought (might be a bad one) is to move the nNPT tests to their own > file/test so that the tests don't need to fiddle with page tables midway through. > > The quick and dirty approach would be to turn the current main() into a small > helper, minus its call to __setup_vm(). Yeah this seems to be a better idea. Based on your suggestions, I am planning to do following. 1) Move svm_npt_rsvd_bits_test() to file svm_npt.c and run it with __setup_vm() 2) There are 7 more npt test cases in svm_tests.c, move them to svm_npt.c Below are the test cases: npt_nx, npt_np, npt_us, npt_rw, npt_rw_pfwalk, npt_l1mmio, npt_rw_l1mmio 3) Change __setup_vm() to setup_vm() in svm_tests.c ( after doing this #AC exception test will work fine without the need of set_user_mask_all and clear_user_mask_all) > > Longer term, I think it'd make sense to add svm/ + vmx/ subdirectories, and turn > much of the common code into proper libraries, e.g. test_wanted() can and should > be common helper, probably with more glue code to allow declaring a set of subtests. > But for now I think we can just add svm_npt.c or whatever. I agree, we can do something similar in future. ^ permalink raw reply [flat|nested] 13+ messages in thread
* [kvm-unit-tests PATCH 2/3] x86: Make exception_mnemonic() visible to the tests 2022-02-07 5:11 [kvm-unit-tests PATCH 0/3] nSVM: Add testing for routing L2 exceptions Manali Shukla 2022-02-07 5:12 ` [kvm-unit-tests PATCH 1/3] x86: Add routines to set/clear PT_USER_MASK for all pages Manali Shukla @ 2022-02-07 5:12 ` Manali Shukla 2022-02-07 5:12 ` [kvm-unit-tests PATCH 3/3] x86: nSVM: Add an exception test framework and tests Manali Shukla 2 siblings, 0 replies; 13+ messages in thread From: Manali Shukla @ 2022-02-07 5:12 UTC (permalink / raw) To: pbonzini; +Cc: kvm, seanjc, aaronlewis From: Aaron Lewis <aaronlewis@google.com> exception_mnemonic() is a useful function for more than just desc.c. Make it global, so it can be used in other KUT tests. Signed-off-by: Aaron Lewis <aaronlewis@google.com> Signed-off-by: Manali Shukla <manali.shukla@amd.com> --- lib/x86/desc.c | 2 +- lib/x86/desc.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/x86/desc.c b/lib/x86/desc.c index 16b7256..c2eb16e 100644 --- a/lib/x86/desc.c +++ b/lib/x86/desc.c @@ -91,7 +91,7 @@ struct ex_record { extern struct ex_record exception_table_start, exception_table_end; -static const char* exception_mnemonic(int vector) +const char* exception_mnemonic(int vector) { switch(vector) { case 0: return "#DE"; diff --git a/lib/x86/desc.h b/lib/x86/desc.h index 9b81da0..ad6277b 100644 --- a/lib/x86/desc.h +++ b/lib/x86/desc.h @@ -224,6 +224,7 @@ void set_intr_alt_stack(int e, void *fn); void print_current_tss_info(void); handler handle_exception(u8 v, handler fn); void unhandled_exception(struct ex_regs *regs, bool cpu); +const char* exception_mnemonic(int vector); bool test_for_exception(unsigned int ex, void (*trigger_func)(void *data), void *data); -- 2.30.2 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* [kvm-unit-tests PATCH 3/3] x86: nSVM: Add an exception test framework and tests 2022-02-07 5:11 [kvm-unit-tests PATCH 0/3] nSVM: Add testing for routing L2 exceptions Manali Shukla 2022-02-07 5:12 ` [kvm-unit-tests PATCH 1/3] x86: Add routines to set/clear PT_USER_MASK for all pages Manali Shukla 2022-02-07 5:12 ` [kvm-unit-tests PATCH 2/3] x86: Make exception_mnemonic() visible to the tests Manali Shukla @ 2022-02-07 5:12 ` Manali Shukla 2022-02-14 20:20 ` Aaron Lewis 2 siblings, 1 reply; 13+ messages in thread From: Manali Shukla @ 2022-02-07 5:12 UTC (permalink / raw) To: pbonzini; +Cc: kvm, seanjc, aaronlewis Set up a test framework that verifies an exception occurring in L2 is forwarded to the right place (L1 or L2) It adds an exception test array and exception callbacks to that array. Tests two conditions for each exception 1) Exception generated in L2, is handled by L2 when L2 exception handler is registered. 2) Exception generated in L2, is handled by L1 when intercept exception bit map is set in L1. Add testing for below exceptions: (#GP, #UD, #DE, #BP, #NM, #OF, #DB, #AC) 1. #GP is generated in c by non-canonical access in L2. 2. #UD is generated by calling "ud2" instruction in L2. 3. #DE is generated using instrumented code which generates divide by zero condition 4. #BP is generated by calling "int3" instruction in L2. 5. #NM is generated by calling floating point instruction "fnop" in L2 when TS bit is set. 6. #OF is generated using instrumented code and "into" instruction is called in that code in L2. 7. #DB is generated by setting TF bit before entering to L2. 8. #AC is genrated by writing 8 bytes to 4 byte aligned address in L2 user mode when AM bit is set in CR0 register and AC bit is set in RFLAGS Signed-off-by: Manali Shukla <manali.shukla@amd.com> --- x86/svm_tests.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) diff --git a/x86/svm_tests.c b/x86/svm_tests.c index 0707786..66bfb51 100644 --- a/x86/svm_tests.c +++ b/x86/svm_tests.c @@ -10,6 +10,7 @@ #include "isr.h" #include "apic.h" #include "delay.h" +#include "x86/usermode.h" #define SVM_EXIT_MAX_DR_INTERCEPT 0x3f @@ -3074,6 +3075,189 @@ static void svm_nm_test(void) "fnop with CR0.TS and CR0.EM unset no #NM excpetion"); } +static void svm_l2_gp_test(struct svm_test *test) +{ + *(volatile u64 *)NONCANONICAL = 0; +} + +static void svm_l2_ud_test(struct svm_test *test) +{ + asm volatile ("ud2"); +} + +static void svm_l2_de_test(struct svm_test *test) +{ + asm volatile ( + "xor %%eax, %%eax\n\t" + "xor %%ebx, %%ebx\n\t" + "xor %%edx, %%edx\n\t" + "idiv %%ebx\n\t" + ::: "eax", "ebx", "edx"); +} + +static void svm_l2_bp_test(struct svm_test *svm) +{ + asm volatile ("int3"); +} + +static void svm_l2_nm_test(struct svm_test *svm) +{ + write_cr0(read_cr0() | X86_CR0_TS); + asm volatile("fnop"); +} + +static void svm_l2_of_test(struct svm_test *svm) +{ + struct far_pointer32 fp = { + .offset = (uintptr_t)&&into, + .selector = KERNEL_CS32, + }; + uintptr_t rsp; + + asm volatile ("mov %%rsp, %0" : "=r"(rsp)); + + if (fp.offset != (uintptr_t)&&into) { + printf("Codee address too high.\n"); + return; + } + + if ((u32)rsp != rsp) { + printf("Stack address too high.\n"); + } + + asm goto("lcall *%0" : : "m" (fp) : "rax" : into); + return; +into: + asm volatile (".code32;" + "movl $0x7fffffff, %eax;" + "addl %eax, %eax;" + "into;" + "lret;" + ".code64"); + __builtin_unreachable(); +} + +static void svm_l2_db_test(struct svm_test *test) +{ + write_rflags(read_rflags() | X86_EFLAGS_TF); +} + +static uint64_t usermode_callback(void) +{ + /* + * Trigger an #AC by writing 8 bytes to a 4-byte aligned address. + * Disclaimer: It is assumed that the stack pointer is aligned + * on a 16-byte boundary as x86_64 stacks should be. + */ + asm volatile("movq $0, -0x4(%rsp)"); + + return 0; +} + +static void svm_l2_ac_test(struct svm_test *test) +{ + bool hit_ac = false; + + write_cr0(read_cr0() | X86_CR0_AM); + write_rflags(read_rflags() | X86_EFLAGS_AC); + + run_in_user(usermode_callback, AC_VECTOR, 0, 0, 0, 0, &hit_ac); + + report(hit_ac, "Usermode #AC handled in L2"); + vmmcall(); +} + +static void svm_ac_init(void) +{ + set_user_mask_all(phys_to_virt(read_cr3()), PAGE_LEVEL); +} + +static void svm_ac_uninit(void) +{ + clear_user_mask_all(phys_to_virt(read_cr3()), PAGE_LEVEL); +} + +struct svm_exception_test { + u8 vector; + void (*guest_code)(struct svm_test*); + void (*init_test)(void); + void (*uninit_test)(void); +}; + +struct svm_exception_test svm_exception_tests[] = { + { GP_VECTOR, svm_l2_gp_test }, + { UD_VECTOR, svm_l2_ud_test }, + { DE_VECTOR, svm_l2_de_test }, + { BP_VECTOR, svm_l2_bp_test }, + { NM_VECTOR, svm_l2_nm_test }, + { OF_VECTOR, svm_l2_of_test }, + { DB_VECTOR, svm_l2_db_test }, + { AC_VECTOR, svm_l2_ac_test, svm_ac_init, svm_ac_uninit }, +}; + +static u8 svm_exception_test_vector; + +static void svm_exception_handler(struct ex_regs *regs) +{ + report(regs->vector == svm_exception_test_vector, + "Handling %s in L2's exception handler", + exception_mnemonic(svm_exception_test_vector)); + vmmcall(); +} + +static void handle_exception_in_l2(u8 vector) +{ + handler old_handler = handle_exception(vector, svm_exception_handler); + svm_exception_test_vector = vector; + + report(svm_vmrun() == SVM_EXIT_VMMCALL, + "%s handled by L2", exception_mnemonic(vector)); + + handle_exception(vector, old_handler); +} + +static void handle_exception_in_l1(u32 vector) +{ + u32 old_ie = vmcb->control.intercept_exceptions; + + vmcb->control.intercept_exceptions |= (1ULL << vector); + + report(svm_vmrun() == (SVM_EXIT_EXCP_BASE + vector), + "%s handled by L1", exception_mnemonic(vector)); + + vmcb->control.intercept_exceptions = old_ie; +} + +static void svm_exception_test(void) +{ + struct svm_exception_test *t; + int i; + + for (i = 0; i < ARRAY_SIZE(svm_exception_tests); i++) { + t = &svm_exception_tests[i]; + test_set_guest(t->guest_code); + if (t->init_test) + t->init_test(); + + handle_exception_in_l2(t->vector); + + if (t->uninit_test) + t->uninit_test(); + + vmcb_ident(vmcb); + + if (t->init_test) + t->init_test(); + + handle_exception_in_l1(t->vector); + + if (t->uninit_test) + t->uninit_test(); + + vmcb_ident(vmcb); + } +} + struct svm_test svm_tests[] = { { "null", default_supported, default_prepare, default_prepare_gif_clear, null_test, @@ -3196,5 +3380,6 @@ struct svm_test svm_tests[] = { TEST(svm_nm_test), TEST(svm_int3_test), TEST(svm_into_test), + TEST(svm_exception_test), { NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; -- 2.30.2 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [kvm-unit-tests PATCH 3/3] x86: nSVM: Add an exception test framework and tests 2022-02-07 5:12 ` [kvm-unit-tests PATCH 3/3] x86: nSVM: Add an exception test framework and tests Manali Shukla @ 2022-02-14 20:20 ` Aaron Lewis 2022-02-17 3:26 ` Shukla, Manali 0 siblings, 1 reply; 13+ messages in thread From: Aaron Lewis @ 2022-02-14 20:20 UTC (permalink / raw) To: Manali Shukla; +Cc: pbonzini, kvm, seanjc > +static void svm_l2_nm_test(struct svm_test *svm) > +{ > + write_cr0(read_cr0() | X86_CR0_TS); > + asm volatile("fnop"); > +} > + > +static void svm_l2_of_test(struct svm_test *svm) > +{ > + struct far_pointer32 fp = { > + .offset = (uintptr_t)&&into, > + .selector = KERNEL_CS32, > + }; > + uintptr_t rsp; > + > + asm volatile ("mov %%rsp, %0" : "=r"(rsp)); > + > + if (fp.offset != (uintptr_t)&&into) { > + printf("Codee address too high.\n"); Nit: Code > + return; > + } > + > + if ((u32)rsp != rsp) { > + printf("Stack address too high.\n"); > + } > + > + asm goto("lcall *%0" : : "m" (fp) : "rax" : into); > + return; > +into: > + asm volatile (".code32;" > + "movl $0x7fffffff, %eax;" > + "addl %eax, %eax;" > + "into;" > + "lret;" > + ".code64"); > + __builtin_unreachable(); > +} > + > +static void svm_l2_ac_test(struct svm_test *test) > +{ > + bool hit_ac = false; > + > + write_cr0(read_cr0() | X86_CR0_AM); > + write_rflags(read_rflags() | X86_EFLAGS_AC); > + > + run_in_user(usermode_callback, AC_VECTOR, 0, 0, 0, 0, &hit_ac); > + > + report(hit_ac, "Usermode #AC handled in L2"); > + vmmcall(); > +} > + > +static void svm_ac_init(void) > +{ > + set_user_mask_all(phys_to_virt(read_cr3()), PAGE_LEVEL); > +} > + > +static void svm_ac_uninit(void) > +{ > + clear_user_mask_all(phys_to_virt(read_cr3()), PAGE_LEVEL); > +} > + > +struct svm_exception_test { > + u8 vector; > + void (*guest_code)(struct svm_test*); > + void (*init_test)(void); > + void (*uninit_test)(void); > +}; > + > +struct svm_exception_test svm_exception_tests[] = { > + { GP_VECTOR, svm_l2_gp_test }, > + { UD_VECTOR, svm_l2_ud_test }, > + { DE_VECTOR, svm_l2_de_test }, > + { BP_VECTOR, svm_l2_bp_test }, > + { NM_VECTOR, svm_l2_nm_test }, > + { OF_VECTOR, svm_l2_of_test }, > + { DB_VECTOR, svm_l2_db_test }, > + { AC_VECTOR, svm_l2_ac_test, svm_ac_init, svm_ac_uninit }, > +}; If you set and clear PT_USER_MASK in svm_l2_ac_test() before calling into userspace you can remove init_test and uninit_test from the framework all together. That will simplify the code. Further, it would be nice to then hoist this framework and the one in vmx into a common x86 file, but looking at this that may be something to think about in the future. There would have to be wrappers when interacting with the vmc{s,b} and macros at the very least. > + > +static u8 svm_exception_test_vector; > + > +static void svm_exception_handler(struct ex_regs *regs) > +{ > + report(regs->vector == svm_exception_test_vector, > + "Handling %s in L2's exception handler", > + exception_mnemonic(svm_exception_test_vector)); > + vmmcall(); > +} > + ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [kvm-unit-tests PATCH 3/3] x86: nSVM: Add an exception test framework and tests 2022-02-14 20:20 ` Aaron Lewis @ 2022-02-17 3:26 ` Shukla, Manali 2022-02-17 14:46 ` Aaron Lewis 0 siblings, 1 reply; 13+ messages in thread From: Shukla, Manali @ 2022-02-17 3:26 UTC (permalink / raw) To: Aaron Lewis, Manali Shukla; +Cc: pbonzini, kvm, seanjc On 2/15/2022 1:50 AM, Aaron Lewis wrote: >> +static void svm_l2_nm_test(struct svm_test *svm) >> +{ >> + write_cr0(read_cr0() | X86_CR0_TS); >> + asm volatile("fnop"); >> +} >> + >> +static void svm_l2_of_test(struct svm_test *svm) >> +{ >> + struct far_pointer32 fp = { >> + .offset = (uintptr_t)&&into, >> + .selector = KERNEL_CS32, >> + }; >> + uintptr_t rsp; >> + >> + asm volatile ("mov %%rsp, %0" : "=r"(rsp)); >> + >> + if (fp.offset != (uintptr_t)&&into) { >> + printf("Codee address too high.\n"); > > Nit: Code > >> + return; >> + } >> + >> + if ((u32)rsp != rsp) { >> + printf("Stack address too high.\n"); >> + } >> + >> + asm goto("lcall *%0" : : "m" (fp) : "rax" : into); >> + return; >> +into: >> + asm volatile (".code32;" >> + "movl $0x7fffffff, %eax;" >> + "addl %eax, %eax;" >> + "into;" >> + "lret;" >> + ".code64"); >> + __builtin_unreachable(); >> +} >> + > >> +static void svm_l2_ac_test(struct svm_test *test) >> +{ >> + bool hit_ac = false; >> + >> + write_cr0(read_cr0() | X86_CR0_AM); >> + write_rflags(read_rflags() | X86_EFLAGS_AC); >> + >> + run_in_user(usermode_callback, AC_VECTOR, 0, 0, 0, 0, &hit_ac); >> + >> + report(hit_ac, "Usermode #AC handled in L2"); >> + vmmcall(); >> +} >> + >> +static void svm_ac_init(void) >> +{ >> + set_user_mask_all(phys_to_virt(read_cr3()), PAGE_LEVEL); >> +} >> + >> +static void svm_ac_uninit(void) >> +{ >> + clear_user_mask_all(phys_to_virt(read_cr3()), PAGE_LEVEL); >> +} >> + >> +struct svm_exception_test { >> + u8 vector; >> + void (*guest_code)(struct svm_test*); >> + void (*init_test)(void); >> + void (*uninit_test)(void); >> +}; >> + >> +struct svm_exception_test svm_exception_tests[] = { >> + { GP_VECTOR, svm_l2_gp_test }, >> + { UD_VECTOR, svm_l2_ud_test }, >> + { DE_VECTOR, svm_l2_de_test }, >> + { BP_VECTOR, svm_l2_bp_test }, >> + { NM_VECTOR, svm_l2_nm_test }, >> + { OF_VECTOR, svm_l2_of_test }, >> + { DB_VECTOR, svm_l2_db_test }, >> + { AC_VECTOR, svm_l2_ac_test, svm_ac_init, svm_ac_uninit }, >> +}; > > If you set and clear PT_USER_MASK in svm_l2_ac_test() before calling > into userspace you can remove init_test and uninit_test from the > framework all together. That will simplify the code. > If clear user mask is called after userspace code, when #AC exception is intercepted by L1, the control directly goes to L1 and it does not reach clear_user_mask_all() function (called after user space code function run_in_user()). That is why I have added init_test and uninit_test function > Further, it would be nice to then hoist this framework and the one in > vmx into a common x86 file, but looking at this that may be something > to think about in the future. There would have to be wrappers when > interacting with the vmc{s,b} and macros at the very least. > Yeah we can think of this in future. >> + >> +static u8 svm_exception_test_vector; >> + >> +static void svm_exception_handler(struct ex_regs *regs) >> +{ >> + report(regs->vector == svm_exception_test_vector, >> + "Handling %s in L2's exception handler", >> + exception_mnemonic(svm_exception_test_vector)); >> + vmmcall(); >> +} >> + -Manali ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [kvm-unit-tests PATCH 3/3] x86: nSVM: Add an exception test framework and tests 2022-02-17 3:26 ` Shukla, Manali @ 2022-02-17 14:46 ` Aaron Lewis 0 siblings, 0 replies; 13+ messages in thread From: Aaron Lewis @ 2022-02-17 14:46 UTC (permalink / raw) To: Shukla, Manali; +Cc: Manali Shukla, pbonzini, kvm, seanjc > >> +}; > > > > If you set and clear PT_USER_MASK in svm_l2_ac_test() before calling > > into userspace you can remove init_test and uninit_test from the > > framework all together. That will simplify the code. > > > If clear user mask is called after userspace code, when #AC exception is > intercepted by L1, the control directly goes to L1 and it does not reach > clear_user_mask_all() function (called after user space code function run_in_user()). > > That is why I have added init_test and uninit_test function > Ah, that makes sense. Though IIUC, you are now moving the set/clear elsewhere, right? If so, it seems like init_test() and uninit_test() are no longer needed. > > Further, it would be nice to then hoist this framework and the one in > > vmx into a common x86 file, but looking at this that may be something > > to think about in the future. There would have to be wrappers when > > interacting with the vmc{s,b} and macros at the very least. > > ^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2022-02-20 5:35 UTC | newest] Thread overview: 13+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2022-02-07 5:11 [kvm-unit-tests PATCH 0/3] nSVM: Add testing for routing L2 exceptions Manali Shukla 2022-02-07 5:12 ` [kvm-unit-tests PATCH 1/3] x86: Add routines to set/clear PT_USER_MASK for all pages Manali Shukla 2022-02-14 19:30 ` Sean Christopherson 2022-02-17 3:55 ` Shukla, Manali 2022-02-17 14:34 ` Aaron Lewis 2022-02-20 4:42 ` Shukla, Manali 2022-02-17 15:20 ` Sean Christopherson 2022-02-20 5:35 ` Shukla, Manali 2022-02-07 5:12 ` [kvm-unit-tests PATCH 2/3] x86: Make exception_mnemonic() visible to the tests Manali Shukla 2022-02-07 5:12 ` [kvm-unit-tests PATCH 3/3] x86: nSVM: Add an exception test framework and tests Manali Shukla 2022-02-14 20:20 ` Aaron Lewis 2022-02-17 3:26 ` Shukla, Manali 2022-02-17 14:46 ` Aaron Lewis
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox