18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PowerNV OPAL in-memory console interface 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2014 IBM Corp. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <asm/io.h> 98c2ecf20Sopenharmony_ci#include <asm/opal.h> 108c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/types.h> 138c2ecf20Sopenharmony_ci#include <asm/barrier.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "powernv.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* OPAL in-memory console. Defined in OPAL source at core/console.c */ 188c2ecf20Sopenharmony_cistruct memcons { 198c2ecf20Sopenharmony_ci __be64 magic; 208c2ecf20Sopenharmony_ci#define MEMCONS_MAGIC 0x6630696567726173L 218c2ecf20Sopenharmony_ci __be64 obuf_phys; 228c2ecf20Sopenharmony_ci __be64 ibuf_phys; 238c2ecf20Sopenharmony_ci __be32 obuf_size; 248c2ecf20Sopenharmony_ci __be32 ibuf_size; 258c2ecf20Sopenharmony_ci __be32 out_pos; 268c2ecf20Sopenharmony_ci#define MEMCONS_OUT_POS_WRAP 0x80000000u 278c2ecf20Sopenharmony_ci#define MEMCONS_OUT_POS_MASK 0x00ffffffu 288c2ecf20Sopenharmony_ci __be32 in_prod; 298c2ecf20Sopenharmony_ci __be32 in_cons; 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic struct memcons *opal_memcons = NULL; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cissize_t memcons_copy(struct memcons *mc, char *to, loff_t pos, size_t count) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci const char *conbuf; 378c2ecf20Sopenharmony_ci ssize_t ret; 388c2ecf20Sopenharmony_ci size_t first_read = 0; 398c2ecf20Sopenharmony_ci uint32_t out_pos, avail; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci if (!mc) 428c2ecf20Sopenharmony_ci return -ENODEV; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci out_pos = be32_to_cpu(READ_ONCE(mc->out_pos)); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* Now we've read out_pos, put a barrier in before reading the new 478c2ecf20Sopenharmony_ci * data it points to in conbuf. */ 488c2ecf20Sopenharmony_ci smp_rmb(); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci conbuf = phys_to_virt(be64_to_cpu(mc->obuf_phys)); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci /* When the buffer has wrapped, read from the out_pos marker to the end 538c2ecf20Sopenharmony_ci * of the buffer, and then read the remaining data as in the un-wrapped 548c2ecf20Sopenharmony_ci * case. */ 558c2ecf20Sopenharmony_ci if (out_pos & MEMCONS_OUT_POS_WRAP) { 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci out_pos &= MEMCONS_OUT_POS_MASK; 588c2ecf20Sopenharmony_ci avail = be32_to_cpu(mc->obuf_size) - out_pos; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci ret = memory_read_from_buffer(to, count, &pos, 618c2ecf20Sopenharmony_ci conbuf + out_pos, avail); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (ret < 0) 648c2ecf20Sopenharmony_ci goto out; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci first_read = ret; 678c2ecf20Sopenharmony_ci to += first_read; 688c2ecf20Sopenharmony_ci count -= first_read; 698c2ecf20Sopenharmony_ci pos -= avail; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (count <= 0) 728c2ecf20Sopenharmony_ci goto out; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* Sanity check. The firmware should not do this to us. */ 768c2ecf20Sopenharmony_ci if (out_pos > be32_to_cpu(mc->obuf_size)) { 778c2ecf20Sopenharmony_ci pr_err("OPAL: memory console corruption. Aborting read.\n"); 788c2ecf20Sopenharmony_ci return -EINVAL; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci ret = memory_read_from_buffer(to, count, &pos, conbuf, out_pos); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (ret < 0) 848c2ecf20Sopenharmony_ci goto out; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci ret += first_read; 878c2ecf20Sopenharmony_ciout: 888c2ecf20Sopenharmony_ci return ret; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cissize_t opal_msglog_copy(char *to, loff_t pos, size_t count) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci return memcons_copy(opal_memcons, to, pos, count); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic ssize_t opal_msglog_read(struct file *file, struct kobject *kobj, 978c2ecf20Sopenharmony_ci struct bin_attribute *bin_attr, char *to, 988c2ecf20Sopenharmony_ci loff_t pos, size_t count) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci return opal_msglog_copy(to, pos, count); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic struct bin_attribute opal_msglog_attr = { 1048c2ecf20Sopenharmony_ci .attr = {.name = "msglog", .mode = 0400}, 1058c2ecf20Sopenharmony_ci .read = opal_msglog_read 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistruct memcons *memcons_init(struct device_node *node, const char *mc_prop_name) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci u64 mcaddr; 1118c2ecf20Sopenharmony_ci struct memcons *mc; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (of_property_read_u64(node, mc_prop_name, &mcaddr)) { 1148c2ecf20Sopenharmony_ci pr_warn("%s property not found, no message log\n", 1158c2ecf20Sopenharmony_ci mc_prop_name); 1168c2ecf20Sopenharmony_ci goto out_err; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci mc = phys_to_virt(mcaddr); 1208c2ecf20Sopenharmony_ci if (!mc) { 1218c2ecf20Sopenharmony_ci pr_warn("memory console address is invalid\n"); 1228c2ecf20Sopenharmony_ci goto out_err; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (be64_to_cpu(mc->magic) != MEMCONS_MAGIC) { 1268c2ecf20Sopenharmony_ci pr_warn("memory console version is invalid\n"); 1278c2ecf20Sopenharmony_ci goto out_err; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return mc; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ciout_err: 1338c2ecf20Sopenharmony_ci return NULL; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ciu32 memcons_get_size(struct memcons *mc) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci return be32_to_cpu(mc->ibuf_size) + be32_to_cpu(mc->obuf_size); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_civoid __init opal_msglog_init(void) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci opal_memcons = memcons_init(opal_node, "ibm,opal-memcons"); 1448c2ecf20Sopenharmony_ci if (!opal_memcons) { 1458c2ecf20Sopenharmony_ci pr_warn("OPAL: memcons failed to load from ibm,opal-memcons\n"); 1468c2ecf20Sopenharmony_ci return; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci opal_msglog_attr.size = memcons_get_size(opal_memcons); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_civoid __init opal_msglog_sysfs_init(void) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci if (!opal_memcons) { 1558c2ecf20Sopenharmony_ci pr_warn("OPAL: message log initialisation failed, not creating sysfs entry\n"); 1568c2ecf20Sopenharmony_ci return; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (sysfs_create_bin_file(opal_kobj, &opal_msglog_attr) != 0) 1608c2ecf20Sopenharmony_ci pr_warn("OPAL: sysfs file creation failed\n"); 1618c2ecf20Sopenharmony_ci} 162