x86/mm: split out pv grant table code

Move the code to pv/grant_table.c. Nothing needs to be done with
regard to headers.

Signed-off-by: Wei Liu <wei.liu2@citrix.com>
Acked-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
Wei Liu committed 2017-09-22 16:31:50 +01:00
1 parent 39061c6099
commit 0bf001d21b
3 files changed
+328 -283

No files matched your search

-283
View File
@@ -3631,289 +3631,6 @@ long do_mmu_update(
return rc;
}
static unsigned int grant_to_pte_flags(unsigned int grant_flags,
unsigned int cache_flags)
{
unsigned int pte_flags =
_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_GNTTAB | _PAGE_NX;
if ( grant_flags & GNTMAP_application_map )
pte_flags |= _PAGE_USER;
if ( !(grant_flags & GNTMAP_readonly) )
pte_flags |= _PAGE_RW;
pte_flags |= MASK_INSR((grant_flags >> _GNTMAP_guest_avail0), _PAGE_AVAIL);
pte_flags |= cacheattr_to_pte_flags(cache_flags >> 5);
return pte_flags;
}
int create_grant_pv_mapping(uint64_t addr, unsigned long frame,
unsigned int flags, unsigned int cache_flags)
{
struct vcpu *curr = current;
struct domain *currd = curr->domain;
l1_pgentry_t nl1e, ol1e = { }, *pl1e;
struct page_info *page;
mfn_t gl1mfn;
int rc = GNTST_general_error;
nl1e = l1e_from_pfn(frame, grant_to_pte_flags(flags, cache_flags));
nl1e = adjust_guest_l1e(nl1e, currd);
/*
* The meaning of addr depends on GNTMAP_contains_pte. It is either a
* machine address of an L1e the guest has nominated to be altered, or a
* linear address we need to look up the appropriate L1e for.
*/
if ( flags & GNTMAP_contains_pte )
{
/* addr must be suitably aligned, or we will corrupt adjacent ptes. */
if ( !IS_ALIGNED(addr, sizeof(nl1e)) )
{
gdprintk(XENLOG_WARNING,
"Misaligned PTE address %"PRIx64"\n", addr);
goto out;
}
gl1mfn = _mfn(addr >> PAGE_SHIFT);
if ( !get_page_from_mfn(gl1mfn, currd) )
goto out;
pl1e = map_domain_page(gl1mfn) + (addr & ~PAGE_MASK);
}
else
{
/* Guest trying to pass an out-of-range linear address? */
if ( is_pv_32bit_domain(currd) && addr != (uint32_t)addr )
goto out;
pl1e = map_guest_l1e(addr, &gl1mfn);
if ( !pl1e )
{
gdprintk(XENLOG_WARNING,
"Could not find L1 PTE for linear address %"PRIx64"\n",
addr);
goto out;
}
if ( !get_page_from_mfn(gl1mfn, currd) )
goto out_unmap;
}
page = mfn_to_page(gl1mfn);
if ( !page_lock(page) )
goto out_put;
if ( (page->u.inuse.type_info & PGT_type_mask) != PGT_l1_page_table )
goto out_unlock;
ol1e = *pl1e;
if ( UPDATE_ENTRY(l1, pl1e, ol1e, nl1e, mfn_x(gl1mfn), curr, 0) )
rc = GNTST_okay;
out_unlock:
page_unlock(page);
out_put:
put_page(page);
out_unmap:
unmap_domain_page(pl1e);
if ( rc == GNTST_okay )
put_page_from_l1e(ol1e, currd);
out:
return rc;
}
/*
* This exists soley for implementing GNTABOP_unmap_and_replace, the ABI of
* which is bizarre. This GNTTABOP isn't used any more, but was used by
* classic-xen kernels and PVOps Linux before the M2P_OVERRIDE infrastructure
* was replaced with something which actually worked.
*
* Look up the L1e mapping linear, and zap it. Return the L1e via *out.
* Returns a boolean indicating success. If success, the caller is
* responsible for calling put_page_from_l1e().
*/
static bool steal_linear_address(unsigned long linear, l1_pgentry_t *out)
{
struct vcpu *curr = current;
struct domain *currd = curr->domain;
l1_pgentry_t *pl1e, ol1e;
struct page_info *page;
mfn_t gl1mfn;
bool okay = false;
ASSERT(is_pv_domain(currd));
pl1e = map_guest_l1e(linear, &gl1mfn);
if ( !pl1e )
{
gdprintk(XENLOG_WARNING,
"Could not find L1 PTE for linear %"PRIx64"\n", linear);
goto out;
}
if ( !get_page_from_mfn(gl1mfn, currd) )
goto out_unmap;
page = mfn_to_page(gl1mfn);
if ( !page_lock(page) )
goto out_put;
if ( (page->u.inuse.type_info & PGT_type_mask) != PGT_l1_page_table )
goto out_unlock;
ol1e = *pl1e;
okay = UPDATE_ENTRY(l1, pl1e, ol1e, l1e_empty(), mfn_x(gl1mfn), curr, 0);
out_unlock:
page_unlock(page);
out_put:
put_page(page);
out_unmap:
unmap_domain_page(pl1e);
if ( okay )
*out = ol1e;
out:
return okay;
}
/*
* Passing a new_addr of zero is taken to mean destroy. Passing a non-zero
* new_addr has only ever been available via GNTABOP_unmap_and_replace, and
* only when !(flags & GNTMAP_contains_pte).
*/
int replace_grant_pv_mapping(uint64_t addr, unsigned long frame,
uint64_t new_addr, unsigned int flags)
{
struct vcpu *curr = current;
struct domain *currd = curr->domain;
l1_pgentry_t nl1e = l1e_empty(), ol1e, *pl1e;
struct page_info *page;
mfn_t gl1mfn;
int rc = GNTST_general_error;
unsigned int grant_pte_flags = grant_to_pte_flags(flags, 0);
/*
* On top of the explicit settings done by create_grant_pv_mapping()
* also open-code relevant parts of adjust_guest_l1e(). Don't mirror
* available and cachability flags, though.
*/
if ( !is_pv_32bit_domain(currd) )
grant_pte_flags |= (grant_pte_flags & _PAGE_USER)
? _PAGE_GLOBAL
: _PAGE_GUEST_KERNEL | _PAGE_USER;
/*
* addr comes from Xen's active_entry tracking, and was used successfully
* to create a grant.
*
* The meaning of addr depends on GNTMAP_contains_pte. It is either a
* machine address of an L1e the guest has nominated to be altered, or a
* linear address we need to look up the appropriate L1e for.
*/
if ( flags & GNTMAP_contains_pte )
{
/* Replace not available in this addressing mode. */
if ( new_addr )
goto out;
/* Sanity check that we won't clobber the pagetable. */
if ( !IS_ALIGNED(addr, sizeof(nl1e)) )
{
ASSERT_UNREACHABLE();
goto out;
}
gl1mfn = _mfn(addr >> PAGE_SHIFT);
if ( !get_page_from_mfn(gl1mfn, currd) )
goto out;
pl1e = map_domain_page(gl1mfn) + (addr & ~PAGE_MASK);
}
else
{
if ( is_pv_32bit_domain(currd) )
{
if ( addr != (uint32_t)addr )
{
ASSERT_UNREACHABLE();
goto out;
}
/* Guest trying to pass an out-of-range linear address? */
if ( new_addr != (uint32_t)new_addr )
goto out;
}
if ( new_addr && !steal_linear_address(new_addr, &nl1e) )
goto out;
pl1e = map_guest_l1e(addr, &gl1mfn);
if ( !pl1e )
goto out;
if ( !get_page_from_mfn(gl1mfn, currd) )
goto out_unmap;
}
page = mfn_to_page(gl1mfn);
if ( !page_lock(page) )
goto out_put;
if ( (page->u.inuse.type_info & PGT_type_mask) != PGT_l1_page_table )
goto out_unlock;
ol1e = *pl1e;
/*
* Check that the address supplied is actually mapped to frame (with
* appropriate permissions).
*/
if ( unlikely(l1e_get_pfn(ol1e) != frame) ||
unlikely((l1e_get_flags(ol1e) ^ grant_pte_flags) &
(_PAGE_PRESENT | _PAGE_RW)) )
{
gdprintk(XENLOG_ERR,
"PTE %"PRIpte" for %"PRIx64" doesn't match grant (%"PRIpte")\n",
l1e_get_intpte(ol1e), addr,
l1e_get_intpte(l1e_from_pfn(frame, grant_pte_flags)));
goto out_unlock;
}
if ( unlikely((l1e_get_flags(ol1e) ^ grant_pte_flags) &
~(_PAGE_AVAIL | PAGE_CACHE_ATTRS)) )
gdprintk(XENLOG_WARNING,
"PTE flags %x for %"PRIx64" don't match grant (%x)\n",
l1e_get_flags(ol1e), addr, grant_pte_flags);
if ( UPDATE_ENTRY(l1, pl1e, ol1e, nl1e, mfn_x(gl1mfn), curr, 0) )
rc = GNTST_okay;
out_unlock:
page_unlock(page);
out_put:
put_page(page);
out_unmap:
unmap_domain_page(pl1e);
out:
/* If there was an error, we are still responsible for the stolen pte. */
if ( rc )
put_page_from_l1e(nl1e, currd);
return rc;
}
int donate_page(
struct domain *d, struct page_info *page, unsigned int memflags)
{
+1
View File
@@ -4,6 +4,7 @@ obj-y += emulate.o
obj-y += emul-gate-op.o
obj-y += emul-inv-op.o
obj-y += emul-priv-op.o
obj-y += grant_table.o
obj-y += hypercall.o
obj-y += iret.o
obj-y += misc-hypercalls.o
+327
View File
@@ -0,0 +1,327 @@
/*
* pv/grant_table.c
*
* Grant table interfaces for PV guests
*
* Copyright (C) 2017 Wei Liu <wei.liu2@citrix.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms and conditions of the GNU General Public
* License, version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; If not, see <http://www.gnu.org/licenses/>.
*/
#include <xen/types.h>
#include <public/grant_table.h>
#include <asm/p2m.h>
#include <asm/pv/mm.h>
#include "mm.h"
/* Override macros from asm/page.h to make them work with mfn_t */
#undef mfn_to_page
#define mfn_to_page(mfn) __mfn_to_page(mfn_x(mfn))
#undef page_to_mfn
#define page_to_mfn(pg) _mfn(__page_to_mfn(pg))
static unsigned int grant_to_pte_flags(unsigned int grant_flags,
unsigned int cache_flags)
{
unsigned int pte_flags =
_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_GNTTAB | _PAGE_NX;
if ( grant_flags & GNTMAP_application_map )
pte_flags |= _PAGE_USER;
if ( !(grant_flags & GNTMAP_readonly) )
pte_flags |= _PAGE_RW;
pte_flags |= MASK_INSR((grant_flags >> _GNTMAP_guest_avail0), _PAGE_AVAIL);
pte_flags |= cacheattr_to_pte_flags(cache_flags >> 5);
return pte_flags;
}
int create_grant_pv_mapping(uint64_t addr, unsigned long frame,
unsigned int flags, unsigned int cache_flags)
{
struct vcpu *curr = current;
struct domain *currd = curr->domain;
l1_pgentry_t nl1e, ol1e = { }, *pl1e;
struct page_info *page;
mfn_t gl1mfn;
int rc = GNTST_general_error;
nl1e = l1e_from_pfn(frame, grant_to_pte_flags(flags, cache_flags));
nl1e = adjust_guest_l1e(nl1e, currd);
/*
* The meaning of addr depends on GNTMAP_contains_pte. It is either a
* machine address of an L1e the guest has nominated to be altered, or a
* linear address we need to look up the appropriate L1e for.
*/
if ( flags & GNTMAP_contains_pte )
{
/* addr must be suitably aligned, or we will corrupt adjacent ptes. */
if ( !IS_ALIGNED(addr, sizeof(nl1e)) )
{
gdprintk(XENLOG_WARNING,
"Misaligned PTE address %"PRIx64"\n", addr);
goto out;
}
gl1mfn = _mfn(addr >> PAGE_SHIFT);
if ( !get_page_from_mfn(gl1mfn, currd) )
goto out;
pl1e = map_domain_page(gl1mfn) + (addr & ~PAGE_MASK);
}
else
{
/* Guest trying to pass an out-of-range linear address? */
if ( is_pv_32bit_domain(currd) && addr != (uint32_t)addr )
goto out;
pl1e = map_guest_l1e(addr, &gl1mfn);
if ( !pl1e )
{
gdprintk(XENLOG_WARNING,
"Could not find L1 PTE for linear address %"PRIx64"\n",
addr);
goto out;
}
if ( !get_page_from_mfn(gl1mfn, currd) )
goto out_unmap;
}
page = mfn_to_page(gl1mfn);
if ( !page_lock(page) )
goto out_put;
if ( (page->u.inuse.type_info & PGT_type_mask) != PGT_l1_page_table )
goto out_unlock;
ol1e = *pl1e;
if ( UPDATE_ENTRY(l1, pl1e, ol1e, nl1e, mfn_x(gl1mfn), curr, 0) )
rc = GNTST_okay;
out_unlock:
page_unlock(page);
out_put:
put_page(page);
out_unmap:
unmap_domain_page(pl1e);
if ( rc == GNTST_okay )
put_page_from_l1e(ol1e, currd);
out:
return rc;
}
/*
* This exists soley for implementing GNTABOP_unmap_and_replace, the ABI of
* which is bizarre. This GNTTABOP isn't used any more, but was used by
* classic-xen kernels and PVOps Linux before the M2P_OVERRIDE infrastructure
* was replaced with something which actually worked.
*
* Look up the L1e mapping linear, and zap it. Return the L1e via *out.
* Returns a boolean indicating success. If success, the caller is
* responsible for calling put_page_from_l1e().
*/
static bool steal_linear_address(unsigned long linear, l1_pgentry_t *out)
{
struct vcpu *curr = current;
struct domain *currd = curr->domain;
l1_pgentry_t *pl1e, ol1e;
struct page_info *page;
mfn_t gl1mfn;
bool okay = false;
ASSERT(is_pv_domain(currd));
pl1e = map_guest_l1e(linear, &gl1mfn);
if ( !pl1e )
{
gdprintk(XENLOG_WARNING,
"Could not find L1 PTE for linear %"PRIx64"\n", linear);
goto out;
}
if ( !get_page_from_mfn(gl1mfn, currd) )
goto out_unmap;
page = mfn_to_page(gl1mfn);
if ( !page_lock(page) )
goto out_put;
if ( (page->u.inuse.type_info & PGT_type_mask) != PGT_l1_page_table )
goto out_unlock;
ol1e = *pl1e;
okay = UPDATE_ENTRY(l1, pl1e, ol1e, l1e_empty(), mfn_x(gl1mfn), curr, 0);
out_unlock:
page_unlock(page);
out_put:
put_page(page);
out_unmap:
unmap_domain_page(pl1e);
if ( okay )
*out = ol1e;
out:
return okay;
}
/*
* Passing a new_addr of zero is taken to mean destroy. Passing a non-zero
* new_addr has only ever been available via GNTABOP_unmap_and_replace, and
* only when !(flags & GNTMAP_contains_pte).
*/
int replace_grant_pv_mapping(uint64_t addr, unsigned long frame,
uint64_t new_addr, unsigned int flags)
{
struct vcpu *curr = current;
struct domain *currd = curr->domain;
l1_pgentry_t nl1e = l1e_empty(), ol1e, *pl1e;
struct page_info *page;
mfn_t gl1mfn;
int rc = GNTST_general_error;
unsigned int grant_pte_flags = grant_to_pte_flags(flags, 0);
/*
* On top of the explicit settings done by create_grant_pv_mapping()
* also open-code relevant parts of adjust_guest_l1e(). Don't mirror
* available and cachability flags, though.
*/
if ( !is_pv_32bit_domain(currd) )
grant_pte_flags |= (grant_pte_flags & _PAGE_USER)
? _PAGE_GLOBAL
: _PAGE_GUEST_KERNEL | _PAGE_USER;
/*
* addr comes from Xen's active_entry tracking, and was used successfully
* to create a grant.
*
* The meaning of addr depends on GNTMAP_contains_pte. It is either a
* machine address of an L1e the guest has nominated to be altered, or a
* linear address we need to look up the appropriate L1e for.
*/
if ( flags & GNTMAP_contains_pte )
{
/* Replace not available in this addressing mode. */
if ( new_addr )
goto out;
/* Sanity check that we won't clobber the pagetable. */
if ( !IS_ALIGNED(addr, sizeof(nl1e)) )
{
ASSERT_UNREACHABLE();
goto out;
}
gl1mfn = _mfn(addr >> PAGE_SHIFT);
if ( !get_page_from_mfn(gl1mfn, currd) )
goto out;
pl1e = map_domain_page(gl1mfn) + (addr & ~PAGE_MASK);
}
else
{
if ( is_pv_32bit_domain(currd) )
{
if ( addr != (uint32_t)addr )
{
ASSERT_UNREACHABLE();
goto out;
}
/* Guest trying to pass an out-of-range linear address? */
if ( new_addr != (uint32_t)new_addr )
goto out;
}
if ( new_addr && !steal_linear_address(new_addr, &nl1e) )
goto out;
pl1e = map_guest_l1e(addr, &gl1mfn);
if ( !pl1e )
goto out;
if ( !get_page_from_mfn(gl1mfn, currd) )
goto out_unmap;
}
page = mfn_to_page(gl1mfn);
if ( !page_lock(page) )
goto out_put;
if ( (page->u.inuse.type_info & PGT_type_mask) != PGT_l1_page_table )
goto out_unlock;
ol1e = *pl1e;
/*
* Check that the address supplied is actually mapped to frame (with
* appropriate permissions).
*/
if ( unlikely(l1e_get_pfn(ol1e) != frame) ||
unlikely((l1e_get_flags(ol1e) ^ grant_pte_flags) &
(_PAGE_PRESENT | _PAGE_RW)) )
{
gdprintk(XENLOG_ERR,
"PTE %"PRIpte" for %"PRIx64" doesn't match grant (%"PRIpte")\n",
l1e_get_intpte(ol1e), addr,
l1e_get_intpte(l1e_from_pfn(frame, grant_pte_flags)));
goto out_unlock;
}
if ( unlikely((l1e_get_flags(ol1e) ^ grant_pte_flags) &
~(_PAGE_AVAIL | PAGE_CACHE_ATTRS)) )
gdprintk(XENLOG_WARNING,
"PTE flags %x for %"PRIx64" don't match grant (%x)\n",
l1e_get_flags(ol1e), addr, grant_pte_flags);
if ( UPDATE_ENTRY(l1, pl1e, ol1e, nl1e, mfn_x(gl1mfn), curr, 0) )
rc = GNTST_okay;
out_unlock:
page_unlock(page);
out_put:
put_page(page);
out_unmap:
unmap_domain_page(pl1e);
out:
/* If there was an error, we are still responsible for the stolen pte. */
if ( rc )
put_page_from_l1e(nl1e, currd);
return rc;
}
/*
* Local variables:
* mode: C
* c-file-style: "BSD"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.