diff options
author | Laura Abbott <labbott@fedoraproject.org> | 2016-02-05 19:24:47 -0500 |
---|---|---|
committer | Catalin Marinas <catalin.marinas@arm.com> | 2016-02-16 10:40:30 -0500 |
commit | 83863f25e4b8214e994ef8b5647aad614d74b45d (patch) | |
tree | def928e193b847e4035a6d895efafc9f9b881896 /arch/arm64 | |
parent | 132233a759580f5ce9b1bfaac9073e47d03c460d (diff) |
arm64: Add support for ARCH_SUPPORTS_DEBUG_PAGEALLOC
ARCH_SUPPORTS_DEBUG_PAGEALLOC provides a hook to map and unmap
pages for debugging purposes. This requires memory be mapped
with PAGE_SIZE mappings since breaking down larger mappings
at runtime will lead to TLB conflicts. Check if debug_pagealloc
is enabled at runtime and if so, map everyting with PAGE_SIZE
pages. Implement the functions to actually map/unmap the
pages at runtime.
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Mark Rutland <mark.rutland@arm.com>
Tested-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Laura Abbott <labbott@fedoraproject.org>
[catalin.marinas@arm.com: static annotation block_mappings_allowed() and #ifdef]
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Diffstat (limited to 'arch/arm64')
-rw-r--r-- | arch/arm64/Kconfig | 3 | ||||
-rw-r--r-- | arch/arm64/mm/mmu.c | 26 | ||||
-rw-r--r-- | arch/arm64/mm/pageattr.c | 46 |
3 files changed, 63 insertions, 12 deletions
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 53e48a617fef..5a1f2e47d153 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig | |||
@@ -537,6 +537,9 @@ config HOTPLUG_CPU | |||
537 | source kernel/Kconfig.preempt | 537 | source kernel/Kconfig.preempt |
538 | source kernel/Kconfig.hz | 538 | source kernel/Kconfig.hz |
539 | 539 | ||
540 | config ARCH_SUPPORTS_DEBUG_PAGEALLOC | ||
541 | def_bool y | ||
542 | |||
540 | config ARCH_HAS_HOLES_MEMORYMODEL | 543 | config ARCH_HAS_HOLES_MEMORYMODEL |
541 | def_bool y if SPARSEMEM | 544 | def_bool y if SPARSEMEM |
542 | 545 | ||
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index ef0d66cf5535..a6f28e5b9585 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c | |||
@@ -149,6 +149,26 @@ static void split_pud(pud_t *old_pud, pmd_t *pmd) | |||
149 | } while (pmd++, i++, i < PTRS_PER_PMD); | 149 | } while (pmd++, i++, i < PTRS_PER_PMD); |
150 | } | 150 | } |
151 | 151 | ||
152 | #ifdef CONFIG_DEBUG_PAGEALLOC | ||
153 | static bool block_mappings_allowed(phys_addr_t (*pgtable_alloc)(void)) | ||
154 | { | ||
155 | |||
156 | /* | ||
157 | * If debug_page_alloc is enabled we must map the linear map | ||
158 | * using pages. However, other mappings created by | ||
159 | * create_mapping_noalloc must use sections in some cases. Allow | ||
160 | * sections to be used in those cases, where no pgtable_alloc | ||
161 | * function is provided. | ||
162 | */ | ||
163 | return !pgtable_alloc || !debug_pagealloc_enabled(); | ||
164 | } | ||
165 | #else | ||
166 | static bool block_mappings_allowed(phys_addr_t (*pgtable_alloc)(void)) | ||
167 | { | ||
168 | return true; | ||
169 | } | ||
170 | #endif | ||
171 | |||
152 | static void alloc_init_pmd(pud_t *pud, unsigned long addr, unsigned long end, | 172 | static void alloc_init_pmd(pud_t *pud, unsigned long addr, unsigned long end, |
153 | phys_addr_t phys, pgprot_t prot, | 173 | phys_addr_t phys, pgprot_t prot, |
154 | phys_addr_t (*pgtable_alloc)(void)) | 174 | phys_addr_t (*pgtable_alloc)(void)) |
@@ -181,7 +201,8 @@ static void alloc_init_pmd(pud_t *pud, unsigned long addr, unsigned long end, | |||
181 | do { | 201 | do { |
182 | next = pmd_addr_end(addr, end); | 202 | next = pmd_addr_end(addr, end); |
183 | /* try section mapping first */ | 203 | /* try section mapping first */ |
184 | if (((addr | next | phys) & ~SECTION_MASK) == 0) { | 204 | if (((addr | next | phys) & ~SECTION_MASK) == 0 && |
205 | block_mappings_allowed(pgtable_alloc)) { | ||
185 | pmd_t old_pmd =*pmd; | 206 | pmd_t old_pmd =*pmd; |
186 | set_pmd(pmd, __pmd(phys | | 207 | set_pmd(pmd, __pmd(phys | |
187 | pgprot_val(mk_sect_prot(prot)))); | 208 | pgprot_val(mk_sect_prot(prot)))); |
@@ -241,7 +262,8 @@ static void alloc_init_pud(pgd_t *pgd, unsigned long addr, unsigned long end, | |||
241 | /* | 262 | /* |
242 | * For 4K granule only, attempt to put down a 1GB block | 263 | * For 4K granule only, attempt to put down a 1GB block |
243 | */ | 264 | */ |
244 | if (use_1G_block(addr, next, phys)) { | 265 | if (use_1G_block(addr, next, phys) && |
266 | block_mappings_allowed(pgtable_alloc)) { | ||
245 | pud_t old_pud = *pud; | 267 | pud_t old_pud = *pud; |
246 | set_pud(pud, __pud(phys | | 268 | set_pud(pud, __pud(phys | |
247 | pgprot_val(mk_sect_prot(prot)))); | 269 | pgprot_val(mk_sect_prot(prot)))); |
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c index 0795c3a36d8f..ca6d268e3313 100644 --- a/arch/arm64/mm/pageattr.c +++ b/arch/arm64/mm/pageattr.c | |||
@@ -37,14 +37,31 @@ static int change_page_range(pte_t *ptep, pgtable_t token, unsigned long addr, | |||
37 | return 0; | 37 | return 0; |
38 | } | 38 | } |
39 | 39 | ||
40 | /* | ||
41 | * This function assumes that the range is mapped with PAGE_SIZE pages. | ||
42 | */ | ||
43 | static int __change_memory_common(unsigned long start, unsigned long size, | ||
44 | pgprot_t set_mask, pgprot_t clear_mask) | ||
45 | { | ||
46 | struct page_change_data data; | ||
47 | int ret; | ||
48 | |||
49 | data.set_mask = set_mask; | ||
50 | data.clear_mask = clear_mask; | ||
51 | |||
52 | ret = apply_to_page_range(&init_mm, start, size, change_page_range, | ||
53 | &data); | ||
54 | |||
55 | flush_tlb_kernel_range(start, start + size); | ||
56 | return ret; | ||
57 | } | ||
58 | |||
40 | static int change_memory_common(unsigned long addr, int numpages, | 59 | static int change_memory_common(unsigned long addr, int numpages, |
41 | pgprot_t set_mask, pgprot_t clear_mask) | 60 | pgprot_t set_mask, pgprot_t clear_mask) |
42 | { | 61 | { |
43 | unsigned long start = addr; | 62 | unsigned long start = addr; |
44 | unsigned long size = PAGE_SIZE*numpages; | 63 | unsigned long size = PAGE_SIZE*numpages; |
45 | unsigned long end = start + size; | 64 | unsigned long end = start + size; |
46 | int ret; | ||
47 | struct page_change_data data; | ||
48 | struct vm_struct *area; | 65 | struct vm_struct *area; |
49 | 66 | ||
50 | if (!PAGE_ALIGNED(addr)) { | 67 | if (!PAGE_ALIGNED(addr)) { |
@@ -75,14 +92,7 @@ static int change_memory_common(unsigned long addr, int numpages, | |||
75 | if (!numpages) | 92 | if (!numpages) |
76 | return 0; | 93 | return 0; |
77 | 94 | ||
78 | data.set_mask = set_mask; | 95 | return __change_memory_common(start, size, set_mask, clear_mask); |
79 | data.clear_mask = clear_mask; | ||
80 | |||
81 | ret = apply_to_page_range(&init_mm, start, size, change_page_range, | ||
82 | &data); | ||
83 | |||
84 | flush_tlb_kernel_range(start, end); | ||
85 | return ret; | ||
86 | } | 96 | } |
87 | 97 | ||
88 | int set_memory_ro(unsigned long addr, int numpages) | 98 | int set_memory_ro(unsigned long addr, int numpages) |
@@ -114,3 +124,19 @@ int set_memory_x(unsigned long addr, int numpages) | |||
114 | __pgprot(PTE_PXN)); | 124 | __pgprot(PTE_PXN)); |
115 | } | 125 | } |
116 | EXPORT_SYMBOL_GPL(set_memory_x); | 126 | EXPORT_SYMBOL_GPL(set_memory_x); |
127 | |||
128 | #ifdef CONFIG_DEBUG_PAGEALLOC | ||
129 | void __kernel_map_pages(struct page *page, int numpages, int enable) | ||
130 | { | ||
131 | unsigned long addr = (unsigned long) page_address(page); | ||
132 | |||
133 | if (enable) | ||
134 | __change_memory_common(addr, PAGE_SIZE * numpages, | ||
135 | __pgprot(PTE_VALID), | ||
136 | __pgprot(0)); | ||
137 | else | ||
138 | __change_memory_common(addr, PAGE_SIZE * numpages, | ||
139 | __pgprot(0), | ||
140 | __pgprot(PTE_VALID)); | ||
141 | } | ||
142 | #endif | ||