Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86-64: align RODATA kernel section to 2MB with CONFIG_DEBUG_RODATA

CONFIG_DEBUG_RODATA chops the large pages spanning boundaries of kernel
text/rodata/data to small 4KB pages as they are mapped with different
attributes (text as RO, RODATA as RO and NX etc).

On x86_64, preserve the large page mappings for kernel text/rodata/data
boundaries when CONFIG_DEBUG_RODATA is enabled. This is done by allowing the
RODATA section to be hugepage aligned and having same RWX attributes
for the 2MB page boundaries

Extra Memory pages padding the sections will be freed during the end of the boot
and the kernel identity mappings will have different RWX permissions compared to
the kernel text mappings.

Kernel identity mappings to these physical pages will be mapped with smaller
pages but large page mappings are still retained for kernel text,rodata,data
mappings.

Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
LKML-Reference: <20091014220254.190119924@sbs-t61.sc.intel.com>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>

authored by

Suresh Siddha and committed by
H. Peter Anvin
74e08179 b9af7c0d

+50 -1
+6
arch/x86/include/asm/sections.h
··· 2 2 #define _ASM_X86_SECTIONS_H 3 3 4 4 #include <asm-generic/sections.h> 5 + #include <asm/uaccess.h> 5 6 6 7 extern char __brk_base[], __brk_limit[]; 8 + extern struct exception_table_entry __stop___ex_table[]; 9 + 10 + #if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA) 11 + extern char __end_rodata_hpage_align[]; 12 + #endif 7 13 8 14 #endif /* _ASM_X86_SECTIONS_H */
+17
arch/x86/kernel/vmlinux.lds.S
··· 41 41 jiffies_64 = jiffies; 42 42 #endif 43 43 44 + #if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA) 45 + 46 + #define X64_ALIGN_DEBUG_RODATA_BEGIN . = ALIGN(HPAGE_SIZE); 47 + 48 + #define X64_ALIGN_DEBUG_RODATA_END \ 49 + . = ALIGN(HPAGE_SIZE); \ 50 + __end_rodata_hpage_align = .; 51 + 52 + #else 53 + 54 + #define X64_ALIGN_DEBUG_RODATA_BEGIN 55 + #define X64_ALIGN_DEBUG_RODATA_END 56 + 57 + #endif 58 + 44 59 PHDRS { 45 60 text PT_LOAD FLAGS(5); /* R_E */ 46 61 data PT_LOAD FLAGS(7); /* RWE */ ··· 105 90 106 91 EXCEPTION_TABLE(16) :text = 0x9090 107 92 93 + X64_ALIGN_DEBUG_RODATA_BEGIN 108 94 RO_DATA(PAGE_SIZE) 95 + X64_ALIGN_DEBUG_RODATA_END 109 96 110 97 /* Data */ 111 98 .data : AT(ADDR(.data) - LOAD_OFFSET) {
+13 -1
arch/x86/mm/init_64.c
··· 727 727 728 728 void mark_rodata_ro(void) 729 729 { 730 - unsigned long start = PFN_ALIGN(_text), end = PFN_ALIGN(__end_rodata); 730 + unsigned long start = PFN_ALIGN(_text); 731 731 unsigned long rodata_start = 732 732 ((unsigned long)__start_rodata + PAGE_SIZE - 1) & PAGE_MASK; 733 + unsigned long end = (unsigned long) &__end_rodata_hpage_align; 734 + unsigned long text_end = PAGE_ALIGN((unsigned long) &__stop___ex_table); 735 + unsigned long rodata_end = PAGE_ALIGN((unsigned long) &__end_rodata); 736 + unsigned long data_start = (unsigned long) &_sdata; 733 737 734 738 printk(KERN_INFO "Write protecting the kernel read-only data: %luk\n", 735 739 (end - start) >> 10); ··· 756 752 printk(KERN_INFO "Testing CPA: again\n"); 757 753 set_memory_ro(start, (end-start) >> PAGE_SHIFT); 758 754 #endif 755 + 756 + free_init_pages("unused kernel memory", 757 + (unsigned long) page_address(virt_to_page(text_end)), 758 + (unsigned long) 759 + page_address(virt_to_page(rodata_start))); 760 + free_init_pages("unused kernel memory", 761 + (unsigned long) page_address(virt_to_page(rodata_end)), 762 + (unsigned long) page_address(virt_to_page(data_start))); 759 763 } 760 764 761 765 #endif
+14
arch/x86/mm/pageattr.c
··· 279 279 __pa((unsigned long)__end_rodata) >> PAGE_SHIFT)) 280 280 pgprot_val(forbidden) |= _PAGE_RW; 281 281 282 + #if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA) 283 + /* 284 + * Kernel text mappings for the large page aligned .rodata section 285 + * will be read-only. For the kernel identity mappings covering 286 + * the holes caused by this alignment can be anything. 287 + * 288 + * This will preserve the large page mappings for kernel text/data 289 + * at no extra cost. 290 + */ 291 + if (within(address, (unsigned long)_text, 292 + (unsigned long)__end_rodata_hpage_align)) 293 + pgprot_val(forbidden) |= _PAGE_RW; 294 + #endif 295 + 282 296 prot = __pgprot(pgprot_val(prot) & ~pgprot_val(forbidden)); 283 297 284 298 return prot;