Subject: [PATCH v3 2/2] powerpc/powernv: Add ultravisor message log interface

From: Claudio Carvalho <[email-masked]>
Date: Wed, 28 Aug 2019 00:56:46 -0300

The ultravisor (UV) provides an in-memory console which follows the OPAL
in-memory console structure.

This patch extends the OPAL msglog code to initialize the UV memory
console and provide the "/sys/firmware/ultravisor/msglog" interface
for userspace to view the UV message log.

Signed-off-by: Claudio Carvalho <[email-masked]>
---
This patch applies on top of the "kvmppc: Paravirtualize KVM to support
ultravisor" patch series submitted by Claudio Carvalho.
---
 arch/powerpc/include/asm/ultravisor.h        |  8 ++++
 arch/powerpc/platforms/powernv/opal-msglog.c | 36 ++++++++++++++++++
 arch/powerpc/platforms/powernv/ultravisor.c  | 40 ++++++++++++++++++++
 3 files changed, 84 insertions(+)

diff --git a/arch/powerpc/include/asm/ultravisor.h b/arch/powerpc/include/asm/ultravisor.h
index d7aa97aa7834..62932d403847 100644
--- a/arch/powerpc/include/asm/ultravisor.h
+++ b/arch/powerpc/include/asm/ultravisor.h
@@ -12,6 +12,14 @@
 #include <asm/ultravisor-api.h>
 #include <asm/firmware.h>
 
+/* /sys/firmware/ultravisor */
+extern struct kobject *ultravisor_kobj;
+
+/* /ibm,ultravisor/ibm,uv-firmware */
+extern struct device_node *uv_firmware_node;
+
+void ultra_msglog_init(void);
+void ultra_msglog_sysfs_init(void);
 int early_init_dt_scan_ultravisor(unsigned long node, const char *uname,
 				  int depth, void *data);
 
diff --git a/arch/powerpc/platforms/powernv/opal-msglog.c b/arch/powerpc/platforms/powernv/opal-msglog.c
index 0e8eb62c8afe..7c6b2001e62f 100644
--- a/arch/powerpc/platforms/powernv/opal-msglog.c
+++ b/arch/powerpc/platforms/powernv/opal-msglog.c
@@ -11,6 +11,7 @@
 #include <linux/of.h>
 #include <linux/types.h>
 #include <asm/barrier.h>
+#include <asm/ultravisor.h>
 
 /* OPAL in-memory console. Defined in OPAL source at core/console.c */
 struct memcons {
@@ -28,6 +29,7 @@ struct memcons {
 };
 
 static struct memcons *opal_memcons = NULL;
+static struct memcons *ultra_memcons;
 
 static ssize_t memcons_copy(struct memcons *mc, char *to, loff_t pos,
 			   size_t count)
@@ -104,6 +106,18 @@ static struct bin_attribute opal_msglog_attr = {
 	.read = opal_msglog_read
 };
 
+static ssize_t ultra_msglog_read(struct file *file, struct kobject *kobj,
+				struct bin_attribute *bin_attr, char *to,
+				loff_t pos, size_t count)
+{
+	return memcons_copy(ultra_memcons, to, pos, count);
+}
+
+static struct bin_attribute ultra_msglog_attr = {
+	.attr = {.name = "msglog", .mode = 0400},
+	.read = ultra_msglog_read
+};
+
 static struct memcons *memcons_load_from_dt(struct device_node *node,
 					    const char *mc_prop_name)
 {
@@ -159,3 +173,25 @@ void __init opal_msglog_sysfs_init(void)
 	if (sysfs_create_bin_file(opal_kobj, &opal_msglog_attr) != 0)
 		pr_warn("OPAL: sysfs file creation failed\n");
 }
+
+void __init ultra_msglog_init(void)
+{
+	ultra_memcons = memcons_load_from_dt(uv_firmware_node, "memcons");
+	if (!ultra_memcons) {
+		pr_warn("Ultravisor: memcons failed to load from DT\n");
+		return;
+	}
+
+	ultra_msglog_attr.size = memcons_get_size(ultra_memcons);
+}
+
+void __init ultra_msglog_sysfs_init(void)
+{
+	if (!ultra_memcons) {
+		pr_warn("Ultravisor: msglog initialisation failed, not creating sysfs entry\n");
+		return;
+	}
+
+	if (sysfs_create_bin_file(ultravisor_kobj, &ultra_msglog_attr) != 0)
+		pr_warn("Ultravisor: sysfs msglog file creation failed\n");
+}
diff --git a/arch/powerpc/platforms/powernv/ultravisor.c b/arch/powerpc/platforms/powernv/ultravisor.c
index 02ac57b4bded..4ec63b7c0c78 100644
--- a/arch/powerpc/platforms/powernv/ultravisor.c
+++ b/arch/powerpc/platforms/powernv/ultravisor.c
@@ -8,9 +8,14 @@
 #include <linux/init.h>
 #include <linux/printk.h>
 #include <linux/of_fdt.h>
+#include <linux/of.h>
 
 #include <asm/ultravisor.h>
 #include <asm/firmware.h>
+#include <asm/machdep.h>
+
+struct kobject *ultravisor_kobj;
+struct device_node *uv_firmware_node;
 
 int __init early_init_dt_scan_ultravisor(unsigned long node, const char *uname,
 					 int depth, void *data)
@@ -22,3 +27,38 @@ int __init early_init_dt_scan_ultravisor(unsigned long node, const char *uname,
 	pr_debug("Ultravisor detected!\n");
 	return 1;
 }
+
+static int __init ultra_sysfs_init(void)
+{
+	ultravisor_kobj = kobject_create_and_add("ultravisor", firmware_kobj);
+	if (!ultravisor_kobj) {
+		pr_warn("kobject_create_and_add ultravisor failed\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int __init ultra_init(void)
+{
+	int rc;
+
+	if (!firmware_has_feature(FW_FEATURE_ULTRAVISOR))
+		goto out;
+
+	uv_firmware_node = of_find_compatible_node(NULL, NULL,
+						   "ibm,uv-firmware");
+	if (!uv_firmware_node) {
+		pr_err("ibm,uv-firmware node not found\n");
+		return -ENODEV;
+	}
+
+	ultra_msglog_init();
+
+	rc = ultra_sysfs_init();
+	if (rc == 0)
+		ultra_msglog_sysfs_init();
+out:
+	return 0;
+}
+machine_subsys_initcall(powernv, ultra_init);
-- 
2.20.1