kvm: arm64: Disclose SPCI partition details in sysfs

This allows user space to discover the preloaded partitions. Only
information that a VMM needs is made available.

Signed-off-by: Andrew Scull <ascull@google.com>
diff --git a/Documentation/ABI/testing/sysfs-hypervisor-spci b/Documentation/ABI/testing/sysfs-hypervisor-spci
new file mode 100644
index 0000000..48f0b55
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-hypervisor-spci
@@ -0,0 +1,18 @@
+What:		/sys/hypervisor/spci/partitionX/uuid
+Date:		April 2020
+KernelVersion:	5.8
+Contact:	kvmarm@lists.cs.columbia.edu
+Description:	The UUID of the partition.
+
+What:		/sys/hypervisor/spci/partitionX/vcpus
+Date:		April 2020
+KernelVersion:	5.8
+Contact:	kvmarm@lists.cs.columbia.edu
+Description:	The number of vCPUs declared by the partition.
+
+What:		/sys/hypervisor/spci/partitionX/exec_state
+Date:		April 2020
+KernelVersion:	5.8
+Contact:	kvmarm@lists.cs.columbia.edu
+Description:	The execution state of the partition.
+		Currently "AArch32" or "AArch64".
diff --git a/MAINTAINERS b/MAINTAINERS
index 7bff7e9..3130c32 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9295,6 +9295,7 @@
 L:	kvmarm@lists.cs.columbia.edu
 S:	Maintained
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm.git
+F:	Documentation/ABI/testing/sysfs-hypervisor-spci
 F:	arch/arm64/include/asm/kvm*
 F:	arch/arm64/include/uapi/asm/kvm*
 F:	arch/arm64/kvm/
diff --git a/arch/arm64/kvm/spci.c b/arch/arm64/kvm/spci.c
index d73f956..b5225bd 100644
--- a/arch/arm64/kvm/spci.c
+++ b/arch/arm64/kvm/spci.c
@@ -157,6 +157,60 @@
 	return 0;
 }
 
+static struct kobj_type spci_part_ktype = {
+	.sysfs_ops = &kobj_sysfs_ops,
+};
+
+#define kobj_to_part(kobj) container_of(kobj, struct kvm_spci_partition, kobj)
+#define SPCI_PART_ATTR_RO(_name, _fmt, ...)				\
+static ssize_t _name##_show(struct kobject *kobj,			\
+			    struct kobj_attribute *attr,		\
+			    char *buf)					\
+{									\
+	struct kvm_spci_partition *part = kobj_to_part(kobj);		\
+									\
+	return sprintf(buf, _fmt, __VA_ARGS__);				\
+}									\
+static struct kobj_attribute spci_part_attr_##_name = __ATTR_RO(_name)
+
+SPCI_PART_ATTR_RO(uuid, "%pU\n", &part->uuid);
+SPCI_PART_ATTR_RO(vcpus, "%d\n", part->nr_vcpus);
+SPCI_PART_ATTR_RO(exec_state, "%s\n", part->is_32bit ? "AArch32" : "AArch64");
+
+static struct attribute *spci_part_attrs[] = {
+	&spci_part_attr_uuid.attr,
+	&spci_part_attr_vcpus.attr,
+	&spci_part_attr_exec_state.attr,
+	NULL,
+};
+
+static const struct attribute_group spci_part_attr_group = {
+	.attrs = spci_part_attrs,
+};
+
+static struct kobject *spci_kobj;
+
+static int spci_partition_create_sysfs(struct kvm_spci_partition *part)
+{
+	int err;
+
+	err = kobject_init_and_add(&part->kobj, &spci_part_ktype, spci_kobj,
+				   "partition%d", part->id);
+	if (err)
+		goto out;
+
+	err = sysfs_create_group(&part->kobj, &spci_part_attr_group);
+	if (err)
+		goto out_put_kobj;
+
+	return 0;
+
+out_put_kobj:
+	kobject_put(&part->kobj);
+out:
+	return err;
+}
+
 static int spci_parse_partitions(struct device_node *spci_np)
 {
 	struct device_node *np, *prev_np = spci_np;
@@ -223,6 +277,10 @@
 		}
 
 		part->id = nr_parts;
+		if (spci_partition_create_sysfs(part)) {
+			kvm_err("%s: failed to create sysfs entries\n", pfx);
+			goto next_free_part;
+		}
 		++nr_parts;
 
 		INIT_LIST_HEAD(&part->list);
@@ -307,8 +365,13 @@
 
 int kvm_spci_init(void)
 {
-	int ret = spci_parse_dt_node();
+	int ret;
 
+	spci_kobj = kobject_create_and_add("spci", hypervisor_kobj);
+	if (!spci_kobj)
+		return -ENOMEM;
+
+	ret = spci_parse_dt_node();
 	if (ret)
 		return ret;
 
diff --git a/include/kvm/arm_spci.h b/include/kvm/arm_spci.h
index 54fd69b..71fde30 100644
--- a/include/kvm/arm_spci.h
+++ b/include/kvm/arm_spci.h
@@ -8,6 +8,7 @@
 #define __KVM_ARM_SPCI_H
 
 #include <linux/of_reserved_mem.h>
+#include <linux/kobject.h>
 
 enum kvm_spci_mem_prot {
 	KVM_SPCI_MEM_PROT_X	= 1 << 0,
@@ -23,6 +24,7 @@
 };
 
 struct kvm_spci_partition {
+	struct kobject		kobj;
 	struct list_head	list;
 	int			id;
 	uuid_t			uuid;