aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mm/init.c
diff options
context:
space:
mode:
authorKees Cook <keescook@chromium.org>2014-04-03 20:28:11 -0400
committerKees Cook <keescook@chromium.org>2014-10-16 17:38:54 -0400
commit1e6b48116a95046ec51f3d40f83aff8b006674d7 (patch)
tree1c18e08416613ef84513cb2cd52679e7af6d4d7c /arch/arm/mm/init.c
parent23a4e4050ba9c98ab67db0980a9fb20e5096d9ea (diff)
ARM: mm: allow non-text sections to be non-executable
Adds CONFIG_ARM_KERNMEM_PERMS to separate the kernel memory regions into section-sized areas that can have different permisions. Performs the NX permission changes during free_initmem, so that init memory can be reclaimed. This uses section size instead of PMD size to reduce memory lost to padding on non-LPAE systems. Based on work by Brad Spengler, Larry Bassel, and Laura Abbott. Signed-off-by: Kees Cook <keescook@chromium.org> Tested-by: Laura Abbott <lauraa@codeaurora.org> Acked-by: Nicolas Pitre <nico@linaro.org>
Diffstat (limited to 'arch/arm/mm/init.c')
-rw-r--r--arch/arm/mm/init.c101
1 files changed, 100 insertions, 1 deletions
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index ad82c05bfc3a..e6bfe76b2f59 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -29,6 +29,7 @@
29#include <asm/prom.h> 29#include <asm/prom.h>
30#include <asm/sections.h> 30#include <asm/sections.h>
31#include <asm/setup.h> 31#include <asm/setup.h>
32#include <asm/system_info.h>
32#include <asm/tlb.h> 33#include <asm/tlb.h>
33#include <asm/fixmap.h> 34#include <asm/fixmap.h>
34 35
@@ -615,7 +616,99 @@ void __init mem_init(void)
615 } 616 }
616} 617}
617 618
618void free_initmem(void) 619#ifdef CONFIG_ARM_KERNMEM_PERMS
620struct section_perm {
621 unsigned long start;
622 unsigned long end;
623 pmdval_t mask;
624 pmdval_t prot;
625};
626
627struct section_perm nx_perms[] = {
628 /* Make pages tables, etc before _stext RW (set NX). */
629 {
630 .start = PAGE_OFFSET,
631 .end = (unsigned long)_stext,
632 .mask = ~PMD_SECT_XN,
633 .prot = PMD_SECT_XN,
634 },
635 /* Make init RW (set NX). */
636 {
637 .start = (unsigned long)__init_begin,
638 .end = (unsigned long)_sdata,
639 .mask = ~PMD_SECT_XN,
640 .prot = PMD_SECT_XN,
641 },
642};
643
644/*
645 * Updates section permissions only for the current mm (sections are
646 * copied into each mm). During startup, this is the init_mm. Is only
647 * safe to be called with preemption disabled, as under stop_machine().
648 */
649static inline void section_update(unsigned long addr, pmdval_t mask,
650 pmdval_t prot)
651{
652 struct mm_struct *mm;
653 pmd_t *pmd;
654
655 mm = current->active_mm;
656 pmd = pmd_offset(pud_offset(pgd_offset(mm, addr), addr), addr);
657
658#ifdef CONFIG_ARM_LPAE
659 pmd[0] = __pmd((pmd_val(pmd[0]) & mask) | prot);
660#else
661 if (addr & SECTION_SIZE)
662 pmd[1] = __pmd((pmd_val(pmd[1]) & mask) | prot);
663 else
664 pmd[0] = __pmd((pmd_val(pmd[0]) & mask) | prot);
665#endif
666 flush_pmd_entry(pmd);
667 local_flush_tlb_kernel_range(addr, addr + SECTION_SIZE);
668}
669
670/* Make sure extended page tables are in use. */
671static inline bool arch_has_strict_perms(void)
672{
673 if (cpu_architecture() < CPU_ARCH_ARMv6)
674 return false;
675
676 return !!(get_cr() & CR_XP);
677}
678
679#define set_section_perms(perms, field) { \
680 size_t i; \
681 unsigned long addr; \
682 \
683 if (!arch_has_strict_perms()) \
684 return; \
685 \
686 for (i = 0; i < ARRAY_SIZE(perms); i++) { \
687 if (!IS_ALIGNED(perms[i].start, SECTION_SIZE) || \
688 !IS_ALIGNED(perms[i].end, SECTION_SIZE)) { \
689 pr_err("BUG: section %lx-%lx not aligned to %lx\n", \
690 perms[i].start, perms[i].end, \
691 SECTION_SIZE); \
692 continue; \
693 } \
694 \
695 for (addr = perms[i].start; \
696 addr < perms[i].end; \
697 addr += SECTION_SIZE) \
698 section_update(addr, perms[i].mask, \
699 perms[i].field); \
700 } \
701}
702
703static inline void fix_kernmem_perms(void)
704{
705 set_section_perms(nx_perms, prot);
706}
707#else
708static inline void fix_kernmem_perms(void) { }
709#endif /* CONFIG_ARM_KERNMEM_PERMS */
710
711void free_tcmmem(void)
619{ 712{
620#ifdef CONFIG_HAVE_TCM 713#ifdef CONFIG_HAVE_TCM
621 extern char __tcm_start, __tcm_end; 714 extern char __tcm_start, __tcm_end;
@@ -623,6 +716,12 @@ void free_initmem(void)
623 poison_init_mem(&__tcm_start, &__tcm_end - &__tcm_start); 716 poison_init_mem(&__tcm_start, &__tcm_end - &__tcm_start);
624 free_reserved_area(&__tcm_start, &__tcm_end, -1, "TCM link"); 717 free_reserved_area(&__tcm_start, &__tcm_end, -1, "TCM link");
625#endif 718#endif
719}
720
721void free_initmem(void)
722{
723 fix_kernmem_perms();
724 free_tcmmem();
626 725
627 poison_init_mem(__init_begin, __init_end - __init_begin); 726 poison_init_mem(__init_begin, __init_end - __init_begin);
628 if (!machine_is_integrator() && !machine_is_cintegrator()) 727 if (!machine_is_integrator() && !machine_is_cintegrator())