diff options
Diffstat (limited to 'arch/unicore32/mm/pgd.c')
| -rw-r--r-- | arch/unicore32/mm/pgd.c | 102 | 
1 files changed, 102 insertions, 0 deletions
diff --git a/arch/unicore32/mm/pgd.c b/arch/unicore32/mm/pgd.c new file mode 100644 index 00000000000..08b8d4295e7 --- /dev/null +++ b/arch/unicore32/mm/pgd.c @@ -0,0 +1,102 @@ +/* + * linux/arch/unicore32/mm/pgd.c + * + * Code specific to PKUnity SoC and UniCore ISA + * + * Copyright (C) 2001-2010 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/mm.h> +#include <linux/gfp.h> +#include <linux/highmem.h> + +#include <asm/pgalloc.h> +#include <asm/page.h> +#include <asm/tlbflush.h> + +#include "mm.h" + +#define FIRST_KERNEL_PGD_NR	(FIRST_USER_PGD_NR + USER_PTRS_PER_PGD) + +/* + * need to get a 4k page for level 1 + */ +pgd_t *get_pgd_slow(struct mm_struct *mm) +{ +	pgd_t *new_pgd, *init_pgd; +	pmd_t *new_pmd, *init_pmd; +	pte_t *new_pte, *init_pte; + +	new_pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, 0); +	if (!new_pgd) +		goto no_pgd; + +	memset(new_pgd, 0, FIRST_KERNEL_PGD_NR * sizeof(pgd_t)); + +	/* +	 * Copy over the kernel and IO PGD entries +	 */ +	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)); + +	clean_dcache_area(new_pgd, PTRS_PER_PGD * sizeof(pgd_t)); + +	if (!vectors_high()) { +		/* +		 * On UniCore, first page must always be allocated since it +		 * contains the machine vectors. +		 */ +		new_pmd = pmd_alloc(mm, (pud_t *)new_pgd, 0); +		if (!new_pmd) +			goto no_pmd; + +		new_pte = pte_alloc_map(mm, NULL, new_pmd, 0); +		if (!new_pte) +			goto no_pte; + +		init_pmd = pmd_offset((pud_t *)init_pgd, 0); +		init_pte = pte_offset_map(init_pmd, 0); +		set_pte(new_pte, *init_pte); +		pte_unmap(init_pte); +		pte_unmap(new_pte); +	} + +	return new_pgd; + +no_pte: +	pmd_free(mm, new_pmd); +no_pmd: +	free_pages((unsigned long)new_pgd, 0); +no_pgd: +	return NULL; +} + +void free_pgd_slow(struct mm_struct *mm, pgd_t *pgd) +{ +	pmd_t *pmd; +	pgtable_t pte; + +	if (!pgd) +		return; + +	/* pgd is always present and good */ +	pmd = pmd_off(pgd, 0); +	if (pmd_none(*pmd)) +		goto free; +	if (pmd_bad(*pmd)) { +		pmd_ERROR(*pmd); +		pmd_clear(pmd); +		goto free; +	} + +	pte = pmd_pgtable(*pmd); +	pmd_clear(pmd); +	pte_free(mm, pte); +	pmd_free(mm, pmd); +free: +	free_pages((unsigned long) pgd, 0); +}  | 
