mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 02:30:03 +01:00
Guests are initially started with caches disabled and so we need to make sure they see consistent data in RAM (requiring a cache clean) but also that they do not have old stale data suddenly appear in the caches when they enable their caches (requiring the invalidate). This can be split into two halves. First we must flush each page as it is allocated to the guest. It is not sufficient to do the flush at scrub time since this will miss pages which are ballooned out by the guest (where the guest must scrub if it cares about not leaking the pagecontent). We need to clean as well as invalidate to make sure that any scrubbing which has occured gets committed to real RAM. To achieve this add a new cacheflush_page function, which is a stub on x86. Secondly we need to flush anything which the domain builder touches, which we do via a new domctl. Signed-off-by: Ian Campbell <ian.campbell@citrix.com> Acked-by: Ian Jackson <ian.jackson@eu.citrix.com> Acked-by: Julien Grall <julien.grall@linaro.org> Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> Acked-by: Jan Beulich <jbeulich@suse.com> Cc: keir@xen.org
447 lines
13 KiB
C
447 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;
|
|
}
|
|
|
|
/* Guest shouldn't really touch its grant table until it has
|
|
* enabled its caches. But lets be nice. */
|
|
xc_domain_cacheflush(xch, domid, gnttab_gmfn, 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:
|
|
*/
|