2005-04-17 02:20:36 +04:00
/*
2006-09-27 18:40:28 +04:00
* linux / arch / arm / mm / pgd . c
2005-04-17 02:20:36 +04:00
*
2005-10-28 17:48:37 +04:00
* Copyright ( C ) 1998 - 2005 Russell King
2005-04-17 02:20:36 +04:00
*
* 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/highmem.h>
# include <asm/pgalloc.h>
# include <asm/page.h>
# include <asm/tlbflush.h>
2006-08-21 20:06:38 +04:00
# include "mm.h"
2005-04-17 02:20:36 +04:00
# define FIRST_KERNEL_PGD_NR (FIRST_USER_PGD_NR + USER_PTRS_PER_PGD)
/*
* need to get a 16 k 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 , 2 ) ;
if ( ! new_pgd )
goto no_pgd ;
2008-10-27 14:24:09 +03:00
memset ( new_pgd , 0 , FIRST_KERNEL_PGD_NR * sizeof ( pgd_t ) ) ;
2005-04-17 02:20:36 +04:00
2005-06-27 17:08:56 +04:00
/*
* Copy over the kernel and IO PGD entries
*/
2005-04-17 02:20:36 +04:00
init_pgd = pgd_offset_k ( 0 ) ;
2005-06-27 17:08:56 +04:00
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 ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! vectors_high ( ) ) {
/*
* On ARM , first page must always be allocated since it
* contains the machine vectors .
*/
new_pmd = pmd_alloc ( mm , new_pgd , 0 ) ;
if ( ! new_pmd )
goto no_pmd ;
new_pte = pte_alloc_map ( mm , new_pmd , 0 ) ;
if ( ! new_pte )
goto no_pte ;
init_pmd = pmd_offset ( init_pgd , 0 ) ;
init_pte = pte_offset_map_nested ( init_pmd , 0 ) ;
2006-12-13 17:34:43 +03:00
set_pte_ext ( new_pte , * init_pte , 0 ) ;
2005-04-17 02:20:36 +04:00
pte_unmap_nested ( init_pte ) ;
pte_unmap ( new_pte ) ;
}
return new_pgd ;
no_pte :
2008-02-05 09:29:14 +03:00
pmd_free ( mm , new_pmd ) ;
2005-04-17 02:20:36 +04:00
no_pmd :
free_pages ( ( unsigned long ) new_pgd , 2 ) ;
no_pgd :
return NULL ;
}
2008-02-05 09:29:14 +03:00
void free_pgd_slow ( struct mm_struct * mm , pgd_t * pgd )
2005-04-17 02:20:36 +04:00
{
pmd_t * pmd ;
2008-02-27 15:44:59 +03:00
pgtable_t pte ;
2005-04-17 02:20:36 +04:00
if ( ! pgd )
return ;
/* pgd is always present and good */
2005-05-09 23:52:51 +04:00
pmd = pmd_off ( pgd , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( pmd_none ( * pmd ) )
goto free ;
if ( pmd_bad ( * pmd ) ) {
pmd_ERROR ( * pmd ) ;
pmd_clear ( pmd ) ;
goto free ;
}
2008-02-27 15:44:59 +03:00
pte = pmd_pgtable ( * pmd ) ;
2005-04-17 02:20:36 +04:00
pmd_clear ( pmd ) ;
2008-02-05 09:29:14 +03:00
pte_free ( mm , pte ) ;
pmd_free ( mm , pmd ) ;
2005-04-17 02:20:36 +04:00
free :
free_pages ( ( unsigned long ) pgd , 2 ) ;
}