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