Loading arch/arm64/include/asm/pgtable.h +1 −0 Original line number Diff line number Diff line Loading @@ -393,6 +393,7 @@ static inline int pmd_protnone(pmd_t pmd) #define pud_write(pud) pte_write(pud_pte(pud)) #define pud_pfn(pud) (((pud_val(pud) & PUD_MASK) & PHYS_MASK) >> PAGE_SHIFT) #define pfn_pud(pfn,prot) (__pud(((phys_addr_t)(pfn) << PAGE_SHIFT) | pgprot_val(prot))) #define set_pmd_at(mm, addr, pmdp, pmd) set_pte_at(mm, addr, (pte_t *)pmdp, pmd_pte(pmd)) Loading arch/arm64/include/asm/tlbflush.h +24 −8 Original line number Diff line number Diff line Loading @@ -60,6 +60,15 @@ __tlbi(op, (arg) | USER_ASID_FLAG); \ } while (0) /* This macro creates a properly formatted VA operand for the TLBI */ #define __TLBI_VADDR(addr, asid) \ ({ \ unsigned long __ta = (addr) >> 12; \ __ta &= GENMASK_ULL(43, 0); \ __ta |= (unsigned long)(asid) << 48; \ __ta; \ }) /* * TLB Management * ============== Loading Loading @@ -117,7 +126,7 @@ static inline void flush_tlb_all(void) static inline void flush_tlb_mm(struct mm_struct *mm) { unsigned long asid = ASID(mm) << 48; unsigned long asid = __TLBI_VADDR(0, ASID(mm)); dsb(ishst); __tlbi(aside1is, asid); Loading @@ -128,7 +137,7 @@ static inline void flush_tlb_mm(struct mm_struct *mm) static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) { unsigned long addr = uaddr >> 12 | (ASID(vma->vm_mm) << 48); unsigned long addr = __TLBI_VADDR(uaddr, ASID(vma->vm_mm)); dsb(ishst); __tlbi(vale1is, addr); Loading @@ -146,7 +155,7 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end, bool last_level) { unsigned long asid = ASID(vma->vm_mm) << 48; unsigned long asid = ASID(vma->vm_mm); unsigned long addr; if ((end - start) > MAX_TLB_RANGE) { Loading @@ -154,8 +163,8 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma, return; } start = asid | (start >> 12); end = asid | (end >> 12); start = __TLBI_VADDR(start, asid); end = __TLBI_VADDR(end, asid); dsb(ishst); for (addr = start; addr < end; addr += 1 << (PAGE_SHIFT - 12)) { Loading Loading @@ -185,8 +194,8 @@ static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end return; } start >>= 12; end >>= 12; start = __TLBI_VADDR(start, 0); end = __TLBI_VADDR(end, 0); dsb(ishst); for (addr = start; addr < end; addr += 1 << (PAGE_SHIFT - 12)) Loading @@ -202,13 +211,20 @@ static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end static inline void __flush_tlb_pgtable(struct mm_struct *mm, unsigned long uaddr) { unsigned long addr = uaddr >> 12 | (ASID(mm) << 48); unsigned long addr = __TLBI_VADDR(uaddr, ASID(mm)); __tlbi(vae1is, addr); __tlbi_user(vae1is, addr); dsb(ish); } static inline void __flush_tlb_kernel_pgtable(unsigned long kaddr) { unsigned long addr = __TLBI_VADDR(kaddr, 0); __tlbi(vaae1is, addr); dsb(ish); } #endif #endif arch/arm64/mm/mmu.c +66 −6 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ #include <asm/memblock.h> #include <asm/mmu_context.h> #include <asm/ptdump.h> #include <asm/tlbflush.h> #define NO_BLOCK_MAPPINGS BIT(0) #define NO_CONT_MAPPINGS BIT(1) Loading Loading @@ -1352,15 +1353,33 @@ int __init arch_ioremap_pmd_supported(void) int pud_set_huge(pud_t *pud, phys_addr_t phys, pgprot_t prot) { pgprot_t sect_prot = __pgprot(PUD_TYPE_SECT | pgprot_val(mk_sect_prot(prot))); pud_t new_pud = pfn_pud(__phys_to_pfn(phys), sect_prot); /* Only allow permission changes for now */ if (!pgattr_change_is_safe(READ_ONCE(pud_val(*pud)), pud_val(new_pud))) return 0; BUG_ON(phys & ~PUD_MASK); set_pud(pud, __pud(phys | PUD_TYPE_SECT | pgprot_val(mk_sect_prot(prot)))); set_pud(pud, new_pud); return 1; } int pmd_set_huge(pmd_t *pmd, phys_addr_t phys, pgprot_t prot) { pgprot_t sect_prot = __pgprot(PMD_TYPE_SECT | pgprot_val(mk_sect_prot(prot))); pmd_t new_pmd = pfn_pmd(__phys_to_pfn(phys), sect_prot); /* Only allow permission changes for now */ if (!pgattr_change_is_safe(READ_ONCE(pmd_val(*pmd)), pmd_val(new_pmd))) return 0; BUG_ON(phys & ~PMD_MASK); set_pmd(pmd, __pmd(phys | PMD_TYPE_SECT | pgprot_val(mk_sect_prot(prot)))); set_pmd(pmd, new_pmd); return 1; } Loading @@ -1380,12 +1399,53 @@ int pmd_clear_huge(pmd_t *pmd) return 1; } int pud_free_pmd_page(pud_t *pud, unsigned long addr) int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr) { return pud_none(*pud); pte_t *table; pmd_t pmd; pmd = READ_ONCE(*pmdp); if (!pmd_present(pmd)) return 1; if (!pmd_table(pmd)) { VM_WARN_ON(!pmd_table(pmd)); return 1; } table = pte_offset_kernel(pmdp, addr); pmd_clear(pmdp); __flush_tlb_kernel_pgtable(addr); pte_free_kernel(NULL, table); return 1; } int pmd_free_pte_page(pmd_t *pmd, unsigned long addr) int pud_free_pmd_page(pud_t *pudp, unsigned long addr) { return pmd_none(*pmd); pmd_t *table; pmd_t *pmdp; pud_t pud; unsigned long next, end; pud = READ_ONCE(*pudp); if (!pud_present(pud)) return 1; if (!pud_table(pud)) { VM_WARN_ON(!pud_table(pud)); return 1; } table = pmd_offset(pudp, addr); pmdp = table; next = addr; end = addr + PUD_SIZE; do { pmd_free_pte_page(pmdp, next); } while (pmdp++, next += PMD_SIZE, next != end); pud_clear(pudp); __flush_tlb_kernel_pgtable(addr); pmd_free(NULL, table); return 1; } Loading
arch/arm64/include/asm/pgtable.h +1 −0 Original line number Diff line number Diff line Loading @@ -393,6 +393,7 @@ static inline int pmd_protnone(pmd_t pmd) #define pud_write(pud) pte_write(pud_pte(pud)) #define pud_pfn(pud) (((pud_val(pud) & PUD_MASK) & PHYS_MASK) >> PAGE_SHIFT) #define pfn_pud(pfn,prot) (__pud(((phys_addr_t)(pfn) << PAGE_SHIFT) | pgprot_val(prot))) #define set_pmd_at(mm, addr, pmdp, pmd) set_pte_at(mm, addr, (pte_t *)pmdp, pmd_pte(pmd)) Loading
arch/arm64/include/asm/tlbflush.h +24 −8 Original line number Diff line number Diff line Loading @@ -60,6 +60,15 @@ __tlbi(op, (arg) | USER_ASID_FLAG); \ } while (0) /* This macro creates a properly formatted VA operand for the TLBI */ #define __TLBI_VADDR(addr, asid) \ ({ \ unsigned long __ta = (addr) >> 12; \ __ta &= GENMASK_ULL(43, 0); \ __ta |= (unsigned long)(asid) << 48; \ __ta; \ }) /* * TLB Management * ============== Loading Loading @@ -117,7 +126,7 @@ static inline void flush_tlb_all(void) static inline void flush_tlb_mm(struct mm_struct *mm) { unsigned long asid = ASID(mm) << 48; unsigned long asid = __TLBI_VADDR(0, ASID(mm)); dsb(ishst); __tlbi(aside1is, asid); Loading @@ -128,7 +137,7 @@ static inline void flush_tlb_mm(struct mm_struct *mm) static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) { unsigned long addr = uaddr >> 12 | (ASID(vma->vm_mm) << 48); unsigned long addr = __TLBI_VADDR(uaddr, ASID(vma->vm_mm)); dsb(ishst); __tlbi(vale1is, addr); Loading @@ -146,7 +155,7 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end, bool last_level) { unsigned long asid = ASID(vma->vm_mm) << 48; unsigned long asid = ASID(vma->vm_mm); unsigned long addr; if ((end - start) > MAX_TLB_RANGE) { Loading @@ -154,8 +163,8 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma, return; } start = asid | (start >> 12); end = asid | (end >> 12); start = __TLBI_VADDR(start, asid); end = __TLBI_VADDR(end, asid); dsb(ishst); for (addr = start; addr < end; addr += 1 << (PAGE_SHIFT - 12)) { Loading Loading @@ -185,8 +194,8 @@ static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end return; } start >>= 12; end >>= 12; start = __TLBI_VADDR(start, 0); end = __TLBI_VADDR(end, 0); dsb(ishst); for (addr = start; addr < end; addr += 1 << (PAGE_SHIFT - 12)) Loading @@ -202,13 +211,20 @@ static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end static inline void __flush_tlb_pgtable(struct mm_struct *mm, unsigned long uaddr) { unsigned long addr = uaddr >> 12 | (ASID(mm) << 48); unsigned long addr = __TLBI_VADDR(uaddr, ASID(mm)); __tlbi(vae1is, addr); __tlbi_user(vae1is, addr); dsb(ish); } static inline void __flush_tlb_kernel_pgtable(unsigned long kaddr) { unsigned long addr = __TLBI_VADDR(kaddr, 0); __tlbi(vaae1is, addr); dsb(ish); } #endif #endif
arch/arm64/mm/mmu.c +66 −6 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ #include <asm/memblock.h> #include <asm/mmu_context.h> #include <asm/ptdump.h> #include <asm/tlbflush.h> #define NO_BLOCK_MAPPINGS BIT(0) #define NO_CONT_MAPPINGS BIT(1) Loading Loading @@ -1352,15 +1353,33 @@ int __init arch_ioremap_pmd_supported(void) int pud_set_huge(pud_t *pud, phys_addr_t phys, pgprot_t prot) { pgprot_t sect_prot = __pgprot(PUD_TYPE_SECT | pgprot_val(mk_sect_prot(prot))); pud_t new_pud = pfn_pud(__phys_to_pfn(phys), sect_prot); /* Only allow permission changes for now */ if (!pgattr_change_is_safe(READ_ONCE(pud_val(*pud)), pud_val(new_pud))) return 0; BUG_ON(phys & ~PUD_MASK); set_pud(pud, __pud(phys | PUD_TYPE_SECT | pgprot_val(mk_sect_prot(prot)))); set_pud(pud, new_pud); return 1; } int pmd_set_huge(pmd_t *pmd, phys_addr_t phys, pgprot_t prot) { pgprot_t sect_prot = __pgprot(PMD_TYPE_SECT | pgprot_val(mk_sect_prot(prot))); pmd_t new_pmd = pfn_pmd(__phys_to_pfn(phys), sect_prot); /* Only allow permission changes for now */ if (!pgattr_change_is_safe(READ_ONCE(pmd_val(*pmd)), pmd_val(new_pmd))) return 0; BUG_ON(phys & ~PMD_MASK); set_pmd(pmd, __pmd(phys | PMD_TYPE_SECT | pgprot_val(mk_sect_prot(prot)))); set_pmd(pmd, new_pmd); return 1; } Loading @@ -1380,12 +1399,53 @@ int pmd_clear_huge(pmd_t *pmd) return 1; } int pud_free_pmd_page(pud_t *pud, unsigned long addr) int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr) { return pud_none(*pud); pte_t *table; pmd_t pmd; pmd = READ_ONCE(*pmdp); if (!pmd_present(pmd)) return 1; if (!pmd_table(pmd)) { VM_WARN_ON(!pmd_table(pmd)); return 1; } table = pte_offset_kernel(pmdp, addr); pmd_clear(pmdp); __flush_tlb_kernel_pgtable(addr); pte_free_kernel(NULL, table); return 1; } int pmd_free_pte_page(pmd_t *pmd, unsigned long addr) int pud_free_pmd_page(pud_t *pudp, unsigned long addr) { return pmd_none(*pmd); pmd_t *table; pmd_t *pmdp; pud_t pud; unsigned long next, end; pud = READ_ONCE(*pudp); if (!pud_present(pud)) return 1; if (!pud_table(pud)) { VM_WARN_ON(!pud_table(pud)); return 1; } table = pmd_offset(pudp, addr); pmdp = table; next = addr; end = addr + PUD_SIZE; do { pmd_free_pte_page(pmdp, next); } while (pmdp++, next += PMD_SIZE, next != end); pud_clear(pudp); __flush_tlb_kernel_pgtable(addr); pmd_free(NULL, table); return 1; }