Skip to content

Commit

Permalink
powerpc/mce: Add debugfs interface to inject MCE
Browse files Browse the repository at this point in the history
To test machine check handling, add debugfs interface to inject
slb multihit errors.

To inject slb multihit:
 #echo 1 > /sys/kernel/debug/powerpc/mce_error_inject/inject_slb_multihit

Signed-off-by: Ganesh Goudar <ganeshgr@linux.ibm.com>
Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
  • Loading branch information
Ganesh Goudar authored and intel-lab-lkp committed Sep 17, 2020
1 parent a60337c commit 4ab1196
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 0 deletions.
9 changes: 9 additions & 0 deletions arch/powerpc/Kconfig.debug
Expand Up @@ -398,3 +398,12 @@ config KASAN_SHADOW_OFFSET
hex
depends on KASAN
default 0xe0000000

config MCE_ERROR_INJECT
bool "Enable MCE error injection through debugfs"
depends on DEBUG_FS
default y
help
This option creates an mce_error_inject directory in the
powerpc debugfs directory that allows limited injection of
Machine Check Errors (MCEs).
2 changes: 2 additions & 0 deletions arch/powerpc/sysdev/Makefile
Expand Up @@ -52,3 +52,5 @@ obj-$(CONFIG_PPC_XICS) += xics/
obj-$(CONFIG_PPC_XIVE) += xive/

obj-$(CONFIG_GE_FPGA) += ge/

obj-$(CONFIG_MCE_ERROR_INJECT) += mce_error_inject.o
148 changes: 148 additions & 0 deletions arch/powerpc/sysdev/mce_error_inject.c
@@ -0,0 +1,148 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Machine Check Exception injection code
*/

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <asm/debugfs.h>

static inline unsigned long get_slb_index(void)
{
unsigned long index;

index = get_paca()->stab_rr;

/*
* simple round-robin replacement of slb starting at SLB_NUM_BOLTED.
*/
if (index < (mmu_slb_size - 1))
index++;
else
index = SLB_NUM_BOLTED;
get_paca()->stab_rr = index;
return index;
}

#define slb_esid_mask(ssize) \
(((ssize) == MMU_SEGSIZE_256M) ? ESID_MASK : ESID_MASK_1T)

static inline unsigned long mk_esid_data(unsigned long ea, int ssize,
unsigned long slot)
{
return (ea & slb_esid_mask(ssize)) | SLB_ESID_V | slot;
}

#define slb_vsid_shift(ssize) \
((ssize) == MMU_SEGSIZE_256M ? SLB_VSID_SHIFT : SLB_VSID_SHIFT_1T)

static inline unsigned long mk_vsid_data(unsigned long ea, int ssize,
unsigned long flags)
{
return (get_kernel_vsid(ea, ssize) << slb_vsid_shift(ssize)) | flags |
((unsigned long)ssize << SLB_VSID_SSIZE_SHIFT);
}

static void insert_slb_entry(char *p, int ssize)
{
unsigned long flags, entry;
struct paca_struct *paca;

flags = SLB_VSID_KERNEL | mmu_psize_defs[MMU_PAGE_64K].sllp;

preempt_disable();

paca = get_paca();

entry = get_slb_index();
asm volatile("slbmte %0,%1" :
: "r" (mk_vsid_data((unsigned long)p, ssize, flags)),
"r" (mk_esid_data((unsigned long)p, ssize, entry))
: "memory");

entry = get_slb_index();
asm volatile("slbmte %0,%1" :
: "r" (mk_vsid_data((unsigned long)p, ssize, flags)),
"r" (mk_esid_data((unsigned long)p, ssize, entry))
: "memory");
preempt_enable();
p[0] = '!';
}

static void inject_vmalloc_slb_multihit(void)
{
char *p;

p = vmalloc(2048);
if (!p)
return;

insert_slb_entry(p, MMU_SEGSIZE_1T);
vfree(p);
}

static void inject_kmalloc_slb_multihit(void)
{
char *p;

p = kmalloc(2048, GFP_KERNEL);
if (!p)
return;

insert_slb_entry(p, MMU_SEGSIZE_1T);
kfree(p);
}

static ssize_t inject_slb_multihit(const char __user *u_buf, size_t count)
{
char buf[32];
size_t buf_size;

buf_size = min(count, (sizeof(buf) - 1));
if (copy_from_user(buf, u_buf, buf_size))
return -EFAULT;
buf[buf_size] = '\0';

if (buf[0] != '1')
return -EINVAL;

inject_vmalloc_slb_multihit();
inject_kmalloc_slb_multihit();
return count;
}

static ssize_t inject_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
static ssize_t (*func)(const char __user *, size_t);

func = file->f_inode->i_private;
return func(buf, count);
}

static const struct file_operations inject_fops = {
.write = inject_write,
.llseek = default_llseek,
};

static int mce_error_inject_setup(void)
{
struct dentry *mce_error_inject_dir;

mce_error_inject_dir = debugfs_create_dir("mce_error_inject",
powerpc_debugfs_root);

if (mmu_has_feature(MMU_FTR_HPTE_TABLE)) {
(void)debugfs_create_file("inject_slb_multihit", 0200,
mce_error_inject_dir,
&inject_slb_multihit,
&inject_fops);
}

return 0;
}

device_initcall(mce_error_inject_setup);

0 comments on commit 4ab1196

Please sign in to comment.