forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
96 lines
2.0 KiB
96 lines
2.0 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
// Copyright (C) 2005-2017 Andes Technology Corporation |
|
|
|
#include <linux/init_task.h> |
|
|
|
#define __HAVE_ARCH_PGD_FREE |
|
#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_lruvec_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_lruvec_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; |
|
p4d_t *p4d; |
|
pud_t *pud; |
|
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); |
|
p4d = p4d_offset(pgd, i << PGDIR_SHIFT); |
|
pud = pud_offset(p4d, i << PGDIR_SHIFT); |
|
pmd = pmd_offset(pud + i, i << PGDIR_SHIFT); |
|
set_pmd(pmd, __pmd(pmdval)); |
|
} |
|
}
|
|
|