2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2014-04-01 14:28:19 +10:30
/*
* 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>
2020-08-04 10:54:06 +10:00
# include "powernv.h"
2014-04-01 14:28:19 +10:30
/* 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 ;
} ;
2016-02-09 18:17:48 +11:00
static struct memcons * opal_memcons = NULL ;
2019-08-28 23:05:20 +10:00
ssize_t memcons_copy ( struct memcons * mc , char * to , loff_t pos , size_t count )
2014-04-01 14:28:19 +10:30
{
const char * conbuf ;
2014-06-10 16:03:59 +10:00
ssize_t ret ;
size_t first_read = 0 ;
2014-04-01 14:28:19 +10:30
uint32_t out_pos , avail ;
2019-08-28 23:05:20 +10:00
if ( ! mc )
2014-04-01 14:28:19 +10:30
return - ENODEV ;
2019-08-28 23:05:20 +10:00
out_pos = be32_to_cpu ( READ_ONCE ( mc - > out_pos ) ) ;
2014-04-01 14:28:19 +10:30
/* Now we've read out_pos, put a barrier in before reading the new
* data it points to in conbuf . */
smp_rmb ( ) ;
2019-08-28 23:05:20 +10:00
conbuf = phys_to_virt ( be64_to_cpu ( mc - > obuf_phys ) ) ;
2014-04-01 14:28:19 +10:30
/* 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 ;
2019-08-28 23:05:20 +10:00
avail = be32_to_cpu ( mc - > obuf_size ) - out_pos ;
2014-04-01 14:28:19 +10:30
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 ;
2014-06-10 16:03:59 +10:00
if ( count < = 0 )
goto out ;
2014-04-01 14:28:19 +10:30
}
/* Sanity check. The firmware should not do this to us. */
2019-08-28 23:05:20 +10:00
if ( out_pos > be32_to_cpu ( mc - > obuf_size ) ) {
2014-04-01 14:28:19 +10:30
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 ;
}
2019-08-28 23:05:20 +10:00
ssize_t opal_msglog_copy ( char * to , loff_t pos , size_t count )
{
return memcons_copy ( opal_memcons , to , pos , count ) ;
}
2016-02-09 18:17:48 +11:00
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 ) ;
}
2014-04-01 14:28:19 +10:30
static struct bin_attribute opal_msglog_attr = {
2019-02-27 14:02:29 +11:00
. attr = { . name = " msglog " , . mode = 0400 } ,
2014-04-01 14:28:19 +10:30
. read = opal_msglog_read
} ;
2021-12-16 17:00:26 -05:00
struct memcons * __init memcons_init ( struct device_node * node , const char * mc_prop_name )
2014-04-01 14:28:19 +10:30
{
u64 mcaddr ;
struct memcons * mc ;
2019-08-28 23:05:20 +10:00
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 ;
2014-04-01 14:28:19 +10:30
}
mc = phys_to_virt ( mcaddr ) ;
if ( ! mc ) {
2019-08-28 23:05:20 +10:00
pr_warn ( " memory console address is invalid \n " ) ;
goto out_err ;
2014-04-01 14:28:19 +10:30
}
if ( be64_to_cpu ( mc - > magic ) ! = MEMCONS_MAGIC ) {
2019-08-28 23:05:20 +10:00
pr_warn ( " memory console version is invalid \n " ) ;
goto out_err ;
2014-04-01 14:28:19 +10:30
}
2019-08-28 23:05:20 +10:00
return mc ;
out_err :
return NULL ;
}
2021-12-16 17:00:26 -05:00
u32 __init memcons_get_size ( struct memcons * mc )
2019-08-28 23:05:20 +10:00
{
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 ;
}
2017-01-13 14:23:49 +10:30
2019-08-28 23:05:20 +10:00
opal_msglog_attr . size = memcons_get_size ( opal_memcons ) ;
2016-02-09 18:17:48 +11:00
}
2014-04-01 14:28:19 +10:30
2016-02-09 18:17:48 +11:00
void __init opal_msglog_sysfs_init ( void )
{
2016-02-18 12:12:54 +11:00
if ( ! opal_memcons ) {
pr_warn ( " OPAL: message log initialisation failed, not creating sysfs entry \n " ) ;
return ;
}
2014-04-01 14:28:19 +10:30
if ( sysfs_create_bin_file ( opal_kobj , & opal_msglog_attr ) ! = 0 )
pr_warn ( " OPAL: sysfs file creation failed \n " ) ;
}