Merge tag 'x86_mm_for_6.2_v2' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
[linux-2.6-microblaze.git] / arch / x86 / mm / pat / set_memory.c
index ef34ba2..356758b 100644 (file)
@@ -220,6 +220,23 @@ within_inclusive(unsigned long addr, unsigned long start, unsigned long end)
 
 #ifdef CONFIG_X86_64
 
+/*
+ * The kernel image is mapped into two places in the virtual address space
+ * (addresses without KASLR, of course):
+ *
+ * 1. The kernel direct map (0xffff880000000000)
+ * 2. The "high kernel map" (0xffffffff81000000)
+ *
+ * We actually execute out of #2. If we get the address of a kernel symbol, it
+ * points to #2, but almost all physical-to-virtual translations point to #1.
+ *
+ * This is so that we can have both a directmap of all physical memory *and*
+ * take full advantage of the the limited (s32) immediate addressing range (2G)
+ * of x86_64.
+ *
+ * See Documentation/x86/x86_64/mm.rst for more detail.
+ */
+
 static inline unsigned long highmap_start_pfn(void)
 {
        return __pa_symbol(_text) >> PAGE_SHIFT;
@@ -605,10 +622,6 @@ static inline pgprot_t verify_rwx(pgprot_t old, pgprot_t new, unsigned long star
 {
        unsigned long end;
 
-       /* Kernel text is rw at boot up */
-       if (system_state == SYSTEM_BOOTING)
-               return new;
-
        /*
         * 32-bit has some unfixable W+X issues, like EFI code
         * and writeable data being in the same page.  Disable
@@ -765,11 +778,11 @@ phys_addr_t slow_virt_to_phys(void *__virt_addr)
        switch (level) {
        case PG_LEVEL_1G:
                phys_addr = (phys_addr_t)pud_pfn(*(pud_t *)pte) << PAGE_SHIFT;
-               offset = virt_addr & ~PUD_PAGE_MASK;
+               offset = virt_addr & ~PUD_MASK;
                break;
        case PG_LEVEL_2M:
                phys_addr = (phys_addr_t)pmd_pfn(*(pmd_t *)pte) << PAGE_SHIFT;
-               offset = virt_addr & ~PMD_PAGE_MASK;
+               offset = virt_addr & ~PMD_MASK;
                break;
        default:
                phys_addr = (phys_addr_t)pte_pfn(*pte) << PAGE_SHIFT;
@@ -1059,7 +1072,7 @@ __split_large_page(struct cpa_data *cpa, pte_t *kpte, unsigned long address,
        case PG_LEVEL_1G:
                ref_prot = pud_pgprot(*(pud_t *)kpte);
                ref_pfn = pud_pfn(*(pud_t *)kpte);
-               pfninc = PMD_PAGE_SIZE >> PAGE_SHIFT;
+               pfninc = PMD_SIZE >> PAGE_SHIFT;
                lpaddr = address & PUD_MASK;
                lpinc = PMD_SIZE;
                /*
@@ -1646,8 +1659,11 @@ repeat:
        return err;
 }
 
-static int __change_page_attr_set_clr(struct cpa_data *cpa, int checkalias);
+static int __change_page_attr_set_clr(struct cpa_data *cpa, int primary);
 
+/*
+ * Check the directmap and "high kernel map" 'aliases'.
+ */
 static int cpa_process_alias(struct cpa_data *cpa)
 {
        struct cpa_data alias_cpa;
@@ -1671,6 +1687,12 @@ static int cpa_process_alias(struct cpa_data *cpa)
                alias_cpa.flags &= ~(CPA_PAGES_ARRAY | CPA_ARRAY);
                alias_cpa.curpage = 0;
 
+               /* Directmap always has NX set, do not modify. */
+               if (__supported_pte_mask & _PAGE_NX) {
+                       alias_cpa.mask_clr.pgprot &= ~_PAGE_NX;
+                       alias_cpa.mask_set.pgprot &= ~_PAGE_NX;
+               }
+
                cpa->force_flush_all = 1;
 
                ret = __change_page_attr_set_clr(&alias_cpa, 0);
@@ -1693,6 +1715,15 @@ static int cpa_process_alias(struct cpa_data *cpa)
                alias_cpa.flags &= ~(CPA_PAGES_ARRAY | CPA_ARRAY);
                alias_cpa.curpage = 0;
 
+               /*
+                * [_text, _brk_end) also covers data, do not modify NX except
+                * in cases where the highmap is the primary target.
+                */
+               if (__supported_pte_mask & _PAGE_NX) {
+                       alias_cpa.mask_clr.pgprot &= ~_PAGE_NX;
+                       alias_cpa.mask_set.pgprot &= ~_PAGE_NX;
+               }
+
                cpa->force_flush_all = 1;
                /*
                 * The high mapping range is imprecise, so ignore the
@@ -1705,12 +1736,19 @@ static int cpa_process_alias(struct cpa_data *cpa)
        return 0;
 }
 
-static int __change_page_attr_set_clr(struct cpa_data *cpa, int checkalias)
+static int __change_page_attr_set_clr(struct cpa_data *cpa, int primary)
 {
        unsigned long numpages = cpa->numpages;
        unsigned long rempages = numpages;
        int ret = 0;
 
+       /*
+        * No changes, easy!
+        */
+       if (!(pgprot_val(cpa->mask_set) | pgprot_val(cpa->mask_clr)) &&
+           !cpa->force_split)
+               return ret;
+
        while (rempages) {
                /*
                 * Store the remaining nr of pages for the large page
@@ -1723,13 +1761,13 @@ static int __change_page_attr_set_clr(struct cpa_data *cpa, int checkalias)
 
                if (!debug_pagealloc_enabled())
                        spin_lock(&cpa_lock);
-               ret = __change_page_attr(cpa, checkalias);
+               ret = __change_page_attr(cpa, primary);
                if (!debug_pagealloc_enabled())
                        spin_unlock(&cpa_lock);
                if (ret)
                        goto out;
 
-               if (checkalias) {
+               if (primary && !(cpa->flags & CPA_NO_CHECK_ALIAS)) {
                        ret = cpa_process_alias(cpa);
                        if (ret)
                                goto out;
@@ -1757,7 +1795,7 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages,
                                    struct page **pages)
 {
        struct cpa_data cpa;
-       int ret, cache, checkalias;
+       int ret, cache;
 
        memset(&cpa, 0, sizeof(cpa));
 
@@ -1803,20 +1841,11 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages,
        cpa.numpages = numpages;
        cpa.mask_set = mask_set;
        cpa.mask_clr = mask_clr;
-       cpa.flags = 0;
+       cpa.flags = in_flag;
        cpa.curpage = 0;
        cpa.force_split = force_split;
 
-       if (in_flag & (CPA_ARRAY | CPA_PAGES_ARRAY))
-               cpa.flags |= in_flag;
-
-       /* No alias checking for _NX bit modifications */
-       checkalias = (pgprot_val(mask_set) | pgprot_val(mask_clr)) != _PAGE_NX;
-       /* Has caller explicitly disabled alias checking? */
-       if (in_flag & CPA_NO_CHECK_ALIAS)
-               checkalias = 0;
-
-       ret = __change_page_attr_set_clr(&cpa, checkalias);
+       ret = __change_page_attr_set_clr(&cpa, 1);
 
        /*
         * Check whether we really changed something:
@@ -2047,6 +2076,16 @@ int set_memory_ro(unsigned long addr, int numpages)
        return change_page_attr_clear(&addr, numpages, __pgprot(_PAGE_RW), 0);
 }
 
+int set_memory_rox(unsigned long addr, int numpages)
+{
+       pgprot_t clr = __pgprot(_PAGE_RW);
+
+       if (__supported_pte_mask & _PAGE_NX)
+               clr.pgprot |= _PAGE_NX;
+
+       return change_page_attr_clear(&addr, numpages, clr, 0);
+}
+
 int set_memory_rw(unsigned long addr, int numpages)
 {
        return change_page_attr_set(&addr, numpages, __pgprot(_PAGE_RW), 0);
@@ -2059,11 +2098,9 @@ int set_memory_np(unsigned long addr, int numpages)
 
 int set_memory_np_noalias(unsigned long addr, int numpages)
 {
-       int cpa_flags = CPA_NO_CHECK_ALIAS;
-
        return change_page_attr_set_clr(&addr, numpages, __pgprot(0),
                                        __pgprot(_PAGE_PRESENT), 0,
-                                       cpa_flags, NULL);
+                                       CPA_NO_CHECK_ALIAS, NULL);
 }
 
 int set_memory_4k(unsigned long addr, int numpages)
@@ -2280,7 +2317,7 @@ static int __set_pages_p(struct page *page, int numpages)
                                .numpages = numpages,
                                .mask_set = __pgprot(_PAGE_PRESENT | _PAGE_RW),
                                .mask_clr = __pgprot(0),
-                               .flags = 0};
+                               .flags = CPA_NO_CHECK_ALIAS };
 
        /*
         * No alias checking needed for setting present flag. otherwise,
@@ -2288,7 +2325,7 @@ static int __set_pages_p(struct page *page, int numpages)
         * mappings (this adds to complexity if we want to do this from
         * atomic context especially). Let's keep it simple!
         */
-       return __change_page_attr_set_clr(&cpa, 0);
+       return __change_page_attr_set_clr(&cpa, 1);
 }
 
 static int __set_pages_np(struct page *page, int numpages)
@@ -2299,7 +2336,7 @@ static int __set_pages_np(struct page *page, int numpages)
                                .numpages = numpages,
                                .mask_set = __pgprot(0),
                                .mask_clr = __pgprot(_PAGE_PRESENT | _PAGE_RW),
-                               .flags = 0};
+                               .flags = CPA_NO_CHECK_ALIAS };
 
        /*
         * No alias checking needed for setting not present flag. otherwise,
@@ -2307,7 +2344,7 @@ static int __set_pages_np(struct page *page, int numpages)
         * mappings (this adds to complexity if we want to do this from
         * atomic context especially). Let's keep it simple!
         */
-       return __change_page_attr_set_clr(&cpa, 0);
+       return __change_page_attr_set_clr(&cpa, 1);
 }
 
 int set_direct_map_invalid_noflush(struct page *page)
@@ -2378,7 +2415,7 @@ int __init kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address,
                .numpages = numpages,
                .mask_set = __pgprot(0),
                .mask_clr = __pgprot(~page_flags & (_PAGE_NX|_PAGE_RW)),
-               .flags = 0,
+               .flags = CPA_NO_CHECK_ALIAS,
        };
 
        WARN_ONCE(num_online_cpus() > 1, "Don't call after initializing SMP");
@@ -2391,7 +2428,7 @@ int __init kernel_map_pages_in_pgd(pgd_t *pgd, u64 pfn, unsigned long address,
 
        cpa.mask_set = __pgprot(_PAGE_PRESENT | page_flags);
 
-       retval = __change_page_attr_set_clr(&cpa, 0);
+       retval = __change_page_attr_set_clr(&cpa, 1);
        __flush_tlb_all();
 
 out:
@@ -2421,12 +2458,12 @@ int __init kernel_unmap_pages_in_pgd(pgd_t *pgd, unsigned long address,
                .numpages       = numpages,
                .mask_set       = __pgprot(0),
                .mask_clr       = __pgprot(_PAGE_PRESENT | _PAGE_RW),
-               .flags          = 0,
+               .flags          = CPA_NO_CHECK_ALIAS,
        };
 
        WARN_ONCE(num_online_cpus() > 1, "Don't call after initializing SMP");
 
-       retval = __change_page_attr_set_clr(&cpa, 0);
+       retval = __change_page_attr_set_clr(&cpa, 1);
        __flush_tlb_all();
 
        return retval;