2019-09-11 20:26:33 +05:30
// SPDX-License-Identifier: GPL-2.0-only
/*
* Interface for exporting the OPAL ELF core .
* Heavily inspired from fs / proc / vmcore . c
*
* Copyright 2019 , Hari Bathini , IBM Corporation .
*/
# define pr_fmt(fmt) "opal core: " fmt
# include <linux/memblock.h>
# include <linux/uaccess.h>
# include <linux/proc_fs.h>
# include <linux/elf.h>
# include <linux/elfcore.h>
2019-09-11 20:26:45 +05:30
# include <linux/kobject.h>
# include <linux/sysfs.h>
2019-09-11 20:26:33 +05:30
# include <linux/slab.h>
# include <linux/crash_core.h>
# include <linux/of.h>
# include <asm/page.h>
# include <asm/opal.h>
# include <asm/fadump-internal.h>
# include "opal-fadump.h"
# define MAX_PT_LOAD_CNT 8
/* NT_AUXV note related info */
# define AUXV_CNT 1
# define AUXV_DESC_SZ (((2 * AUXV_CNT) + 1) * sizeof(Elf64_Off))
struct opalcore_config {
u32 num_cpus ;
/* PIR value of crashing CPU */
u32 crashing_cpu ;
/* CPU state data info from F/W */
u64 cpu_state_destination_vaddr ;
u64 cpu_state_data_size ;
u64 cpu_state_entry_size ;
/* OPAL memory to be exported as PT_LOAD segments */
u64 ptload_addr [ MAX_PT_LOAD_CNT ] ;
u64 ptload_size [ MAX_PT_LOAD_CNT ] ;
u64 ptload_cnt ;
/* Pointer to the first PT_LOAD in the ELF core file */
Elf64_Phdr * ptload_phdr ;
/* Total size of opalcore file. */
size_t opalcore_size ;
/* Buffer for all the ELF core headers and the PT_NOTE */
size_t opalcorebuf_sz ;
char * opalcorebuf ;
/* NT_AUXV buffer */
char auxv_buf [ AUXV_DESC_SZ ] ;
} ;
struct opalcore {
struct list_head list ;
u64 paddr ;
size_t size ;
loff_t offset ;
} ;
static LIST_HEAD ( opalcore_list ) ;
static struct opalcore_config * oc_conf ;
static const struct opal_mpipl_fadump * opalc_metadata ;
static const struct opal_mpipl_fadump * opalc_cpu_metadata ;
2019-12-11 21:39:08 +05:30
struct kobject * mpipl_kobj ;
2019-09-11 20:26:33 +05:30
/*
* Set crashing CPU ' s signal to SIGUSR1 . if the kernel is triggered
* by kernel , SIGTERM otherwise .
*/
bool kernel_initiated ;
static struct opalcore * __init get_new_element ( void )
{
return kzalloc ( sizeof ( struct opalcore ) , GFP_KERNEL ) ;
}
static inline int is_opalcore_usable ( void )
{
return ( oc_conf & & oc_conf - > opalcorebuf ! = NULL ) ? 1 : 0 ;
}
static Elf64_Word * append_elf64_note ( Elf64_Word * buf , char * name ,
u32 type , void * data ,
size_t data_len )
{
Elf64_Nhdr * note = ( Elf64_Nhdr * ) buf ;
Elf64_Word namesz = strlen ( name ) + 1 ;
note - > n_namesz = cpu_to_be32 ( namesz ) ;
note - > n_descsz = cpu_to_be32 ( data_len ) ;
note - > n_type = cpu_to_be32 ( type ) ;
buf + = DIV_ROUND_UP ( sizeof ( * note ) , sizeof ( Elf64_Word ) ) ;
memcpy ( buf , name , namesz ) ;
buf + = DIV_ROUND_UP ( namesz , sizeof ( Elf64_Word ) ) ;
memcpy ( buf , data , data_len ) ;
buf + = DIV_ROUND_UP ( data_len , sizeof ( Elf64_Word ) ) ;
return buf ;
}
static void fill_prstatus ( struct elf_prstatus * prstatus , int pir ,
struct pt_regs * regs )
{
memset ( prstatus , 0 , sizeof ( struct elf_prstatus ) ) ;
elf_core_copy_kernel_regs ( & ( prstatus - > pr_reg ) , regs ) ;
/*
* Overload PID with PIR value .
* As a PIR value could also be ' 0 ' , add an offset of ' 100 '
* to every PIR to avoid misinterpretations in GDB .
*/
prstatus - > pr_pid = cpu_to_be32 ( 100 + pir ) ;
prstatus - > pr_ppid = cpu_to_be32 ( 1 ) ;
/*
* Indicate SIGUSR1 for crash initiated from kernel .
* SIGTERM otherwise .
*/
if ( pir = = oc_conf - > crashing_cpu ) {
short sig ;
sig = kernel_initiated ? SIGUSR1 : SIGTERM ;
prstatus - > pr_cursig = cpu_to_be16 ( sig ) ;
}
}
static Elf64_Word * auxv_to_elf64_notes ( Elf64_Word * buf ,
u64 opal_boot_entry )
{
Elf64_Off * bufp = ( Elf64_Off * ) oc_conf - > auxv_buf ;
int idx = 0 ;
memset ( bufp , 0 , AUXV_DESC_SZ ) ;
/* Entry point of OPAL */
bufp [ idx + + ] = cpu_to_be64 ( AT_ENTRY ) ;
bufp [ idx + + ] = cpu_to_be64 ( opal_boot_entry ) ;
/* end of vector */
bufp [ idx + + ] = cpu_to_be64 ( AT_NULL ) ;
buf = append_elf64_note ( buf , CRASH_CORE_NOTE_NAME , NT_AUXV ,
oc_conf - > auxv_buf , AUXV_DESC_SZ ) ;
return buf ;
}
/*
* Read from the ELF header and then the crash dump .
* Returns number of bytes read on success , - errno on failure .
*/
static ssize_t read_opalcore ( struct file * file , struct kobject * kobj ,
struct bin_attribute * bin_attr , char * to ,
loff_t pos , size_t count )
{
struct opalcore * m ;
ssize_t tsz , avail ;
loff_t tpos = pos ;
if ( pos > = oc_conf - > opalcore_size )
return 0 ;
/* Adjust count if it goes beyond opalcore size */
avail = oc_conf - > opalcore_size - pos ;
if ( count > avail )
count = avail ;
if ( count = = 0 )
return 0 ;
/* Read ELF core header and/or PT_NOTE segment */
if ( tpos < oc_conf - > opalcorebuf_sz ) {
tsz = min_t ( size_t , oc_conf - > opalcorebuf_sz - tpos , count ) ;
memcpy ( to , oc_conf - > opalcorebuf + tpos , tsz ) ;
to + = tsz ;
tpos + = tsz ;
count - = tsz ;
}
list_for_each_entry ( m , & opalcore_list , list ) {
/* nothing more to read here */
if ( count = = 0 )
break ;
if ( tpos < m - > offset + m - > size ) {
void * addr ;
tsz = min_t ( size_t , m - > offset + m - > size - tpos , count ) ;
addr = ( void * ) ( m - > paddr + tpos - m - > offset ) ;
memcpy ( to , __va ( addr ) , tsz ) ;
to + = tsz ;
tpos + = tsz ;
count - = tsz ;
}
}
return ( tpos - pos ) ;
}
static struct bin_attribute opal_core_attr = {
. attr = { . name = " core " , . mode = 0400 } ,
. read = read_opalcore
} ;
/*
* Read CPU state dump data and convert it into ELF notes .
*
* Each register entry is of 16 bytes , A numerical identifier along with
* a GPR / SPR flag in the first 8 bytes and the register value in the next
* 8 bytes . For more details refer to F / W documentation .
*/
static Elf64_Word * __init opalcore_append_cpu_notes ( Elf64_Word * buf )
{
u32 thread_pir , size_per_thread , regs_offset , regs_cnt , reg_esize ;
struct hdat_fadump_thread_hdr * thdr ;
struct elf_prstatus prstatus ;
Elf64_Word * first_cpu_note ;
struct pt_regs regs ;
char * bufp ;
int i ;
size_per_thread = oc_conf - > cpu_state_entry_size ;
bufp = __va ( oc_conf - > cpu_state_destination_vaddr ) ;
/*
* Offset for register entries , entry size and registers count is
* duplicated in every thread header in keeping with HDAT format .
* Use these values from the first thread header .
*/
thdr = ( struct hdat_fadump_thread_hdr * ) bufp ;
regs_offset = ( offsetof ( struct hdat_fadump_thread_hdr , offset ) +
be32_to_cpu ( thdr - > offset ) ) ;
reg_esize = be32_to_cpu ( thdr - > esize ) ;
regs_cnt = be32_to_cpu ( thdr - > ecnt ) ;
pr_debug ( " --------CPU State Data------------ \n " ) ;
pr_debug ( " NumCpus : %u \n " , oc_conf - > num_cpus ) ;
pr_debug ( " \t Offset: %u, Entry size: %u, Cnt: %u \n " ,
regs_offset , reg_esize , regs_cnt ) ;
/*
* Skip past the first CPU note . Fill this note with the
* crashing CPU ' s prstatus .
*/
first_cpu_note = buf ;
buf = append_elf64_note ( buf , CRASH_CORE_NOTE_NAME , NT_PRSTATUS ,
& prstatus , sizeof ( prstatus ) ) ;
for ( i = 0 ; i < oc_conf - > num_cpus ; i + + , bufp + = size_per_thread ) {
thdr = ( struct hdat_fadump_thread_hdr * ) bufp ;
thread_pir = be32_to_cpu ( thdr - > pir ) ;
pr_debug ( " [%04d] PIR: 0x%x, core state: 0x%02x \n " ,
i , thread_pir , thdr - > core_state ) ;
/*
* Register state data of MAX cores is provided by firmware ,
* but some of this cores may not be active . So , while
* processing register state data , check core state and
* skip threads that belong to inactive cores .
*/
if ( thdr - > core_state = = HDAT_FADUMP_CORE_INACTIVE )
continue ;
opal_fadump_read_regs ( ( bufp + regs_offset ) , regs_cnt ,
reg_esize , false , & regs ) ;
pr_debug ( " PIR 0x%x - R1 : 0x%llx, NIP : 0x%llx \n " , thread_pir ,
be64_to_cpu ( regs . gpr [ 1 ] ) , be64_to_cpu ( regs . nip ) ) ;
fill_prstatus ( & prstatus , thread_pir , & regs ) ;
if ( thread_pir ! = oc_conf - > crashing_cpu ) {
buf = append_elf64_note ( buf , CRASH_CORE_NOTE_NAME ,
NT_PRSTATUS , & prstatus ,
sizeof ( prstatus ) ) ;
} else {
/*
* Add crashing CPU as the first NT_PRSTATUS note for
* GDB to process the core file appropriately .
*/
append_elf64_note ( first_cpu_note , CRASH_CORE_NOTE_NAME ,
NT_PRSTATUS , & prstatus ,
sizeof ( prstatus ) ) ;
}
}
return buf ;
}
static int __init create_opalcore ( void )
{
u64 opal_boot_entry , opal_base_addr , paddr ;
u32 hdr_size , cpu_notes_size , count ;
struct device_node * dn ;
struct opalcore * new ;
loff_t opalcore_off ;
struct page * page ;
Elf64_Phdr * phdr ;
Elf64_Ehdr * elf ;
int i , ret ;
char * bufp ;
/* Get size of header & CPU notes for OPAL core */
hdr_size = ( sizeof ( Elf64_Ehdr ) +
( ( oc_conf - > ptload_cnt + 1 ) * sizeof ( Elf64_Phdr ) ) ) ;
cpu_notes_size = ( ( oc_conf - > num_cpus * ( CRASH_CORE_NOTE_HEAD_BYTES +
CRASH_CORE_NOTE_NAME_BYTES +
CRASH_CORE_NOTE_DESC_BYTES ) ) +
( CRASH_CORE_NOTE_HEAD_BYTES +
CRASH_CORE_NOTE_NAME_BYTES + AUXV_DESC_SZ ) ) ;
/* Allocate buffer to setup OPAL core */
oc_conf - > opalcorebuf_sz = PAGE_ALIGN ( hdr_size + cpu_notes_size ) ;
oc_conf - > opalcorebuf = alloc_pages_exact ( oc_conf - > opalcorebuf_sz ,
GFP_KERNEL | __GFP_ZERO ) ;
if ( ! oc_conf - > opalcorebuf ) {
pr_err ( " Not enough memory to setup OPAL core (size: %lu) \n " ,
oc_conf - > opalcorebuf_sz ) ;
oc_conf - > opalcorebuf_sz = 0 ;
return - ENOMEM ;
}
count = oc_conf - > opalcorebuf_sz / PAGE_SIZE ;
page = virt_to_page ( oc_conf - > opalcorebuf ) ;
for ( i = 0 ; i < count ; i + + )
mark_page_reserved ( page + i ) ;
pr_debug ( " opalcorebuf = 0x%llx \n " , ( u64 ) oc_conf - > opalcorebuf ) ;
/* Read OPAL related device-tree entries */
dn = of_find_node_by_name ( NULL , " ibm,opal " ) ;
if ( dn ) {
ret = of_property_read_u64 ( dn , " opal-base-address " ,
& opal_base_addr ) ;
pr_debug ( " opal-base-address: %llx \n " , opal_base_addr ) ;
ret | = of_property_read_u64 ( dn , " opal-boot-address " ,
& opal_boot_entry ) ;
pr_debug ( " opal-boot-address: %llx \n " , opal_boot_entry ) ;
}
if ( ! dn | | ret )
pr_warn ( " WARNING: Failed to read OPAL base & entry values \n " ) ;
/* Use count to keep track of the program headers */
count = 0 ;
bufp = oc_conf - > opalcorebuf ;
elf = ( Elf64_Ehdr * ) bufp ;
bufp + = sizeof ( Elf64_Ehdr ) ;
memcpy ( elf - > e_ident , ELFMAG , SELFMAG ) ;
elf - > e_ident [ EI_CLASS ] = ELF_CLASS ;
elf - > e_ident [ EI_DATA ] = ELFDATA2MSB ;
elf - > e_ident [ EI_VERSION ] = EV_CURRENT ;
elf - > e_ident [ EI_OSABI ] = ELF_OSABI ;
memset ( elf - > e_ident + EI_PAD , 0 , EI_NIDENT - EI_PAD ) ;
elf - > e_type = cpu_to_be16 ( ET_CORE ) ;
elf - > e_machine = cpu_to_be16 ( ELF_ARCH ) ;
elf - > e_version = cpu_to_be32 ( EV_CURRENT ) ;
elf - > e_entry = 0 ;
elf - > e_phoff = cpu_to_be64 ( sizeof ( Elf64_Ehdr ) ) ;
elf - > e_shoff = 0 ;
elf - > e_flags = 0 ;
elf - > e_ehsize = cpu_to_be16 ( sizeof ( Elf64_Ehdr ) ) ;
elf - > e_phentsize = cpu_to_be16 ( sizeof ( Elf64_Phdr ) ) ;
elf - > e_phnum = 0 ;
elf - > e_shentsize = 0 ;
elf - > e_shnum = 0 ;
elf - > e_shstrndx = 0 ;
phdr = ( Elf64_Phdr * ) bufp ;
bufp + = sizeof ( Elf64_Phdr ) ;
phdr - > p_type = cpu_to_be32 ( PT_NOTE ) ;
phdr - > p_flags = 0 ;
phdr - > p_align = 0 ;
phdr - > p_paddr = phdr - > p_vaddr = 0 ;
phdr - > p_offset = cpu_to_be64 ( hdr_size ) ;
phdr - > p_filesz = phdr - > p_memsz = cpu_to_be64 ( cpu_notes_size ) ;
count + + ;
opalcore_off = oc_conf - > opalcorebuf_sz ;
oc_conf - > ptload_phdr = ( Elf64_Phdr * ) bufp ;
paddr = 0 ;
for ( i = 0 ; i < oc_conf - > ptload_cnt ; i + + ) {
phdr = ( Elf64_Phdr * ) bufp ;
bufp + = sizeof ( Elf64_Phdr ) ;
phdr - > p_type = cpu_to_be32 ( PT_LOAD ) ;
phdr - > p_flags = cpu_to_be32 ( PF_R | PF_W | PF_X ) ;
phdr - > p_align = 0 ;
new = get_new_element ( ) ;
if ( ! new )
return - ENOMEM ;
new - > paddr = oc_conf - > ptload_addr [ i ] ;
new - > size = oc_conf - > ptload_size [ i ] ;
new - > offset = opalcore_off ;
list_add_tail ( & new - > list , & opalcore_list ) ;
phdr - > p_paddr = cpu_to_be64 ( paddr ) ;
phdr - > p_vaddr = cpu_to_be64 ( opal_base_addr + paddr ) ;
phdr - > p_filesz = phdr - > p_memsz =
cpu_to_be64 ( oc_conf - > ptload_size [ i ] ) ;
phdr - > p_offset = cpu_to_be64 ( opalcore_off ) ;
count + + ;
opalcore_off + = oc_conf - > ptload_size [ i ] ;
paddr + = oc_conf - > ptload_size [ i ] ;
}
elf - > e_phnum = cpu_to_be16 ( count ) ;
bufp = ( char * ) opalcore_append_cpu_notes ( ( Elf64_Word * ) bufp ) ;
bufp = ( char * ) auxv_to_elf64_notes ( ( Elf64_Word * ) bufp , opal_boot_entry ) ;
oc_conf - > opalcore_size = opalcore_off ;
return 0 ;
}
static void opalcore_cleanup ( void )
{
if ( oc_conf = = NULL )
return ;
/* Remove OPAL core sysfs file */
2019-12-11 21:39:08 +05:30
sysfs_remove_bin_file ( mpipl_kobj , & opal_core_attr ) ;
2019-09-11 20:26:33 +05:30
oc_conf - > ptload_phdr = NULL ;
oc_conf - > ptload_cnt = 0 ;
/* free the buffer used for setting up OPAL core */
if ( oc_conf - > opalcorebuf ) {
void * end = ( void * ) ( ( u64 ) oc_conf - > opalcorebuf +
oc_conf - > opalcorebuf_sz ) ;
free_reserved_area ( oc_conf - > opalcorebuf , end , - 1 , NULL ) ;
oc_conf - > opalcorebuf = NULL ;
oc_conf - > opalcorebuf_sz = 0 ;
}
kfree ( oc_conf ) ;
oc_conf = NULL ;
}
__exitcall ( opalcore_cleanup ) ;
static void __init opalcore_config_init ( void )
{
u32 idx , cpu_data_version ;
struct device_node * np ;
const __be32 * prop ;
u64 addr = 0 ;
int i , ret ;
np = of_find_node_by_path ( " /ibm,opal/dump " ) ;
if ( np = = NULL )
return ;
if ( ! of_device_is_compatible ( np , " ibm,opal-dump " ) ) {
pr_warn ( " Support missing for this f/w version! \n " ) ;
return ;
}
/* Check if dump has been initiated on last reboot */
prop = of_get_property ( np , " mpipl-boot " , NULL ) ;
if ( ! prop ) {
of_node_put ( np ) ;
return ;
}
/* Get OPAL metadata */
ret = opal_mpipl_query_tag ( OPAL_MPIPL_TAG_OPAL , & addr ) ;
if ( ( ret ! = OPAL_SUCCESS ) | | ! addr ) {
pr_err ( " Failed to get OPAL metadata (%d) \n " , ret ) ;
goto error_out ;
}
addr = be64_to_cpu ( addr ) ;
pr_debug ( " OPAL metadata addr: %llx \n " , addr ) ;
opalc_metadata = __va ( addr ) ;
/* Get OPAL CPU metadata */
ret = opal_mpipl_query_tag ( OPAL_MPIPL_TAG_CPU , & addr ) ;
if ( ( ret ! = OPAL_SUCCESS ) | | ! addr ) {
pr_err ( " Failed to get OPAL CPU metadata (%d) \n " , ret ) ;
goto error_out ;
}
addr = be64_to_cpu ( addr ) ;
pr_debug ( " CPU metadata addr: %llx \n " , addr ) ;
opalc_cpu_metadata = __va ( addr ) ;
/* Allocate memory for config buffer */
oc_conf = kzalloc ( sizeof ( struct opalcore_config ) , GFP_KERNEL ) ;
if ( oc_conf = = NULL )
goto error_out ;
/* Parse OPAL metadata */
if ( opalc_metadata - > version ! = OPAL_MPIPL_VERSION ) {
pr_warn ( " Supported OPAL metadata version: %u, found: %u! \n " ,
OPAL_MPIPL_VERSION , opalc_metadata - > version ) ;
pr_warn ( " WARNING: F/W using newer OPAL metadata format!! \n " ) ;
}
oc_conf - > ptload_cnt = 0 ;
idx = be32_to_cpu ( opalc_metadata - > region_cnt ) ;
if ( idx > MAX_PT_LOAD_CNT ) {
pr_warn ( " WARNING: OPAL regions count (%d) adjusted to limit (%d) " ,
2020-09-16 14:21:29 +08:00
idx , MAX_PT_LOAD_CNT ) ;
2019-09-11 20:26:33 +05:30
idx = MAX_PT_LOAD_CNT ;
}
for ( i = 0 ; i < idx ; i + + ) {
oc_conf - > ptload_addr [ oc_conf - > ptload_cnt ] =
be64_to_cpu ( opalc_metadata - > region [ i ] . dest ) ;
oc_conf - > ptload_size [ oc_conf - > ptload_cnt + + ] =
be64_to_cpu ( opalc_metadata - > region [ i ] . size ) ;
}
oc_conf - > ptload_cnt = i ;
oc_conf - > crashing_cpu = be32_to_cpu ( opalc_metadata - > crashing_pir ) ;
if ( ! oc_conf - > ptload_cnt ) {
pr_err ( " OPAL memory regions not found \n " ) ;
goto error_out ;
}
/* Parse OPAL CPU metadata */
cpu_data_version = be32_to_cpu ( opalc_cpu_metadata - > cpu_data_version ) ;
if ( cpu_data_version ! = HDAT_FADUMP_CPU_DATA_VER ) {
pr_warn ( " Supported CPU data version: %u, found: %u! \n " ,
HDAT_FADUMP_CPU_DATA_VER , cpu_data_version ) ;
pr_warn ( " WARNING: F/W using newer CPU state data format!! \n " ) ;
}
addr = be64_to_cpu ( opalc_cpu_metadata - > region [ 0 ] . dest ) ;
if ( ! addr ) {
pr_err ( " CPU state data not found! \n " ) ;
goto error_out ;
}
oc_conf - > cpu_state_destination_vaddr = ( u64 ) __va ( addr ) ;
oc_conf - > cpu_state_data_size =
be64_to_cpu ( opalc_cpu_metadata - > region [ 0 ] . size ) ;
oc_conf - > cpu_state_entry_size =
be32_to_cpu ( opalc_cpu_metadata - > cpu_data_size ) ;
if ( ( oc_conf - > cpu_state_entry_size = = 0 ) | |
( oc_conf - > cpu_state_entry_size > oc_conf - > cpu_state_data_size ) ) {
pr_err ( " CPU state data is invalid. \n " ) ;
goto error_out ;
}
oc_conf - > num_cpus = ( oc_conf - > cpu_state_data_size /
oc_conf - > cpu_state_entry_size ) ;
of_node_put ( np ) ;
return ;
error_out :
pr_err ( " Could not export /sys/firmware/opal/core \n " ) ;
opalcore_cleanup ( ) ;
of_node_put ( np ) ;
}
2019-12-11 21:39:08 +05:30
static ssize_t release_core_store ( struct kobject * kobj ,
struct kobj_attribute * attr ,
const char * buf , size_t count )
2019-09-11 20:26:45 +05:30
{
int input = - 1 ;
if ( kstrtoint ( buf , 0 , & input ) )
return - EINVAL ;
if ( input = = 1 ) {
if ( oc_conf = = NULL ) {
pr_err ( " '/sys/firmware/opal/core' file not accessible! \n " ) ;
return - EPERM ;
}
/*
* Take away ' / sys / firmware / opal / core ' and release all memory
* used for exporting this file .
*/
opalcore_cleanup ( ) ;
} else
return - EINVAL ;
return count ;
}
2019-12-11 21:39:08 +05:30
static struct kobj_attribute opalcore_rel_attr = __ATTR_WO ( release_core ) ;
static struct attribute * mpipl_attr [ ] = {
& opalcore_rel_attr . attr ,
NULL ,
} ;
static struct bin_attribute * mpipl_bin_attr [ ] = {
& opal_core_attr ,
NULL ,
} ;
static struct attribute_group mpipl_group = {
. attrs = mpipl_attr ,
. bin_attrs = mpipl_bin_attr ,
} ;
2019-09-11 20:26:45 +05:30
2019-09-11 20:26:33 +05:30
static int __init opalcore_init ( void )
{
int rc = - 1 ;
opalcore_config_init ( ) ;
if ( oc_conf = = NULL )
return rc ;
create_opalcore ( ) ;
/*
* If oc_conf - > opalcorebuf = is set in the 2 nd kernel ,
* then capture the dump .
*/
if ( ! ( is_opalcore_usable ( ) ) ) {
2019-12-11 21:39:08 +05:30
pr_err ( " Failed to export /sys/firmware/opal/mpipl/core \n " ) ;
2019-09-11 20:26:33 +05:30
opalcore_cleanup ( ) ;
return rc ;
}
/* Set OPAL core file size */
opal_core_attr . size = oc_conf - > opalcore_size ;
2019-12-11 21:39:08 +05:30
mpipl_kobj = kobject_create_and_add ( " mpipl " , opal_kobj ) ;
if ( ! mpipl_kobj ) {
pr_err ( " unable to create mpipl kobject \n " ) ;
return - ENOMEM ;
}
2019-09-11 20:26:33 +05:30
/* Export OPAL core sysfs file */
2019-12-11 21:39:08 +05:30
rc = sysfs_create_group ( mpipl_kobj , & mpipl_group ) ;
if ( rc ) {
pr_err ( " mpipl sysfs group creation failed (%d) " , rc ) ;
2019-09-11 20:26:33 +05:30
opalcore_cleanup ( ) ;
return rc ;
}
2019-12-11 21:39:08 +05:30
/* The /sys/firmware/opal/core is moved to /sys/firmware/opal/mpipl/
* directory , need to create symlink at old location to maintain
* backward compatibility .
*/
rc = compat_only_sysfs_link_entry_to_kobj ( opal_kobj , mpipl_kobj ,
" core " , NULL ) ;
2019-09-11 20:26:45 +05:30
if ( rc ) {
2019-12-11 21:39:08 +05:30
pr_err ( " unable to create core symlink (%d) \n " , rc ) ;
return rc ;
2019-09-11 20:26:45 +05:30
}
2019-09-11 20:26:33 +05:30
return 0 ;
}
fs_initcall ( opalcore_init ) ;