mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
In order to set up the grant table for HVM guests, libxc needs to map the grant table temporarily. At the moment, it does this by adding the grant page to the HVM guest's p2m table in the MMIO hole (at gfn 0xFFFFE), then mapping that gfn, setting up the table, then unmapping the gfn and removing it from the p2m table. This breaks with PVH guests with 4G or more of ram, because there is no MMIO hole; so it ends up clobbering a valid RAM p2m entry, then leaving a "hole" when it removes the grant map from the p2m table. Since the guest thinks this is normal ram, when it maps it and tries to access the page, it crashes. This patch maps the page at max_gfn+1 instead. Signed-off-by: Roger Pau Monné <roger.pau@citrix.com> Acked-by: Tim Deegan <tim@xen.org> Acked-by: Ian Campbell <ian.campbell@citrix.com> Acked-by: Keir Fraser <keir@xen.org> Acked-by: Eddie Dong <eddie.dong@intel.com>
443 lines
13 KiB
C
443 lines
13 KiB
C
/*
|
|
* Xen domain builder -- xen booter.
|
|
*
|
|
* This is the code which actually boots a fresh
|
|
* prepared domain image as xen guest domain.
|
|
*
|
|
* ==> this is the only domain builder code piece
|
|
* where xen hypercalls are allowed <==
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* written 2006 by Gerd Hoffmann <kraxel@suse.de>.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
#include <zlib.h>
|
|
|
|
#include "xg_private.h"
|
|
#include "xc_dom.h"
|
|
#include <xen/hvm/params.h>
|
|
#include <xen/grant_table.h>
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
static int setup_hypercall_page(struct xc_dom_image *dom)
|
|
{
|
|
DECLARE_DOMCTL;
|
|
xen_pfn_t pfn;
|
|
int rc;
|
|
|
|
if ( dom->parms.virt_hypercall == -1 )
|
|
return 0;
|
|
pfn = (dom->parms.virt_hypercall - dom->parms.virt_base)
|
|
>> XC_DOM_PAGE_SHIFT(dom);
|
|
|
|
DOMPRINTF("%s: vaddr=0x%" PRIx64 " pfn=0x%" PRIpfn "", __FUNCTION__,
|
|
dom->parms.virt_hypercall, pfn);
|
|
domctl.cmd = XEN_DOMCTL_hypercall_init;
|
|
domctl.domain = dom->guest_domid;
|
|
domctl.u.hypercall_init.gmfn = xc_dom_p2m_guest(dom, pfn);
|
|
rc = do_domctl(dom->xch, &domctl);
|
|
if ( rc != 0 )
|
|
xc_dom_panic(dom->xch,
|
|
XC_INTERNAL_ERROR, "%s: HYPERCALL_INIT failed (rc=%d)",
|
|
__FUNCTION__, rc);
|
|
return rc;
|
|
}
|
|
|
|
static int launch_vm(xc_interface *xch, domid_t domid,
|
|
vcpu_guest_context_any_t *ctxt)
|
|
{
|
|
int rc;
|
|
|
|
xc_dom_printf(xch, "%s: called, ctxt=%p", __FUNCTION__, ctxt);
|
|
rc = xc_vcpu_setcontext(xch, domid, 0, ctxt);
|
|
if ( rc != 0 )
|
|
xc_dom_panic(xch, XC_INTERNAL_ERROR,
|
|
"%s: SETVCPUCONTEXT failed (rc=%d)", __FUNCTION__, rc);
|
|
return rc;
|
|
}
|
|
|
|
static int clear_page(struct xc_dom_image *dom, xen_pfn_t pfn)
|
|
{
|
|
xen_pfn_t dst;
|
|
int rc;
|
|
|
|
if ( pfn == 0 )
|
|
return 0;
|
|
|
|
dst = xc_dom_p2m_host(dom, pfn);
|
|
DOMPRINTF("%s: pfn 0x%" PRIpfn ", mfn 0x%" PRIpfn "",
|
|
__FUNCTION__, pfn, dst);
|
|
rc = xc_clear_domain_page(dom->xch, dom->guest_domid, dst);
|
|
if ( rc != 0 )
|
|
xc_dom_panic(dom->xch, XC_INTERNAL_ERROR,
|
|
"%s: xc_clear_domain_page failed (pfn 0x%" PRIpfn
|
|
", rc=%d)", __FUNCTION__, pfn, rc);
|
|
return rc;
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
int xc_dom_compat_check(struct xc_dom_image *dom)
|
|
{
|
|
xen_capabilities_info_t xen_caps;
|
|
char *item, *ptr;
|
|
int match, found = 0;
|
|
|
|
strncpy(xen_caps, dom->xen_caps, XEN_CAPABILITIES_INFO_LEN - 1);
|
|
xen_caps[XEN_CAPABILITIES_INFO_LEN - 1] = '\0';
|
|
|
|
for ( item = strtok_r(xen_caps, " ", &ptr);
|
|
item != NULL ; item = strtok_r(NULL, " ", &ptr) )
|
|
{
|
|
match = !strcmp(dom->guest_type, item);
|
|
DOMPRINTF("%s: supported guest type: %s%s", __FUNCTION__,
|
|
item, match ? " <= matches" : "");
|
|
if ( match )
|
|
found++;
|
|
}
|
|
if ( !found )
|
|
xc_dom_panic(dom->xch, XC_INVALID_KERNEL,
|
|
"%s: guest type %s not supported by xen kernel, sorry",
|
|
__FUNCTION__, dom->guest_type);
|
|
|
|
return found;
|
|
}
|
|
|
|
int xc_dom_boot_xen_init(struct xc_dom_image *dom, xc_interface *xch, domid_t domid)
|
|
{
|
|
dom->xch = xch;
|
|
dom->guest_domid = domid;
|
|
|
|
dom->xen_version = xc_version(xch, XENVER_version, NULL);
|
|
if ( xc_version(xch, XENVER_capabilities, &dom->xen_caps) < 0 )
|
|
{
|
|
xc_dom_panic(xch, XC_INTERNAL_ERROR, "can't get xen capabilities");
|
|
return -1;
|
|
}
|
|
DOMPRINTF("%s: ver %d.%d, caps %s", __FUNCTION__,
|
|
dom->xen_version >> 16, dom->xen_version & 0xff,
|
|
dom->xen_caps);
|
|
return 0;
|
|
}
|
|
|
|
int xc_dom_boot_mem_init(struct xc_dom_image *dom)
|
|
{
|
|
long rc;
|
|
|
|
DOMPRINTF_CALLED(dom->xch);
|
|
|
|
rc = arch_setup_meminit(dom);
|
|
if ( rc != 0 )
|
|
{
|
|
xc_dom_panic(dom->xch, XC_OUT_OF_MEMORY,
|
|
"%s: can't allocate low memory for domain",
|
|
__FUNCTION__);
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void *xc_dom_boot_domU_map(struct xc_dom_image *dom, xen_pfn_t pfn,
|
|
xen_pfn_t count)
|
|
{
|
|
int page_shift = XC_DOM_PAGE_SHIFT(dom);
|
|
privcmd_mmap_entry_t *entries;
|
|
void *ptr;
|
|
int i;
|
|
int err;
|
|
|
|
entries = xc_dom_malloc(dom, count * sizeof(privcmd_mmap_entry_t));
|
|
if ( entries == NULL )
|
|
{
|
|
xc_dom_panic(dom->xch, XC_INTERNAL_ERROR,
|
|
"%s: failed to mmap domU pages 0x%" PRIpfn "+0x%" PRIpfn
|
|
" [malloc]", __FUNCTION__, pfn, count);
|
|
return NULL;
|
|
}
|
|
|
|
for ( i = 0; i < count; i++ )
|
|
entries[i].mfn = xc_dom_p2m_host(dom, pfn + i);
|
|
|
|
ptr = xc_map_foreign_ranges(dom->xch, dom->guest_domid,
|
|
count << page_shift, PROT_READ | PROT_WRITE, 1 << page_shift,
|
|
entries, count);
|
|
if ( ptr == NULL )
|
|
{
|
|
err = errno;
|
|
xc_dom_panic(dom->xch, XC_INTERNAL_ERROR,
|
|
"%s: failed to mmap domU pages 0x%" PRIpfn "+0x%" PRIpfn
|
|
" [mmap, errno=%i (%s)]", __FUNCTION__, pfn, count,
|
|
err, strerror(err));
|
|
return NULL;
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
int xc_dom_boot_image(struct xc_dom_image *dom)
|
|
{
|
|
DECLARE_HYPERCALL_BUFFER(vcpu_guest_context_any_t, ctxt);
|
|
xc_dominfo_t info;
|
|
int rc;
|
|
|
|
ctxt = xc_hypercall_buffer_alloc(dom->xch, ctxt, sizeof(*ctxt));
|
|
if ( ctxt == NULL )
|
|
return -1;
|
|
|
|
DOMPRINTF_CALLED(dom->xch);
|
|
|
|
/* misc stuff*/
|
|
if ( (rc = arch_setup_bootearly(dom)) != 0 )
|
|
return rc;
|
|
|
|
/* collect some info */
|
|
rc = xc_domain_getinfo(dom->xch, dom->guest_domid, 1, &info);
|
|
if ( rc < 0 )
|
|
{
|
|
xc_dom_panic(dom->xch, XC_INTERNAL_ERROR,
|
|
"%s: getdomaininfo failed (rc=%d)", __FUNCTION__, rc);
|
|
return rc;
|
|
}
|
|
if ( rc == 0 || info.domid != dom->guest_domid )
|
|
{
|
|
xc_dom_panic(dom->xch, XC_INTERNAL_ERROR,
|
|
"%s: Huh? No domains found (nr_domains=%d) "
|
|
"or domid mismatch (%d != %d)", __FUNCTION__,
|
|
rc, info.domid, dom->guest_domid);
|
|
return -1;
|
|
}
|
|
dom->shared_info_mfn = info.shared_info_frame;
|
|
|
|
/* sanity checks */
|
|
if ( !xc_dom_compat_check(dom) )
|
|
return -1;
|
|
|
|
/* initial mm setup */
|
|
if ( (rc = xc_dom_update_guest_p2m(dom)) != 0 )
|
|
return rc;
|
|
if ( dom->arch_hooks->setup_pgtables )
|
|
if ( (rc = dom->arch_hooks->setup_pgtables(dom)) != 0 )
|
|
return rc;
|
|
|
|
if ( (rc = clear_page(dom, dom->console_pfn)) != 0 )
|
|
return rc;
|
|
if ( (rc = clear_page(dom, dom->xenstore_pfn)) != 0 )
|
|
return rc;
|
|
|
|
/* start info page */
|
|
if ( dom->arch_hooks->start_info )
|
|
dom->arch_hooks->start_info(dom);
|
|
|
|
/* hypercall page */
|
|
if ( (rc = setup_hypercall_page(dom)) != 0 )
|
|
return rc;
|
|
xc_dom_log_memory_footprint(dom);
|
|
|
|
/* misc x86 stuff */
|
|
if ( (rc = arch_setup_bootlate(dom)) != 0 )
|
|
return rc;
|
|
|
|
/* let the vm run */
|
|
memset(ctxt, 0, sizeof(*ctxt));
|
|
if ( (rc = dom->arch_hooks->vcpu(dom, ctxt)) != 0 )
|
|
return rc;
|
|
xc_dom_unmap_all(dom);
|
|
rc = launch_vm(dom->xch, dom->guest_domid, ctxt);
|
|
|
|
xc_hypercall_buffer_free(dom->xch, ctxt);
|
|
return rc;
|
|
}
|
|
|
|
static xen_pfn_t xc_dom_gnttab_setup(xc_interface *xch, domid_t domid)
|
|
{
|
|
gnttab_setup_table_t setup;
|
|
DECLARE_HYPERCALL_BUFFER(xen_pfn_t, gmfnp);
|
|
int rc;
|
|
xen_pfn_t gmfn;
|
|
|
|
gmfnp = xc_hypercall_buffer_alloc(xch, gmfnp, sizeof(*gmfnp));
|
|
if (gmfnp == NULL)
|
|
return -1;
|
|
|
|
setup.dom = domid;
|
|
setup.nr_frames = 1;
|
|
set_xen_guest_handle(setup.frame_list, gmfnp);
|
|
setup.status = 0;
|
|
|
|
rc = xc_gnttab_op(xch, GNTTABOP_setup_table, &setup, sizeof(setup), 1);
|
|
gmfn = *gmfnp;
|
|
xc_hypercall_buffer_free(xch, gmfnp);
|
|
|
|
if ( rc != 0 || setup.status != GNTST_okay )
|
|
{
|
|
xc_dom_panic(xch, XC_INTERNAL_ERROR,
|
|
"%s: failed to setup domU grant table "
|
|
"[errno=%d, status=%" PRId16 "]\n",
|
|
__FUNCTION__, rc != 0 ? errno : 0, setup.status);
|
|
return -1;
|
|
}
|
|
|
|
return gmfn;
|
|
}
|
|
|
|
int xc_dom_gnttab_seed(xc_interface *xch, domid_t domid,
|
|
xen_pfn_t console_gmfn,
|
|
xen_pfn_t xenstore_gmfn,
|
|
domid_t console_domid,
|
|
domid_t xenstore_domid)
|
|
{
|
|
|
|
xen_pfn_t gnttab_gmfn;
|
|
grant_entry_v1_t *gnttab;
|
|
|
|
gnttab_gmfn = xc_dom_gnttab_setup(xch, domid);
|
|
if ( gnttab_gmfn == -1 )
|
|
return -1;
|
|
|
|
gnttab = xc_map_foreign_range(xch,
|
|
domid,
|
|
PAGE_SIZE,
|
|
PROT_READ|PROT_WRITE,
|
|
gnttab_gmfn);
|
|
if ( gnttab == NULL )
|
|
{
|
|
xc_dom_panic(xch, XC_INTERNAL_ERROR,
|
|
"%s: failed to map domU grant table "
|
|
"[errno=%d]\n",
|
|
__FUNCTION__, errno);
|
|
return -1;
|
|
}
|
|
|
|
if ( domid != console_domid && console_gmfn != -1)
|
|
{
|
|
gnttab[GNTTAB_RESERVED_CONSOLE].flags = GTF_permit_access;
|
|
gnttab[GNTTAB_RESERVED_CONSOLE].domid = console_domid;
|
|
gnttab[GNTTAB_RESERVED_CONSOLE].frame = console_gmfn;
|
|
}
|
|
if ( domid != xenstore_domid && xenstore_gmfn != -1)
|
|
{
|
|
gnttab[GNTTAB_RESERVED_XENSTORE].flags = GTF_permit_access;
|
|
gnttab[GNTTAB_RESERVED_XENSTORE].domid = xenstore_domid;
|
|
gnttab[GNTTAB_RESERVED_XENSTORE].frame = xenstore_gmfn;
|
|
}
|
|
|
|
if ( munmap(gnttab, PAGE_SIZE) == -1 )
|
|
{
|
|
xc_dom_panic(xch, XC_INTERNAL_ERROR,
|
|
"%s: failed to unmap domU grant table "
|
|
"[errno=%d]\n",
|
|
__FUNCTION__, errno);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int xc_dom_gnttab_hvm_seed(xc_interface *xch, domid_t domid,
|
|
xen_pfn_t console_gpfn,
|
|
xen_pfn_t xenstore_gpfn,
|
|
domid_t console_domid,
|
|
domid_t xenstore_domid)
|
|
{
|
|
int rc;
|
|
xen_pfn_t max_gfn;
|
|
struct xen_add_to_physmap xatp = {
|
|
.domid = domid,
|
|
.space = XENMAPSPACE_grant_table,
|
|
.idx = 0,
|
|
};
|
|
struct xen_remove_from_physmap xrfp = {
|
|
.domid = domid,
|
|
};
|
|
|
|
max_gfn = xc_domain_maximum_gpfn(xch, domid);
|
|
if ( max_gfn <= 0 ) {
|
|
xc_dom_panic(xch, XC_INTERNAL_ERROR,
|
|
"%s: failed to get max gfn "
|
|
"[errno=%d]\n",
|
|
__FUNCTION__, errno);
|
|
return -1;
|
|
}
|
|
xatp.gpfn = max_gfn + 1;
|
|
xrfp.gpfn = max_gfn + 1;
|
|
|
|
rc = do_memory_op(xch, XENMEM_add_to_physmap, &xatp, sizeof(xatp));
|
|
if ( rc != 0 )
|
|
{
|
|
xc_dom_panic(xch, XC_INTERNAL_ERROR,
|
|
"%s: failed to add gnttab to physmap "
|
|
"[errno=%d]\n",
|
|
__FUNCTION__, errno);
|
|
return -1;
|
|
}
|
|
|
|
rc = xc_dom_gnttab_seed(xch, domid,
|
|
console_gpfn, xenstore_gpfn,
|
|
console_domid, xenstore_domid);
|
|
if (rc != 0)
|
|
{
|
|
xc_dom_panic(xch, XC_INTERNAL_ERROR,
|
|
"%s: failed to seed gnttab entries\n",
|
|
__FUNCTION__);
|
|
(void) do_memory_op(xch, XENMEM_remove_from_physmap, &xrfp, sizeof(xrfp));
|
|
return -1;
|
|
}
|
|
|
|
rc = do_memory_op(xch, XENMEM_remove_from_physmap, &xrfp, sizeof(xrfp));
|
|
if (rc != 0)
|
|
{
|
|
xc_dom_panic(xch, XC_INTERNAL_ERROR,
|
|
"%s: failed to remove gnttab from physmap "
|
|
"[errno=%d]\n",
|
|
__FUNCTION__, errno);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int xc_dom_gnttab_init(struct xc_dom_image *dom)
|
|
{
|
|
if ( xc_dom_feature_translated(dom) ) {
|
|
return xc_dom_gnttab_hvm_seed(dom->xch, dom->guest_domid,
|
|
dom->console_pfn, dom->xenstore_pfn,
|
|
dom->console_domid, dom->xenstore_domid);
|
|
} else {
|
|
return xc_dom_gnttab_seed(dom->xch, dom->guest_domid,
|
|
xc_dom_p2m_host(dom, dom->console_pfn),
|
|
xc_dom_p2m_host(dom, dom->xenstore_pfn),
|
|
dom->console_domid, dom->xenstore_domid);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* mode: C
|
|
* c-file-style: "BSD"
|
|
* c-basic-offset: 4
|
|
* tab-width: 4
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*/
|