|
29 | 29 | #include <asm/prom.h> |
30 | 30 | #include <asm/sections.h> |
31 | 31 | #include <asm/setup.h> |
| 32 | +#include <asm/system_info.h> |
32 | 33 | #include <asm/tlb.h> |
33 | 34 | #include <asm/fixmap.h> |
34 | 35 |
|
@@ -615,14 +616,112 @@ void __init mem_init(void) |
615 | 616 | } |
616 | 617 | } |
617 | 618 |
|
618 | | -void free_initmem(void) |
| 619 | +#ifdef CONFIG_ARM_KERNMEM_PERMS |
| 620 | +struct section_perm { |
| 621 | + unsigned long start; |
| 622 | + unsigned long end; |
| 623 | + pmdval_t mask; |
| 624 | + pmdval_t prot; |
| 625 | +}; |
| 626 | + |
| 627 | +struct 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 | + */ |
| 649 | +static 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. */ |
| 671 | +static 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 | + |
| 703 | +static inline void fix_kernmem_perms(void) |
| 704 | +{ |
| 705 | + set_section_perms(nx_perms, prot); |
| 706 | +} |
| 707 | +#else |
| 708 | +static inline void fix_kernmem_perms(void) { } |
| 709 | +#endif /* CONFIG_ARM_KERNMEM_PERMS */ |
| 710 | + |
| 711 | +void free_tcmmem(void) |
619 | 712 | { |
620 | 713 | #ifdef CONFIG_HAVE_TCM |
621 | 714 | extern char __tcm_start, __tcm_end; |
622 | 715 |
|
623 | 716 | poison_init_mem(&__tcm_start, &__tcm_end - &__tcm_start); |
624 | 717 | free_reserved_area(&__tcm_start, &__tcm_end, -1, "TCM link"); |
625 | 718 | #endif |
| 719 | +} |
| 720 | + |
| 721 | +void free_initmem(void) |
| 722 | +{ |
| 723 | + fix_kernmem_perms(); |
| 724 | + free_tcmmem(); |
626 | 725 |
|
627 | 726 | poison_init_mem(__init_begin, __init_end - __init_begin); |
628 | 727 | if (!machine_is_integrator() && !machine_is_cintegrator()) |
|
0 commit comments