aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWill Deacon <will.deacon@arm.com>2012-08-10 12:51:18 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2012-08-11 04:15:59 -0400
commit47f1204329237a0f8655f5a9f14a38ac81946ca1 (patch)
tree863782a6d372973865860a3e55d79a0cbd01880d
parent237ec6f2e51d2fc2ff37c7c5f1ccc9264d09c85b (diff)
ARM: 7487/1: mm: avoid setting nG bit for user mappings that aren't present
Swap entries are encoding in ptes such that !pte_present(pte) and pte_file(pte). The remaining bits of the descriptor are used to identify the swapfile and offset within it to the swap entry. When writing such a pte for a user virtual address, set_pte_at unconditionally sets the nG bit, which (in the case of LPAE) will corrupt the swapfile offset and lead to a BUG: [ 140.494067] swap_free: Unused swap offset entry 000763b4 [ 140.509989] BUG: Bad page map in process rs:main Q:Reg pte:0ec76800 pmd:8f92e003 This patch fixes the problem by only setting the nG bit for user mappings that are actually present. Cc: <stable@vger.kernel.org> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r--arch/arm/include/asm/pgtable.h34
-rw-r--r--arch/arm/mm/flush.c2
2 files changed, 18 insertions, 18 deletions
diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h
index f66626d71e7d..d88f9f049e95 100644
--- a/arch/arm/include/asm/pgtable.h
+++ b/arch/arm/include/asm/pgtable.h
@@ -195,6 +195,18 @@ static inline pte_t *pmd_page_vaddr(pmd_t pmd)
195 195
196#define pte_clear(mm,addr,ptep) set_pte_ext(ptep, __pte(0), 0) 196#define pte_clear(mm,addr,ptep) set_pte_ext(ptep, __pte(0), 0)
197 197
198#define pte_none(pte) (!pte_val(pte))
199#define pte_present(pte) (pte_val(pte) & L_PTE_PRESENT)
200#define pte_write(pte) (!(pte_val(pte) & L_PTE_RDONLY))
201#define pte_dirty(pte) (pte_val(pte) & L_PTE_DIRTY)
202#define pte_young(pte) (pte_val(pte) & L_PTE_YOUNG)
203#define pte_exec(pte) (!(pte_val(pte) & L_PTE_XN))
204#define pte_special(pte) (0)
205
206#define pte_present_user(pte) \
207 ((pte_val(pte) & (L_PTE_PRESENT | L_PTE_USER)) == \
208 (L_PTE_PRESENT | L_PTE_USER))
209
198#if __LINUX_ARM_ARCH__ < 6 210#if __LINUX_ARM_ARCH__ < 6
199static inline void __sync_icache_dcache(pte_t pteval) 211static inline void __sync_icache_dcache(pte_t pteval)
200{ 212{
@@ -206,25 +218,15 @@ extern void __sync_icache_dcache(pte_t pteval);
206static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, 218static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
207 pte_t *ptep, pte_t pteval) 219 pte_t *ptep, pte_t pteval)
208{ 220{
209 if (addr >= TASK_SIZE) 221 unsigned long ext = 0;
210 set_pte_ext(ptep, pteval, 0); 222
211 else { 223 if (addr < TASK_SIZE && pte_present_user(pteval)) {
212 __sync_icache_dcache(pteval); 224 __sync_icache_dcache(pteval);
213 set_pte_ext(ptep, pteval, PTE_EXT_NG); 225 ext |= PTE_EXT_NG;
214 } 226 }
215}
216 227
217#define pte_none(pte) (!pte_val(pte)) 228 set_pte_ext(ptep, pteval, ext);
218#define pte_present(pte) (pte_val(pte) & L_PTE_PRESENT) 229}
219#define pte_write(pte) (!(pte_val(pte) & L_PTE_RDONLY))
220#define pte_dirty(pte) (pte_val(pte) & L_PTE_DIRTY)
221#define pte_young(pte) (pte_val(pte) & L_PTE_YOUNG)
222#define pte_exec(pte) (!(pte_val(pte) & L_PTE_XN))
223#define pte_special(pte) (0)
224
225#define pte_present_user(pte) \
226 ((pte_val(pte) & (L_PTE_PRESENT | L_PTE_USER)) == \
227 (L_PTE_PRESENT | L_PTE_USER))
228 230
229#define PTE_BIT_FUNC(fn,op) \ 231#define PTE_BIT_FUNC(fn,op) \
230static inline pte_t pte_##fn(pte_t pte) { pte_val(pte) op; return pte; } 232static inline pte_t pte_##fn(pte_t pte) { pte_val(pte) op; return pte; }
diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c
index 77458548e031..40ca11ed6e5f 100644
--- a/arch/arm/mm/flush.c
+++ b/arch/arm/mm/flush.c
@@ -231,8 +231,6 @@ void __sync_icache_dcache(pte_t pteval)
231 struct page *page; 231 struct page *page;
232 struct address_space *mapping; 232 struct address_space *mapping;
233 233
234 if (!pte_present_user(pteval))
235 return;
236 if (cache_is_vipt_nonaliasing() && !pte_exec(pteval)) 234 if (cache_is_vipt_nonaliasing() && !pte_exec(pteval))
237 /* only flush non-aliasing VIPT caches for exec mappings */ 235 /* only flush non-aliasing VIPT caches for exec mappings */
238 return; 236 return;