/*
* Copyright 2013 Tilera Corporation. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for
* more details.
*
* A code-rewriter that handles unaligned exception.
*/
#include <linux/smp.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/sched/debug.h>
#include <linux/sched/task.h>
#include <linux/thread_info.h>
#include <linux/uaccess.h>
#include <linux/mman.h>
#include <linux/types.h>
#include <linux/err.h>
#include <linux/extable.h>
#include <linux/compat.h>
#include <linux/prctl.h>
#include <asm/cacheflush.h>
#include <asm/traps.h>
#include <linux/uaccess.h>
#include <asm/unaligned.h>
#include <arch/abi.h>
#include <arch/spr_def.h>
#include <arch/opcode.h>
/*
* This file handles unaligned exception for tile-Gx. The tilepro's unaligned
* exception is supported out of single_step.c
*/
int unaligned_printk;
static int __init setup_unaligned_printk(char *str)
{
long val;
if (kstrtol(str, 0, &val) != 0)
return 0;
unaligned_printk = val;
pr_info("Printk for each unaligned data accesses is %s\n",
unaligned_printk ? "enabled" : "disabled");
return 1;
}
__setup("unaligned_printk=", setup_unaligned_printk);
unsigned int unaligned_fixup_count;
#ifdef __tilegx__
/*
* Unalign data jit fixup code fragement. Reserved space is 128 bytes.
* The 1st 64-bit word saves fault PC address, 2nd word is the fault
* instruction bundle followed by 14 JIT bundles.
*/
struct unaligned_jit_fragment {
unsigned long pc;
tilegx_bundle_bits bundle;
tilegx_bundle_bits insn[14];
};
/*
* Check if a nop or fnop at bundle's pipeline X0.
*/
static bool is_bundle_x0_nop(tilegx_bundle_bits bundle)
{
return (((get_UnaryOpcodeExtension_X0(bundle) ==
NOP_UNARY_OPCODE_X0) &&
(get_RRROpcodeExtension_X0(bundle) ==
UNARY_RRR_0_OPCODE_X0) &&
(get_Opcode_X0(bundle) ==
RRR_0_OPCODE_X0)) ||
((get_UnaryOpcodeExtension_X0(bundle) ==
FNOP_UNARY_OPCODE_X0) &&
(get_RRROpcodeExtension_X0(bundle) ==
UNARY_RRR_0_OPCODE_X0) &&
(get_Opcode_X0(bundle) ==
RRR_0_OPCODE_X0)));
}
/*
* Check if nop or fnop at bundle's pipeline X1.
*/
static bool is_bundle_x1_nop(tilegx_bundle_bits bundle)
{
return (((get_UnaryOpcodeExtension_X1(bundle) ==
NOP_UNARY_OPCODE_X1) &&
(get_RRROpcodeExtension_X1(bundle) ==
UNARY_RRR_0_OPCODE_X1) &&
(get_Opcode_X1(bundle) ==
RRR_0_OPCODE_X1)) ||
((get_UnaryOpcodeExtension_X1(bundle) ==
FNOP_UNARY_OPCODE_X1) &&
(get_RRROpcodeExtension_X1(bundle) ==
UNARY_RRR_0_OPCODE_X1) &&
(get_Opcode_X1(bundle) ==
RRR_0_OPCODE_X1)));
}
/*
* Check if nop or fnop at bundle's Y0 pipeline.
*/
static bool is_bundle_y0_nop(tilegx_bundle_bits bundle)
{
return (((get_UnaryOpcodeExtension_Y0(bundle) ==
NOP_UNARY_OPCODE_Y0) &&
(get_RRROpcodeExtension_Y0(bundle) ==
UNARY_RRR_1_OPCODE_Y0) &&
(get_Opcode_Y0(bundle) ==
RRR_1_OPCODE_Y0)) ||
((get_UnaryOpcodeExtension_Y0(bundle) ==
FNOP_UNARY_OPCODE_Y0) &&
(get_RRROpcodeExtension_Y0(bundle) ==
UNARY_RRR_1_OPCODE_Y0) &&
(get_Opcode_Y0(bundle) ==
RRR_1_OPCODE_Y0)));
}
/*
* Check if nop or fnop at bundle's pipeline Y1.
*/
static bool is_bundle_y1_nop(tilegx_bundle_bits bundle)
{
return (((get_UnaryOpcodeExtension_Y1(bundle) ==
NOP_UNARY_OPCODE_Y1) &&
(get_RRROpcodeExtension_Y1(bundle) ==
UNARY_RRR_1_OPCODE_Y1) &&
(get_Opcode_Y1(bundle) ==
RRR_1_OPCODE_Y1)) ||
((get_UnaryOpcodeExtension_Y1(bundle) ==
FNOP_UNARY_OPCODE_Y1) &&
(get_RRROpcodeExtension_Y1(bundle) ==
UNARY_RRR_1_OPCODE_Y1) &&
(get_Opcode_Y1(bundle) ==
RRR_1_OPCODE_Y1)));
}
/*
* Test if a bundle's y0 and y1 pipelines are both nop or fnop.
*/
static bool is_y0_y1_nop(tilegx_bundle_bits bundle)
{
return is_bundle_y0_nop(bundle) && is_bundle_y1_nop(bundle);
}
/*
* Test if a bundle's x0 and x1 pipelines are both nop or fnop.
*/
static bool is_x0_x1_nop(tilegx_bundle_bits bundle)
{
return is_bundle_x0_nop(bundle) && is_bundle_x1_nop(bundle);
}
/*
* Find the destination, source registers of fault unalign access instruction
* at X1 or Y2. Also, allocate up to 3 scratch registers clob1, clob2 and
* clob3, which are guaranteed different from any register used in the fault
* bundle. r_alias is used to return if the other instructions other than the
|