Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | // SPDX-License-Identifier: GPL-2.0-or-later /* * PowerNV OPAL in-memory console interface * * Copyright 2014 IBM Corp. */ #include <asm/io.h> #include <asm/opal.h> #include <linux/debugfs.h> #include <linux/of.h> #include <linux/types.h> #include <asm/barrier.h> #include "powernv.h" /* OPAL in-memory console. Defined in OPAL source at core/console.c */ struct memcons { __be64 magic; #define MEMCONS_MAGIC 0x6630696567726173L __be64 obuf_phys; __be64 ibuf_phys; __be32 obuf_size; __be32 ibuf_size; __be32 out_pos; #define MEMCONS_OUT_POS_WRAP 0x80000000u #define MEMCONS_OUT_POS_MASK 0x00ffffffu __be32 in_prod; __be32 in_cons; }; static struct memcons *opal_memcons = NULL; ssize_t memcons_copy(struct memcons *mc, char *to, loff_t pos, size_t count) { const char *conbuf; ssize_t ret; size_t first_read = 0; uint32_t out_pos, avail; if (!mc) return -ENODEV; out_pos = be32_to_cpu(READ_ONCE(mc->out_pos)); /* Now we've read out_pos, put a barrier in before reading the new * data it points to in conbuf. */ smp_rmb(); conbuf = phys_to_virt(be64_to_cpu(mc->obuf_phys)); /* When the buffer has wrapped, read from the out_pos marker to the end * of the buffer, and then read the remaining data as in the un-wrapped * case. */ if (out_pos & MEMCONS_OUT_POS_WRAP) { out_pos &= MEMCONS_OUT_POS_MASK; avail = be32_to_cpu(mc->obuf_size) - out_pos; ret = memory_read_from_buffer(to, count, &pos, conbuf + out_pos, avail); if (ret < 0) goto out; first_read = ret; to += first_read; count -= first_read; pos -= avail; if (count <= 0) goto out; } /* Sanity check. The firmware should not do this to us. */ if (out_pos > be32_to_cpu(mc->obuf_size)) { pr_err("OPAL: memory console corruption. Aborting read.\n"); return -EINVAL; } ret = memory_read_from_buffer(to, count, &pos, conbuf, out_pos); if (ret < 0) goto out; ret += first_read; out: return ret; } ssize_t opal_msglog_copy(char *to, loff_t pos, size_t count) { return memcons_copy(opal_memcons, to, pos, count); } static ssize_t opal_msglog_read(struct file *file, struct kobject *kobj, struct bin_attribute *bin_attr, char *to, loff_t pos, size_t count) { return opal_msglog_copy(to, pos, count); } static struct bin_attribute opal_msglog_attr = { .attr = {.name = "msglog", .mode = 0400}, .read = opal_msglog_read }; struct memcons *__init memcons_init(struct device_node *node, const char *mc_prop_name) { u64 mcaddr; struct memcons *mc; if (of_property_read_u64(node, mc_prop_name, &mcaddr)) { pr_warn("%s property not found, no message log\n", mc_prop_name); goto out_err; } mc = phys_to_virt(mcaddr); if (!mc) { pr_warn("memory console address is invalid\n"); goto out_err; } if (be64_to_cpu(mc->magic) != MEMCONS_MAGIC) { pr_warn("memory console version is invalid\n"); goto out_err; } return mc; out_err: return NULL; } u32 __init memcons_get_size(struct memcons *mc) { return be32_to_cpu(mc->ibuf_size) + be32_to_cpu(mc->obuf_size); } void __init opal_msglog_init(void) { opal_memcons = memcons_init(opal_node, "ibm,opal-memcons"); if (!opal_memcons) { pr_warn("OPAL: memcons failed to load from ibm,opal-memcons\n"); return; } opal_msglog_attr.size = memcons_get_size(opal_memcons); } void __init opal_msglog_sysfs_init(void) { if (!opal_memcons) { pr_warn("OPAL: message log initialisation failed, not creating sysfs entry\n"); return; } if (sysfs_create_bin_file(opal_kobj, &opal_msglog_attr) != 0) pr_warn("OPAL: sysfs file creation failed\n"); } |