Message ID | 1407594349-9291-11-git-send-email-eric.auger@linaro.org |
---|---|
State | New |
Headers | show |
On 09.08.14 16:25, Eric Auger wrote: > Generates the device node of VFIO devices, if any are invoked in > -device option. In case VFIO devices require more complex node > generation, they can be handled before. > > Signed-off-by: Eric Auger <eric.auger@linaro.org> > --- > hw/arm/dyn_sysbus_devtree.c | 138 ++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 138 insertions(+) > > diff --git a/hw/arm/dyn_sysbus_devtree.c b/hw/arm/dyn_sysbus_devtree.c > index 56af62f..ac34f07 100644 > --- a/hw/arm/dyn_sysbus_devtree.c > +++ b/hw/arm/dyn_sysbus_devtree.c > @@ -1,6 +1,139 @@ > #include "hw/arm/dyn_sysbus_devtree.h" > #include "qemu/error-report.h" > #include "sysemu/device_tree.h" > +#include "hw/vfio/vfio-platform.h" > + > +static void vfio_fdt_add_device_node(SysBusDevice *sbdev, void *opaque); > + > +static char *format_compat(char * compat) > +{ > + char *str_ptr, *corrected_compat; > + /* > + * process compatibility property string passed by end-user > + * replaces / by , and ; by NUL character > + */ > + corrected_compat = g_strdup(compat); > + /* > + * the total length of the string has to include also the last > + * NUL char. > + */ > + > + str_ptr = corrected_compat; > + while ((str_ptr = strchr(str_ptr, '/')) != NULL) { > + *str_ptr = ','; > + } > + > + /* substitute ";" with the NUL char */ > + str_ptr = corrected_compat; > + while ((str_ptr = strchr(str_ptr, ';')) != NULL) { > + *str_ptr = '\0'; > + } > + > + return corrected_compat; > +} > + > +static void wrap_vfio_fdt_add_node(SysBusDevice *sbdev, void *opaque) > +{ > + PlatformDevtreeData *data = opaque; > + VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); > + VFIODevice *vbasedev = &vdev->vbasedev; > + gchar irq_number_prop[8]; > + Object *obj = OBJECT(sbdev); > + char *corrected_compat; > + uint64_t irq_number; > + int compat_str_len = strlen(vdev->compat)+1; > + int i; > + > + corrected_compat = format_compat(vdev->compat); > + snprintf(vdev->compat, compat_str_len, "%s", corrected_compat); > + g_free(corrected_compat); > + > + vfio_fdt_add_device_node(sbdev, opaque); > + > + for (i = 0; i < vbasedev->num_irqs; i++) { > + snprintf(irq_number_prop, sizeof(irq_number_prop), "irq[%d]", i); > + irq_number = object_property_get_int(obj, irq_number_prop, NULL) > + + data->irq_start; > + /* > + * for setting irqfd up we must provide the virtual IRQ number > + * which is the sum of irq_start and actual platform bus irq > + * index. At realize point we do not have this info. > + */ > + if (vdev->irqfd_allowed) { > + vfio_setup_irqfd(sbdev, i, irq_number); > + } > + } > +} > + > +static void vfio_fdt_add_device_node(SysBusDevice *sbdev, void *opaque) > +{ > + PlatformDevtreeData *data = opaque; > + void *fdt = data->fdt; > + const char *parent_node = data->node; > + int compat_str_len; > + char *nodename; > + int i, ret; > + uint32_t *irq_attr; > + uint64_t *reg_attr; > + uint64_t mmio_base; > + uint64_t irq_number; > + gchar mmio_base_prop[8]; > + gchar irq_number_prop[8]; > + VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); > + VFIODevice *vbasedev = &vdev->vbasedev; > + Object *obj = OBJECT(sbdev); > + > + mmio_base = object_property_get_int(obj, "mmio[0]", NULL); > + > + nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, > + vbasedev->name, > + mmio_base); > + > + qemu_fdt_add_subnode(fdt, nodename); > + > + compat_str_len = strlen(vdev->compat) + 1; > + qemu_fdt_setprop(fdt, nodename, "compatible", > + vdev->compat, compat_str_len); > + > + reg_attr = g_new(uint64_t, vbasedev->num_regions*4); > + > + for (i = 0; i < vbasedev->num_regions; i++) { > + snprintf(mmio_base_prop, sizeof(mmio_base_prop), "mmio[%d]", i); > + mmio_base = object_property_get_int(obj, mmio_base_prop, NULL); > + reg_attr[2*i] = 1; > + reg_attr[2*i+1] = mmio_base; > + reg_attr[2*i+2] = 1; > + reg_attr[2*i+3] = memory_region_size(&vdev->regions[i]->mem); > + } > + > + ret = qemu_fdt_setprop_sized_cells_from_array(fdt, nodename, "reg", > + vbasedev->num_regions*2, reg_attr); > + if (ret < 0) { > + error_report("could not set reg property of node %s", nodename); > + } > + > + irq_attr = g_new(uint32_t, vbasedev->num_irqs*3); > + > + for (i = 0; i < vbasedev->num_irqs; i++) { > + snprintf(irq_number_prop, sizeof(irq_number_prop), "irq[%d]", i); > + irq_number = object_property_get_int(obj, irq_number_prop, NULL) > + + data->irq_start; > + irq_attr[3*i] = cpu_to_be32(0); > + irq_attr[3*i+1] = cpu_to_be32(irq_number); > + irq_attr[3*i+2] = cpu_to_be32(0x4); > + } > + > + ret = qemu_fdt_setprop(fdt, nodename, "interrupts", > + irq_attr, vbasedev->num_irqs*3*sizeof(uint32_t)); > + if (ret < 0) { > + error_report("could not set interrupts property of node %s", > + nodename); > + } > + > + g_free(nodename); > + g_free(irq_attr); > + g_free(reg_attr); > +} > > int sysbus_device_create_devtree(Object *obj, void *opaque) > { > @@ -17,6 +150,11 @@ int sysbus_device_create_devtree(Object *obj, void *opaque) > return object_child_foreach(obj, sysbus_device_create_devtree, data); > } > > + if (object_dynamic_cast(obj, TYPE_VFIO_PLATFORM)) { You should only ever check for specific VFIO device types. A generic "vfio-platform" device type will not work, since you won't have enough auxiliary information once devices become more complicated. Alex
On 08/11/2014 11:40 AM, Alexander Graf wrote: > > On 09.08.14 16:25, Eric Auger wrote: >> Generates the device node of VFIO devices, if any are invoked in >> -device option. In case VFIO devices require more complex node >> generation, they can be handled before. >> >> Signed-off-by: Eric Auger <eric.auger@linaro.org> >> --- >> hw/arm/dyn_sysbus_devtree.c | 138 >> ++++++++++++++++++++++++++++++++++++++++++++ >> 1 file changed, 138 insertions(+) >> >> diff --git a/hw/arm/dyn_sysbus_devtree.c b/hw/arm/dyn_sysbus_devtree.c >> index 56af62f..ac34f07 100644 >> --- a/hw/arm/dyn_sysbus_devtree.c >> +++ b/hw/arm/dyn_sysbus_devtree.c >> @@ -1,6 +1,139 @@ >> #include "hw/arm/dyn_sysbus_devtree.h" >> #include "qemu/error-report.h" >> #include "sysemu/device_tree.h" >> +#include "hw/vfio/vfio-platform.h" >> + >> +static void vfio_fdt_add_device_node(SysBusDevice *sbdev, void *opaque); >> + >> +static char *format_compat(char * compat) >> +{ >> + char *str_ptr, *corrected_compat; >> + /* >> + * process compatibility property string passed by end-user >> + * replaces / by , and ; by NUL character >> + */ >> + corrected_compat = g_strdup(compat); >> + /* >> + * the total length of the string has to include also the last >> + * NUL char. >> + */ >> + >> + str_ptr = corrected_compat; >> + while ((str_ptr = strchr(str_ptr, '/')) != NULL) { >> + *str_ptr = ','; >> + } >> + >> + /* substitute ";" with the NUL char */ >> + str_ptr = corrected_compat; >> + while ((str_ptr = strchr(str_ptr, ';')) != NULL) { >> + *str_ptr = '\0'; >> + } >> + >> + return corrected_compat; >> +} >> + >> +static void wrap_vfio_fdt_add_node(SysBusDevice *sbdev, void *opaque) >> +{ >> + PlatformDevtreeData *data = opaque; >> + VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); >> + VFIODevice *vbasedev = &vdev->vbasedev; >> + gchar irq_number_prop[8]; >> + Object *obj = OBJECT(sbdev); >> + char *corrected_compat; >> + uint64_t irq_number; >> + int compat_str_len = strlen(vdev->compat)+1; >> + int i; >> + >> + corrected_compat = format_compat(vdev->compat); >> + snprintf(vdev->compat, compat_str_len, "%s", corrected_compat); >> + g_free(corrected_compat); >> + >> + vfio_fdt_add_device_node(sbdev, opaque); >> + >> + for (i = 0; i < vbasedev->num_irqs; i++) { >> + snprintf(irq_number_prop, sizeof(irq_number_prop), "irq[%d]", >> i); >> + irq_number = object_property_get_int(obj, irq_number_prop, NULL) >> + + data->irq_start; >> + /* >> + * for setting irqfd up we must provide the virtual IRQ number >> + * which is the sum of irq_start and actual platform bus irq >> + * index. At realize point we do not have this info. >> + */ >> + if (vdev->irqfd_allowed) { >> + vfio_setup_irqfd(sbdev, i, irq_number); >> + } >> + } >> +} >> + >> +static void vfio_fdt_add_device_node(SysBusDevice *sbdev, void *opaque) >> +{ >> + PlatformDevtreeData *data = opaque; >> + void *fdt = data->fdt; >> + const char *parent_node = data->node; >> + int compat_str_len; >> + char *nodename; >> + int i, ret; >> + uint32_t *irq_attr; >> + uint64_t *reg_attr; >> + uint64_t mmio_base; >> + uint64_t irq_number; >> + gchar mmio_base_prop[8]; >> + gchar irq_number_prop[8]; >> + VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); >> + VFIODevice *vbasedev = &vdev->vbasedev; >> + Object *obj = OBJECT(sbdev); >> + >> + mmio_base = object_property_get_int(obj, "mmio[0]", NULL); >> + >> + nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, >> + vbasedev->name, >> + mmio_base); >> + >> + qemu_fdt_add_subnode(fdt, nodename); >> + >> + compat_str_len = strlen(vdev->compat) + 1; >> + qemu_fdt_setprop(fdt, nodename, "compatible", >> + vdev->compat, compat_str_len); >> + >> + reg_attr = g_new(uint64_t, vbasedev->num_regions*4); >> + >> + for (i = 0; i < vbasedev->num_regions; i++) { >> + snprintf(mmio_base_prop, sizeof(mmio_base_prop), "mmio[%d]", i); >> + mmio_base = object_property_get_int(obj, mmio_base_prop, NULL); >> + reg_attr[2*i] = 1; >> + reg_attr[2*i+1] = mmio_base; >> + reg_attr[2*i+2] = 1; >> + reg_attr[2*i+3] = memory_region_size(&vdev->regions[i]->mem); >> + } >> + >> + ret = qemu_fdt_setprop_sized_cells_from_array(fdt, nodename, "reg", >> + vbasedev->num_regions*2, reg_attr); >> + if (ret < 0) { >> + error_report("could not set reg property of node %s", nodename); >> + } >> + >> + irq_attr = g_new(uint32_t, vbasedev->num_irqs*3); >> + >> + for (i = 0; i < vbasedev->num_irqs; i++) { >> + snprintf(irq_number_prop, sizeof(irq_number_prop), "irq[%d]", >> i); >> + irq_number = object_property_get_int(obj, irq_number_prop, NULL) >> + + data->irq_start; >> + irq_attr[3*i] = cpu_to_be32(0); >> + irq_attr[3*i+1] = cpu_to_be32(irq_number); >> + irq_attr[3*i+2] = cpu_to_be32(0x4); >> + } >> + >> + ret = qemu_fdt_setprop(fdt, nodename, "interrupts", >> + irq_attr, vbasedev->num_irqs*3*sizeof(uint32_t)); >> + if (ret < 0) { >> + error_report("could not set interrupts property of node %s", >> + nodename); >> + } >> + >> + g_free(nodename); >> + g_free(irq_attr); >> + g_free(reg_attr); >> +} >> int sysbus_device_create_devtree(Object *obj, void *opaque) >> { >> @@ -17,6 +150,11 @@ int sysbus_device_create_devtree(Object *obj, void >> *opaque) >> return object_child_foreach(obj, >> sysbus_device_create_devtree, data); >> } >> + if (object_dynamic_cast(obj, TYPE_VFIO_PLATFORM)) { > > You should only ever check for specific VFIO device types. A generic > "vfio-platform" device type will not work, since you won't have enough > auxiliary information once devices become more complicated. Hi Alex, I will re-submit this week with calxeda-xgmac derived device back again and abstract class too. Thanks Eric > > > Alex >
+static void vfio_fdt_add_device_node(SysBusDevice *sbdev, void *opaque) +{ + PlatformDevtreeData *data = opaque; + void *fdt = data->fdt; + const char *parent_node = data->node; + int compat_str_len; + char *nodename; + int i, ret; + uint32_t *irq_attr; + uint64_t *reg_attr; + uint64_t mmio_base; + uint64_t irq_number; + gchar mmio_base_prop[8]; + gchar irq_number_prop[8]; + VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); + VFIODevice *vbasedev = &vdev->vbasedev; + Object *obj = OBJECT(sbdev); + + mmio_base = object_property_get_int(obj, "mmio[0]", NULL); + + nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, + vbasedev->name, + mmio_base); + + qemu_fdt_add_subnode(fdt, nodename); + + compat_str_len = strlen(vdev->compat) + 1; + qemu_fdt_setprop(fdt, nodename, "compatible", + vdev->compat, compat_str_len); + + reg_attr = g_new(uint64_t, vbasedev->num_regions*4); + + for (i = 0; i < vbasedev->num_regions; i++) { + snprintf(mmio_base_prop, sizeof(mmio_base_prop), "mmio[%d]", i); + mmio_base = object_property_get_int(obj, mmio_base_prop, NULL); + reg_attr[2*i] = 1; + reg_attr[2*i+1] = mmio_base; + reg_attr[2*i+2] = 1; + reg_attr[2*i+3] = memory_region_size(&vdev->regions[i]->mem); + } This should be 4 instead of 2. Also, to support 64 bit systems I think this should be 2 instead of 1.
On 18 August 2014 22:54, Joel Schopp <joel.schopp@amd.com> wrote: > > +static void vfio_fdt_add_device_node(SysBusDevice *sbdev, void *opaque) > +{ > + PlatformDevtreeData *data = opaque; > + void *fdt = data->fdt; > + const char *parent_node = data->node; > + int compat_str_len; > + char *nodename; > + int i, ret; > + uint32_t *irq_attr; > + uint64_t *reg_attr; > + uint64_t mmio_base; > + uint64_t irq_number; > + gchar mmio_base_prop[8]; > + gchar irq_number_prop[8]; > + VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); > + VFIODevice *vbasedev = &vdev->vbasedev; > + Object *obj = OBJECT(sbdev); > + > + mmio_base = object_property_get_int(obj, "mmio[0]", NULL); > + > + nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, > + vbasedev->name, > + mmio_base); > + > + qemu_fdt_add_subnode(fdt, nodename); > + > + compat_str_len = strlen(vdev->compat) + 1; At this point you've already substituted the NULs in, so you can't call strlen(), I think. > + qemu_fdt_setprop(fdt, nodename, "compatible", > + vdev->compat, compat_str_len); > + > + reg_attr = g_new(uint64_t, vbasedev->num_regions*4); > + > + for (i = 0; i < vbasedev->num_regions; i++) { > + snprintf(mmio_base_prop, sizeof(mmio_base_prop), "mmio[%d]", i); > + mmio_base = object_property_get_int(obj, mmio_base_prop, NULL); > + reg_attr[2*i] = 1; > + reg_attr[2*i+1] = mmio_base; > + reg_attr[2*i+2] = 1; > + reg_attr[2*i+3] = memory_region_size(&vdev->regions[i]->mem); > + } > > This should be 4 instead of 2. > Also, to support 64 bit systems I think this should be 2 instead of 1. Actually it depends entirely on what the board has done to create the device tree node that we're inserting this child node into. For ARM boot.c sets both #address-cells and #size-cells to 2 regardless of whether the system is 32 or 64 bits, for simplicity. I imagine PPC does something different. If we're editing a dtb that the user passed in (which I think would be pretty lunatic so we shouldn't do this) we'd have to actually walk the dtb to try to figure out what the semantics of the reg property should be. thanks -- PMM
On 08/18/2014 05:11 PM, Peter Maydell wrote: > On 18 August 2014 22:54, Joel Schopp <joel.schopp@amd.com> wrote: >> +static void vfio_fdt_add_device_node(SysBusDevice *sbdev, void *opaque) >> +{ >> + PlatformDevtreeData *data = opaque; >> + void *fdt = data->fdt; >> + const char *parent_node = data->node; >> + int compat_str_len; >> + char *nodename; >> + int i, ret; >> + uint32_t *irq_attr; >> + uint64_t *reg_attr; >> + uint64_t mmio_base; >> + uint64_t irq_number; >> + gchar mmio_base_prop[8]; >> + gchar irq_number_prop[8]; >> + VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); >> + VFIODevice *vbasedev = &vdev->vbasedev; >> + Object *obj = OBJECT(sbdev); >> + >> + mmio_base = object_property_get_int(obj, "mmio[0]", NULL); >> + >> + nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, >> + vbasedev->name, >> + mmio_base); >> + >> + qemu_fdt_add_subnode(fdt, nodename); >> + >> + compat_str_len = strlen(vdev->compat) + 1; > At this point you've already substituted the NULs in, > so you can't call strlen(), I think. > >> + qemu_fdt_setprop(fdt, nodename, "compatible", >> + vdev->compat, compat_str_len); >> + >> + reg_attr = g_new(uint64_t, vbasedev->num_regions*4); >> + >> + for (i = 0; i < vbasedev->num_regions; i++) { >> + snprintf(mmio_base_prop, sizeof(mmio_base_prop), "mmio[%d]", i); >> + mmio_base = object_property_get_int(obj, mmio_base_prop, NULL); >> + reg_attr[2*i] = 1; >> + reg_attr[2*i+1] = mmio_base; >> + reg_attr[2*i+2] = 1; >> + reg_attr[2*i+3] = memory_region_size(&vdev->regions[i]->mem); >> + } >> >> This should be 4 instead of 2. >> Also, to support 64 bit systems I think this should be 2 instead of 1. > Actually it depends entirely on what the board has done to > create the device tree node that we're inserting this child > node into. For ARM boot.c sets both #address-cells and > #size-cells to 2 regardless of whether the system is 32 > or 64 bits, for simplicity. I imagine PPC does something > different. If we're editing a dtb that the user passed in (which > I think would be pretty lunatic so we shouldn't do this) > we'd have to actually walk the dtb to try to figure out what > the semantics of the reg property should be. For the index [2*i],[2*i+1], etc is clearly a bug as when i = 1 it will overwrite two of the values. Changing that to [4*i],[4*i+1],etc fixes it. I think you are right on the size. I also wonder if the user doesn't pass in a dtb if qemu should try to recreate the device-tree entry from the platform device entry in the host kernel? If so would that best be done by recreating the values from /proc/device-tree ? I also wish that qemu had a flag to output the generated dtb to a file much like lkvm (kvmtool) has.
On 08/18/2014 11:54 PM, Joel Schopp wrote: > > +static void vfio_fdt_add_device_node(SysBusDevice *sbdev, void *opaque) > +{ > + PlatformDevtreeData *data = opaque; > + void *fdt = data->fdt; > + const char *parent_node = data->node; > + int compat_str_len; > + char *nodename; > + int i, ret; > + uint32_t *irq_attr; > + uint64_t *reg_attr; > + uint64_t mmio_base; > + uint64_t irq_number; > + gchar mmio_base_prop[8]; > + gchar irq_number_prop[8]; > + VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); > + VFIODevice *vbasedev = &vdev->vbasedev; > + Object *obj = OBJECT(sbdev); > + > + mmio_base = object_property_get_int(obj, "mmio[0]", NULL); > + > + nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, > + vbasedev->name, > + mmio_base); > + > + qemu_fdt_add_subnode(fdt, nodename); > + > + compat_str_len = strlen(vdev->compat) + 1; > + qemu_fdt_setprop(fdt, nodename, "compatible", > + vdev->compat, compat_str_len); > + > + reg_attr = g_new(uint64_t, vbasedev->num_regions*4); > + > + for (i = 0; i < vbasedev->num_regions; i++) { > + snprintf(mmio_base_prop, sizeof(mmio_base_prop), "mmio[%d]", i); > + mmio_base = object_property_get_int(obj, mmio_base_prop, NULL); > + reg_attr[2*i] = 1; > + reg_attr[2*i+1] = mmio_base; > + reg_attr[2*i+2] = 1; > + reg_attr[2*i+3] = memory_region_size(&vdev->regions[i]->mem); > + } > > This should be 4 instead of 2. Hi Joel, Yes definitively! Forgot to restore the original value after trying different qemu_fdt_setprop_* functions. sorry for that. Best Regards Eric > Also, to support 64 bit systems I think this should be 2 instead of 1. >
On 08/19/2014 12:11 AM, Peter Maydell wrote: > On 18 August 2014 22:54, Joel Schopp <joel.schopp@amd.com> wrote: >> >> +static void vfio_fdt_add_device_node(SysBusDevice *sbdev, void *opaque) >> +{ >> + PlatformDevtreeData *data = opaque; >> + void *fdt = data->fdt; >> + const char *parent_node = data->node; >> + int compat_str_len; >> + char *nodename; >> + int i, ret; >> + uint32_t *irq_attr; >> + uint64_t *reg_attr; >> + uint64_t mmio_base; >> + uint64_t irq_number; >> + gchar mmio_base_prop[8]; >> + gchar irq_number_prop[8]; >> + VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); >> + VFIODevice *vbasedev = &vdev->vbasedev; >> + Object *obj = OBJECT(sbdev); >> + >> + mmio_base = object_property_get_int(obj, "mmio[0]", NULL); >> + >> + nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, >> + vbasedev->name, >> + mmio_base); >> + >> + qemu_fdt_add_subnode(fdt, nodename); >> + >> + compat_str_len = strlen(vdev->compat) + 1; > > At this point you've already substituted the NULs in, > so you can't call strlen(), I think. Hi Peter, yes you're right. Thanks > >> + qemu_fdt_setprop(fdt, nodename, "compatible", >> + vdev->compat, compat_str_len); >> + >> + reg_attr = g_new(uint64_t, vbasedev->num_regions*4); >> + >> + for (i = 0; i < vbasedev->num_regions; i++) { >> + snprintf(mmio_base_prop, sizeof(mmio_base_prop), "mmio[%d]", i); >> + mmio_base = object_property_get_int(obj, mmio_base_prop, NULL); >> + reg_attr[2*i] = 1; >> + reg_attr[2*i+1] = mmio_base; >> + reg_attr[2*i+2] = 1; >> + reg_attr[2*i+3] = memory_region_size(&vdev->regions[i]->mem); >> + } >> >> This should be 4 instead of 2. >> Also, to support 64 bit systems I think this should be 2 instead of 1. > > Actually it depends entirely on what the board has done to > create the device tree node that we're inserting this child > node into. For ARM boot.c sets both #address-cells and > #size-cells to 2 regardless of whether the system is 32 > or 64 bits, for simplicity. I imagine PPC does something > different. If we're editing a dtb that the user passed in (which > I think would be pretty lunatic so we shouldn't do this) > we'd have to actually walk the dtb to try to figure out what > the semantics of the reg property should be. Putting size=1 was the only solution I found to use an offset relative to the parent bus instead of an absolute base address. I would explain this because, in platform_bus_create_devtree, the function that creates the "platform bus" node, #address-cells and #size-cells currently are set to 1. I assume the motivation was that bus size was supposed to be smaller than 4GB. Then I guess the problem is shifted to the inclusion of the platform bus in any ARM platform. Thanks Eric > > thanks > -- PMM >
On 08/19/2014 12:26 AM, Joel Schopp wrote: > > On 08/18/2014 05:11 PM, Peter Maydell wrote: >> On 18 August 2014 22:54, Joel Schopp <joel.schopp@amd.com> wrote: >>> +static void vfio_fdt_add_device_node(SysBusDevice *sbdev, void *opaque) >>> +{ >>> + PlatformDevtreeData *data = opaque; >>> + void *fdt = data->fdt; >>> + const char *parent_node = data->node; >>> + int compat_str_len; >>> + char *nodename; >>> + int i, ret; >>> + uint32_t *irq_attr; >>> + uint64_t *reg_attr; >>> + uint64_t mmio_base; >>> + uint64_t irq_number; >>> + gchar mmio_base_prop[8]; >>> + gchar irq_number_prop[8]; >>> + VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); >>> + VFIODevice *vbasedev = &vdev->vbasedev; >>> + Object *obj = OBJECT(sbdev); >>> + >>> + mmio_base = object_property_get_int(obj, "mmio[0]", NULL); >>> + >>> + nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, >>> + vbasedev->name, >>> + mmio_base); >>> + >>> + qemu_fdt_add_subnode(fdt, nodename); >>> + >>> + compat_str_len = strlen(vdev->compat) + 1; >> At this point you've already substituted the NULs in, >> so you can't call strlen(), I think. >> >>> + qemu_fdt_setprop(fdt, nodename, "compatible", >>> + vdev->compat, compat_str_len); >>> + >>> + reg_attr = g_new(uint64_t, vbasedev->num_regions*4); >>> + >>> + for (i = 0; i < vbasedev->num_regions; i++) { >>> + snprintf(mmio_base_prop, sizeof(mmio_base_prop), "mmio[%d]", i); >>> + mmio_base = object_property_get_int(obj, mmio_base_prop, NULL); >>> + reg_attr[2*i] = 1; >>> + reg_attr[2*i+1] = mmio_base; >>> + reg_attr[2*i+2] = 1; >>> + reg_attr[2*i+3] = memory_region_size(&vdev->regions[i]->mem); >>> + } >>> >>> This should be 4 instead of 2. >>> Also, to support 64 bit systems I think this should be 2 instead of 1. >> Actually it depends entirely on what the board has done to >> create the device tree node that we're inserting this child >> node into. For ARM boot.c sets both #address-cells and >> #size-cells to 2 regardless of whether the system is 32 >> or 64 bits, for simplicity. I imagine PPC does something >> different. If we're editing a dtb that the user passed in (which >> I think would be pretty lunatic so we shouldn't do this) >> we'd have to actually walk the dtb to try to figure out what >> the semantics of the reg property should be. > For the index [2*i],[2*i+1], etc is clearly a bug as when i = 1 it will > overwrite two of the values. Changing that to [4*i],[4*i+1],etc fixes it. > > I think you are right on the size. I also wonder if the user doesn't > pass in a dtb if qemu should try to recreate the device-tree entry from > the platform device entry in the host kernel? If so would that best be > done by recreating the values from /proc/device-tree ? Antonios recently submitted a patch to retrieve dt info from the vfio platform device. [RFC 0/4] VFIO: PLATFORM: Return device tree info for a platform device node https://www.mail-archive.com/kvm@vger.kernel.org/msg106282.html Best Regards Eric > > I also wish that qemu had a flag to output the generated dtb to a file > much like lkvm (kvmtool) has. >
On 19 August 2014 08:24, Eric Auger <eric.auger@linaro.org> wrote: > Putting size=1 was the only solution I found to use an offset relative > to the parent bus instead of an absolute base address. I would explain > this because, in platform_bus_create_devtree, the function that creates > the "platform bus" node, #address-cells and #size-cells currently are > set to 1. I assume the motivation was that bus size was supposed to be > smaller than 4GB. Then I guess the problem is shifted to the inclusion > of the platform bus in any ARM platform. Ah, I see. Yes, if the containing node is setting addr/size to 1 then 1 is correct, and the limitation then is just the 4GB max. thanks -- PMM
On 19.08.14 00:26, Joel Schopp wrote: > > On 08/18/2014 05:11 PM, Peter Maydell wrote: >> On 18 August 2014 22:54, Joel Schopp <joel.schopp@amd.com> wrote: >>> +static void vfio_fdt_add_device_node(SysBusDevice *sbdev, void *opaque) >>> +{ >>> + PlatformDevtreeData *data = opaque; >>> + void *fdt = data->fdt; >>> + const char *parent_node = data->node; >>> + int compat_str_len; >>> + char *nodename; >>> + int i, ret; >>> + uint32_t *irq_attr; >>> + uint64_t *reg_attr; >>> + uint64_t mmio_base; >>> + uint64_t irq_number; >>> + gchar mmio_base_prop[8]; >>> + gchar irq_number_prop[8]; >>> + VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); >>> + VFIODevice *vbasedev = &vdev->vbasedev; >>> + Object *obj = OBJECT(sbdev); >>> + >>> + mmio_base = object_property_get_int(obj, "mmio[0]", NULL); >>> + >>> + nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, >>> + vbasedev->name, >>> + mmio_base); >>> + >>> + qemu_fdt_add_subnode(fdt, nodename); >>> + >>> + compat_str_len = strlen(vdev->compat) + 1; >> At this point you've already substituted the NULs in, >> so you can't call strlen(), I think. >> >>> + qemu_fdt_setprop(fdt, nodename, "compatible", >>> + vdev->compat, compat_str_len); >>> + >>> + reg_attr = g_new(uint64_t, vbasedev->num_regions*4); >>> + >>> + for (i = 0; i < vbasedev->num_regions; i++) { >>> + snprintf(mmio_base_prop, sizeof(mmio_base_prop), "mmio[%d]", i); >>> + mmio_base = object_property_get_int(obj, mmio_base_prop, NULL); >>> + reg_attr[2*i] = 1; >>> + reg_attr[2*i+1] = mmio_base; >>> + reg_attr[2*i+2] = 1; >>> + reg_attr[2*i+3] = memory_region_size(&vdev->regions[i]->mem); >>> + } >>> >>> This should be 4 instead of 2. >>> Also, to support 64 bit systems I think this should be 2 instead of 1. >> Actually it depends entirely on what the board has done to >> create the device tree node that we're inserting this child >> node into. For ARM boot.c sets both #address-cells and >> #size-cells to 2 regardless of whether the system is 32 >> or 64 bits, for simplicity. I imagine PPC does something >> different. If we're editing a dtb that the user passed in (which >> I think would be pretty lunatic so we shouldn't do this) >> we'd have to actually walk the dtb to try to figure out what >> the semantics of the reg property should be. > For the index [2*i],[2*i+1], etc is clearly a bug as when i = 1 it will > overwrite two of the values. Changing that to [4*i],[4*i+1],etc fixes it. > > I think you are right on the size. I also wonder if the user doesn't > pass in a dtb if qemu should try to recreate the device-tree entry from > the platform device entry in the host kernel? If so would that best be > done by recreating the values from /proc/device-tree ? > > I also wish that qemu had a flag to output the generated dtb to a file > much like lkvm (kvmtool) has. It does. "qemu-system-foo -machine dumpdtb=mydtb.dtb" should dump the generated dtb into a file called mydtb.dtb. Alex
>> For the index [2*i],[2*i+1], etc is clearly a bug as when i = 1 it will >> overwrite two of the values. Changing that to [4*i],[4*i+1],etc fixes it. >> >> I think you are right on the size. I also wonder if the user doesn't >> pass in a dtb if qemu should try to recreate the device-tree entry from >> the platform device entry in the host kernel? If so would that best be >> done by recreating the values from /proc/device-tree ? >> >> I also wish that qemu had a flag to output the generated dtb to a file >> much like lkvm (kvmtool) has. > It does. "qemu-system-foo -machine dumpdtb=mydtb.dtb" should dump the > generated dtb into a file called mydtb.dtb. Would a patch that adds this output to --help be welcomed?
On 19.08.14 16:15, Joel Schopp wrote: > >>> For the index [2*i],[2*i+1], etc is clearly a bug as when i = 1 it will >>> overwrite two of the values. Changing that to [4*i],[4*i+1],etc fixes it. >>> >>> I think you are right on the size. I also wonder if the user doesn't >>> pass in a dtb if qemu should try to recreate the device-tree entry from >>> the platform device entry in the host kernel? If so would that best be >>> done by recreating the values from /proc/device-tree ? >>> >>> I also wish that qemu had a flag to output the generated dtb to a file >>> much like lkvm (kvmtool) has. >> It does. "qemu-system-foo -machine dumpdtb=mydtb.dtb" should dump the >> generated dtb into a file called mydtb.dtb. > > Would a patch that adds this output to --help be welcomed? Not sure -help is the right place for these debugging options. But I would definitely love to see all -machine options properly documented in the man page! Alex
diff --git a/hw/arm/dyn_sysbus_devtree.c b/hw/arm/dyn_sysbus_devtree.c index 56af62f..ac34f07 100644 --- a/hw/arm/dyn_sysbus_devtree.c +++ b/hw/arm/dyn_sysbus_devtree.c @@ -1,6 +1,139 @@ #include "hw/arm/dyn_sysbus_devtree.h" #include "qemu/error-report.h" #include "sysemu/device_tree.h" +#include "hw/vfio/vfio-platform.h" + +static void vfio_fdt_add_device_node(SysBusDevice *sbdev, void *opaque); + +static char *format_compat(char * compat) +{ + char *str_ptr, *corrected_compat; + /* + * process compatibility property string passed by end-user + * replaces / by , and ; by NUL character + */ + corrected_compat = g_strdup(compat); + /* + * the total length of the string has to include also the last + * NUL char. + */ + + str_ptr = corrected_compat; + while ((str_ptr = strchr(str_ptr, '/')) != NULL) { + *str_ptr = ','; + } + + /* substitute ";" with the NUL char */ + str_ptr = corrected_compat; + while ((str_ptr = strchr(str_ptr, ';')) != NULL) { + *str_ptr = '\0'; + } + + return corrected_compat; +} + +static void wrap_vfio_fdt_add_node(SysBusDevice *sbdev, void *opaque) +{ + PlatformDevtreeData *data = opaque; + VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); + VFIODevice *vbasedev = &vdev->vbasedev; + gchar irq_number_prop[8]; + Object *obj = OBJECT(sbdev); + char *corrected_compat; + uint64_t irq_number; + int compat_str_len = strlen(vdev->compat)+1; + int i; + + corrected_compat = format_compat(vdev->compat); + snprintf(vdev->compat, compat_str_len, "%s", corrected_compat); + g_free(corrected_compat); + + vfio_fdt_add_device_node(sbdev, opaque); + + for (i = 0; i < vbasedev->num_irqs; i++) { + snprintf(irq_number_prop, sizeof(irq_number_prop), "irq[%d]", i); + irq_number = object_property_get_int(obj, irq_number_prop, NULL) + + data->irq_start; + /* + * for setting irqfd up we must provide the virtual IRQ number + * which is the sum of irq_start and actual platform bus irq + * index. At realize point we do not have this info. + */ + if (vdev->irqfd_allowed) { + vfio_setup_irqfd(sbdev, i, irq_number); + } + } +} + +static void vfio_fdt_add_device_node(SysBusDevice *sbdev, void *opaque) +{ + PlatformDevtreeData *data = opaque; + void *fdt = data->fdt; + const char *parent_node = data->node; + int compat_str_len; + char *nodename; + int i, ret; + uint32_t *irq_attr; + uint64_t *reg_attr; + uint64_t mmio_base; + uint64_t irq_number; + gchar mmio_base_prop[8]; + gchar irq_number_prop[8]; + VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); + VFIODevice *vbasedev = &vdev->vbasedev; + Object *obj = OBJECT(sbdev); + + mmio_base = object_property_get_int(obj, "mmio[0]", NULL); + + nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, + vbasedev->name, + mmio_base); + + qemu_fdt_add_subnode(fdt, nodename); + + compat_str_len = strlen(vdev->compat) + 1; + qemu_fdt_setprop(fdt, nodename, "compatible", + vdev->compat, compat_str_len); + + reg_attr = g_new(uint64_t, vbasedev->num_regions*4); + + for (i = 0; i < vbasedev->num_regions; i++) { + snprintf(mmio_base_prop, sizeof(mmio_base_prop), "mmio[%d]", i); + mmio_base = object_property_get_int(obj, mmio_base_prop, NULL); + reg_attr[2*i] = 1; + reg_attr[2*i+1] = mmio_base; + reg_attr[2*i+2] = 1; + reg_attr[2*i+3] = memory_region_size(&vdev->regions[i]->mem); + } + + ret = qemu_fdt_setprop_sized_cells_from_array(fdt, nodename, "reg", + vbasedev->num_regions*2, reg_attr); + if (ret < 0) { + error_report("could not set reg property of node %s", nodename); + } + + irq_attr = g_new(uint32_t, vbasedev->num_irqs*3); + + for (i = 0; i < vbasedev->num_irqs; i++) { + snprintf(irq_number_prop, sizeof(irq_number_prop), "irq[%d]", i); + irq_number = object_property_get_int(obj, irq_number_prop, NULL) + + data->irq_start; + irq_attr[3*i] = cpu_to_be32(0); + irq_attr[3*i+1] = cpu_to_be32(irq_number); + irq_attr[3*i+2] = cpu_to_be32(0x4); + } + + ret = qemu_fdt_setprop(fdt, nodename, "interrupts", + irq_attr, vbasedev->num_irqs*3*sizeof(uint32_t)); + if (ret < 0) { + error_report("could not set interrupts property of node %s", + nodename); + } + + g_free(nodename); + g_free(irq_attr); + g_free(reg_attr); +} int sysbus_device_create_devtree(Object *obj, void *opaque) { @@ -17,6 +150,11 @@ int sysbus_device_create_devtree(Object *obj, void *opaque) return object_child_foreach(obj, sysbus_device_create_devtree, data); } + if (object_dynamic_cast(obj, TYPE_VFIO_PLATFORM)) { + wrap_vfio_fdt_add_node(sbdev, data); + matched = true; + } + if (!matched) { error_report("Device %s is not supported by this machine yet.", qdev_fw_name(DEVICE(dev)));
Generates the device node of VFIO devices, if any are invoked in -device option. In case VFIO devices require more complex node generation, they can be handled before. Signed-off-by: Eric Auger <eric.auger@linaro.org> --- hw/arm/dyn_sysbus_devtree.c | 138 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+)