diff --git a/tools/flask/policy/policy/modules/xen/xen.if b/tools/flask/policy/policy/modules/xen/xen.if index 641c797994..fa69c9d3dc 100644 --- a/tools/flask/policy/policy/modules/xen/xen.if +++ b/tools/flask/policy/policy/modules/xen/xen.if @@ -49,7 +49,7 @@ define(`create_domain_common', ` getdomaininfo hypercall setvcpucontext setextvcpucontext getscheduler getvcpuinfo getvcpuextstate getaddrsize getaffinity setaffinity }; - allow $1 $2:domain2 { set_cpuid settsc setscheduler setclaim set_max_evtchn set_vnumainfo get_vnumainfo psr_cmt_op }; + allow $1 $2:domain2 { set_cpuid settsc setscheduler setclaim set_max_evtchn set_vnumainfo get_vnumainfo psr_cmt_op configure_domain }; allow $1 $2:security check_context; allow $1 $2:shadow enable; allow $1 $2:mmu { map_read map_write adjust memorymap physmap pinpage mmuext_op }; diff --git a/tools/libxc/include/xenctrl.h b/tools/libxc/include/xenctrl.h index 564e18750e..45e282c956 100644 --- a/tools/libxc/include/xenctrl.h +++ b/tools/libxc/include/xenctrl.h @@ -483,6 +483,12 @@ int xc_domain_create(xc_interface *xch, uint32_t flags, uint32_t *pdomid); +#if defined(__arm__) || defined(__aarch64__) +typedef xen_domctl_arm_configuredomain_t xc_domain_configuration_t; + +int xc_domain_configure(xc_interface *xch, uint32_t domid, + xc_domain_configuration_t *config); +#endif /* Functions to produce a dump of a given domain * xc_domain_dumpcore - produces a dump to a specified file diff --git a/tools/libxc/xc_domain.c b/tools/libxc/xc_domain.c index a9bcd4a0bf..b071dea083 100644 --- a/tools/libxc/xc_domain.c +++ b/tools/libxc/xc_domain.c @@ -48,6 +48,26 @@ int xc_domain_create(xc_interface *xch, return 0; } +#if defined(__arm__) || defined(__aarch64__) +int xc_domain_configure(xc_interface *xch, uint32_t domid, + xc_domain_configuration_t *config) +{ + int rc; + DECLARE_DOMCTL; + + domctl.cmd = XEN_DOMCTL_arm_configure_domain; + domctl.domain = (domid_t)domid; + /* xc_domain_configure_t is an alias of xen_domctl_arm_configuredomain */ + memcpy(&domctl.u.configuredomain, config, sizeof(*config)); + + rc = do_domctl(xch, &domctl); + if ( !rc ) + memcpy(config, &domctl.u.configuredomain, sizeof(*config)); + + return rc; +} +#endif + int xc_domain_cacheflush(xc_interface *xch, uint32_t domid, xen_pfn_t start_pfn, xen_pfn_t nr_pfns) { diff --git a/tools/libxl/libxl_arm.c b/tools/libxl/libxl_arm.c index a122e4aac6..448ac078d7 100644 --- a/tools/libxl/libxl_arm.c +++ b/tools/libxl/libxl_arm.c @@ -23,6 +23,18 @@ #define DT_IRQ_TYPE_LEVEL_HIGH 0x00000004 #define DT_IRQ_TYPE_LEVEL_LOW 0x00000008 +static const char *gicv_to_string(uint8_t gic_version) +{ + switch (gic_version) { + case XEN_DOMCTL_CONFIG_GIC_V2: + return "V2"; + case XEN_DOMCTL_CONFIG_GIC_V3: + return "V3"; + default: + return "unknown"; + } +} + int libxl__arch_domain_create(libxl__gc *gc, libxl_domain_config *d_config, uint32_t domid) { @@ -307,9 +319,9 @@ static int make_memory_nodes(libxl__gc *gc, void *fdt, return 0; } -static int make_intc_node(libxl__gc *gc, void *fdt, - uint64_t gicd_base, uint64_t gicd_size, - uint64_t gicc_base, uint64_t gicc_size) +static int make_gicv2_node(libxl__gc *gc, void *fdt, + uint64_t gicd_base, uint64_t gicd_size, + uint64_t gicc_base, uint64_t gicc_size) { int res; const char *name = GCSPRINTF("interrupt-controller@%"PRIx64, gicd_base); @@ -350,6 +362,56 @@ static int make_intc_node(libxl__gc *gc, void *fdt, return 0; } +static int make_gicv3_node(libxl__gc *gc, void *fdt) +{ + int res; + const uint64_t gicd_base = GUEST_GICV3_GICD_BASE; + const uint64_t gicd_size = GUEST_GICV3_GICD_SIZE; + const uint64_t gicr0_base = GUEST_GICV3_GICR0_BASE; + const uint64_t gicr0_size = GUEST_GICV3_GICR0_SIZE; + const char *name = GCSPRINTF("interrupt-controller@%"PRIx64, gicd_base); + + res = fdt_begin_node(fdt, name); + if (res) return res; + + res = fdt_property_compat(gc, fdt, 1, "arm,gic-v3"); + if (res) return res; + + res = fdt_property_cell(fdt, "#interrupt-cells", 3); + if (res) return res; + + res = fdt_property_cell(fdt, "#address-cells", 0); + if (res) return res; + + res = fdt_property(fdt, "interrupt-controller", NULL, 0); + if (res) return res; + + res = fdt_property_cell(fdt, "redistributor-stride", + GUEST_GICV3_RDIST_STRIDE); + if (res) return res; + + res = fdt_property_cell(fdt, "#redistributor-regions", + GUEST_GICV3_RDIST_REGIONS); + if (res) return res; + + res = fdt_property_regs(gc, fdt, ROOT_ADDRESS_CELLS, ROOT_SIZE_CELLS, + 2, + gicd_base, gicd_size, + gicr0_base, gicr0_size); + if (res) return res; + + res = fdt_property_cell(fdt, "linux,phandle", PHANDLE_GIC); + if (res) return res; + + res = fdt_property_cell(fdt, "phandle", PHANDLE_GIC); + if (res) return res; + + res = fdt_end_node(fdt); + if (res) return res; + + return 0; +} + static int make_timer_node(libxl__gc *gc, void *fdt, const struct arch_info *ainfo) { int res; @@ -456,6 +518,7 @@ int libxl__arch_domain_init_hw_description(libxl__gc *gc, libxl_domain_build_info *info, struct xc_dom_image *dom) { + xc_domain_configuration_t config; void *fdt = NULL; int rc, res; size_t fdt_size = 0; @@ -471,8 +534,16 @@ int libxl__arch_domain_init_hw_description(libxl__gc *gc, ainfo = get_arch_info(gc, dom); if (ainfo == NULL) return ERROR_FAIL; + LOG(DEBUG, "configure the domain"); + config.gic_version = XEN_DOMCTL_CONFIG_GIC_DEFAULT; + if (xc_domain_configure(CTX->xch, dom->guest_domid, &config) != 0) { + LOG(ERROR, "couldn't configure the domain"); + return ERROR_FAIL; + } + LOG(DEBUG, "constructing DTB for Xen version %d.%d guest", vers->xen_version_major, vers->xen_version_minor); + LOG(DEBUG, " - vGIC version: %s", gicv_to_string(config.gic_version)); /* * Call "call" handling FDR_ERR_*. Will either: @@ -520,9 +591,21 @@ next_resize: FDT( make_psci_node(gc, fdt) ); FDT( make_memory_nodes(gc, fdt, dom) ); - FDT( make_intc_node(gc, fdt, - GUEST_GICD_BASE, GUEST_GICD_SIZE, - GUEST_GICC_BASE, GUEST_GICD_SIZE) ); + + switch (config.gic_version) { + case XEN_DOMCTL_CONFIG_GIC_V2: + FDT( make_gicv2_node(gc, fdt, + GUEST_GICD_BASE, GUEST_GICD_SIZE, + GUEST_GICC_BASE, GUEST_GICC_SIZE) ); + break; + case XEN_DOMCTL_CONFIG_GIC_V3: + FDT( make_gicv3_node(gc, fdt) ); + break; + default: + LOG(ERROR, "Unknown GIC version %d", config.gic_version); + rc = ERROR_FAIL; + goto out; + } FDT( make_timer_node(gc, fdt, ainfo) ); FDT( make_hypervisor_node(gc, fdt, vers) ); diff --git a/xen/arch/arm/domctl.c b/xen/arch/arm/domctl.c index 45974e735f..d246e84590 100644 --- a/xen/arch/arm/domctl.c +++ b/xen/arch/arm/domctl.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include long arch_do_domctl(struct xen_domctl *domctl, struct domain *d, @@ -30,6 +32,39 @@ long arch_do_domctl(struct xen_domctl *domctl, struct domain *d, return p2m_cache_flush(d, s, e); } + case XEN_DOMCTL_arm_configure_domain: + { + uint8_t gic_version; + + /* + * Currently the vGIC is emulating the same version of the + * hardware GIC. Only the value XEN_DOMCTL_CONFIG_GIC_DEFAULT + * is allowed. The DOMCTL will return the actual version of the + * GIC. + */ + if ( domctl->u.configuredomain.gic_version != XEN_DOMCTL_CONFIG_GIC_DEFAULT ) + return -EOPNOTSUPP; + + switch ( gic_hw_version() ) + { + case GIC_V3: + gic_version = XEN_DOMCTL_CONFIG_GIC_V3; + break; + case GIC_V2: + gic_version = XEN_DOMCTL_CONFIG_GIC_V2; + break; + default: + BUG(); + } + + domctl->u.configuredomain.gic_version = gic_version; + + /* TODO: Make the copy generic for all ARCH domctl */ + if ( __copy_to_guest(u_domctl, domctl, 1) ) + return -EFAULT; + + return 0; + } default: return subarch_do_domctl(domctl, d, u_domctl); diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c index 91161a2225..076aa627fb 100644 --- a/xen/arch/arm/gic-v3.c +++ b/xen/arch/arm/gic-v3.c @@ -906,7 +906,21 @@ static int gicv_v3_init(struct domain *d) d->arch.vgic.rdist_count = gicv3.rdist_count; } else - d->arch.vgic.dbase = GUEST_GICD_BASE; + { + d->arch.vgic.dbase = GUEST_GICV3_GICD_BASE; + d->arch.vgic.dbase_size = GUEST_GICV3_GICD_SIZE; + + /* XXX: Only one Re-distributor region mapped for the guest */ + BUILD_BUG_ON(GUEST_GICV3_RDIST_REGIONS != 1); + + d->arch.vgic.rdist_count = GUEST_GICV3_RDIST_REGIONS; + d->arch.vgic.rdist_stride = GUEST_GICV3_RDIST_STRIDE; + + /* The first redistributor should contain enough space for all CPUs */ + BUILD_BUG_ON((GUEST_GICV3_GICR0_SIZE / GUEST_GICV3_RDIST_STRIDE) < MAX_VIRT_CPUS); + d->arch.vgic.rbase[0] = GUEST_GICV3_GICR0_BASE; + d->arch.vgic.rbase_size[0] = GUEST_GICV3_GICR0_SIZE; + } d->arch.vgic.nr_lines = 0; diff --git a/xen/include/public/arch-arm.h b/xen/include/public/arch-arm.h index ac54cd61e0..e7116064c7 100644 --- a/xen/include/public/arch-arm.h +++ b/xen/include/public/arch-arm.h @@ -364,11 +364,27 @@ typedef uint64_t xen_callback_t; */ /* Physical Address Space */ + +/* vGIC mappings: Only one set of mapping is used by the guest. + * Therefore they can overlap. + */ + +/* vGIC v2 mappings */ #define GUEST_GICD_BASE 0x03001000ULL #define GUEST_GICD_SIZE 0x00001000ULL #define GUEST_GICC_BASE 0x03002000ULL #define GUEST_GICC_SIZE 0x00000100ULL +/* vGIC v3 mappings */ +#define GUEST_GICV3_GICD_BASE 0x03001000ULL +#define GUEST_GICV3_GICD_SIZE 0x00010000ULL + +#define GUEST_GICV3_RDIST_STRIDE 0x20000ULL +#define GUEST_GICV3_RDIST_REGIONS 1 + +#define GUEST_GICV3_GICR0_BASE 0x03020000ULL /* vCPU0 - vCPU7 */ +#define GUEST_GICV3_GICR0_SIZE 0x00100000ULL + /* 16MB == 4096 pages reserved for guest to use as a region to map its * grant table in. */ diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h index 58b19e7c1f..8da204e41e 100644 --- a/xen/include/public/domctl.h +++ b/xen/include/public/domctl.h @@ -68,6 +68,19 @@ struct xen_domctl_createdomain { typedef struct xen_domctl_createdomain xen_domctl_createdomain_t; DEFINE_XEN_GUEST_HANDLE(xen_domctl_createdomain_t); +#if defined(__arm__) || defined(__aarch64__) +#define XEN_DOMCTL_CONFIG_GIC_DEFAULT 0 +#define XEN_DOMCTL_CONFIG_GIC_V2 1 +#define XEN_DOMCTL_CONFIG_GIC_V3 2 +/* XEN_DOMCTL_configure_domain */ +struct xen_domctl_arm_configuredomain { + /* IN/OUT parameters */ + uint8_t gic_version; +}; +typedef struct xen_domctl_arm_configuredomain xen_domctl_arm_configuredomain_t; +DEFINE_XEN_GUEST_HANDLE(xen_domctl_arm_configuredomain_t); +#endif + /* XEN_DOMCTL_getdomaininfo */ struct xen_domctl_getdomaininfo { /* OUT variables. */ @@ -1056,6 +1069,7 @@ struct xen_domctl { #define XEN_DOMCTL_set_vcpu_msrs 73 #define XEN_DOMCTL_setvnumainfo 74 #define XEN_DOMCTL_psr_cmt_op 75 +#define XEN_DOMCTL_arm_configure_domain 76 #define XEN_DOMCTL_gdbsx_guestmemio 1000 #define XEN_DOMCTL_gdbsx_pausevcpu 1001 #define XEN_DOMCTL_gdbsx_unpausevcpu 1002 @@ -1064,6 +1078,9 @@ struct xen_domctl { domid_t domain; union { struct xen_domctl_createdomain createdomain; +#if defined(__arm__) || defined(__aarch64__) + struct xen_domctl_arm_configuredomain configuredomain; +#endif struct xen_domctl_getdomaininfo getdomaininfo; struct xen_domctl_getmemlist getmemlist; struct xen_domctl_getpageframeinfo getpageframeinfo; diff --git a/xen/xsm/flask/hooks.c b/xen/xsm/flask/hooks.c index 6d0fe72859..846cf88abb 100644 --- a/xen/xsm/flask/hooks.c +++ b/xen/xsm/flask/hooks.c @@ -727,6 +727,9 @@ static int flask_domctl(struct domain *d, int cmd) case XEN_DOMCTL_psr_cmt_op: return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__PSR_CMT_OP); + case XEN_DOMCTL_configure_domain: + return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__CONFIGURE_DOMAIN); + default: printk("flask_domctl: Unknown op %d\n", cmd); return -EPERM; diff --git a/xen/xsm/flask/policy/access_vectors b/xen/xsm/flask/policy/access_vectors index de0c707fdf..bfe2fa5638 100644 --- a/xen/xsm/flask/policy/access_vectors +++ b/xen/xsm/flask/policy/access_vectors @@ -216,6 +216,8 @@ class domain2 get_vnumainfo # XEN_DOMCTL_psr_cmt_op psr_cmt_op +# XEN_DOMCTL_configure_domain + configure_domain } # Similar to class domain, but primarily contains domctls related to HVM domains