Now actually implementing the serialise callback for iommufd. On KHO activate, iterate through all persisted domains and write their metadata to the device tree format. For now just a few fields are serialised to demonstrate the concept. To actually make this useful a lot more field and related objects will need to be serialised too. --- drivers/iommu/iommufd/iommufd_private.h | 2 + drivers/iommu/iommufd/main.c | 2 +- drivers/iommu/iommufd/serialise.c | 81 ++++++++++++++++++++++++- 3 files changed, 81 insertions(+), 4 deletions(-) diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h index a26728646a22..ad8d180269bd 100644 --- a/drivers/iommu/iommufd/iommufd_private.h +++ b/drivers/iommu/iommufd/iommufd_private.h @@ -18,6 +18,8 @@ struct iommu_group; struct iommu_option; struct iommufd_device; +extern struct xarray persistent_iommufds; + struct iommufd_ctx { struct file *file; struct xarray objects; diff --git a/drivers/iommu/iommufd/main.c b/drivers/iommu/iommufd/main.c index fa4f0fe336ad..21a7e1ad40d1 100644 --- a/drivers/iommu/iommufd/main.c +++ b/drivers/iommu/iommufd/main.c @@ -30,7 +30,7 @@ struct iommufd_object_ops { static const struct iommufd_object_ops iommufd_object_ops[]; static struct miscdevice vfio_misc_dev; -static DEFINE_XARRAY_ALLOC(persistent_iommufds); +DEFINE_XARRAY_ALLOC(persistent_iommufds); struct iommufd_object *_iommufd_object_alloc(struct iommufd_ctx *ictx, size_t size, diff --git a/drivers/iommu/iommufd/serialise.c b/drivers/iommu/iommufd/serialise.c index 6e8bcc384771..6b4c306dce40 100644 --- a/drivers/iommu/iommufd/serialise.c +++ b/drivers/iommu/iommufd/serialise.c @@ -1,19 +1,94 @@ // SPDX-License-Identifier: GPL-2.0-only #include <linux/kexec.h> +#include <linux/libfdt.h> #include "iommufd_private.h" +#include "io_pagetable.h" + +/** + * Serialised format: + * /iommufd + * compatible = "iommufd-v0", + * iommufds = [ + * persistent_id = { + * account_mode = u8 + * ioases = [ + * { + * areas = [ + * ] + * } + * ] + * } + * ] + */ +static int serialise_iommufd(void *fdt, struct iommufd_ctx *ictx) +{ + int err = 0; + char name[24]; + struct iommufd_object *obj; + unsigned long obj_idx; + + snprintf(name, sizeof(name), "%lu", ictx->persistent_id); + err |= fdt_begin_node(fdt, name); + err |= fdt_begin_node(fdt, "ioases"); + xa_for_each(&ictx->objects, obj_idx, obj) { + struct iommufd_ioas *ioas; + struct iopt_area *area; + int area_idx = 0; + + if (obj->type != IOMMUFD_OBJ_IOAS) + continue; + + ioas = (struct iommufd_ioas *) obj; + snprintf(name, sizeof(name), "%lu", obj_idx); + err |= fdt_begin_node(fdt, name); + + for (area = iopt_area_iter_first(&ioas->iopt, 0, ULONG_MAX); area; + area = iopt_area_iter_next(area, 0, ULONG_MAX)) { + unsigned long iova_start, iova_len; + + snprintf(name, sizeof(name), "%i", area_idx); + err |= fdt_begin_node(fdt, name); + iova_start = iopt_area_iova(area); + iova_len = iopt_area_length(area); + err |= fdt_property(fdt, "iova-start", + &iova_start, sizeof(iova_start)); + err |= fdt_property(fdt, "iova-len", + &iova_len, sizeof(iova_len)); + err |= fdt_property(fdt, "iommu-prot", + &area->iommu_prot, sizeof(area->iommu_prot)); + err |= fdt_end_node(fdt); /* area_idx */ + ++area_idx; + } + err |= fdt_end_node(fdt); /* ioas obj_idx */ + } + err |= fdt_end_node(fdt); /* ioases*/ + err |= fdt_end_node(fdt); /* ictx->persistent_id */ + return 0; +} int iommufd_serialise_kho(struct notifier_block *self, unsigned long cmd, void *fdt) { - pr_info("would serialise here\n"); + static const char compatible[] = "iommufd-v0"; + struct iommufd_ctx *ictx; + unsigned long xa_idx; + int err = 0; + switch (cmd) { case KEXEC_KHO_ABORT: /* Would do serialise rollback here. */ return NOTIFY_DONE; case KEXEC_KHO_DUMP: - /* Would do serialise here. */ - return NOTIFY_DONE; + err |= fdt_begin_node(fdt, "iommufd"); + fdt_property(fdt, "compatible", compatible, sizeof(compatible)); + err |= fdt_begin_node(fdt, "iommufds"); + xa_for_each(&persistent_iommufds, xa_idx, ictx) { + err |= serialise_iommufd(fdt, ictx); + } + err |= fdt_end_node(fdt); /* iommufds */ + err |= fdt_end_node(fdt); /* iommufd */ + return err? NOTIFY_BAD : NOTIFY_DONE; default: return NOTIFY_BAD; } -- 2.34.1