162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include "lkdtm.h"
462306a36Sopenharmony_ci#include <linux/slab.h>
562306a36Sopenharmony_ci#include <linux/vmalloc.h>
662306a36Sopenharmony_ci#include <asm/mmu.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/* Inserts new slb entries */
962306a36Sopenharmony_cistatic void insert_slb_entry(unsigned long p, int ssize, int page_size)
1062306a36Sopenharmony_ci{
1162306a36Sopenharmony_ci	unsigned long flags;
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci	flags = SLB_VSID_KERNEL | mmu_psize_defs[page_size].sllp;
1462306a36Sopenharmony_ci	preempt_disable();
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci	asm volatile("slbmte %0,%1" :
1762306a36Sopenharmony_ci		     : "r" (mk_vsid_data(p, ssize, flags)),
1862306a36Sopenharmony_ci		       "r" (mk_esid_data(p, ssize, SLB_NUM_BOLTED))
1962306a36Sopenharmony_ci		     : "memory");
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	asm volatile("slbmte %0,%1" :
2262306a36Sopenharmony_ci			: "r" (mk_vsid_data(p, ssize, flags)),
2362306a36Sopenharmony_ci			  "r" (mk_esid_data(p, ssize, SLB_NUM_BOLTED + 1))
2462306a36Sopenharmony_ci			: "memory");
2562306a36Sopenharmony_ci	preempt_enable();
2662306a36Sopenharmony_ci}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* Inject slb multihit on vmalloc-ed address i.e 0xD00... */
2962306a36Sopenharmony_cistatic int inject_vmalloc_slb_multihit(void)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	char *p;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	p = vmalloc(PAGE_SIZE);
3462306a36Sopenharmony_ci	if (!p)
3562306a36Sopenharmony_ci		return -ENOMEM;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	insert_slb_entry((unsigned long)p, MMU_SEGSIZE_1T, mmu_vmalloc_psize);
3862306a36Sopenharmony_ci	/*
3962306a36Sopenharmony_ci	 * This triggers exception, If handled correctly we must recover
4062306a36Sopenharmony_ci	 * from this error.
4162306a36Sopenharmony_ci	 */
4262306a36Sopenharmony_ci	p[0] = '!';
4362306a36Sopenharmony_ci	vfree(p);
4462306a36Sopenharmony_ci	return 0;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* Inject slb multihit on kmalloc-ed address i.e 0xC00... */
4862306a36Sopenharmony_cistatic int inject_kmalloc_slb_multihit(void)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	char *p;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	p = kmalloc(2048, GFP_KERNEL);
5362306a36Sopenharmony_ci	if (!p)
5462306a36Sopenharmony_ci		return -ENOMEM;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	insert_slb_entry((unsigned long)p, MMU_SEGSIZE_1T, mmu_linear_psize);
5762306a36Sopenharmony_ci	/*
5862306a36Sopenharmony_ci	 * This triggers exception, If handled correctly we must recover
5962306a36Sopenharmony_ci	 * from this error.
6062306a36Sopenharmony_ci	 */
6162306a36Sopenharmony_ci	p[0] = '!';
6262306a36Sopenharmony_ci	kfree(p);
6362306a36Sopenharmony_ci	return 0;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/*
6762306a36Sopenharmony_ci * Few initial SLB entries are bolted. Add a test to inject
6862306a36Sopenharmony_ci * multihit in bolted entry 0.
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_cistatic void insert_dup_slb_entry_0(void)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	unsigned long test_address = PAGE_OFFSET, *test_ptr;
7362306a36Sopenharmony_ci	unsigned long esid, vsid;
7462306a36Sopenharmony_ci	unsigned long i = 0;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	test_ptr = (unsigned long *)test_address;
7762306a36Sopenharmony_ci	preempt_disable();
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	asm volatile("slbmfee  %0,%1" : "=r" (esid) : "r" (i));
8062306a36Sopenharmony_ci	asm volatile("slbmfev  %0,%1" : "=r" (vsid) : "r" (i));
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/* for i !=0 we would need to mask out the old entry number */
8362306a36Sopenharmony_ci	asm volatile("slbmte %0,%1" :
8462306a36Sopenharmony_ci			: "r" (vsid),
8562306a36Sopenharmony_ci			  "r" (esid | SLB_NUM_BOLTED)
8662306a36Sopenharmony_ci			: "memory");
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	asm volatile("slbmfee  %0,%1" : "=r" (esid) : "r" (i));
8962306a36Sopenharmony_ci	asm volatile("slbmfev  %0,%1" : "=r" (vsid) : "r" (i));
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/* for i !=0 we would need to mask out the old entry number */
9262306a36Sopenharmony_ci	asm volatile("slbmte %0,%1" :
9362306a36Sopenharmony_ci			: "r" (vsid),
9462306a36Sopenharmony_ci			  "r" (esid | (SLB_NUM_BOLTED + 1))
9562306a36Sopenharmony_ci			: "memory");
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	pr_info("%s accessing test address 0x%lx: 0x%lx\n",
9862306a36Sopenharmony_ci		__func__, test_address, *test_ptr);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	preempt_enable();
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic void lkdtm_PPC_SLB_MULTIHIT(void)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	if (!radix_enabled()) {
10662306a36Sopenharmony_ci		pr_info("Injecting SLB multihit errors\n");
10762306a36Sopenharmony_ci		/*
10862306a36Sopenharmony_ci		 * These need not be separate tests, And they do pretty
10962306a36Sopenharmony_ci		 * much same thing. In any case we must recover from the
11062306a36Sopenharmony_ci		 * errors introduced by these functions, machine would not
11162306a36Sopenharmony_ci		 * survive these tests in case of failure to handle.
11262306a36Sopenharmony_ci		 */
11362306a36Sopenharmony_ci		inject_vmalloc_slb_multihit();
11462306a36Sopenharmony_ci		inject_kmalloc_slb_multihit();
11562306a36Sopenharmony_ci		insert_dup_slb_entry_0();
11662306a36Sopenharmony_ci		pr_info("Recovered from SLB multihit errors\n");
11762306a36Sopenharmony_ci	} else {
11862306a36Sopenharmony_ci		pr_err("XFAIL: This test is for ppc64 and with hash mode MMU only\n");
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic struct crashtype crashtypes[] = {
12362306a36Sopenharmony_ci	CRASHTYPE(PPC_SLB_MULTIHIT),
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistruct crashtype_category powerpc_crashtypes = {
12762306a36Sopenharmony_ci	.crashtypes = crashtypes,
12862306a36Sopenharmony_ci	.len	    = ARRAY_SIZE(crashtypes),
12962306a36Sopenharmony_ci};
130