@ -167,6 +167,37 @@ static void set_pte_table(u64 *pte, u64 *table)
* pte = PTE_TYPE_TABLE | ( ulong ) table ;
}
/* Splits a block PTE into table with subpages spanning the old block */
static void split_block ( u64 * pte , int level )
{
u64 old_pte = * pte ;
u64 * new_table ;
u64 i = 0 ;
/* level describes the parent level, we need the child ones */
int levelshift = level2shift ( level + 1 ) ;
if ( pte_type ( pte ) ! = PTE_TYPE_BLOCK )
panic ( " PTE %p (%llx) is not a block. Some driver code wants to "
" modify dcache settings for an range not covered in "
" mem_map. " , pte , old_pte ) ;
new_table = create_table ( ) ;
debug ( " Splitting pte %p (%llx) into %p \n " , pte , old_pte , new_table ) ;
for ( i = 0 ; i < MAX_PTE_ENTRIES ; i + + ) {
new_table [ i ] = old_pte | ( i < < levelshift ) ;
/* Level 3 block PTEs have the table type */
if ( ( level + 1 ) = = 3 )
new_table [ i ] | = PTE_TYPE_TABLE ;
debug ( " Setting new_table[%lld] = %llx \n " , i , new_table [ i ] ) ;
}
/* Set the new table into effect */
set_pte_table ( pte , new_table ) ;
}
/* Add one mm_region map entry to the page tables */
static void add_map ( struct mm_region * map )
{
@ -188,6 +219,8 @@ static void add_map(struct mm_region *map)
for ( level = 1 ; level < 4 ; level + + ) {
pte = find_pte ( addr , level ) ;
if ( ! pte )
panic ( " pte not found \n " ) ;
blocksize = 1ULL < < level2shift ( level ) ;
debug ( " Checking if pte fits for addr=%llx size=%llx "
" blocksize=%llx \n " , addr , size , blocksize ) ;
@ -199,48 +232,21 @@ static void add_map(struct mm_region *map)
addr + = blocksize ;
size - = blocksize ;
break ;
} else if ( ( pte_type ( pte ) = = PTE_TYPE_FAULT ) ) {
} else if ( pte_type ( pte ) = = PTE_TYPE_FAULT ) {
/* Page doesn't fit, create subpages */
debug ( " Creating subtable for addr 0x%llx "
" blksize=%llx \n " , addr , blocksize ) ;
new_table = create_table ( ) ;
set_pte_table ( pte , new_table ) ;
} else if ( pte_type ( pte ) = = PTE_TYPE_BLOCK ) {
debug ( " Split block into subtable for addr 0x%llx blksize=0x%llx \n " ,
addr , blocksize ) ;
split_block ( pte , level ) ;
}
}
}
}
/* Splits a block PTE into table with subpages spanning the old block */
static void split_block ( u64 * pte , int level )
{
u64 old_pte = * pte ;
u64 * new_table ;
u64 i = 0 ;
/* level describes the parent level, we need the child ones */
int levelshift = level2shift ( level + 1 ) ;
if ( pte_type ( pte ) ! = PTE_TYPE_BLOCK )
panic ( " PTE %p (%llx) is not a block. Some driver code wants to "
" modify dcache settings for an range not covered in "
" mem_map. " , pte , old_pte ) ;
new_table = create_table ( ) ;
debug ( " Splitting pte %p (%llx) into %p \n " , pte , old_pte , new_table ) ;
for ( i = 0 ; i < MAX_PTE_ENTRIES ; i + + ) {
new_table [ i ] = old_pte | ( i < < levelshift ) ;
/* Level 3 block PTEs have the table type */
if ( ( level + 1 ) = = 3 )
new_table [ i ] | = PTE_TYPE_TABLE ;
debug ( " Setting new_table[%lld] = %llx \n " , i , new_table [ i ] ) ;
}
/* Set the new table into effect */
set_pte_table ( pte , new_table ) ;
}
enum pte_type {
PTE_INVAL ,
PTE_BLOCK ,