diff options
Diffstat (limited to 'arch/sparc/mm')
-rw-r--r-- | arch/sparc/mm/fault_64.c | 98 | ||||
-rw-r--r-- | arch/sparc/mm/gup.c | 2 | ||||
-rw-r--r-- | arch/sparc/mm/init_64.c | 12 | ||||
-rw-r--r-- | arch/sparc/mm/tlb.c | 26 | ||||
-rw-r--r-- | arch/sparc/mm/tsb.c | 14 |
5 files changed, 93 insertions, 59 deletions
diff --git a/arch/sparc/mm/fault_64.c b/arch/sparc/mm/fault_64.c index 69bb818fdd79..4ced3fc66130 100644 --- a/arch/sparc/mm/fault_64.c +++ b/arch/sparc/mm/fault_64.c | |||
@@ -96,38 +96,51 @@ static unsigned int get_user_insn(unsigned long tpc) | |||
96 | pte_t *ptep, pte; | 96 | pte_t *ptep, pte; |
97 | unsigned long pa; | 97 | unsigned long pa; |
98 | u32 insn = 0; | 98 | u32 insn = 0; |
99 | unsigned long pstate; | ||
100 | 99 | ||
101 | if (pgd_none(*pgdp)) | 100 | if (pgd_none(*pgdp) || unlikely(pgd_bad(*pgdp))) |
102 | goto outret; | 101 | goto out; |
103 | pudp = pud_offset(pgdp, tpc); | 102 | pudp = pud_offset(pgdp, tpc); |
104 | if (pud_none(*pudp)) | 103 | if (pud_none(*pudp) || unlikely(pud_bad(*pudp))) |
105 | goto outret; | 104 | goto out; |
106 | pmdp = pmd_offset(pudp, tpc); | ||
107 | if (pmd_none(*pmdp)) | ||
108 | goto outret; | ||
109 | 105 | ||
110 | /* This disables preemption for us as well. */ | 106 | /* This disables preemption for us as well. */ |
111 | __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate)); | 107 | local_irq_disable(); |
112 | __asm__ __volatile__("wrpr %0, %1, %%pstate" | 108 | |
113 | : : "r" (pstate), "i" (PSTATE_IE)); | 109 | pmdp = pmd_offset(pudp, tpc); |
114 | ptep = pte_offset_map(pmdp, tpc); | 110 | if (pmd_none(*pmdp) || unlikely(pmd_bad(*pmdp))) |
115 | pte = *ptep; | 111 | goto out_irq_enable; |
116 | if (!pte_present(pte)) | ||
117 | goto out; | ||
118 | 112 | ||
119 | pa = (pte_pfn(pte) << PAGE_SHIFT); | 113 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE |
120 | pa += (tpc & ~PAGE_MASK); | 114 | if (pmd_trans_huge(*pmdp)) { |
115 | if (pmd_trans_splitting(*pmdp)) | ||
116 | goto out_irq_enable; | ||
121 | 117 | ||
122 | /* Use phys bypass so we don't pollute dtlb/dcache. */ | 118 | pa = pmd_pfn(*pmdp) << PAGE_SHIFT; |
123 | __asm__ __volatile__("lduwa [%1] %2, %0" | 119 | pa += tpc & ~HPAGE_MASK; |
124 | : "=r" (insn) | ||
125 | : "r" (pa), "i" (ASI_PHYS_USE_EC)); | ||
126 | 120 | ||
121 | /* Use phys bypass so we don't pollute dtlb/dcache. */ | ||
122 | __asm__ __volatile__("lduwa [%1] %2, %0" | ||
123 | : "=r" (insn) | ||
124 | : "r" (pa), "i" (ASI_PHYS_USE_EC)); | ||
125 | } else | ||
126 | #endif | ||
127 | { | ||
128 | ptep = pte_offset_map(pmdp, tpc); | ||
129 | pte = *ptep; | ||
130 | if (pte_present(pte)) { | ||
131 | pa = (pte_pfn(pte) << PAGE_SHIFT); | ||
132 | pa += (tpc & ~PAGE_MASK); | ||
133 | |||
134 | /* Use phys bypass so we don't pollute dtlb/dcache. */ | ||
135 | __asm__ __volatile__("lduwa [%1] %2, %0" | ||
136 | : "=r" (insn) | ||
137 | : "r" (pa), "i" (ASI_PHYS_USE_EC)); | ||
138 | } | ||
139 | pte_unmap(ptep); | ||
140 | } | ||
141 | out_irq_enable: | ||
142 | local_irq_enable(); | ||
127 | out: | 143 | out: |
128 | pte_unmap(ptep); | ||
129 | __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate)); | ||
130 | outret: | ||
131 | return insn; | 144 | return insn; |
132 | } | 145 | } |
133 | 146 | ||
@@ -153,7 +166,8 @@ show_signal_msg(struct pt_regs *regs, int sig, int code, | |||
153 | } | 166 | } |
154 | 167 | ||
155 | static void do_fault_siginfo(int code, int sig, struct pt_regs *regs, | 168 | static void do_fault_siginfo(int code, int sig, struct pt_regs *regs, |
156 | unsigned int insn, int fault_code) | 169 | unsigned long fault_addr, unsigned int insn, |
170 | int fault_code) | ||
157 | { | 171 | { |
158 | unsigned long addr; | 172 | unsigned long addr; |
159 | siginfo_t info; | 173 | siginfo_t info; |
@@ -161,10 +175,18 @@ static void do_fault_siginfo(int code, int sig, struct pt_regs *regs, | |||
161 | info.si_code = code; | 175 | info.si_code = code; |
162 | info.si_signo = sig; | 176 | info.si_signo = sig; |
163 | info.si_errno = 0; | 177 | info.si_errno = 0; |
164 | if (fault_code & FAULT_CODE_ITLB) | 178 | if (fault_code & FAULT_CODE_ITLB) { |
165 | addr = regs->tpc; | 179 | addr = regs->tpc; |
166 | else | 180 | } else { |
167 | addr = compute_effective_address(regs, insn, 0); | 181 | /* If we were able to probe the faulting instruction, use it |
182 | * to compute a precise fault address. Otherwise use the fault | ||
183 | * time provided address which may only have page granularity. | ||
184 | */ | ||
185 | if (insn) | ||
186 | addr = compute_effective_address(regs, insn, 0); | ||
187 | else | ||
188 | addr = fault_addr; | ||
189 | } | ||
168 | info.si_addr = (void __user *) addr; | 190 | info.si_addr = (void __user *) addr; |
169 | info.si_trapno = 0; | 191 | info.si_trapno = 0; |
170 | 192 | ||
@@ -239,7 +261,7 @@ static void __kprobes do_kernel_fault(struct pt_regs *regs, int si_code, | |||
239 | /* The si_code was set to make clear whether | 261 | /* The si_code was set to make clear whether |
240 | * this was a SEGV_MAPERR or SEGV_ACCERR fault. | 262 | * this was a SEGV_MAPERR or SEGV_ACCERR fault. |
241 | */ | 263 | */ |
242 | do_fault_siginfo(si_code, SIGSEGV, regs, insn, fault_code); | 264 | do_fault_siginfo(si_code, SIGSEGV, regs, address, insn, fault_code); |
243 | return; | 265 | return; |
244 | } | 266 | } |
245 | 267 | ||
@@ -259,18 +281,6 @@ static void noinline __kprobes bogus_32bit_fault_tpc(struct pt_regs *regs) | |||
259 | show_regs(regs); | 281 | show_regs(regs); |
260 | } | 282 | } |
261 | 283 | ||
262 | static void noinline __kprobes bogus_32bit_fault_address(struct pt_regs *regs, | ||
263 | unsigned long addr) | ||
264 | { | ||
265 | static int times; | ||
266 | |||
267 | if (times++ < 10) | ||
268 | printk(KERN_ERR "FAULT[%s:%d]: 32-bit process " | ||
269 | "reports 64-bit fault address [%lx]\n", | ||
270 | current->comm, current->pid, addr); | ||
271 | show_regs(regs); | ||
272 | } | ||
273 | |||
274 | asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs) | 284 | asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs) |
275 | { | 285 | { |
276 | enum ctx_state prev_state = exception_enter(); | 286 | enum ctx_state prev_state = exception_enter(); |
@@ -300,10 +310,8 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs) | |||
300 | goto intr_or_no_mm; | 310 | goto intr_or_no_mm; |
301 | } | 311 | } |
302 | } | 312 | } |
303 | if (unlikely((address >> 32) != 0)) { | 313 | if (unlikely((address >> 32) != 0)) |
304 | bogus_32bit_fault_address(regs, address); | ||
305 | goto intr_or_no_mm; | 314 | goto intr_or_no_mm; |
306 | } | ||
307 | } | 315 | } |
308 | 316 | ||
309 | if (regs->tstate & TSTATE_PRIV) { | 317 | if (regs->tstate & TSTATE_PRIV) { |
@@ -525,7 +533,7 @@ do_sigbus: | |||
525 | * Send a sigbus, regardless of whether we were in kernel | 533 | * Send a sigbus, regardless of whether we were in kernel |
526 | * or user mode. | 534 | * or user mode. |
527 | */ | 535 | */ |
528 | do_fault_siginfo(BUS_ADRERR, SIGBUS, regs, insn, fault_code); | 536 | do_fault_siginfo(BUS_ADRERR, SIGBUS, regs, address, insn, fault_code); |
529 | 537 | ||
530 | /* Kernel mode? Handle exceptions or die */ | 538 | /* Kernel mode? Handle exceptions or die */ |
531 | if (regs->tstate & TSTATE_PRIV) | 539 | if (regs->tstate & TSTATE_PRIV) |
diff --git a/arch/sparc/mm/gup.c b/arch/sparc/mm/gup.c index c4d3da68b800..1aed0432c64b 100644 --- a/arch/sparc/mm/gup.c +++ b/arch/sparc/mm/gup.c | |||
@@ -73,7 +73,7 @@ static int gup_huge_pmd(pmd_t *pmdp, pmd_t pmd, unsigned long addr, | |||
73 | struct page *head, *page, *tail; | 73 | struct page *head, *page, *tail; |
74 | int refs; | 74 | int refs; |
75 | 75 | ||
76 | if (!pmd_large(pmd)) | 76 | if (!(pmd_val(pmd) & _PAGE_VALID)) |
77 | return 0; | 77 | return 0; |
78 | 78 | ||
79 | if (write && !pmd_write(pmd)) | 79 | if (write && !pmd_write(pmd)) |
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c index eafbc65c9c47..ed3c969a5f4c 100644 --- a/arch/sparc/mm/init_64.c +++ b/arch/sparc/mm/init_64.c | |||
@@ -588,7 +588,7 @@ static void __init remap_kernel(void) | |||
588 | int i, tlb_ent = sparc64_highest_locked_tlbent(); | 588 | int i, tlb_ent = sparc64_highest_locked_tlbent(); |
589 | 589 | ||
590 | tte_vaddr = (unsigned long) KERNBASE; | 590 | tte_vaddr = (unsigned long) KERNBASE; |
591 | phys_page = (prom_boot_mapping_phys_low >> 22UL) << 22UL; | 591 | phys_page = (prom_boot_mapping_phys_low >> ILOG2_4MB) << ILOG2_4MB; |
592 | tte_data = kern_large_tte(phys_page); | 592 | tte_data = kern_large_tte(phys_page); |
593 | 593 | ||
594 | kern_locked_tte_data = tte_data; | 594 | kern_locked_tte_data = tte_data; |
@@ -1881,7 +1881,7 @@ void __init paging_init(void) | |||
1881 | 1881 | ||
1882 | BUILD_BUG_ON(NR_CPUS > 4096); | 1882 | BUILD_BUG_ON(NR_CPUS > 4096); |
1883 | 1883 | ||
1884 | kern_base = (prom_boot_mapping_phys_low >> 22UL) << 22UL; | 1884 | kern_base = (prom_boot_mapping_phys_low >> ILOG2_4MB) << ILOG2_4MB; |
1885 | kern_size = (unsigned long)&_end - (unsigned long)KERNBASE; | 1885 | kern_size = (unsigned long)&_end - (unsigned long)KERNBASE; |
1886 | 1886 | ||
1887 | /* Invalidate both kernel TSBs. */ | 1887 | /* Invalidate both kernel TSBs. */ |
@@ -1937,7 +1937,7 @@ void __init paging_init(void) | |||
1937 | shift = kern_base + PAGE_OFFSET - ((unsigned long)KERNBASE); | 1937 | shift = kern_base + PAGE_OFFSET - ((unsigned long)KERNBASE); |
1938 | 1938 | ||
1939 | real_end = (unsigned long)_end; | 1939 | real_end = (unsigned long)_end; |
1940 | num_kernel_image_mappings = DIV_ROUND_UP(real_end - KERNBASE, 1 << 22); | 1940 | num_kernel_image_mappings = DIV_ROUND_UP(real_end - KERNBASE, 1 << ILOG2_4MB); |
1941 | printk("Kernel: Using %d locked TLB entries for main kernel image.\n", | 1941 | printk("Kernel: Using %d locked TLB entries for main kernel image.\n", |
1942 | num_kernel_image_mappings); | 1942 | num_kernel_image_mappings); |
1943 | 1943 | ||
@@ -2094,7 +2094,7 @@ static void __init setup_valid_addr_bitmap_from_pavail(unsigned long *bitmap) | |||
2094 | 2094 | ||
2095 | if (new_start <= old_start && | 2095 | if (new_start <= old_start && |
2096 | new_end >= (old_start + PAGE_SIZE)) { | 2096 | new_end >= (old_start + PAGE_SIZE)) { |
2097 | set_bit(old_start >> 22, bitmap); | 2097 | set_bit(old_start >> ILOG2_4MB, bitmap); |
2098 | goto do_next_page; | 2098 | goto do_next_page; |
2099 | } | 2099 | } |
2100 | } | 2100 | } |
@@ -2143,7 +2143,7 @@ void __init mem_init(void) | |||
2143 | addr = PAGE_OFFSET + kern_base; | 2143 | addr = PAGE_OFFSET + kern_base; |
2144 | last = PAGE_ALIGN(kern_size) + addr; | 2144 | last = PAGE_ALIGN(kern_size) + addr; |
2145 | while (addr < last) { | 2145 | while (addr < last) { |
2146 | set_bit(__pa(addr) >> 22, sparc64_valid_addr_bitmap); | 2146 | set_bit(__pa(addr) >> ILOG2_4MB, sparc64_valid_addr_bitmap); |
2147 | addr += PAGE_SIZE; | 2147 | addr += PAGE_SIZE; |
2148 | } | 2148 | } |
2149 | 2149 | ||
@@ -2267,7 +2267,7 @@ int __meminit vmemmap_populate(unsigned long vstart, unsigned long vend, | |||
2267 | void *block; | 2267 | void *block; |
2268 | 2268 | ||
2269 | if (!(*vmem_pp & _PAGE_VALID)) { | 2269 | if (!(*vmem_pp & _PAGE_VALID)) { |
2270 | block = vmemmap_alloc_block(1UL << 22, node); | 2270 | block = vmemmap_alloc_block(1UL << ILOG2_4MB, node); |
2271 | if (!block) | 2271 | if (!block) |
2272 | return -ENOMEM; | 2272 | return -ENOMEM; |
2273 | 2273 | ||
diff --git a/arch/sparc/mm/tlb.c b/arch/sparc/mm/tlb.c index b12cb5e72812..b89aba217e3b 100644 --- a/arch/sparc/mm/tlb.c +++ b/arch/sparc/mm/tlb.c | |||
@@ -134,7 +134,7 @@ no_cache_flush: | |||
134 | 134 | ||
135 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE | 135 | #ifdef CONFIG_TRANSPARENT_HUGEPAGE |
136 | static void tlb_batch_pmd_scan(struct mm_struct *mm, unsigned long vaddr, | 136 | static void tlb_batch_pmd_scan(struct mm_struct *mm, unsigned long vaddr, |
137 | pmd_t pmd, bool exec) | 137 | pmd_t pmd) |
138 | { | 138 | { |
139 | unsigned long end; | 139 | unsigned long end; |
140 | pte_t *pte; | 140 | pte_t *pte; |
@@ -142,8 +142,11 @@ static void tlb_batch_pmd_scan(struct mm_struct *mm, unsigned long vaddr, | |||
142 | pte = pte_offset_map(&pmd, vaddr); | 142 | pte = pte_offset_map(&pmd, vaddr); |
143 | end = vaddr + HPAGE_SIZE; | 143 | end = vaddr + HPAGE_SIZE; |
144 | while (vaddr < end) { | 144 | while (vaddr < end) { |
145 | if (pte_val(*pte) & _PAGE_VALID) | 145 | if (pte_val(*pte) & _PAGE_VALID) { |
146 | bool exec = pte_exec(*pte); | ||
147 | |||
146 | tlb_batch_add_one(mm, vaddr, exec); | 148 | tlb_batch_add_one(mm, vaddr, exec); |
149 | } | ||
147 | pte++; | 150 | pte++; |
148 | vaddr += PAGE_SIZE; | 151 | vaddr += PAGE_SIZE; |
149 | } | 152 | } |
@@ -177,19 +180,30 @@ void set_pmd_at(struct mm_struct *mm, unsigned long addr, | |||
177 | } | 180 | } |
178 | 181 | ||
179 | if (!pmd_none(orig)) { | 182 | if (!pmd_none(orig)) { |
180 | pte_t orig_pte = __pte(pmd_val(orig)); | ||
181 | bool exec = pte_exec(orig_pte); | ||
182 | |||
183 | addr &= HPAGE_MASK; | 183 | addr &= HPAGE_MASK; |
184 | if (pmd_trans_huge(orig)) { | 184 | if (pmd_trans_huge(orig)) { |
185 | pte_t orig_pte = __pte(pmd_val(orig)); | ||
186 | bool exec = pte_exec(orig_pte); | ||
187 | |||
185 | tlb_batch_add_one(mm, addr, exec); | 188 | tlb_batch_add_one(mm, addr, exec); |
186 | tlb_batch_add_one(mm, addr + REAL_HPAGE_SIZE, exec); | 189 | tlb_batch_add_one(mm, addr + REAL_HPAGE_SIZE, exec); |
187 | } else { | 190 | } else { |
188 | tlb_batch_pmd_scan(mm, addr, orig, exec); | 191 | tlb_batch_pmd_scan(mm, addr, orig); |
189 | } | 192 | } |
190 | } | 193 | } |
191 | } | 194 | } |
192 | 195 | ||
196 | void pmdp_invalidate(struct vm_area_struct *vma, unsigned long address, | ||
197 | pmd_t *pmdp) | ||
198 | { | ||
199 | pmd_t entry = *pmdp; | ||
200 | |||
201 | pmd_val(entry) &= ~_PAGE_VALID; | ||
202 | |||
203 | set_pmd_at(vma->vm_mm, address, pmdp, entry); | ||
204 | flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE); | ||
205 | } | ||
206 | |||
193 | void pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp, | 207 | void pgtable_trans_huge_deposit(struct mm_struct *mm, pmd_t *pmdp, |
194 | pgtable_t pgtable) | 208 | pgtable_t pgtable) |
195 | { | 209 | { |
diff --git a/arch/sparc/mm/tsb.c b/arch/sparc/mm/tsb.c index f5d506fdddad..fe19b81acc09 100644 --- a/arch/sparc/mm/tsb.c +++ b/arch/sparc/mm/tsb.c | |||
@@ -133,7 +133,19 @@ static void setup_tsb_params(struct mm_struct *mm, unsigned long tsb_idx, unsign | |||
133 | mm->context.tsb_block[tsb_idx].tsb_nentries = | 133 | mm->context.tsb_block[tsb_idx].tsb_nentries = |
134 | tsb_bytes / sizeof(struct tsb); | 134 | tsb_bytes / sizeof(struct tsb); |
135 | 135 | ||
136 | base = TSBMAP_BASE; | 136 | switch (tsb_idx) { |
137 | case MM_TSB_BASE: | ||
138 | base = TSBMAP_8K_BASE; | ||
139 | break; | ||
140 | #if defined(CONFIG_HUGETLB_PAGE) || defined(CONFIG_TRANSPARENT_HUGEPAGE) | ||
141 | case MM_TSB_HUGE: | ||
142 | base = TSBMAP_4M_BASE; | ||
143 | break; | ||
144 | #endif | ||
145 | default: | ||
146 | BUG(); | ||
147 | } | ||
148 | |||
137 | tte = pgprot_val(PAGE_KERNEL_LOCKED); | 149 | tte = pgprot_val(PAGE_KERNEL_LOCKED); |
138 | tsb_paddr = __pa(mm->context.tsb_block[tsb_idx].tsb); | 150 | tsb_paddr = __pa(mm->context.tsb_block[tsb_idx].tsb); |
139 | BUG_ON(tsb_paddr & (tsb_bytes - 1UL)); | 151 | BUG_ON(tsb_paddr & (tsb_bytes - 1UL)); |