Skip to content

Commit

Permalink
x86-64: align RODATA kernel section to 2MB with CONFIG_DEBUG_RODATA
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Suresh Siddha authored and H. Peter Anvin committed Oct 20, 2009
1 parent b9af7c0 commit 74e0817
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 1 deletion.
6 changes: 6 additions & 0 deletions arch/x86/include/asm/sections.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
#define _ASM_X86_SECTIONS_H

#include <asm-generic/sections.h>
#include <asm/uaccess.h>

extern char __brk_base[], __brk_limit[];
extern struct exception_table_entry __stop___ex_table[];

#if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA)
extern char __end_rodata_hpage_align[];
#endif

#endif /* _ASM_X86_SECTIONS_H */
17 changes: 17 additions & 0 deletions arch/x86/kernel/vmlinux.lds.S
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,21 @@ ENTRY(phys_startup_64)
jiffies_64 = jiffies;
#endif

#if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA)

#define X64_ALIGN_DEBUG_RODATA_BEGIN . = ALIGN(HPAGE_SIZE);

#define X64_ALIGN_DEBUG_RODATA_END \
. = ALIGN(HPAGE_SIZE); \
__end_rodata_hpage_align = .;

#else

#define X64_ALIGN_DEBUG_RODATA_BEGIN
#define X64_ALIGN_DEBUG_RODATA_END

#endif

PHDRS {
text PT_LOAD FLAGS(5); /* R_E */
data PT_LOAD FLAGS(7); /* RWE */
Expand Down Expand Up @@ -90,7 +105,9 @@ SECTIONS

EXCEPTION_TABLE(16) :text = 0x9090

X64_ALIGN_DEBUG_RODATA_BEGIN
RO_DATA(PAGE_SIZE)
X64_ALIGN_DEBUG_RODATA_END

/* Data */
.data : AT(ADDR(.data) - LOAD_OFFSET) {
Expand Down
14 changes: 13 additions & 1 deletion arch/x86/mm/init_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -727,9 +727,13 @@ void set_kernel_text_ro(void)

void mark_rodata_ro(void)
{
unsigned long start = PFN_ALIGN(_text), end = PFN_ALIGN(__end_rodata);
unsigned long start = PFN_ALIGN(_text);
unsigned long rodata_start =
((unsigned long)__start_rodata + PAGE_SIZE - 1) & PAGE_MASK;
unsigned long end = (unsigned long) &__end_rodata_hpage_align;
unsigned long text_end = PAGE_ALIGN((unsigned long) &__stop___ex_table);
unsigned long rodata_end = PAGE_ALIGN((unsigned long) &__end_rodata);
unsigned long data_start = (unsigned long) &_sdata;

printk(KERN_INFO "Write protecting the kernel read-only data: %luk\n",
(end - start) >> 10);
Expand All @@ -752,6 +756,14 @@ void mark_rodata_ro(void)
printk(KERN_INFO "Testing CPA: again\n");
set_memory_ro(start, (end-start) >> PAGE_SHIFT);
#endif

free_init_pages("unused kernel memory",
(unsigned long) page_address(virt_to_page(text_end)),
(unsigned long)
page_address(virt_to_page(rodata_start)));
free_init_pages("unused kernel memory",
(unsigned long) page_address(virt_to_page(rodata_end)),
(unsigned long) page_address(virt_to_page(data_start)));
}

#endif
Expand Down
14 changes: 14 additions & 0 deletions arch/x86/mm/pageattr.c
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,20 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long address,
__pa((unsigned long)__end_rodata) >> PAGE_SHIFT))
pgprot_val(forbidden) |= _PAGE_RW;

#if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA)
/*
* Kernel text mappings for the large page aligned .rodata section
* will be read-only. For the kernel identity mappings covering
* the holes caused by this alignment can be anything.
*
* This will preserve the large page mappings for kernel text/data
* at no extra cost.
*/
if (within(address, (unsigned long)_text,
(unsigned long)__end_rodata_hpage_align))
pgprot_val(forbidden) |= _PAGE_RW;
#endif

prot = __pgprot(pgprot_val(prot) & ~pgprot_val(forbidden));

return prot;
Expand Down

0 comments on commit 74e0817

Please sign in to comment.