diff options
-rw-r--r-- | arch/x86/mm/pageattr.c | 53 |
1 files changed, 46 insertions, 7 deletions
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index db8ace29514f..b3b19f46c016 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c | |||
@@ -453,7 +453,7 @@ try_preserve_large_page(pte_t *kpte, unsigned long address, | |||
453 | * Check for races, another CPU might have split this page | 453 | * Check for races, another CPU might have split this page |
454 | * up already: | 454 | * up already: |
455 | */ | 455 | */ |
456 | tmp = lookup_address(address, &level); | 456 | tmp = _lookup_address_cpa(cpa, address, &level); |
457 | if (tmp != kpte) | 457 | if (tmp != kpte) |
458 | goto out_unlock; | 458 | goto out_unlock; |
459 | 459 | ||
@@ -559,7 +559,8 @@ out_unlock: | |||
559 | } | 559 | } |
560 | 560 | ||
561 | static int | 561 | static int |
562 | __split_large_page(pte_t *kpte, unsigned long address, struct page *base) | 562 | __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address, |
563 | struct page *base) | ||
563 | { | 564 | { |
564 | pte_t *pbase = (pte_t *)page_address(base); | 565 | pte_t *pbase = (pte_t *)page_address(base); |
565 | unsigned long pfn, pfninc = 1; | 566 | unsigned long pfn, pfninc = 1; |
@@ -572,7 +573,7 @@ __split_large_page(pte_t *kpte, unsigned long address, struct page *base) | |||
572 | * Check for races, another CPU might have split this page | 573 | * Check for races, another CPU might have split this page |
573 | * up for us already: | 574 | * up for us already: |
574 | */ | 575 | */ |
575 | tmp = lookup_address(address, &level); | 576 | tmp = _lookup_address_cpa(cpa, address, &level); |
576 | if (tmp != kpte) { | 577 | if (tmp != kpte) { |
577 | spin_unlock(&pgd_lock); | 578 | spin_unlock(&pgd_lock); |
578 | return 1; | 579 | return 1; |
@@ -648,7 +649,8 @@ __split_large_page(pte_t *kpte, unsigned long address, struct page *base) | |||
648 | return 0; | 649 | return 0; |
649 | } | 650 | } |
650 | 651 | ||
651 | static int split_large_page(pte_t *kpte, unsigned long address) | 652 | static int split_large_page(struct cpa_data *cpa, pte_t *kpte, |
653 | unsigned long address) | ||
652 | { | 654 | { |
653 | struct page *base; | 655 | struct page *base; |
654 | 656 | ||
@@ -660,7 +662,7 @@ static int split_large_page(pte_t *kpte, unsigned long address) | |||
660 | if (!base) | 662 | if (!base) |
661 | return -ENOMEM; | 663 | return -ENOMEM; |
662 | 664 | ||
663 | if (__split_large_page(kpte, address, base)) | 665 | if (__split_large_page(cpa, kpte, address, base)) |
664 | __free_page(base); | 666 | __free_page(base); |
665 | 667 | ||
666 | return 0; | 668 | return 0; |
@@ -1041,6 +1043,9 @@ static int populate_pgd(struct cpa_data *cpa, unsigned long addr) | |||
1041 | static int __cpa_process_fault(struct cpa_data *cpa, unsigned long vaddr, | 1043 | static int __cpa_process_fault(struct cpa_data *cpa, unsigned long vaddr, |
1042 | int primary) | 1044 | int primary) |
1043 | { | 1045 | { |
1046 | if (cpa->pgd) | ||
1047 | return populate_pgd(cpa, vaddr); | ||
1048 | |||
1044 | /* | 1049 | /* |
1045 | * Ignore all non primary paths. | 1050 | * Ignore all non primary paths. |
1046 | */ | 1051 | */ |
@@ -1085,7 +1090,7 @@ static int __change_page_attr(struct cpa_data *cpa, int primary) | |||
1085 | else | 1090 | else |
1086 | address = *cpa->vaddr; | 1091 | address = *cpa->vaddr; |
1087 | repeat: | 1092 | repeat: |
1088 | kpte = lookup_address(address, &level); | 1093 | kpte = _lookup_address_cpa(cpa, address, &level); |
1089 | if (!kpte) | 1094 | if (!kpte) |
1090 | return __cpa_process_fault(cpa, address, primary); | 1095 | return __cpa_process_fault(cpa, address, primary); |
1091 | 1096 | ||
@@ -1149,7 +1154,7 @@ repeat: | |||
1149 | /* | 1154 | /* |
1150 | * We have to split the large page: | 1155 | * We have to split the large page: |
1151 | */ | 1156 | */ |
1152 | err = split_large_page(kpte, address); | 1157 | err = split_large_page(cpa, kpte, address); |
1153 | if (!err) { | 1158 | if (!err) { |
1154 | /* | 1159 | /* |
1155 | * Do a global flush tlb after splitting the large page | 1160 | * Do a global flush tlb after splitting the large page |
@@ -1298,6 +1303,8 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages, | |||
1298 | int ret, cache, checkalias; | 1303 | int ret, cache, checkalias; |
1299 | unsigned long baddr = 0; | 1304 | unsigned long baddr = 0; |
1300 | 1305 | ||
1306 | memset(&cpa, 0, sizeof(cpa)); | ||
1307 | |||
1301 | /* | 1308 | /* |
1302 | * Check, if we are requested to change a not supported | 1309 | * Check, if we are requested to change a not supported |
1303 | * feature: | 1310 | * feature: |
@@ -1744,6 +1751,7 @@ static int __set_pages_p(struct page *page, int numpages) | |||
1744 | { | 1751 | { |
1745 | unsigned long tempaddr = (unsigned long) page_address(page); | 1752 | unsigned long tempaddr = (unsigned long) page_address(page); |
1746 | struct cpa_data cpa = { .vaddr = &tempaddr, | 1753 | struct cpa_data cpa = { .vaddr = &tempaddr, |
1754 | .pgd = NULL, | ||
1747 | .numpages = numpages, | 1755 | .numpages = numpages, |
1748 | .mask_set = __pgprot(_PAGE_PRESENT | _PAGE_RW), | 1756 | .mask_set = __pgprot(_PAGE_PRESENT | _PAGE_RW), |
1749 | .mask_clr = __pgprot(0), | 1757 | .mask_clr = __pgprot(0), |
@@ -1762,6 +1770,7 @@ static int __set_pages_np(struct page *page, int numpages) | |||
1762 | { | 1770 | { |
1763 | unsigned long tempaddr = (unsigned long) page_address(page); | 1771 | unsigned long tempaddr = (unsigned long) page_address(page); |
1764 | struct cpa_data cpa = { .vaddr = &tempaddr, | 1772 | struct cpa_data cpa = { .vaddr = &tempaddr, |
1773 | .pgd = NULL, | ||
1765 | .numpages = numpages, | 1774 | .numpages = numpages, |
1766 | .mask_set = __pgprot(0), | 1775 | .mask_set = __pgprot(0), |
1767 | .mask_clr = __pgprot(_PAGE_PRESENT | _PAGE_RW), | 1776 | .mask_clr = __pgprot(_PAGE_PRESENT | _PAGE_RW), |
@@ -1822,6 +1831,36 @@ bool kernel_page_present(struct page *page) | |||
1822 | 1831 | ||
1823 | #endif /* CONFIG_DEBUG_PAGEALLOC */ | 1832 | #endif /* CONFIG_DEBUG_PAGEALLOC */ |
1824 | 1833 | ||
1834 | int kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address, | ||
1835 | unsigned numpages, unsigned long page_flags) | ||
1836 | { | ||
1837 | int retval = -EINVAL; | ||
1838 | |||
1839 | struct cpa_data cpa = { | ||
1840 | .vaddr = &address, | ||
1841 | .pfn = pfn, | ||
1842 | .pgd = pgd, | ||
1843 | .numpages = numpages, | ||
1844 | .mask_set = __pgprot(0), | ||
1845 | .mask_clr = __pgprot(0), | ||
1846 | .flags = 0, | ||
1847 | }; | ||
1848 | |||
1849 | if (!(__supported_pte_mask & _PAGE_NX)) | ||
1850 | goto out; | ||
1851 | |||
1852 | if (!(page_flags & _PAGE_NX)) | ||
1853 | cpa.mask_clr = __pgprot(_PAGE_NX); | ||
1854 | |||
1855 | cpa.mask_set = __pgprot(_PAGE_PRESENT | page_flags); | ||
1856 | |||
1857 | retval = __change_page_attr_set_clr(&cpa, 0); | ||
1858 | __flush_tlb_all(); | ||
1859 | |||
1860 | out: | ||
1861 | return retval; | ||
1862 | } | ||
1863 | |||
1825 | /* | 1864 | /* |
1826 | * The testcases use internal knowledge of the implementation that shouldn't | 1865 | * The testcases use internal knowledge of the implementation that shouldn't |
1827 | * be exposed to the rest of the kernel. Include these directly here. | 1866 | * be exposed to the rest of the kernel. Include these directly here. |