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