From: Arjan van de Ven Bug fix required for the .rodata work on x86-64: when change_page_attr() and friends need to break up a 2Mb page into 4Kb pages, it always set the NX bit on the PMD, which causes the cpu to consider the entire 2Mb region to be NX regardless of the actual PTE perms. This is fine in general, with one big exception: the 2Mb page that covers the last part of the kernel .text! The fix is to not invent a new permission for the new PMD entry, but to just inherit the existing one minus the PSE bit. Signed-off-by: Arjan van de Ven Signed-off-by: Ingo Molnar Cc: Andi Kleen Signed-off-by: Andrew Morton --- arch/x86_64/mm/pageattr.c | 9 +++++++-- include/asm-x86_64/pgtable.h | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff -puN arch/x86_64/mm/pageattr.c~mark-rodata-section-read-only-generic-x86-64-bugfix arch/x86_64/mm/pageattr.c --- devel/arch/x86_64/mm/pageattr.c~mark-rodata-section-read-only-generic-x86-64-bugfix 2005-11-10 02:19:42.000000000 -0800 +++ devel-akpm/arch/x86_64/mm/pageattr.c 2005-11-10 02:19:42.000000000 -0800 @@ -128,6 +128,7 @@ __change_page_attr(unsigned long address pte_t *kpte; struct page *kpte_page; unsigned kpte_flags; + pgprot_t ref_prot2; kpte = lookup_address(address); if (!kpte) return 0; kpte_page = virt_to_page(((unsigned long)kpte) & PAGE_MASK); @@ -140,10 +141,14 @@ __change_page_attr(unsigned long address * split_large_page will take the reference for this change_page_attr * on the split page. */ - struct page *split = split_large_page(address, prot, ref_prot); + + struct page *split; + ref_prot2 = __pgprot(pgprot_val(pte_pgprot(*lookup_address(address))) & ~(1<<_PAGE_BIT_PSE)); + + split = split_large_page(address, prot, ref_prot2); if (!split) return -ENOMEM; - set_pte(kpte,mk_pte(split, ref_prot)); + set_pte(kpte,mk_pte(split, ref_prot2)); kpte_page = split; } get_page(kpte_page); diff -puN include/asm-x86_64/pgtable.h~mark-rodata-section-read-only-generic-x86-64-bugfix include/asm-x86_64/pgtable.h --- devel/include/asm-x86_64/pgtable.h~mark-rodata-section-read-only-generic-x86-64-bugfix 2005-11-10 02:19:42.000000000 -0800 +++ devel-akpm/include/asm-x86_64/pgtable.h 2005-11-10 02:19:42.000000000 -0800 @@ -122,6 +122,8 @@ static inline pte_t ptep_get_and_clear_f #define pte_same(a, b) ((a).pte == (b).pte) +#define pte_pgprot(a) (__pgprot((a).pte & ~PHYSICAL_PAGE_MASK)) + #define PMD_SIZE (1UL << PMD_SHIFT) #define PMD_MASK (~(PMD_SIZE-1)) #define PUD_SIZE (1UL << PUD_SHIFT) _