|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | // Copyright (C) 2005-2017 Andes Technology Corporation | 
|  |  | 
|  | #include <linux/init_task.h> | 
|  | #include <asm/pgalloc.h> | 
|  |  | 
|  | #define FIRST_KERNEL_PGD_NR	(USER_PTRS_PER_PGD) | 
|  |  | 
|  | /* | 
|  | * need to get a page for level 1 | 
|  | */ | 
|  |  | 
|  | pgd_t *pgd_alloc(struct mm_struct *mm) | 
|  | { | 
|  | pgd_t *new_pgd, *init_pgd; | 
|  | int i; | 
|  |  | 
|  | new_pgd = (pgd_t *) __get_free_pages(GFP_KERNEL, 0); | 
|  | if (!new_pgd) | 
|  | return NULL; | 
|  | for (i = 0; i < PTRS_PER_PGD; i++) { | 
|  | (*new_pgd) = 1; | 
|  | new_pgd++; | 
|  | } | 
|  | new_pgd -= PTRS_PER_PGD; | 
|  |  | 
|  | init_pgd = pgd_offset_k(0); | 
|  |  | 
|  | memcpy(new_pgd + FIRST_KERNEL_PGD_NR, init_pgd + FIRST_KERNEL_PGD_NR, | 
|  | (PTRS_PER_PGD - FIRST_KERNEL_PGD_NR) * sizeof(pgd_t)); | 
|  |  | 
|  | cpu_dcache_wb_range((unsigned long)new_pgd, | 
|  | (unsigned long)new_pgd + | 
|  | PTRS_PER_PGD * sizeof(pgd_t)); | 
|  | inc_zone_page_state(virt_to_page((unsigned long *)new_pgd), | 
|  | NR_PAGETABLE); | 
|  |  | 
|  | return new_pgd; | 
|  | } | 
|  |  | 
|  | void pgd_free(struct mm_struct *mm, pgd_t * pgd) | 
|  | { | 
|  | pmd_t *pmd; | 
|  | struct page *pte; | 
|  |  | 
|  | if (!pgd) | 
|  | return; | 
|  |  | 
|  | pmd = (pmd_t *) pgd; | 
|  | if (pmd_none(*pmd)) | 
|  | goto free; | 
|  | if (pmd_bad(*pmd)) { | 
|  | pmd_ERROR(*pmd); | 
|  | pmd_clear(pmd); | 
|  | goto free; | 
|  | } | 
|  |  | 
|  | pte = pmd_page(*pmd); | 
|  | pmd_clear(pmd); | 
|  | dec_zone_page_state(virt_to_page((unsigned long *)pgd), NR_PAGETABLE); | 
|  | pte_free(mm, pte); | 
|  | mm_dec_nr_ptes(mm); | 
|  | pmd_free(mm, pmd); | 
|  | free: | 
|  | free_pages((unsigned long)pgd, 0); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * In order to soft-boot, we need to insert a 1:1 mapping in place of | 
|  | * the user-mode pages.  This will then ensure that we have predictable | 
|  | * results when turning the mmu off | 
|  | */ | 
|  | void setup_mm_for_reboot(char mode) | 
|  | { | 
|  | unsigned long pmdval; | 
|  | pgd_t *pgd; | 
|  | pmd_t *pmd; | 
|  | int i; | 
|  |  | 
|  | if (current->mm && current->mm->pgd) | 
|  | pgd = current->mm->pgd; | 
|  | else | 
|  | pgd = init_mm.pgd; | 
|  |  | 
|  | for (i = 0; i < USER_PTRS_PER_PGD; i++) { | 
|  | pmdval = (i << PGDIR_SHIFT); | 
|  | pmd = pmd_offset(pgd + i, i << PGDIR_SHIFT); | 
|  | set_pmd(pmd, __pmd(pmdval)); | 
|  | } | 
|  | } |