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@xxxxxxx> --- 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