blob: 478bd2c6aa501a9e78b427506aaa1f8bbaf11c82 [file] [log] [blame]
Thomas Gleixnerd2912cb2019-06-04 10:11:33 +02001// SPDX-License-Identifier: GPL-2.0-only
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
Russell King0c668982006-09-27 15:40:28 +01003 * linux/arch/arm/mm/pgd.c
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
Russell King90072052005-10-28 14:48:37 +01005 * Copyright (C) 1998-2005 Russell King
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07007#include <linux/mm.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +09008#include <linux/gfp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07009#include <linux/highmem.h>
Catalin Marinasda028772011-11-22 17:30:29 +000010#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011
Russell King15d07dc2012-03-28 18:30:01 +010012#include <asm/cp15.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070013#include <asm/pgalloc.h>
14#include <asm/page.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <asm/tlbflush.h>
16
Russell King1b2e2b72006-08-21 17:06:38 +010017#include "mm.h"
18
Catalin Marinasda028772011-11-22 17:30:29 +000019#ifdef CONFIG_ARM_LPAE
Kees Cook6da2ec52018-06-12 13:55:00 -070020#define __pgd_alloc() kmalloc_array(PTRS_PER_PGD, sizeof(pgd_t), GFP_KERNEL)
Catalin Marinasda028772011-11-22 17:30:29 +000021#define __pgd_free(pgd) kfree(pgd)
22#else
Michal Hocko397b0802016-07-26 15:20:59 -070023#define __pgd_alloc() (pgd_t *)__get_free_pages(GFP_KERNEL, 2)
Catalin Marinasda028772011-11-22 17:30:29 +000024#define __pgd_free(pgd) free_pages((unsigned long)pgd, 2)
25#endif
26
Linus Torvalds1da177e2005-04-16 15:20:36 -070027/*
28 * need to get a 16k page for level 1
29 */
Russell Kingb0d03742010-11-21 11:00:56 +000030pgd_t *pgd_alloc(struct mm_struct *mm)
Linus Torvalds1da177e2005-04-16 15:20:36 -070031{
32 pgd_t *new_pgd, *init_pgd;
Russell King516295e2010-11-21 16:27:49 +000033 pud_t *new_pud, *init_pud;
Linus Torvalds1da177e2005-04-16 15:20:36 -070034 pmd_t *new_pmd, *init_pmd;
35 pte_t *new_pte, *init_pte;
36
Catalin Marinasda028772011-11-22 17:30:29 +000037 new_pgd = __pgd_alloc();
Linus Torvalds1da177e2005-04-16 15:20:36 -070038 if (!new_pgd)
39 goto no_pgd;
40
Russell Kinge926f442010-11-21 11:55:37 +000041 memset(new_pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t));
Linus Torvalds1da177e2005-04-16 15:20:36 -070042
Russell Kinga343e602005-06-27 14:08:56 +010043 /*
44 * Copy over the kernel and IO PGD entries
45 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070046 init_pgd = pgd_offset_k(0);
Russell Kinge926f442010-11-21 11:55:37 +000047 memcpy(new_pgd + USER_PTRS_PER_PGD, init_pgd + USER_PTRS_PER_PGD,
48 (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
Russell Kinga343e602005-06-27 14:08:56 +010049
50 clean_dcache_area(new_pgd, PTRS_PER_PGD * sizeof(pgd_t));
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
Catalin Marinasda028772011-11-22 17:30:29 +000052#ifdef CONFIG_ARM_LPAE
53 /*
54 * Allocate PMD table for modules and pkmap mappings.
55 */
56 new_pud = pud_alloc(mm, new_pgd + pgd_index(MODULES_VADDR),
57 MODULES_VADDR);
58 if (!new_pud)
59 goto no_pud;
60
61 new_pmd = pmd_alloc(mm, new_pud, 0);
62 if (!new_pmd)
63 goto no_pmd;
64#endif
65
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 if (!vectors_high()) {
67 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 * On ARM, first page must always be allocated since it
Catalin Marinasda028772011-11-22 17:30:29 +000069 * contains the machine vectors. The vectors are always high
70 * with LPAE.
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 */
Russell King516295e2010-11-21 16:27:49 +000072 new_pud = pud_alloc(mm, new_pgd, 0);
73 if (!new_pud)
74 goto no_pud;
75
76 new_pmd = pmd_alloc(mm, new_pud, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 if (!new_pmd)
78 goto no_pmd;
79
Kirill A. Shutemov3ed3a4f2016-03-17 14:19:11 -070080 new_pte = pte_alloc_map(mm, new_pmd, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 if (!new_pte)
82 goto no_pte;
83
Russell Kinga02d8df2015-08-21 09:38:31 +010084#ifndef CONFIG_ARM_LPAE
85 /*
86 * Modify the PTE pointer to have the correct domain. This
87 * needs to be the vectors domain to avoid the low vectors
88 * being unmapped.
89 */
90 pmd_val(*new_pmd) &= ~PMD_DOMAIN_MASK;
91 pmd_val(*new_pmd) |= PMD_DOMAIN(DOMAIN_VECTORS);
92#endif
93
Russell King516295e2010-11-21 16:27:49 +000094 init_pud = pud_offset(init_pgd, 0);
95 init_pmd = pmd_offset(init_pud, 0);
Peter Zijlstraece0e2b2010-10-26 14:21:52 -070096 init_pte = pte_offset_map(init_pmd, 0);
Russell Kingd8aa7122013-11-28 21:43:40 +000097 set_pte_ext(new_pte + 0, init_pte[0], 0);
98 set_pte_ext(new_pte + 1, init_pte[1], 0);
Peter Zijlstraece0e2b2010-10-26 14:21:52 -070099 pte_unmap(init_pte);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 pte_unmap(new_pte);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 }
102
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 return new_pgd;
104
105no_pte:
Benjamin Herrenschmidt5e541972008-02-04 22:29:14 -0800106 pmd_free(mm, new_pmd);
Kirill A. Shutemovb30fe6c2015-02-11 15:26:53 -0800107 mm_dec_nr_pmds(mm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108no_pmd:
Russell King516295e2010-11-21 16:27:49 +0000109 pud_free(mm, new_pud);
110no_pud:
Catalin Marinasda028772011-11-22 17:30:29 +0000111 __pgd_free(new_pgd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112no_pgd:
113 return NULL;
114}
115
Russell King6e4beb52010-11-21 23:48:55 +0000116void pgd_free(struct mm_struct *mm, pgd_t *pgd_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117{
Russell King6e4beb52010-11-21 23:48:55 +0000118 pgd_t *pgd;
Russell King516295e2010-11-21 16:27:49 +0000119 pud_t *pud;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 pmd_t *pmd;
Uwe Kleine-König0c82d832008-02-27 13:44:59 +0100121 pgtable_t pte;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122
Russell King6e4beb52010-11-21 23:48:55 +0000123 if (!pgd_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124 return;
125
Russell King6e4beb52010-11-21 23:48:55 +0000126 pgd = pgd_base + pgd_index(0);
127 if (pgd_none_or_clear_bad(pgd))
128 goto no_pgd;
129
Russell King516295e2010-11-21 16:27:49 +0000130 pud = pud_offset(pgd, 0);
131 if (pud_none_or_clear_bad(pud))
132 goto no_pud;
133
134 pmd = pmd_offset(pud, 0);
Russell King6e4beb52010-11-21 23:48:55 +0000135 if (pmd_none_or_clear_bad(pmd))
136 goto no_pmd;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137
Uwe Kleine-König0c82d832008-02-27 13:44:59 +0100138 pte = pmd_pgtable(*pmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 pmd_clear(pmd);
Benjamin Herrenschmidt5e541972008-02-04 22:29:14 -0800140 pte_free(mm, pte);
Kirill A. Shutemovc4812902017-11-15 17:35:37 -0800141 mm_dec_nr_ptes(mm);
Russell King6e4beb52010-11-21 23:48:55 +0000142no_pmd:
Russell King516295e2010-11-21 16:27:49 +0000143 pud_clear(pud);
Benjamin Herrenschmidt5e541972008-02-04 22:29:14 -0800144 pmd_free(mm, pmd);
Kirill A. Shutemovb30fe6c2015-02-11 15:26:53 -0800145 mm_dec_nr_pmds(mm);
Russell King516295e2010-11-21 16:27:49 +0000146no_pud:
147 pgd_clear(pgd);
148 pud_free(mm, pud);
Russell King6e4beb52010-11-21 23:48:55 +0000149no_pgd:
Catalin Marinasda028772011-11-22 17:30:29 +0000150#ifdef CONFIG_ARM_LPAE
151 /*
152 * Free modules/pkmap or identity pmd tables.
153 */
154 for (pgd = pgd_base; pgd < pgd_base + PTRS_PER_PGD; pgd++) {
155 if (pgd_none_or_clear_bad(pgd))
156 continue;
157 if (pgd_val(*pgd) & L_PGD_SWAPPER)
158 continue;
159 pud = pud_offset(pgd, 0);
160 if (pud_none_or_clear_bad(pud))
161 continue;
162 pmd = pmd_offset(pud, 0);
163 pud_clear(pud);
164 pmd_free(mm, pmd);
Kirill A. Shutemovb30fe6c2015-02-11 15:26:53 -0800165 mm_dec_nr_pmds(mm);
Catalin Marinasda028772011-11-22 17:30:29 +0000166 pgd_clear(pgd);
167 pud_free(mm, pud);
168 }
169#endif
170 __pgd_free(pgd_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171}