diff options
Diffstat (limited to 'arch/arc/mm')
-rw-r--r-- | arch/arc/mm/Makefile | 5 | ||||
-rw-r--r-- | arch/arc/mm/cache.c | 5 | ||||
-rw-r--r-- | arch/arc/mm/dma.c | 76 | ||||
-rw-r--r-- | arch/arc/mm/extable.c | 5 | ||||
-rw-r--r-- | arch/arc/mm/fault.c | 197 | ||||
-rw-r--r-- | arch/arc/mm/highmem.c | 6 | ||||
-rw-r--r-- | arch/arc/mm/init.c | 5 | ||||
-rw-r--r-- | arch/arc/mm/ioremap.c | 5 | ||||
-rw-r--r-- | arch/arc/mm/mmap.c | 5 | ||||
-rw-r--r-- | arch/arc/mm/tlb.c | 18 | ||||
-rw-r--r-- | arch/arc/mm/tlbex.S | 16 |
11 files changed, 119 insertions, 224 deletions
diff --git a/arch/arc/mm/Makefile b/arch/arc/mm/Makefile index 3703a4969349..633a773369ca 100644 --- a/arch/arc/mm/Makefile +++ b/arch/arc/mm/Makefile @@ -1,10 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only # # Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# obj-y := extable.o ioremap.o dma.o fault.o init.o obj-y += tlb.o tlbex.o cache.o mmap.o diff --git a/arch/arc/mm/cache.c b/arch/arc/mm/cache.c index 63e6e6504699..a2fbea3ee07c 100644 --- a/arch/arc/mm/cache.c +++ b/arch/arc/mm/cache.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * ARC Cache Management * * Copyright (C) 2014-15 Synopsys, Inc. (www.synopsys.com) * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/module.h> diff --git a/arch/arc/mm/dma.c b/arch/arc/mm/dma.c index 1525ac00fd02..62c210e7ee4c 100644 --- a/arch/arc/mm/dma.c +++ b/arch/arc/mm/dma.c @@ -1,9 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/dma-noncoherent.h> @@ -11,51 +8,15 @@ #include <asm/cacheflush.h> /* - * ARCH specific callbacks for generic noncoherent DMA ops (dma/noncoherent.c) + * ARCH specific callbacks for generic noncoherent DMA ops * - hardware IOC not available (or "dma-coherent" not set for device in DT) * - But still handle both coherent and non-coherent requests from caller * * For DMA coherent hardware (IOC) generic code suffices */ -void *arch_dma_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle, - gfp_t gfp, unsigned long attrs) -{ - unsigned long order = get_order(size); - struct page *page; - phys_addr_t paddr; - void *kvaddr; - bool need_coh = !(attrs & DMA_ATTR_NON_CONSISTENT); - - /* - * __GFP_HIGHMEM flag is cleared by upper layer functions - * (in include/linux/dma-mapping.h) so we should never get a - * __GFP_HIGHMEM here. - */ - BUG_ON(gfp & __GFP_HIGHMEM); - - page = alloc_pages(gfp | __GFP_ZERO, order); - if (!page) - return NULL; - - /* This is linear addr (0x8000_0000 based) */ - paddr = page_to_phys(page); - - *dma_handle = paddr; - - /* - * A coherent buffer needs MMU mapping to enforce non-cachability. - * kvaddr is kernel Virtual address (0x7000_0000 based). - */ - if (need_coh) { - kvaddr = ioremap_nocache(paddr, size); - if (kvaddr == NULL) { - __free_pages(page, order); - return NULL; - } - } else { - kvaddr = (void *)(u32)paddr; - } +void arch_dma_prep_coherent(struct page *page, size_t size) +{ /* * Evict any existing L1 and/or L2 lines for the backing page * in case it was used earlier as a normal "cached" page. @@ -66,28 +27,7 @@ void *arch_dma_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle, * Currently flush_cache_vmap nukes the L1 cache completely which * will be optimized as a separate commit */ - if (need_coh) - dma_cache_wback_inv(paddr, size); - - return kvaddr; -} - -void arch_dma_free(struct device *dev, size_t size, void *vaddr, - dma_addr_t dma_handle, unsigned long attrs) -{ - phys_addr_t paddr = dma_handle; - struct page *page = virt_to_page(paddr); - - if (!(attrs & DMA_ATTR_NON_CONSISTENT)) - iounmap((void __force __iomem *)vaddr); - - __free_pages(page, get_order(size)); -} - -long arch_dma_coherent_to_pfn(struct device *dev, void *cpu_addr, - dma_addr_t dma_addr) -{ - return __phys_to_pfn(dma_addr); + dma_cache_wback_inv(page_to_phys(page), size); } /* @@ -164,3 +104,9 @@ void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, dev_info(dev, "use %sncoherent DMA ops\n", dev->dma_coherent ? "" : "non"); } + +static int __init atomic_pool_init(void) +{ + return dma_atomic_pool_init(GFP_KERNEL, pgprot_noncached(PAGE_KERNEL)); +} +postcore_initcall(atomic_pool_init); diff --git a/arch/arc/mm/extable.c b/arch/arc/mm/extable.c index 72125a34e780..b06b09ddf924 100644 --- a/arch/arc/mm/extable.c +++ b/arch/arc/mm/extable.c @@ -1,10 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * * Borrowed heavily from MIPS */ diff --git a/arch/arc/mm/fault.c b/arch/arc/mm/fault.c index 8df1638259f3..3861543b66a0 100644 --- a/arch/arc/mm/fault.c +++ b/arch/arc/mm/fault.c @@ -1,10 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* Page Fault Handling for ARC (TLB Miss / ProtV) * * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/signal.h> @@ -66,31 +63,24 @@ void do_page_fault(unsigned long address, struct pt_regs *regs) struct vm_area_struct *vma = NULL; struct task_struct *tsk = current; struct mm_struct *mm = tsk->mm; - int si_code = 0; - int ret; - vm_fault_t fault; - int write = regs->ecr_cause & ECR_C_PROTV_STORE; /* ST/EX */ - unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; + int sig, si_code = SEGV_MAPERR; + unsigned int write = 0, exec = 0, mask; + vm_fault_t fault = VM_FAULT_SIGSEGV; /* handle_mm_fault() output */ + unsigned int flags; /* handle_mm_fault() input */ /* - * We fault-in kernel-space virtual memory on-demand. The - * 'reference' page table is init_mm.pgd. - * * NOTE! We MUST NOT take any locks for this case. We may * be in an interrupt or a critical region, and should * only copy the information from the master page table, * nothing more. */ - if (address >= VMALLOC_START) { - ret = handle_kernel_vaddr_fault(address); - if (unlikely(ret)) - goto bad_area_nosemaphore; + if (address >= VMALLOC_START && !user_mode(regs)) { + if (unlikely(handle_kernel_vaddr_fault(address))) + goto no_context; else return; } - si_code = SEGV_MAPERR; - /* * If we're in an interrupt or have no user * context, we must not take the fault.. @@ -98,144 +88,117 @@ void do_page_fault(unsigned long address, struct pt_regs *regs) if (faulthandler_disabled() || !mm) goto no_context; + if (regs->ecr_cause & ECR_C_PROTV_STORE) /* ST/EX */ + write = 1; + else if ((regs->ecr_vec == ECR_V_PROTV) && + (regs->ecr_cause == ECR_C_PROTV_INST_FETCH)) + exec = 1; + + flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; if (user_mode(regs)) flags |= FAULT_FLAG_USER; + if (write) + flags |= FAULT_FLAG_WRITE; + retry: down_read(&mm->mmap_sem); + vma = find_vma(mm, address); if (!vma) goto bad_area; - if (vma->vm_start <= address) - goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) - goto bad_area; - if (expand_stack(vma, address)) - goto bad_area; + if (unlikely(address < vma->vm_start)) { + if (!(vma->vm_flags & VM_GROWSDOWN) || expand_stack(vma, address)) + goto bad_area; + } /* - * Ok, we have a good vm_area for this memory access, so - * we can handle it.. + * vm_area is good, now check permissions for this memory access */ -good_area: - si_code = SEGV_ACCERR; - - /* Handle protection violation, execute on heap or stack */ - - if ((regs->ecr_vec == ECR_V_PROTV) && - (regs->ecr_cause == ECR_C_PROTV_INST_FETCH)) + mask = VM_READ; + if (write) + mask = VM_WRITE; + if (exec) + mask = VM_EXEC; + + if (!(vma->vm_flags & mask)) { + si_code = SEGV_ACCERR; goto bad_area; - - if (write) { - if (!(vma->vm_flags & VM_WRITE)) - goto bad_area; - flags |= FAULT_FLAG_WRITE; - } else { - if (!(vma->vm_flags & (VM_READ | VM_EXEC))) - goto bad_area; } - /* - * If for any reason at all we couldn't handle the fault, - * make sure we exit gracefully rather than endlessly redo - * the fault. - */ fault = handle_mm_fault(vma, address, flags); - if (fatal_signal_pending(current)) { + /* + * Fault retry nuances + */ + if (unlikely(fault & VM_FAULT_RETRY)) { /* - * if fault retry, mmap_sem already relinquished by core mm - * so OK to return to user mode (with signal handled first) + * If fault needs to be retried, handle any pending signals + * first (by returning to user mode). + * mmap_sem already relinquished by core mm for RETRY case */ - if (fault & VM_FAULT_RETRY) { + if (fatal_signal_pending(current)) { if (!user_mode(regs)) goto no_context; return; } - } - - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); - - if (likely(!(fault & VM_FAULT_ERROR))) { + /* + * retry state machine + */ if (flags & FAULT_FLAG_ALLOW_RETRY) { - /* To avoid updating stats twice for retry case */ - if (fault & VM_FAULT_MAJOR) { - tsk->maj_flt++; - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, - regs, address); - } else { - tsk->min_flt++; - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, - regs, address); - } - - if (fault & VM_FAULT_RETRY) { - flags &= ~FAULT_FLAG_ALLOW_RETRY; - flags |= FAULT_FLAG_TRIED; - goto retry; - } + flags &= ~FAULT_FLAG_ALLOW_RETRY; + flags |= FAULT_FLAG_TRIED; + goto retry; } - - /* Fault Handled Gracefully */ - up_read(&mm->mmap_sem); - return; } - if (fault & VM_FAULT_OOM) - goto out_of_memory; - else if (fault & VM_FAULT_SIGSEGV) - goto bad_area; - else if (fault & VM_FAULT_SIGBUS) - goto do_sigbus; - - /* no man's land */ - BUG(); +bad_area: + up_read(&mm->mmap_sem); /* - * Something tried to access memory that isn't in our memory map.. - * Fix it, but check if it's kernel or user first.. + * Major/minor page fault accounting + * (in case of retry we only land here once) */ -bad_area: - up_read(&mm->mmap_sem); + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); -bad_area_nosemaphore: - /* User mode accesses just cause a SIGSEGV */ - if (user_mode(regs)) { - tsk->thread.fault_address = address; - force_sig_fault(SIGSEGV, si_code, (void __user *)address, tsk); - return; - } + if (likely(!(fault & VM_FAULT_ERROR))) { + if (fault & VM_FAULT_MAJOR) { + tsk->maj_flt++; + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, + regs, address); + } else { + tsk->min_flt++; + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, + regs, address); + } -no_context: - /* Are we prepared to handle this kernel fault? - * - * (The kernel has valid exception-points in the source - * when it accesses user-memory. When it fails in one - * of those points, we find it in a table and do a jump - * to some fixup code that loads an appropriate error - * code) - */ - if (fixup_exception(regs)) + /* Normal return path: fault Handled Gracefully */ return; + } - die("Oops", regs, address); - -out_of_memory: - up_read(&mm->mmap_sem); + if (!user_mode(regs)) + goto no_context; - if (user_mode(regs)) { + if (fault & VM_FAULT_OOM) { pagefault_out_of_memory(); return; } - goto no_context; + if (fault & VM_FAULT_SIGBUS) { + sig = SIGBUS; + si_code = BUS_ADRERR; + } + else { + sig = SIGSEGV; + } -do_sigbus: - up_read(&mm->mmap_sem); + tsk->thread.fault_address = address; + force_sig_fault(sig, si_code, (void __user *)address); + return; - if (!user_mode(regs)) - goto no_context; +no_context: + if (fixup_exception(regs)) + return; - tsk->thread.fault_address = address; - force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)address, tsk); + die("Oops", regs, address); } diff --git a/arch/arc/mm/highmem.c b/arch/arc/mm/highmem.c index 11f57e2ced8a..a4856bfaedf3 100644 --- a/arch/arc/mm/highmem.c +++ b/arch/arc/mm/highmem.c @@ -1,10 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2015 Synopsys, Inc. (www.synopsys.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * */ #include <linux/memblock.h> diff --git a/arch/arc/mm/init.c b/arch/arc/mm/init.c index 02b7a3b20d7c..0920c969c466 100644 --- a/arch/arc/mm/init.c +++ b/arch/arc/mm/init.c @@ -1,9 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/kernel.h> diff --git a/arch/arc/mm/ioremap.c b/arch/arc/mm/ioremap.c index 9881bd740ccc..fac4adc90204 100644 --- a/arch/arc/mm/ioremap.c +++ b/arch/arc/mm/ioremap.c @@ -1,9 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/vmalloc.h> diff --git a/arch/arc/mm/mmap.c b/arch/arc/mm/mmap.c index 2e13683dfb24..722d26b94307 100644 --- a/arch/arc/mm/mmap.c +++ b/arch/arc/mm/mmap.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * ARC700 mmap * * (started from arm version - for VIPT alias handling) * * Copyright (C) 2013 Synopsys, Inc. (www.synopsys.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/fs.h> diff --git a/arch/arc/mm/tlb.c b/arch/arc/mm/tlb.c index 4097764fea23..10025e199353 100644 --- a/arch/arc/mm/tlb.c +++ b/arch/arc/mm/tlb.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TLB Management (flush/create/diagnostics) for ARC700 * * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * * vineetg: Aug 2011 * -Reintroduce duplicate PD fixup - some customer chips still have the issue * @@ -911,9 +908,11 @@ void do_tlb_overlap_fault(unsigned long cause, unsigned long address, struct pt_regs *regs) { struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu; - unsigned int pd0[mmu->ways]; unsigned long flags; - int set; + int set, n_ways = mmu->ways; + + n_ways = min(n_ways, 4); + BUG_ON(mmu->ways > 4); local_irq_save(flags); @@ -921,9 +920,10 @@ void do_tlb_overlap_fault(unsigned long cause, unsigned long address, for (set = 0; set < mmu->sets; set++) { int is_valid, way; + unsigned int pd0[4]; /* read out all the ways of current set */ - for (way = 0, is_valid = 0; way < mmu->ways; way++) { + for (way = 0, is_valid = 0; way < n_ways; way++) { write_aux_reg(ARC_REG_TLBINDEX, SET_WAY_TO_IDX(mmu, set, way)); write_aux_reg(ARC_REG_TLBCOMMAND, TLBRead); @@ -937,14 +937,14 @@ void do_tlb_overlap_fault(unsigned long cause, unsigned long address, continue; /* Scan the set for duplicate ways: needs a nested loop */ - for (way = 0; way < mmu->ways - 1; way++) { + for (way = 0; way < n_ways - 1; way++) { int n; if (!pd0[way]) continue; - for (n = way + 1; n < mmu->ways; n++) { + for (n = way + 1; n < n_ways; n++) { if (pd0[way] != pd0[n]) continue; diff --git a/arch/arc/mm/tlbex.S b/arch/arc/mm/tlbex.S index 0e1e47a67c73..c55d95dd2f39 100644 --- a/arch/arc/mm/tlbex.S +++ b/arch/arc/mm/tlbex.S @@ -1,12 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * TLB Exception Handling for ARC * * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * * Vineetg: April 2011 : * -MMU v1: moved out legacy code into a seperate file * -MMU v3: PD{0,1} bits layout changed: They don't overlap anymore, @@ -396,6 +393,17 @@ EV_TLBMissD_fast_ret: ; additional label for VDK OS-kit instrumentation ;-------- Common routine to call Linux Page Fault Handler ----------- do_slow_path_pf: +#ifdef CONFIG_ISA_ARCV2 + ; Set Z flag if exception in U mode. Hardware micro-ops do this on any + ; taken interrupt/exception, and thus is already the case at the entry + ; above, but ensuing code would have already clobbered. + ; EXCEPTION_PROLOGUE called in slow path, relies on correct Z flag set + + lr r2, [erstatus] + and r2, r2, STATUS_U_MASK + bxor.f 0, r2, STATUS_U_BIT +#endif + ; Restore the 4-scratch regs saved by fast path miss handler TLBMISS_RESTORE_REGS |