mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
xen: arm: handle variable p2m levels in apply_p2m_changes
As with previous changes this involves conversion from a linear series of lookups into a loop over the levels. Signed-off-by: Ian Campbell <ian.campbell@citrix.com> Cc: Arianna Avanzini <avanzini.arianna@gmail.com> Reviewed-by: Julien Grall <julien.grall@linaro.org>
This commit is contained in:
1 parent
58f0fd8822
commit
82985d7596
1 file changed
+74
-95
+74
-95
@@ -15,7 +15,6 @@
|
||||
|
||||
/* First level P2M is 2 consecutive pages */
|
||||
#define P2M_ROOT_ORDER 1
|
||||
#define P2M_ROOT_ENTRIES (LPAE_ENTRIES<<P2M_ROOT_ORDER)
|
||||
#define P2M_ROOT_PAGES (1<<P2M_ROOT_ORDER)
|
||||
|
||||
static bool_t p2m_valid(lpae_t pte)
|
||||
@@ -119,31 +118,6 @@ void flush_tlb_domain(struct domain *d)
|
||||
p2m_load_VTTBR(current->domain);
|
||||
}
|
||||
|
||||
static int p2m_first_level_index(paddr_t addr)
|
||||
{
|
||||
/*
|
||||
* 1st pages are concatenated so zeroeth offset gives us the
|
||||
* index of the 1st page
|
||||
*/
|
||||
return zeroeth_table_offset(addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Map whichever of the first pages contain addr. The caller should
|
||||
* then use first_table_offset as an index.
|
||||
*/
|
||||
static lpae_t *p2m_map_first(struct p2m_domain *p2m, paddr_t addr)
|
||||
{
|
||||
struct page_info *page;
|
||||
|
||||
if ( first_linear_offset(addr) >= P2M_ROOT_ENTRIES )
|
||||
return NULL;
|
||||
|
||||
page = p2m->root + p2m_first_level_index(addr);
|
||||
|
||||
return __map_domain_page(page);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup the MFN corresponding to a domain's PFN.
|
||||
*
|
||||
@@ -733,13 +707,12 @@ static int apply_p2m_changes(struct domain *d,
|
||||
{
|
||||
int rc, ret;
|
||||
struct p2m_domain *p2m = &d->arch.p2m;
|
||||
lpae_t *first = NULL, *second = NULL, *third = NULL;
|
||||
lpae_t *mappings[4] = { NULL, };
|
||||
paddr_t addr, orig_maddr = maddr;
|
||||
unsigned int level = 0;
|
||||
unsigned long cur_first_page = ~0,
|
||||
cur_first_offset = ~0,
|
||||
cur_second_offset = ~0;
|
||||
unsigned long count = 0;
|
||||
unsigned int cur_root_table = ~0;
|
||||
unsigned int cur_offset[4] = { ~0, };
|
||||
unsigned int count = 0;
|
||||
bool_t flush = false;
|
||||
bool_t flush_pt;
|
||||
|
||||
@@ -751,9 +724,21 @@ static int apply_p2m_changes(struct domain *d,
|
||||
|
||||
spin_lock(&p2m->lock);
|
||||
|
||||
/* Static mapping. P2M_ROOT_PAGES > 1 are handled below */
|
||||
if ( P2M_ROOT_PAGES == 1 )
|
||||
mappings[P2M_ROOT_LEVEL] = __map_domain_page(p2m->root);
|
||||
|
||||
addr = start_gpaddr;
|
||||
while ( addr < end_gpaddr )
|
||||
{
|
||||
int root_table;
|
||||
const unsigned int offsets[4] = {
|
||||
zeroeth_table_offset(addr),
|
||||
first_table_offset(addr),
|
||||
second_table_offset(addr),
|
||||
third_table_offset(addr)
|
||||
};
|
||||
|
||||
/*
|
||||
* Arbitrarily, preempt every 512 operations or 8192 nops.
|
||||
* 512*P2M_ONE_PROGRESS == 8192*P2M_ONE_PROGRESS_NOP == 0x2000
|
||||
@@ -773,76 +758,67 @@ static int apply_p2m_changes(struct domain *d,
|
||||
count = 0;
|
||||
}
|
||||
|
||||
if ( cur_first_page != p2m_first_level_index(addr) )
|
||||
if ( P2M_ROOT_PAGES > 1 )
|
||||
{
|
||||
if ( first ) unmap_domain_page(first);
|
||||
first = p2m_map_first(p2m, addr);
|
||||
if ( !first )
|
||||
int i;
|
||||
/*
|
||||
* Concatenated root-level tables. The table number will be the
|
||||
* offset at the previous level. It is not possible to concatenate
|
||||
* a level-0 root.
|
||||
*/
|
||||
ASSERT(P2M_ROOT_LEVEL > 0);
|
||||
root_table = offsets[P2M_ROOT_LEVEL - 1];
|
||||
if ( root_table >= P2M_ROOT_PAGES )
|
||||
{
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
cur_first_page = p2m_first_level_index(addr);
|
||||
/* Any mapping further down is now invalid */
|
||||
cur_first_offset = cur_second_offset = ~0;
|
||||
|
||||
if ( cur_root_table != root_table )
|
||||
{
|
||||
if ( mappings[P2M_ROOT_LEVEL] )
|
||||
unmap_domain_page(mappings[P2M_ROOT_LEVEL]);
|
||||
mappings[P2M_ROOT_LEVEL] =
|
||||
__map_domain_page(p2m->root + root_table);
|
||||
cur_root_table = root_table;
|
||||
/* Any mapping further down is now invalid */
|
||||
for ( i = P2M_ROOT_LEVEL; i < 4; i++ )
|
||||
cur_offset[i] = ~0;
|
||||
}
|
||||
}
|
||||
|
||||
/* We only use a 3 level p2m at the moment, so no level 0,
|
||||
* current hardware doesn't support super page mappings at
|
||||
* level 0 anyway */
|
||||
|
||||
level = 1;
|
||||
ret = apply_one_level(d, &first[first_table_offset(addr)],
|
||||
level, flush_pt, op,
|
||||
start_gpaddr, end_gpaddr,
|
||||
&addr, &maddr, &flush,
|
||||
mattr, t);
|
||||
if ( ret < 0 ) { rc = ret ; goto out; }
|
||||
count += ret;
|
||||
if ( ret != P2M_ONE_DESCEND ) continue;
|
||||
|
||||
BUG_ON(!p2m_valid(first[first_table_offset(addr)]));
|
||||
|
||||
if ( cur_first_offset != first_table_offset(addr) )
|
||||
for ( level = P2M_ROOT_LEVEL; level < 4; level++ )
|
||||
{
|
||||
if (second) unmap_domain_page(second);
|
||||
second = map_domain_page(first[first_table_offset(addr)].p2m.base);
|
||||
cur_first_offset = first_table_offset(addr);
|
||||
/* Any mapping further down is now invalid */
|
||||
cur_second_offset = ~0;
|
||||
unsigned offset = offsets[level];
|
||||
lpae_t *entry = &mappings[level][offset];
|
||||
|
||||
ret = apply_one_level(d, entry,
|
||||
level, flush_pt, op,
|
||||
start_gpaddr, end_gpaddr,
|
||||
&addr, &maddr, &flush,
|
||||
mattr, t);
|
||||
if ( ret < 0 ) { rc = ret ; goto out; }
|
||||
count += ret;
|
||||
/* L3 had better have done something! We cannot descend any further */
|
||||
BUG_ON(level == 3 && ret == P2M_ONE_DESCEND);
|
||||
if ( ret != P2M_ONE_DESCEND ) break;
|
||||
|
||||
BUG_ON(!p2m_valid(*entry));
|
||||
|
||||
if ( cur_offset[level] != offset )
|
||||
{
|
||||
/* Update mapping for next level */
|
||||
int i;
|
||||
if ( mappings[level+1] )
|
||||
unmap_domain_page(mappings[level+1]);
|
||||
mappings[level+1] = map_domain_page(entry->p2m.base);
|
||||
cur_offset[level] = offset;
|
||||
/* Any mapping further down is now invalid */
|
||||
for ( i = level+1; i < 4; i++ )
|
||||
cur_offset[i] = ~0;
|
||||
}
|
||||
/* else: next level already valid */
|
||||
}
|
||||
/* else: second already valid */
|
||||
|
||||
level = 2;
|
||||
ret = apply_one_level(d,&second[second_table_offset(addr)],
|
||||
level, flush_pt, op,
|
||||
start_gpaddr, end_gpaddr,
|
||||
&addr, &maddr, &flush,
|
||||
mattr, t);
|
||||
if ( ret < 0 ) { rc = ret ; goto out; }
|
||||
count += ret;
|
||||
if ( ret != P2M_ONE_DESCEND ) continue;
|
||||
|
||||
BUG_ON(!p2m_valid(second[second_table_offset(addr)]));
|
||||
|
||||
if ( cur_second_offset != second_table_offset(addr) )
|
||||
{
|
||||
/* map third level */
|
||||
if (third) unmap_domain_page(third);
|
||||
third = map_domain_page(second[second_table_offset(addr)].p2m.base);
|
||||
cur_second_offset = second_table_offset(addr);
|
||||
}
|
||||
|
||||
level = 3;
|
||||
ret = apply_one_level(d, &third[third_table_offset(addr)],
|
||||
level, flush_pt, op,
|
||||
start_gpaddr, end_gpaddr,
|
||||
&addr, &maddr, &flush,
|
||||
mattr, t);
|
||||
if ( ret < 0 ) { rc = ret ; goto out; }
|
||||
/* L3 had better have done something! We cannot descend any further */
|
||||
BUG_ON(ret == P2M_ONE_DESCEND);
|
||||
count += ret;
|
||||
}
|
||||
|
||||
if ( flush )
|
||||
@@ -866,9 +842,6 @@ static int apply_p2m_changes(struct domain *d,
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
if (third) unmap_domain_page(third);
|
||||
if (second) unmap_domain_page(second);
|
||||
if (first) unmap_domain_page(first);
|
||||
if ( rc < 0 && ( op == INSERT || op == ALLOCATE ) &&
|
||||
addr != start_gpaddr )
|
||||
{
|
||||
@@ -884,6 +857,12 @@ out:
|
||||
mattr, p2m_invalid);
|
||||
}
|
||||
|
||||
for ( level = P2M_ROOT_LEVEL; level < 4; level ++ )
|
||||
{
|
||||
if ( mappings[level] )
|
||||
unmap_domain_page(mappings[level]);
|
||||
}
|
||||
|
||||
spin_unlock(&p2m->lock);
|
||||
|
||||
return rc;
|
||||
|
||||