xen/arm: Introduce xen, passthrough property

When a device is marked for passthrough (via the new property
"xen,passthrough"), dom0 must not access to the device (i.e not
loading a driver), but should be able to manage the MMIO/interrupt
of the passthrough device.

The latter part will allow the toolstack to map MMIO/IRQ when a device
is pass through to a guest.

The property "xen,passthrough" will be translated as 'status="disabled"'
in the device tree to avoid DOM0 using the device. We assume that DOM0 is
able to cope with this property (already the case for Linux, and
required by ePAPR).

Rework the function map_device (renamed into handle_device) to:

* For a given device node:
    - Give permission to manage IRQ/MMIO for this device
    - Retrieve the IRQ configuration (i.e edge/level) from the device
    tree
* When the device is not marked for guest passthrough:
    - Assign the device to the guest if it's protected by an IOMMU
    - Map the IRQs and MMIOs regions to the guest

Signed-off-by: Julien Grall <julien.grall@linaro.org>
Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
Acked-by: Ian Campbell <ian.campbell@citrix.com>
This commit is contained in:
Julien Grall authored and Ian Campbell committed 2015-04-02 10:42:34 +01:00
1 parent 3d1fab806c
commit cb818a58f6
5 files changed
+103 -22

No files matched your search

@@ -0,0 +1,9 @@
Device passthrough
===================
Any device with the property "xen,passthrough" set will not be exposed to
DOM0 and therefore no driver will be loaded.
It is highly recommended to set this property on devices which are passed
through since many devices will not cope with being accessed by dom0 and
then handed over to another domain.
+1 -1
View File
@@ -30,7 +30,7 @@ int __init device_init(struct dt_device_node *dev, enum device_class class,
ASSERT(dev != NULL);
if ( !dt_device_is_available(dev) )
if ( !dt_device_is_available(dev) || dt_device_for_passthrough(dev) )
return -ENODEV;
for ( desc = _sdevice; desc != _edevice; desc++ )
+76 -21
View File
@@ -402,7 +402,7 @@ static int write_properties(struct domain *d, struct kernel_info *kinfo,
const struct dt_device_node *node)
{
const char *bootargs = NULL;
const struct dt_property *prop;
const struct dt_property *prop, *status = NULL;
int res = 0;
int had_dom0_bootargs = 0;
@@ -457,6 +457,17 @@ static int write_properties(struct domain *d, struct kernel_info *kinfo,
}
}
/* Don't expose the property "xen,passthrough" to the guest */
if ( dt_property_name_is_equal(prop, "xen,passthrough") )
continue;
/* Remember and skip the status property as Xen may modify it later */
if ( dt_property_name_is_equal(prop, "status") )
{
status = prop;
continue;
}
res = fdt_property(kinfo->fdt, prop->name, prop_data, prop_len);
xfree(new_data);
@@ -465,6 +476,19 @@ static int write_properties(struct domain *d, struct kernel_info *kinfo,
return res;
}
/*
* Override the property "status" to disable the device when it's
* marked for passthrough.
*/
if ( dt_device_for_passthrough(node) )
res = fdt_property_string(kinfo->fdt, "status", "disabled");
else if ( status )
res = fdt_property(kinfo->fdt, "status", status->value,
status->length);
if ( res )
return res;
if ( dt_node_path_is_equal(node, "/chosen") )
{
const struct bootmodule *mod = kinfo->initrd_bootmodule;
@@ -903,8 +927,15 @@ static int make_timer_node(const struct domain *d, void *fdt,
return res;
}
/* Map the device in the domain */
static int map_device(struct domain *d, struct dt_device_node *dev)
/*
* For a given device node:
* - Give permission to the guest to manage IRQ and MMIO range
* - Retrieve the IRQ configuration (i.e edge/level) from device tree
* When the device is not marked for guest passthrough:
* - Assign the device to the guest if it's protected by an IOMMU
* - Map the IRQs and iomem regions to DOM0
*/
static int handle_device(struct domain *d, struct dt_device_node *dev)
{
unsigned int nirq;
unsigned int naddr;
@@ -913,13 +944,15 @@ static int map_device(struct domain *d, struct dt_device_node *dev)
unsigned int irq;
struct dt_raw_irq rirq;
u64 addr, size;
bool_t need_mapping = !dt_device_for_passthrough(dev);
nirq = dt_number_of_irq(dev);
naddr = dt_number_of_address(dev);
DPRINT("%s nirq = %d naddr = %u\n", dt_node_full_name(dev), nirq, naddr);
DPRINT("%s passthrough = %d nirq = %d naddr = %u\n", dt_node_full_name(dev),
need_mapping, nirq, naddr);
if ( dt_device_is_protected(dev) )
if ( dt_device_is_protected(dev) && need_mapping )
{
DPRINT("%s setup iommu\n", dt_node_full_name(dev));
res = iommu_assign_dt_device(d, dev);
@@ -931,7 +964,7 @@ static int map_device(struct domain *d, struct dt_device_node *dev)
}
}
/* Map IRQs */
/* Give permission and map IRQs */
for ( i = 0; i < nirq; i++ )
{
res = dt_device_get_raw_irq(dev, i, &rirq);
@@ -970,16 +1003,34 @@ static int map_device(struct domain *d, struct dt_device_node *dev)
* the IRQ twice. This can legitimately happen if the IRQ is shared
*/
vgic_reserve_virq(d, irq);
res = route_irq_to_guest(d, irq, dt_node_name(dev));
res = irq_permit_access(d, irq);
if ( res )
{
printk(XENLOG_ERR "Unable to route IRQ %u to domain %u\n",
irq, d->domain_id);
printk(XENLOG_ERR "Unable to permit to dom%u access to IRQ %u\n",
d->domain_id, irq);
return res;
}
if ( need_mapping )
{
/*
* Checking the return of vgic_reserve_virq is not
* necessary. It should not fail except when we try to map
* twice the IRQ. This can happen if the IRQ is shared
*/
vgic_reserve_virq(d, irq);
res = route_irq_to_guest(d, irq, dt_node_name(dev));
if ( res )
{
printk(XENLOG_ERR "Unable to route IRQ %u to domain %u\n",
irq, d->domain_id);
return res;
}
}
}
/* Map the address ranges */
/* Give permission and map MMIOs */
for ( i = 0; i < naddr; i++ )
{
res = dt_device_get_address(dev, i, &addr, &size);
@@ -1003,17 +1054,21 @@ static int map_device(struct domain *d, struct dt_device_node *dev)
addr & PAGE_MASK, PAGE_ALIGN(addr + size) - 1);
return res;
}
res = map_mmio_regions(d,
paddr_to_pfn(addr & PAGE_MASK),
DIV_ROUND_UP(size, PAGE_SIZE),
paddr_to_pfn(addr & PAGE_MASK));
if ( res )
if ( need_mapping )
{
printk(XENLOG_ERR "Unable to map 0x%"PRIx64
" - 0x%"PRIx64" in domain %d\n",
addr & PAGE_MASK, PAGE_ALIGN(addr + size) - 1,
d->domain_id);
return res;
res = map_mmio_regions(d,
paddr_to_pfn(addr & PAGE_MASK),
DIV_ROUND_UP(size, PAGE_SIZE),
paddr_to_pfn(addr & PAGE_MASK));
if ( res )
{
printk(XENLOG_ERR "Unable to map 0x%"PRIx64
" - 0x%"PRIx64" in domain %d\n",
addr & PAGE_MASK, PAGE_ALIGN(addr + size) - 1,
d->domain_id);
return res;
}
}
}
@@ -1085,7 +1140,7 @@ static int handle_node(struct domain *d, struct kernel_info *kinfo,
return 0;
}
res = map_device(d, node);
res = handle_device(d, node);
if ( res)
return res;
+6
View File
@@ -1104,6 +1104,12 @@ bool_t dt_device_is_available(const struct dt_device_node *device)
return 0;
}
bool_t dt_device_for_passthrough(const struct dt_device_node *device)
{
return (dt_find_property(device, "xen,passthrough", NULL) != NULL);
}
static int __dt_parse_phandle_with_args(const struct dt_device_node *np,
const char *list_name,
const char *cells_name,
+11
View File
@@ -555,6 +555,17 @@ int dt_n_addr_cells(const struct dt_device_node *np);
*/
bool_t dt_device_is_available(const struct dt_device_node *device);
/**
* dt_device_for_passthrough - Check if a device will be used for
* passthrough later
*
* @device: Node to check
*
* Return true if the property "xen,passthrough" is present in the node,
* false otherwise.
*/
bool_t dt_device_for_passthrough(const struct dt_device_node *device);
/**
* dt_match_node - Tell if a device_node has a matching of dt_device_match
* @matches: array of dt_device_match structures to search in
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.