|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | #include <linux/mm.h> | 
|  | #include <linux/hugetlb.h> | 
|  | #include <linux/security.h> | 
|  | #include <asm/cacheflush.h> | 
|  | #include <asm/machdep.h> | 
|  | #include <asm/mman.h> | 
|  | #include <asm/tlb.h> | 
|  |  | 
|  | void radix__flush_hugetlb_page(struct vm_area_struct *vma, unsigned long vmaddr) | 
|  | { | 
|  | int psize; | 
|  | struct hstate *hstate = hstate_file(vma->vm_file); | 
|  |  | 
|  | psize = hstate_get_psize(hstate); | 
|  | radix__flush_tlb_page_psize(vma->vm_mm, vmaddr, psize); | 
|  | } | 
|  |  | 
|  | void radix__local_flush_hugetlb_page(struct vm_area_struct *vma, unsigned long vmaddr) | 
|  | { | 
|  | int psize; | 
|  | struct hstate *hstate = hstate_file(vma->vm_file); | 
|  |  | 
|  | psize = hstate_get_psize(hstate); | 
|  | radix__local_flush_tlb_page_psize(vma->vm_mm, vmaddr, psize); | 
|  | } | 
|  |  | 
|  | void radix__flush_hugetlb_tlb_range(struct vm_area_struct *vma, unsigned long start, | 
|  | unsigned long end) | 
|  | { | 
|  | int psize; | 
|  | struct hstate *hstate = hstate_file(vma->vm_file); | 
|  |  | 
|  | psize = hstate_get_psize(hstate); | 
|  | radix__flush_tlb_range_psize(vma->vm_mm, start, end, psize); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * A vairant of hugetlb_get_unmapped_area doing topdown search | 
|  | * FIXME!! should we do as x86 does or non hugetlb area does ? | 
|  | * ie, use topdown or not based on mmap_is_legacy check ? | 
|  | */ | 
|  | unsigned long | 
|  | radix__hugetlb_get_unmapped_area(struct file *file, unsigned long addr, | 
|  | unsigned long len, unsigned long pgoff, | 
|  | unsigned long flags) | 
|  | { | 
|  | struct mm_struct *mm = current->mm; | 
|  | struct vm_area_struct *vma; | 
|  | struct hstate *h = hstate_file(file); | 
|  | int fixed = (flags & MAP_FIXED); | 
|  | unsigned long high_limit; | 
|  | struct vm_unmapped_area_info info; | 
|  |  | 
|  | high_limit = DEFAULT_MAP_WINDOW; | 
|  | if (addr >= high_limit || (fixed && (addr + len > high_limit))) | 
|  | high_limit = TASK_SIZE; | 
|  |  | 
|  | if (len & ~huge_page_mask(h)) | 
|  | return -EINVAL; | 
|  | if (len > high_limit) | 
|  | return -ENOMEM; | 
|  |  | 
|  | if (fixed) { | 
|  | if (addr > high_limit - len) | 
|  | return -ENOMEM; | 
|  | if (prepare_hugepage_range(file, addr, len)) | 
|  | return -EINVAL; | 
|  | return addr; | 
|  | } | 
|  |  | 
|  | if (addr) { | 
|  | addr = ALIGN(addr, huge_page_size(h)); | 
|  | vma = find_vma(mm, addr); | 
|  | if (high_limit - len >= addr && addr >= mmap_min_addr && | 
|  | (!vma || addr + len <= vm_start_gap(vma))) | 
|  | return addr; | 
|  | } | 
|  | /* | 
|  | * We are always doing an topdown search here. Slice code | 
|  | * does that too. | 
|  | */ | 
|  | info.flags = VM_UNMAPPED_AREA_TOPDOWN; | 
|  | info.length = len; | 
|  | info.low_limit = max(PAGE_SIZE, mmap_min_addr); | 
|  | info.high_limit = mm->mmap_base + (high_limit - DEFAULT_MAP_WINDOW); | 
|  | info.align_mask = PAGE_MASK & ~huge_page_mask(h); | 
|  | info.align_offset = 0; | 
|  |  | 
|  | return vm_unmapped_area(&info); | 
|  | } | 
|  |  | 
|  | void radix__huge_ptep_modify_prot_commit(struct vm_area_struct *vma, | 
|  | unsigned long addr, pte_t *ptep, | 
|  | pte_t old_pte, pte_t pte) | 
|  | { | 
|  | struct mm_struct *mm = vma->vm_mm; | 
|  |  | 
|  | /* | 
|  | * To avoid NMMU hang while relaxing access we need to flush the tlb before | 
|  | * we set the new value. | 
|  | */ | 
|  | if (is_pte_rw_upgrade(pte_val(old_pte), pte_val(pte)) && | 
|  | (atomic_read(&mm->context.copros) > 0)) | 
|  | radix__flush_hugetlb_page(vma, addr); | 
|  |  | 
|  | set_huge_pte_at(vma->vm_mm, addr, ptep, pte); | 
|  | } |