linux/arch/arm/include/asm/pgalloc.h
<<
>>
Prefs
   1/*
   2 *  arch/arm/include/asm/pgalloc.h
   3 *
   4 *  Copyright (C) 2000-2001 Russell King
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 */
  10#ifndef _ASMARM_PGALLOC_H
  11#define _ASMARM_PGALLOC_H
  12
  13#include <linux/pagemap.h>
  14
  15#include <asm/domain.h>
  16#include <asm/pgtable-hwdef.h>
  17#include <asm/processor.h>
  18#include <asm/cacheflush.h>
  19#include <asm/tlbflush.h>
  20
  21#define check_pgt_cache()               do { } while (0)
  22
  23#ifdef CONFIG_MMU
  24
  25#define _PAGE_USER_TABLE        (PMD_TYPE_TABLE | PMD_BIT4 | PMD_DOMAIN(DOMAIN_USER))
  26#define _PAGE_KERNEL_TABLE      (PMD_TYPE_TABLE | PMD_BIT4 | PMD_DOMAIN(DOMAIN_KERNEL))
  27
  28#ifdef CONFIG_ARM_LPAE
  29
  30static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr)
  31{
  32        return (pmd_t *)get_zeroed_page(GFP_KERNEL);
  33}
  34
  35static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
  36{
  37        BUG_ON((unsigned long)pmd & (PAGE_SIZE-1));
  38        free_page((unsigned long)pmd);
  39}
  40
  41static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd)
  42{
  43        set_pud(pud, __pud(__pa(pmd) | PMD_TYPE_TABLE));
  44}
  45
  46#else   /* !CONFIG_ARM_LPAE */
  47
  48/*
  49 * Since we have only two-level page tables, these are trivial
  50 */
  51#define pmd_alloc_one(mm,addr)          ({ BUG(); ((pmd_t *)2); })
  52#define pmd_free(mm, pmd)               do { } while (0)
  53#define pud_populate(mm,pmd,pte)        BUG()
  54
  55#endif  /* CONFIG_ARM_LPAE */
  56
  57extern pgd_t *pgd_alloc(struct mm_struct *mm);
  58extern void pgd_free(struct mm_struct *mm, pgd_t *pgd);
  59
  60#define PGALLOC_GFP     (GFP_KERNEL | __GFP_ZERO)
  61
  62static inline void clean_pte_table(pte_t *pte)
  63{
  64        clean_dcache_area(pte + PTE_HWTABLE_PTRS, PTE_HWTABLE_SIZE);
  65}
  66
  67/*
  68 * Allocate one PTE table.
  69 *
  70 * This actually allocates two hardware PTE tables, but we wrap this up
  71 * into one table thus:
  72 *
  73 *  +------------+
  74 *  | Linux pt 0 |
  75 *  +------------+
  76 *  | Linux pt 1 |
  77 *  +------------+
  78 *  |  h/w pt 0  |
  79 *  +------------+
  80 *  |  h/w pt 1  |
  81 *  +------------+
  82 */
  83static inline pte_t *
  84pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr)
  85{
  86        pte_t *pte;
  87
  88        pte = (pte_t *)__get_free_page(PGALLOC_GFP);
  89        if (pte)
  90                clean_pte_table(pte);
  91
  92        return pte;
  93}
  94
  95static inline pgtable_t
  96pte_alloc_one(struct mm_struct *mm, unsigned long addr)
  97{
  98        struct page *pte;
  99
 100#ifdef CONFIG_HIGHPTE
 101        pte = alloc_pages(PGALLOC_GFP | __GFP_HIGHMEM, 0);
 102#else
 103        pte = alloc_pages(PGALLOC_GFP, 0);
 104#endif
 105        if (!pte)
 106                return NULL;
 107        if (!PageHighMem(pte))
 108                clean_pte_table(page_address(pte));
 109        if (!pgtable_page_ctor(pte)) {
 110                __free_page(pte);
 111                return NULL;
 112        }
 113        return pte;
 114}
 115
 116/*
 117 * Free one PTE table.
 118 */
 119static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
 120{
 121        if (pte)
 122                free_page((unsigned long)pte);
 123}
 124
 125static inline void pte_free(struct mm_struct *mm, pgtable_t pte)
 126{
 127        pgtable_page_dtor(pte);
 128        __free_page(pte);
 129}
 130
 131static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t pte,
 132                                  pmdval_t prot)
 133{
 134        pmdval_t pmdval = (pte + PTE_HWTABLE_OFF) | prot;
 135        pmdp[0] = __pmd(pmdval);
 136#ifndef CONFIG_ARM_LPAE
 137        pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t));
 138#endif
 139        flush_pmd_entry(pmdp);
 140}
 141
 142/*
 143 * Populate the pmdp entry with a pointer to the pte.  This pmd is part
 144 * of the mm address space.
 145 *
 146 * Ensure that we always set both PMD entries.
 147 */
 148static inline void
 149pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmdp, pte_t *ptep)
 150{
 151        /*
 152         * The pmd must be loaded with the physical address of the PTE table
 153         */
 154        __pmd_populate(pmdp, __pa(ptep), _PAGE_KERNEL_TABLE);
 155}
 156
 157static inline void
 158pmd_populate(struct mm_struct *mm, pmd_t *pmdp, pgtable_t ptep)
 159{
 160        extern pmdval_t user_pmd_table;
 161        pmdval_t prot;
 162
 163        if (__LINUX_ARM_ARCH__ >= 6 && !IS_ENABLED(CONFIG_ARM_LPAE))
 164                prot = user_pmd_table;
 165        else
 166                prot = _PAGE_USER_TABLE;
 167
 168        __pmd_populate(pmdp, page_to_phys(ptep), prot);
 169}
 170#define pmd_pgtable(pmd) pmd_page(pmd)
 171
 172#endif /* CONFIG_MMU */
 173
 174#endif
 175