| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * 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/thread_info.h>
 | 
					
						
							|  |  |  | #include <linux/uaccess.h>
 | 
					
						
							|  |  |  | #include <linux/mman.h>
 | 
					
						
							|  |  |  | #include <linux/types.h>
 | 
					
						
							|  |  |  | #include <linux/err.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/compat.h>
 | 
					
						
							|  |  |  | #include <linux/prctl.h>
 | 
					
						
							| 
									
										
										
										
											2015-03-23 14:23:58 -04:00
										 |  |  | #include <linux/context_tracking.h>
 | 
					
						
							| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  | #include <asm/cacheflush.h>
 | 
					
						
							|  |  |  | #include <asm/traps.h>
 | 
					
						
							|  |  |  | #include <asm/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 | 
					
						
							|  |  |  |  * unalign load/store shares same register with ra, rb and rd. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void find_regs(tilegx_bundle_bits bundle, uint64_t *rd, uint64_t *ra, | 
					
						
							|  |  |  | 		      uint64_t *rb, uint64_t *clob1, uint64_t *clob2, | 
					
						
							|  |  |  | 		      uint64_t *clob3, bool *r_alias) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 	uint64_t reg; | 
					
						
							|  |  |  | 	uint64_t reg_map = 0, alias_reg_map = 0, map; | 
					
						
							| 
									
										
										
										
											2014-06-01 01:00:13 +02:00
										 |  |  | 	bool alias = false; | 
					
						
							| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Parse fault bundle, find potential used registers and mark | 
					
						
							|  |  |  | 	 * corresponding bits in reg_map and alias_map. These 2 bit maps | 
					
						
							|  |  |  | 	 * are used to find the scratch registers and determine if there | 
					
						
							|  |  |  | 	 * is register alais. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (bundle & TILEGX_BUNDLE_MODE_MASK) {  /* Y Mode Bundle. */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		reg = get_SrcA_Y2(bundle); | 
					
						
							|  |  |  | 		reg_map |= 1ULL << reg; | 
					
						
							|  |  |  | 		*ra = reg; | 
					
						
							|  |  |  | 		reg = get_SrcBDest_Y2(bundle); | 
					
						
							|  |  |  | 		reg_map |= 1ULL << reg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (rd) { | 
					
						
							|  |  |  | 			/* Load. */ | 
					
						
							|  |  |  | 			*rd = reg; | 
					
						
							|  |  |  | 			alias_reg_map = (1ULL << *rd) | (1ULL << *ra); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			/* Store. */ | 
					
						
							|  |  |  | 			*rb = reg; | 
					
						
							|  |  |  | 			alias_reg_map = (1ULL << *ra) | (1ULL << *rb); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!is_bundle_y1_nop(bundle)) { | 
					
						
							|  |  |  | 			reg = get_SrcA_Y1(bundle); | 
					
						
							|  |  |  | 			reg_map |= (1ULL << reg); | 
					
						
							|  |  |  | 			map = (1ULL << reg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			reg = get_SrcB_Y1(bundle); | 
					
						
							|  |  |  | 			reg_map |= (1ULL << reg); | 
					
						
							|  |  |  | 			map |= (1ULL << reg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			reg = get_Dest_Y1(bundle); | 
					
						
							|  |  |  | 			reg_map |= (1ULL << reg); | 
					
						
							|  |  |  | 			map |= (1ULL << reg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (map & alias_reg_map) | 
					
						
							|  |  |  | 				alias = true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!is_bundle_y0_nop(bundle)) { | 
					
						
							|  |  |  | 			reg = get_SrcA_Y0(bundle); | 
					
						
							|  |  |  | 			reg_map |= (1ULL << reg); | 
					
						
							|  |  |  | 			map = (1ULL << reg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			reg = get_SrcB_Y0(bundle); | 
					
						
							|  |  |  | 			reg_map |= (1ULL << reg); | 
					
						
							|  |  |  | 			map |= (1ULL << reg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			reg = get_Dest_Y0(bundle); | 
					
						
							|  |  |  | 			reg_map |= (1ULL << reg); | 
					
						
							|  |  |  | 			map |= (1ULL << reg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (map & alias_reg_map) | 
					
						
							|  |  |  | 				alias = true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else	{ /* X Mode Bundle. */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		reg = get_SrcA_X1(bundle); | 
					
						
							|  |  |  | 		reg_map |= (1ULL << reg); | 
					
						
							|  |  |  | 		*ra = reg; | 
					
						
							|  |  |  | 		if (rd)	{ | 
					
						
							|  |  |  | 			/* Load. */ | 
					
						
							|  |  |  | 			reg = get_Dest_X1(bundle); | 
					
						
							|  |  |  | 			reg_map |= (1ULL << reg); | 
					
						
							|  |  |  | 			*rd = reg; | 
					
						
							|  |  |  | 			alias_reg_map = (1ULL << *rd) | (1ULL << *ra); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			/* Store. */ | 
					
						
							|  |  |  | 			reg = get_SrcB_X1(bundle); | 
					
						
							|  |  |  | 			reg_map |= (1ULL << reg); | 
					
						
							|  |  |  | 			*rb = reg; | 
					
						
							|  |  |  | 			alias_reg_map = (1ULL << *ra) | (1ULL << *rb); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!is_bundle_x0_nop(bundle)) { | 
					
						
							|  |  |  | 			reg = get_SrcA_X0(bundle); | 
					
						
							|  |  |  | 			reg_map |= (1ULL << reg); | 
					
						
							|  |  |  | 			map = (1ULL << reg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			reg = get_SrcB_X0(bundle); | 
					
						
							|  |  |  | 			reg_map |= (1ULL << reg); | 
					
						
							|  |  |  | 			map |= (1ULL << reg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			reg = get_Dest_X0(bundle); | 
					
						
							|  |  |  | 			reg_map |= (1ULL << reg); | 
					
						
							|  |  |  | 			map |= (1ULL << reg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (map & alias_reg_map) | 
					
						
							|  |  |  | 				alias = true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * "alias" indicates if the unalign access registers have collision | 
					
						
							|  |  |  | 	 * with others in the same bundle. We jsut simply test all register | 
					
						
							|  |  |  | 	 * operands case (RRR), ignored the case with immidate. If a bundle | 
					
						
							|  |  |  | 	 * has no register alias, we may do fixup in a simple or fast manner. | 
					
						
							|  |  |  | 	 * So if an immidata field happens to hit with a register, we may end | 
					
						
							|  |  |  | 	 * up fall back to the generic handling. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*r_alias = alias; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Flip bits on reg_map. */ | 
					
						
							|  |  |  | 	reg_map ^= -1ULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Scan reg_map lower 54(TREG_SP) bits to find 3 set bits. */ | 
					
						
							|  |  |  | 	for (i = 0; i < TREG_SP; i++) { | 
					
						
							|  |  |  | 		if (reg_map & (0x1ULL << i)) { | 
					
						
							|  |  |  | 			if (*clob1 == -1) { | 
					
						
							|  |  |  | 				*clob1 = i; | 
					
						
							|  |  |  | 			} else if (*clob2 == -1) { | 
					
						
							|  |  |  | 				*clob2 = i; | 
					
						
							|  |  |  | 			} else if (*clob3 == -1) { | 
					
						
							|  |  |  | 				*clob3 = i; | 
					
						
							|  |  |  | 				return; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Sanity check for register ra, rb, rd, clob1/2/3. Return true if any of them | 
					
						
							|  |  |  |  * is unexpected. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool check_regs(uint64_t rd, uint64_t ra, uint64_t rb, | 
					
						
							|  |  |  | 		       uint64_t clob1, uint64_t clob2,  uint64_t clob3) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	bool unexpected = false; | 
					
						
							|  |  |  | 	if ((ra >= 56) && (ra != TREG_ZERO)) | 
					
						
							|  |  |  | 		unexpected = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((clob1 >= 56) || (clob2 >= 56) || (clob3 >= 56)) | 
					
						
							|  |  |  | 		unexpected = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (rd != -1) { | 
					
						
							|  |  |  | 		if ((rd >= 56) && (rd != TREG_ZERO)) | 
					
						
							|  |  |  | 			unexpected = true; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		if ((rb >= 56) && (rb != TREG_ZERO)) | 
					
						
							|  |  |  | 			unexpected = true; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return unexpected; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define  GX_INSN_X0_MASK   ((1ULL << 31) - 1)
 | 
					
						
							|  |  |  | #define  GX_INSN_X1_MASK   (((1ULL << 31) - 1) << 31)
 | 
					
						
							|  |  |  | #define  GX_INSN_Y0_MASK   ((0xFULL << 27) | (0xFFFFFULL))
 | 
					
						
							|  |  |  | #define  GX_INSN_Y1_MASK   (GX_INSN_Y0_MASK << 31)
 | 
					
						
							|  |  |  | #define  GX_INSN_Y2_MASK   ((0x7FULL << 51) | (0x7FULL << 20))
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef __LITTLE_ENDIAN
 | 
					
						
							|  |  |  | #define  GX_INSN_BSWAP(_bundle_)    (_bundle_)
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | #define  GX_INSN_BSWAP(_bundle_)    swab64(_bundle_)
 | 
					
						
							|  |  |  | #endif /* __LITTLE_ENDIAN */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * __JIT_CODE(.) creates template bundles in .rodata.unalign_data section. | 
					
						
							|  |  |  |  * The corresponding static function jix_x#_###(.) generates partial or | 
					
						
							|  |  |  |  * whole bundle based on the template and given arguments. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define __JIT_CODE(_X_)						\
 | 
					
						
							|  |  |  | 	asm (".pushsection .rodata.unalign_data, \"a\"\n"	\ | 
					
						
							|  |  |  | 	     _X_"\n"						\ | 
					
						
							|  |  |  | 	     ".popsection\n") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __JIT_CODE("__unalign_jit_x1_mtspr:   {mtspr 0,  r0}"); | 
					
						
							|  |  |  | static tilegx_bundle_bits jit_x1_mtspr(int spr, int reg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_x1_mtspr; | 
					
						
							|  |  |  | 	return (GX_INSN_BSWAP(__unalign_jit_x1_mtspr) & GX_INSN_X1_MASK) | | 
					
						
							|  |  |  | 		create_MT_Imm14_X1(spr) | create_SrcA_X1(reg); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __JIT_CODE("__unalign_jit_x1_mfspr:   {mfspr r0, 0}"); | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_x1_mfspr(int reg, int spr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_x1_mfspr; | 
					
						
							|  |  |  | 	return (GX_INSN_BSWAP(__unalign_jit_x1_mfspr) & GX_INSN_X1_MASK) | | 
					
						
							|  |  |  | 		create_MF_Imm14_X1(spr) | create_Dest_X1(reg); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __JIT_CODE("__unalign_jit_x0_addi:   {addi  r0, r0, 0; iret}"); | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_x0_addi(int rd, int ra, int imm8) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_x0_addi; | 
					
						
							|  |  |  | 	return (GX_INSN_BSWAP(__unalign_jit_x0_addi) & GX_INSN_X0_MASK) | | 
					
						
							|  |  |  | 		create_Dest_X0(rd) | create_SrcA_X0(ra) | | 
					
						
							|  |  |  | 		create_Imm8_X0(imm8); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __JIT_CODE("__unalign_jit_x1_ldna:   {ldna  r0, r0}"); | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_x1_ldna(int rd, int ra) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_x1_ldna; | 
					
						
							|  |  |  | 	return (GX_INSN_BSWAP(__unalign_jit_x1_ldna) &  GX_INSN_X1_MASK) | | 
					
						
							|  |  |  | 		create_Dest_X1(rd) | create_SrcA_X1(ra); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __JIT_CODE("__unalign_jit_x0_dblalign:   {dblalign r0, r0 ,r0}"); | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_x0_dblalign(int rd, int ra, int rb) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_x0_dblalign; | 
					
						
							|  |  |  | 	return (GX_INSN_BSWAP(__unalign_jit_x0_dblalign) & GX_INSN_X0_MASK) | | 
					
						
							|  |  |  | 		create_Dest_X0(rd) | create_SrcA_X0(ra) | | 
					
						
							|  |  |  | 		create_SrcB_X0(rb); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __JIT_CODE("__unalign_jit_x1_iret:   {iret}"); | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_x1_iret(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_x1_iret; | 
					
						
							|  |  |  | 	return GX_INSN_BSWAP(__unalign_jit_x1_iret) & GX_INSN_X1_MASK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __JIT_CODE("__unalign_jit_x01_fnop:   {fnop;fnop}"); | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_x0_fnop(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_x01_fnop; | 
					
						
							|  |  |  | 	return GX_INSN_BSWAP(__unalign_jit_x01_fnop) & GX_INSN_X0_MASK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_x1_fnop(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_x01_fnop; | 
					
						
							|  |  |  | 	return GX_INSN_BSWAP(__unalign_jit_x01_fnop) & GX_INSN_X1_MASK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __JIT_CODE("__unalign_jit_y2_dummy:   {fnop; fnop; ld zero, sp}"); | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_y2_dummy(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_y2_dummy; | 
					
						
							|  |  |  | 	return GX_INSN_BSWAP(__unalign_jit_y2_dummy) & GX_INSN_Y2_MASK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_y1_fnop(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_y2_dummy; | 
					
						
							|  |  |  | 	return GX_INSN_BSWAP(__unalign_jit_y2_dummy) & GX_INSN_Y1_MASK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __JIT_CODE("__unalign_jit_x1_st1_add:  {st1_add r1, r0, 0}"); | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_x1_st1_add(int ra, int rb, int imm8) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_x1_st1_add; | 
					
						
							|  |  |  | 	return (GX_INSN_BSWAP(__unalign_jit_x1_st1_add) & | 
					
						
							|  |  |  | 		(~create_SrcA_X1(-1)) & | 
					
						
							|  |  |  | 		GX_INSN_X1_MASK) | create_SrcA_X1(ra) | | 
					
						
							|  |  |  | 		create_SrcB_X1(rb) | create_Dest_Imm8_X1(imm8); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __JIT_CODE("__unalign_jit_x1_st:  {crc32_8 r1, r0, r0; st  r0, r0}"); | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_x1_st(int ra, int rb) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_x1_st; | 
					
						
							|  |  |  | 	return (GX_INSN_BSWAP(__unalign_jit_x1_st) & GX_INSN_X1_MASK) | | 
					
						
							|  |  |  | 		create_SrcA_X1(ra) | create_SrcB_X1(rb); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __JIT_CODE("__unalign_jit_x1_st_add:  {st_add  r1, r0, 0}"); | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_x1_st_add(int ra, int rb, int imm8) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_x1_st_add; | 
					
						
							|  |  |  | 	return (GX_INSN_BSWAP(__unalign_jit_x1_st_add) & | 
					
						
							|  |  |  | 		(~create_SrcA_X1(-1)) & | 
					
						
							|  |  |  | 		GX_INSN_X1_MASK) | create_SrcA_X1(ra) | | 
					
						
							|  |  |  | 		create_SrcB_X1(rb) | create_Dest_Imm8_X1(imm8); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __JIT_CODE("__unalign_jit_x1_ld:  {crc32_8 r1, r0, r0; ld  r0, r0}"); | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_x1_ld(int rd, int ra) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_x1_ld; | 
					
						
							|  |  |  | 	return (GX_INSN_BSWAP(__unalign_jit_x1_ld) & GX_INSN_X1_MASK) | | 
					
						
							|  |  |  | 		create_Dest_X1(rd) | create_SrcA_X1(ra); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __JIT_CODE("__unalign_jit_x1_ld_add:  {ld_add  r1, r0, 0}"); | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_x1_ld_add(int rd, int ra, int imm8) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_x1_ld_add; | 
					
						
							|  |  |  | 	return (GX_INSN_BSWAP(__unalign_jit_x1_ld_add) & | 
					
						
							|  |  |  | 		(~create_Dest_X1(-1)) & | 
					
						
							|  |  |  | 		GX_INSN_X1_MASK) | create_Dest_X1(rd) | | 
					
						
							|  |  |  | 		create_SrcA_X1(ra) | create_Imm8_X1(imm8); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __JIT_CODE("__unalign_jit_x0_bfexts:  {bfexts r0, r0, 0, 0}"); | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_x0_bfexts(int rd, int ra, int bfs, int bfe) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_x0_bfexts; | 
					
						
							|  |  |  | 	return (GX_INSN_BSWAP(__unalign_jit_x0_bfexts) & | 
					
						
							|  |  |  | 		GX_INSN_X0_MASK) | | 
					
						
							|  |  |  | 		create_Dest_X0(rd) | create_SrcA_X0(ra) | | 
					
						
							|  |  |  | 		create_BFStart_X0(bfs) | create_BFEnd_X0(bfe); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __JIT_CODE("__unalign_jit_x0_bfextu:  {bfextu r0, r0, 0, 0}"); | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_x0_bfextu(int rd, int ra, int bfs, int bfe) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_x0_bfextu; | 
					
						
							|  |  |  | 	return (GX_INSN_BSWAP(__unalign_jit_x0_bfextu) & | 
					
						
							|  |  |  | 		GX_INSN_X0_MASK) | | 
					
						
							|  |  |  | 		create_Dest_X0(rd) | create_SrcA_X0(ra) | | 
					
						
							|  |  |  | 		create_BFStart_X0(bfs) | create_BFEnd_X0(bfe); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __JIT_CODE("__unalign_jit_x1_addi:  {bfextu r1, r1, 0, 0; addi r0, r0, 0}"); | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_x1_addi(int rd, int ra, int imm8) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_x1_addi; | 
					
						
							|  |  |  | 	return (GX_INSN_BSWAP(__unalign_jit_x1_addi) & GX_INSN_X1_MASK) | | 
					
						
							|  |  |  | 		create_Dest_X1(rd) | create_SrcA_X1(ra) | | 
					
						
							|  |  |  | 		create_Imm8_X1(imm8); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __JIT_CODE("__unalign_jit_x0_shrui:  {shrui r0, r0, 0; iret}"); | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_x0_shrui(int rd, int ra, int imm6) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_x0_shrui; | 
					
						
							|  |  |  | 	return (GX_INSN_BSWAP(__unalign_jit_x0_shrui) & | 
					
						
							|  |  |  | 		GX_INSN_X0_MASK) | | 
					
						
							|  |  |  | 		create_Dest_X0(rd) | create_SrcA_X0(ra) | | 
					
						
							|  |  |  | 		create_ShAmt_X0(imm6); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __JIT_CODE("__unalign_jit_x0_rotli:  {rotli r0, r0, 0; iret}"); | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_x0_rotli(int rd, int ra, int imm6) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_x0_rotli; | 
					
						
							|  |  |  | 	return (GX_INSN_BSWAP(__unalign_jit_x0_rotli) & | 
					
						
							|  |  |  | 		GX_INSN_X0_MASK) | | 
					
						
							|  |  |  | 		create_Dest_X0(rd) | create_SrcA_X0(ra) | | 
					
						
							|  |  |  | 		create_ShAmt_X0(imm6); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | __JIT_CODE("__unalign_jit_x1_bnezt:  {bnezt r0, __unalign_jit_x1_bnezt}"); | 
					
						
							|  |  |  | static tilegx_bundle_bits  jit_x1_bnezt(int ra, int broff) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern  tilegx_bundle_bits __unalign_jit_x1_bnezt; | 
					
						
							|  |  |  | 	return (GX_INSN_BSWAP(__unalign_jit_x1_bnezt) & | 
					
						
							|  |  |  | 		GX_INSN_X1_MASK) | | 
					
						
							|  |  |  | 		create_SrcA_X1(ra) | create_BrOff_X1(broff); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #undef __JIT_CODE
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * This function generates unalign fixup JIT. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2013-09-13 10:57:54 -04:00
										 |  |  |  * We first find unalign load/store instruction's destination, source | 
					
						
							|  |  |  |  * registers: ra, rb and rd. and 3 scratch registers by calling | 
					
						
							| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  |  * find_regs(...). 3 scratch clobbers should not alias with any register | 
					
						
							|  |  |  |  * used in the fault bundle. Then analyze the fault bundle to determine | 
					
						
							|  |  |  |  * if it's a load or store, operand width, branch or address increment etc. | 
					
						
							|  |  |  |  * At last generated JIT is copied into JIT code area in user space. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static | 
					
						
							|  |  |  | void jit_bundle_gen(struct pt_regs *regs, tilegx_bundle_bits bundle, | 
					
						
							|  |  |  | 		    int align_ctl) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct thread_info *info = current_thread_info(); | 
					
						
							|  |  |  | 	struct unaligned_jit_fragment frag; | 
					
						
							|  |  |  | 	struct unaligned_jit_fragment *jit_code_area; | 
					
						
							|  |  |  | 	tilegx_bundle_bits bundle_2 = 0; | 
					
						
							|  |  |  | 	/* If bundle_2_enable = false, bundle_2 is fnop/nop operation. */ | 
					
						
							|  |  |  | 	bool     bundle_2_enable = true; | 
					
						
							| 
									
										
										
										
											2014-06-01 01:00:13 +02:00
										 |  |  | 	uint64_t ra = -1, rb = -1, rd = -1, clob1 = -1, clob2 = -1, clob3 = -1; | 
					
						
							| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Indicate if the unalign access | 
					
						
							|  |  |  | 	 * instruction's registers hit with | 
					
						
							|  |  |  | 	 * others in the same bundle. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	bool     alias = false; | 
					
						
							|  |  |  | 	bool     load_n_store = true; | 
					
						
							|  |  |  | 	bool     load_store_signed = false; | 
					
						
							|  |  |  | 	unsigned int  load_store_size = 8; | 
					
						
							|  |  |  | 	bool     y1_br = false;  /* True, for a branch in same bundle at Y1.*/ | 
					
						
							|  |  |  | 	int      y1_br_reg = 0; | 
					
						
							|  |  |  | 	/* True for link operation. i.e. jalr or lnk at Y1 */ | 
					
						
							|  |  |  | 	bool     y1_lr = false; | 
					
						
							|  |  |  | 	int      y1_lr_reg = 0; | 
					
						
							|  |  |  | 	bool     x1_add = false;/* True, for load/store ADD instruction at X1*/ | 
					
						
							|  |  |  | 	int      x1_add_imm8 = 0; | 
					
						
							|  |  |  | 	bool     unexpected = false; | 
					
						
							|  |  |  | 	int      n = 0, k; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	jit_code_area = | 
					
						
							|  |  |  | 		(struct unaligned_jit_fragment *)(info->unalign_jit_base); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	memset((void *)&frag, 0, sizeof(frag)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* 0: X mode, Otherwise: Y mode. */ | 
					
						
							|  |  |  | 	if (bundle & TILEGX_BUNDLE_MODE_MASK) { | 
					
						
							|  |  |  | 		unsigned int mod, opcode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (get_Opcode_Y1(bundle) == RRR_1_OPCODE_Y1 && | 
					
						
							|  |  |  | 		    get_RRROpcodeExtension_Y1(bundle) == | 
					
						
							|  |  |  | 		    UNARY_RRR_1_OPCODE_Y1) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			opcode = get_UnaryOpcodeExtension_Y1(bundle); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * Test "jalr", "jalrp", "jr", "jrp" instruction at Y1 | 
					
						
							|  |  |  | 			 * pipeline. | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			switch (opcode) { | 
					
						
							|  |  |  | 			case JALR_UNARY_OPCODE_Y1: | 
					
						
							|  |  |  | 			case JALRP_UNARY_OPCODE_Y1: | 
					
						
							|  |  |  | 				y1_lr = true; | 
					
						
							|  |  |  | 				y1_lr_reg = 55; /* Link register. */ | 
					
						
							|  |  |  | 				/* FALLTHROUGH */ | 
					
						
							|  |  |  | 			case JR_UNARY_OPCODE_Y1: | 
					
						
							|  |  |  | 			case JRP_UNARY_OPCODE_Y1: | 
					
						
							|  |  |  | 				y1_br = true; | 
					
						
							|  |  |  | 				y1_br_reg = get_SrcA_Y1(bundle); | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			case LNK_UNARY_OPCODE_Y1: | 
					
						
							|  |  |  | 				/* "lnk" at Y1 pipeline. */ | 
					
						
							|  |  |  | 				y1_lr = true; | 
					
						
							|  |  |  | 				y1_lr_reg = get_Dest_Y1(bundle); | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		opcode = get_Opcode_Y2(bundle); | 
					
						
							|  |  |  | 		mod = get_Mode(bundle); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 *  bundle_2 is bundle after making Y2 as a dummy operation | 
					
						
							|  |  |  | 		 *  - ld zero, sp | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		bundle_2 = (bundle & (~GX_INSN_Y2_MASK)) | jit_y2_dummy(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Make Y1 as fnop if Y1 is a branch or lnk operation. */ | 
					
						
							|  |  |  | 		if (y1_br || y1_lr) { | 
					
						
							|  |  |  | 			bundle_2 &= ~(GX_INSN_Y1_MASK); | 
					
						
							|  |  |  | 			bundle_2 |= jit_y1_fnop(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (is_y0_y1_nop(bundle_2)) | 
					
						
							|  |  |  | 			bundle_2_enable = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (mod == MODE_OPCODE_YC2) { | 
					
						
							|  |  |  | 			/* Store. */ | 
					
						
							|  |  |  | 			load_n_store = false; | 
					
						
							|  |  |  | 			load_store_size = 1 << opcode; | 
					
						
							|  |  |  | 			load_store_signed = false; | 
					
						
							|  |  |  | 			find_regs(bundle, 0, &ra, &rb, &clob1, &clob2, | 
					
						
							|  |  |  | 				  &clob3, &alias); | 
					
						
							|  |  |  | 			if (load_store_size > 8) | 
					
						
							|  |  |  | 				unexpected = true; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			/* Load. */ | 
					
						
							|  |  |  | 			load_n_store = true; | 
					
						
							|  |  |  | 			if (mod == MODE_OPCODE_YB2) { | 
					
						
							|  |  |  | 				switch (opcode) { | 
					
						
							|  |  |  | 				case LD_OPCODE_Y2: | 
					
						
							|  |  |  | 					load_store_signed = false; | 
					
						
							|  |  |  | 					load_store_size = 8; | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				case LD4S_OPCODE_Y2: | 
					
						
							|  |  |  | 					load_store_signed = true; | 
					
						
							|  |  |  | 					load_store_size = 4; | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				case LD4U_OPCODE_Y2: | 
					
						
							|  |  |  | 					load_store_signed = false; | 
					
						
							|  |  |  | 					load_store_size = 4; | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				default: | 
					
						
							|  |  |  | 					unexpected = true; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else if (mod == MODE_OPCODE_YA2) { | 
					
						
							|  |  |  | 				if (opcode == LD2S_OPCODE_Y2) { | 
					
						
							|  |  |  | 					load_store_signed = true; | 
					
						
							|  |  |  | 					load_store_size = 2; | 
					
						
							|  |  |  | 				} else if (opcode == LD2U_OPCODE_Y2) { | 
					
						
							|  |  |  | 					load_store_signed = false; | 
					
						
							|  |  |  | 					load_store_size = 2; | 
					
						
							|  |  |  | 				} else | 
					
						
							|  |  |  | 					unexpected = true; | 
					
						
							|  |  |  | 			} else | 
					
						
							|  |  |  | 				unexpected = true; | 
					
						
							|  |  |  | 			find_regs(bundle, &rd, &ra, &rb, &clob1, &clob2, | 
					
						
							|  |  |  | 				  &clob3, &alias); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		unsigned int opcode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* bundle_2 is bundle after making X1 as "fnop". */ | 
					
						
							|  |  |  | 		bundle_2 = (bundle & (~GX_INSN_X1_MASK)) | jit_x1_fnop(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (is_x0_x1_nop(bundle_2)) | 
					
						
							|  |  |  | 			bundle_2_enable = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (get_Opcode_X1(bundle) == RRR_0_OPCODE_X1) { | 
					
						
							|  |  |  | 			opcode = get_UnaryOpcodeExtension_X1(bundle); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (get_RRROpcodeExtension_X1(bundle) == | 
					
						
							|  |  |  | 			    UNARY_RRR_0_OPCODE_X1) { | 
					
						
							|  |  |  | 				load_n_store = true; | 
					
						
							|  |  |  | 				find_regs(bundle, &rd, &ra, &rb, &clob1, | 
					
						
							|  |  |  | 					  &clob2, &clob3, &alias); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				switch (opcode) { | 
					
						
							|  |  |  | 				case LD_UNARY_OPCODE_X1: | 
					
						
							|  |  |  | 					load_store_signed = false; | 
					
						
							|  |  |  | 					load_store_size = 8; | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				case LD4S_UNARY_OPCODE_X1: | 
					
						
							|  |  |  | 					load_store_signed = true; | 
					
						
							|  |  |  | 					/* FALLTHROUGH */ | 
					
						
							|  |  |  | 				case LD4U_UNARY_OPCODE_X1: | 
					
						
							|  |  |  | 					load_store_size = 4; | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				case LD2S_UNARY_OPCODE_X1: | 
					
						
							|  |  |  | 					load_store_signed = true; | 
					
						
							|  |  |  | 					/* FALLTHROUGH */ | 
					
						
							|  |  |  | 				case LD2U_UNARY_OPCODE_X1: | 
					
						
							|  |  |  | 					load_store_size = 2; | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				default: | 
					
						
							|  |  |  | 					unexpected = true; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				load_n_store = false; | 
					
						
							|  |  |  | 				load_store_signed = false; | 
					
						
							|  |  |  | 				find_regs(bundle, 0, &ra, &rb, | 
					
						
							|  |  |  | 					  &clob1, &clob2, &clob3, | 
					
						
							|  |  |  | 					  &alias); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				opcode = get_RRROpcodeExtension_X1(bundle); | 
					
						
							|  |  |  | 				switch (opcode)	{ | 
					
						
							|  |  |  | 				case ST_RRR_0_OPCODE_X1: | 
					
						
							|  |  |  | 					load_store_size = 8; | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				case ST4_RRR_0_OPCODE_X1: | 
					
						
							|  |  |  | 					load_store_size = 4; | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				case ST2_RRR_0_OPCODE_X1: | 
					
						
							|  |  |  | 					load_store_size = 2; | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				default: | 
					
						
							|  |  |  | 					unexpected = true; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else if (get_Opcode_X1(bundle) == IMM8_OPCODE_X1) { | 
					
						
							|  |  |  | 			load_n_store = true; | 
					
						
							|  |  |  | 			opcode = get_Imm8OpcodeExtension_X1(bundle); | 
					
						
							|  |  |  | 			switch (opcode)	{ | 
					
						
							|  |  |  | 			case LD_ADD_IMM8_OPCODE_X1: | 
					
						
							|  |  |  | 				load_store_size = 8; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			case LD4S_ADD_IMM8_OPCODE_X1: | 
					
						
							|  |  |  | 				load_store_signed = true; | 
					
						
							|  |  |  | 				/* FALLTHROUGH */ | 
					
						
							|  |  |  | 			case LD4U_ADD_IMM8_OPCODE_X1: | 
					
						
							|  |  |  | 				load_store_size = 4; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			case LD2S_ADD_IMM8_OPCODE_X1: | 
					
						
							|  |  |  | 				load_store_signed = true; | 
					
						
							|  |  |  | 				/* FALLTHROUGH */ | 
					
						
							|  |  |  | 			case LD2U_ADD_IMM8_OPCODE_X1: | 
					
						
							|  |  |  | 				load_store_size = 2; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			case ST_ADD_IMM8_OPCODE_X1: | 
					
						
							|  |  |  | 				load_n_store = false; | 
					
						
							|  |  |  | 				load_store_size = 8; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			case ST4_ADD_IMM8_OPCODE_X1: | 
					
						
							|  |  |  | 				load_n_store = false; | 
					
						
							|  |  |  | 				load_store_size = 4; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			case ST2_ADD_IMM8_OPCODE_X1: | 
					
						
							|  |  |  | 				load_n_store = false; | 
					
						
							|  |  |  | 				load_store_size = 2; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			default: | 
					
						
							|  |  |  | 				unexpected = true; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (!unexpected) { | 
					
						
							|  |  |  | 				x1_add = true; | 
					
						
							|  |  |  | 				if (load_n_store) | 
					
						
							|  |  |  | 					x1_add_imm8 = get_Imm8_X1(bundle); | 
					
						
							|  |  |  | 				else | 
					
						
							|  |  |  | 					x1_add_imm8 = get_Dest_Imm8_X1(bundle); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			find_regs(bundle, load_n_store ? (&rd) : NULL, | 
					
						
							|  |  |  | 				  &ra, &rb, &clob1, &clob2, &clob3, &alias); | 
					
						
							|  |  |  | 		} else | 
					
						
							|  |  |  | 			unexpected = true; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Some sanity check for register numbers extracted from fault bundle. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (check_regs(rd, ra, rb, clob1, clob2, clob3) == true) | 
					
						
							|  |  |  | 		unexpected = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Give warning if register ra has an aligned address. */ | 
					
						
							|  |  |  | 	if (!unexpected) | 
					
						
							|  |  |  | 		WARN_ON(!((load_store_size - 1) & (regs->regs[ra]))); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Fault came from kernel space, here we only need take care of | 
					
						
							|  |  |  | 	 * unaligned "get_user/put_user" macros defined in "uaccess.h". | 
					
						
							|  |  |  | 	 * Basically, we will handle bundle like this: | 
					
						
							|  |  |  | 	 * {ld/2u/4s rd, ra; movei rx, 0} or {st/2/4 ra, rb; movei rx, 0} | 
					
						
							|  |  |  | 	 * (Refer to file "arch/tile/include/asm/uaccess.h" for details). | 
					
						
							|  |  |  | 	 * For either load or store, byte-wise operation is performed by calling | 
					
						
							|  |  |  | 	 * get_user() or put_user(). If the macro returns non-zero value, | 
					
						
							|  |  |  | 	 * set the value to rx, otherwise set zero to rx. Finally make pc point | 
					
						
							|  |  |  | 	 * to next bundle and return. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (EX1_PL(regs->ex1) != USER_PL) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		unsigned long rx = 0; | 
					
						
							|  |  |  | 		unsigned long x = 0, ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (y1_br || y1_lr || x1_add || | 
					
						
							|  |  |  | 		    (load_store_signed != | 
					
						
							|  |  |  | 		     (load_n_store && load_store_size == 4))) { | 
					
						
							|  |  |  | 			/* No branch, link, wrong sign-ext or load/store add. */ | 
					
						
							|  |  |  | 			unexpected = true; | 
					
						
							|  |  |  | 		} else if (!unexpected) { | 
					
						
							|  |  |  | 			if (bundle & TILEGX_BUNDLE_MODE_MASK) { | 
					
						
							|  |  |  | 				/*
 | 
					
						
							|  |  |  | 				 * Fault bundle is Y mode. | 
					
						
							|  |  |  | 				 * Check if the Y1 and Y0 is the form of | 
					
						
							|  |  |  | 				 * { movei rx, 0; nop/fnop }, if yes, | 
					
						
							|  |  |  | 				 * find the rx. | 
					
						
							|  |  |  | 				 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if ((get_Opcode_Y1(bundle) == ADDI_OPCODE_Y1) | 
					
						
							|  |  |  | 				    && (get_SrcA_Y1(bundle) == TREG_ZERO) && | 
					
						
							|  |  |  | 				    (get_Imm8_Y1(bundle) == 0) && | 
					
						
							|  |  |  | 				    is_bundle_y0_nop(bundle)) { | 
					
						
							|  |  |  | 					rx = get_Dest_Y1(bundle); | 
					
						
							|  |  |  | 				} else if ((get_Opcode_Y0(bundle) == | 
					
						
							|  |  |  | 					    ADDI_OPCODE_Y0) && | 
					
						
							|  |  |  | 					   (get_SrcA_Y0(bundle) == TREG_ZERO) && | 
					
						
							|  |  |  | 					   (get_Imm8_Y0(bundle) == 0) && | 
					
						
							|  |  |  | 					   is_bundle_y1_nop(bundle)) { | 
					
						
							|  |  |  | 					rx = get_Dest_Y0(bundle); | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					unexpected = true; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				/*
 | 
					
						
							|  |  |  | 				 * Fault bundle is X mode. | 
					
						
							|  |  |  | 				 * Check if the X0 is 'movei rx, 0', | 
					
						
							|  |  |  | 				 * if yes, find the rx. | 
					
						
							|  |  |  | 				 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if ((get_Opcode_X0(bundle) == IMM8_OPCODE_X0) | 
					
						
							|  |  |  | 				    && (get_Imm8OpcodeExtension_X0(bundle) == | 
					
						
							|  |  |  | 					ADDI_IMM8_OPCODE_X0) && | 
					
						
							|  |  |  | 				    (get_SrcA_X0(bundle) == TREG_ZERO) && | 
					
						
							|  |  |  | 				    (get_Imm8_X0(bundle) == 0)) { | 
					
						
							|  |  |  | 					rx = get_Dest_X0(bundle); | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					unexpected = true; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* rx should be less than 56. */ | 
					
						
							|  |  |  | 			if (!unexpected && (rx >= 56)) | 
					
						
							|  |  |  | 				unexpected = true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!search_exception_tables(regs->pc)) { | 
					
						
							|  |  |  | 			/* No fixup in the exception tables for the pc. */ | 
					
						
							|  |  |  | 			unexpected = true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (unexpected) { | 
					
						
							|  |  |  | 			/* Unexpected unalign kernel fault. */ | 
					
						
							|  |  |  | 			struct task_struct *tsk = validate_current(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			bust_spinlocks(1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			show_regs(regs); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (unlikely(tsk->pid < 2)) { | 
					
						
							|  |  |  | 				panic("Kernel unalign fault running %s!", | 
					
						
							|  |  |  | 				      tsk->pid ? "init" : "the idle task"); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | #ifdef SUPPORT_DIE
 | 
					
						
							|  |  |  | 			die("Oops", regs); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 			bust_spinlocks(1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			do_group_exit(SIGKILL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			unsigned long i, b = 0; | 
					
						
							|  |  |  | 			unsigned char *ptr = | 
					
						
							|  |  |  | 				(unsigned char *)regs->regs[ra]; | 
					
						
							|  |  |  | 			if (load_n_store) { | 
					
						
							|  |  |  | 				/* handle get_user(x, ptr) */ | 
					
						
							|  |  |  | 				for (i = 0; i < load_store_size; i++) { | 
					
						
							|  |  |  | 					ret = get_user(b, ptr++); | 
					
						
							|  |  |  | 					if (!ret) { | 
					
						
							|  |  |  | 						/* Success! update x. */ | 
					
						
							|  |  |  | #ifdef __LITTLE_ENDIAN
 | 
					
						
							|  |  |  | 						x |= (b << (8 * i)); | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 						x <<= 8; | 
					
						
							|  |  |  | 						x |= b; | 
					
						
							|  |  |  | #endif /* __LITTLE_ENDIAN */
 | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						x = 0; | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				/* Sign-extend 4-byte loads. */ | 
					
						
							|  |  |  | 				if (load_store_size == 4) | 
					
						
							|  |  |  | 					x = (long)(int)x; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				/* Set register rd. */ | 
					
						
							|  |  |  | 				regs->regs[rd] = x; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				/* Set register rx. */ | 
					
						
							|  |  |  | 				regs->regs[rx] = ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				/* Bump pc. */ | 
					
						
							|  |  |  | 				regs->pc += 8; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				/* Handle put_user(x, ptr) */ | 
					
						
							|  |  |  | 				x = regs->regs[rb]; | 
					
						
							|  |  |  | #ifdef __LITTLE_ENDIAN
 | 
					
						
							|  |  |  | 				b = x; | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 				/*
 | 
					
						
							|  |  |  | 				 * Swap x in order to store x from low | 
					
						
							|  |  |  | 				 * to high memory same as the | 
					
						
							|  |  |  | 				 * little-endian case. | 
					
						
							|  |  |  | 				 */ | 
					
						
							|  |  |  | 				switch (load_store_size) { | 
					
						
							|  |  |  | 				case 8: | 
					
						
							|  |  |  | 					b = swab64(x); | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				case 4: | 
					
						
							|  |  |  | 					b = swab32(x); | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				case 2: | 
					
						
							|  |  |  | 					b = swab16(x); | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | #endif /* __LITTLE_ENDIAN */
 | 
					
						
							|  |  |  | 				for (i = 0; i < load_store_size; i++) { | 
					
						
							|  |  |  | 					ret = put_user(b, ptr++); | 
					
						
							|  |  |  | 					if (ret) | 
					
						
							|  |  |  | 						break; | 
					
						
							|  |  |  | 					/* Success! shift 1 byte. */ | 
					
						
							|  |  |  | 					b >>= 8; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				/* Set register rx. */ | 
					
						
							|  |  |  | 				regs->regs[rx] = ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				/* Bump pc. */ | 
					
						
							|  |  |  | 				regs->pc += 8; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		unaligned_fixup_count++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (unaligned_printk) { | 
					
						
							| 
									
										
										
										
											2014-10-31 10:50:46 -07:00
										 |  |  | 			pr_info("%s/%d - Unalign fixup for kernel access to userspace %lx\n", | 
					
						
							| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  | 				current->comm, current->pid, regs->regs[ra]); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Done! Return to the exception handler. */ | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((align_ctl == 0) || unexpected) { | 
					
						
							|  |  |  | 		siginfo_t info = { | 
					
						
							|  |  |  | 			.si_signo = SIGBUS, | 
					
						
							|  |  |  | 			.si_code = BUS_ADRALN, | 
					
						
							|  |  |  | 			.si_addr = (unsigned char __user *)0 | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		if (unaligned_printk) | 
					
						
							| 
									
										
										
										
											2014-10-31 10:50:46 -07:00
										 |  |  | 			pr_info("Unalign bundle: unexp @%llx, %llx\n", | 
					
						
							| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  | 				(unsigned long long)regs->pc, | 
					
						
							|  |  |  | 				(unsigned long long)bundle); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (ra < 56) { | 
					
						
							|  |  |  | 			unsigned long uaa = (unsigned long)regs->regs[ra]; | 
					
						
							|  |  |  | 			/* Set bus Address. */ | 
					
						
							|  |  |  | 			info.si_addr = (unsigned char __user *)uaa; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		unaligned_fixup_count++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		trace_unhandled_signal("unaligned fixup trap", regs, | 
					
						
							|  |  |  | 				       (unsigned long)info.si_addr, SIGBUS); | 
					
						
							|  |  |  | 		force_sig_info(info.si_signo, &info, current); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef __LITTLE_ENDIAN
 | 
					
						
							|  |  |  | #define UA_FIXUP_ADDR_DELTA          1
 | 
					
						
							|  |  |  | #define UA_FIXUP_BFEXT_START(_B_)    0
 | 
					
						
							|  |  |  | #define UA_FIXUP_BFEXT_END(_B_)     (8 * (_B_) - 1)
 | 
					
						
							|  |  |  | #else /* __BIG_ENDIAN */
 | 
					
						
							|  |  |  | #define UA_FIXUP_ADDR_DELTA          -1
 | 
					
						
							|  |  |  | #define UA_FIXUP_BFEXT_START(_B_)   (64 - 8 * (_B_))
 | 
					
						
							|  |  |  | #define UA_FIXUP_BFEXT_END(_B_)      63
 | 
					
						
							|  |  |  | #endif /* __LITTLE_ENDIAN */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((ra != rb) && (rd != TREG_SP) && !alias && | 
					
						
							|  |  |  | 	    !y1_br && !y1_lr && !x1_add) { | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Simple case: ra != rb and no register alias found, | 
					
						
							|  |  |  | 		 * and no branch or link. This will be the majority. | 
					
						
							|  |  |  | 		 * We can do a little better for simplae case than the | 
					
						
							|  |  |  | 		 * generic scheme below. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (!load_n_store) { | 
					
						
							|  |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * Simple store: ra != rb, no need for scratch register. | 
					
						
							|  |  |  | 			 * Just store and rotate to right bytewise. | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | #ifdef __BIG_ENDIAN
 | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_addi(ra, ra, load_store_size - 1) | | 
					
						
							|  |  |  | 				jit_x1_fnop(); | 
					
						
							|  |  |  | #endif /* __BIG_ENDIAN */
 | 
					
						
							|  |  |  | 			for (k = 0; k < load_store_size; k++) { | 
					
						
							|  |  |  | 				/* Store a byte. */ | 
					
						
							|  |  |  | 				frag.insn[n++] = | 
					
						
							|  |  |  | 					jit_x0_rotli(rb, rb, 56) | | 
					
						
							|  |  |  | 					jit_x1_st1_add(ra, rb, | 
					
						
							|  |  |  | 						       UA_FIXUP_ADDR_DELTA); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | #ifdef __BIG_ENDIAN
 | 
					
						
							|  |  |  | 			frag.insn[n] = jit_x1_addi(ra, ra, 1); | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 			frag.insn[n] = jit_x1_addi(ra, ra, | 
					
						
							|  |  |  | 						   -1 * load_store_size); | 
					
						
							|  |  |  | #endif /* __LITTLE_ENDIAN */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (load_store_size == 8) { | 
					
						
							|  |  |  | 				frag.insn[n] |= jit_x0_fnop(); | 
					
						
							|  |  |  | 			} else if (load_store_size == 4) { | 
					
						
							|  |  |  | 				frag.insn[n] |= jit_x0_rotli(rb, rb, 32); | 
					
						
							|  |  |  | 			} else { /* = 2 */ | 
					
						
							|  |  |  | 				frag.insn[n] |= jit_x0_rotli(rb, rb, 16); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			n++; | 
					
						
							|  |  |  | 			if (bundle_2_enable) | 
					
						
							|  |  |  | 				frag.insn[n++] = bundle_2; | 
					
						
							|  |  |  | 			frag.insn[n++] = jit_x0_fnop() | jit_x1_iret(); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			if (rd == ra) { | 
					
						
							|  |  |  | 				/* Use two clobber registers: clob1/2. */ | 
					
						
							|  |  |  | 				frag.insn[n++] = | 
					
						
							|  |  |  | 					jit_x0_addi(TREG_SP, TREG_SP, -16) | | 
					
						
							|  |  |  | 					jit_x1_fnop(); | 
					
						
							|  |  |  | 				frag.insn[n++] = | 
					
						
							|  |  |  | 					jit_x0_addi(clob1, ra, 7) | | 
					
						
							|  |  |  | 					jit_x1_st_add(TREG_SP, clob1, -8); | 
					
						
							|  |  |  | 				frag.insn[n++] = | 
					
						
							|  |  |  | 					jit_x0_addi(clob2, ra, 0) | | 
					
						
							|  |  |  | 					jit_x1_st(TREG_SP, clob2); | 
					
						
							|  |  |  | 				frag.insn[n++] = | 
					
						
							|  |  |  | 					jit_x0_fnop() | | 
					
						
							|  |  |  | 					jit_x1_ldna(rd, ra); | 
					
						
							|  |  |  | 				frag.insn[n++] = | 
					
						
							|  |  |  | 					jit_x0_fnop() | | 
					
						
							|  |  |  | 					jit_x1_ldna(clob1, clob1); | 
					
						
							|  |  |  | 				/*
 | 
					
						
							|  |  |  | 				 * Note: we must make sure that rd must not | 
					
						
							|  |  |  | 				 * be sp. Recover clob1/2 from stack. | 
					
						
							|  |  |  | 				 */ | 
					
						
							|  |  |  | 				frag.insn[n++] = | 
					
						
							|  |  |  | 					jit_x0_dblalign(rd, clob1, clob2) | | 
					
						
							|  |  |  | 					jit_x1_ld_add(clob2, TREG_SP, 8); | 
					
						
							|  |  |  | 				frag.insn[n++] = | 
					
						
							|  |  |  | 					jit_x0_fnop() | | 
					
						
							|  |  |  | 					jit_x1_ld_add(clob1, TREG_SP, 16); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				/* Use one clobber register: clob1 only. */ | 
					
						
							|  |  |  | 				frag.insn[n++] = | 
					
						
							|  |  |  | 					jit_x0_addi(TREG_SP, TREG_SP, -16) | | 
					
						
							|  |  |  | 					jit_x1_fnop(); | 
					
						
							|  |  |  | 				frag.insn[n++] = | 
					
						
							|  |  |  | 					jit_x0_addi(clob1, ra, 7) | | 
					
						
							|  |  |  | 					jit_x1_st(TREG_SP, clob1); | 
					
						
							|  |  |  | 				frag.insn[n++] = | 
					
						
							|  |  |  | 					jit_x0_fnop() | | 
					
						
							|  |  |  | 					jit_x1_ldna(rd, ra); | 
					
						
							|  |  |  | 				frag.insn[n++] = | 
					
						
							|  |  |  | 					jit_x0_fnop() | | 
					
						
							|  |  |  | 					jit_x1_ldna(clob1, clob1); | 
					
						
							|  |  |  | 				/*
 | 
					
						
							|  |  |  | 				 * Note: we must make sure that rd must not | 
					
						
							|  |  |  | 				 * be sp. Recover clob1 from stack. | 
					
						
							|  |  |  | 				 */ | 
					
						
							|  |  |  | 				frag.insn[n++] = | 
					
						
							|  |  |  | 					jit_x0_dblalign(rd, clob1, ra) | | 
					
						
							|  |  |  | 					jit_x1_ld_add(clob1, TREG_SP, 16); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (bundle_2_enable) | 
					
						
							|  |  |  | 				frag.insn[n++] = bundle_2; | 
					
						
							|  |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * For non 8-byte load, extract corresponding bytes and | 
					
						
							|  |  |  | 			 * signed extension. | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			if (load_store_size == 4) { | 
					
						
							|  |  |  | 				if (load_store_signed) | 
					
						
							|  |  |  | 					frag.insn[n++] = | 
					
						
							|  |  |  | 						jit_x0_bfexts( | 
					
						
							|  |  |  | 							rd, rd, | 
					
						
							|  |  |  | 							UA_FIXUP_BFEXT_START(4), | 
					
						
							|  |  |  | 							UA_FIXUP_BFEXT_END(4)) | | 
					
						
							|  |  |  | 						jit_x1_fnop(); | 
					
						
							|  |  |  | 				else | 
					
						
							|  |  |  | 					frag.insn[n++] = | 
					
						
							|  |  |  | 						jit_x0_bfextu( | 
					
						
							|  |  |  | 							rd, rd, | 
					
						
							|  |  |  | 							UA_FIXUP_BFEXT_START(4), | 
					
						
							|  |  |  | 							UA_FIXUP_BFEXT_END(4)) | | 
					
						
							|  |  |  | 						jit_x1_fnop(); | 
					
						
							|  |  |  | 			} else if (load_store_size == 2) { | 
					
						
							|  |  |  | 				if (load_store_signed) | 
					
						
							|  |  |  | 					frag.insn[n++] = | 
					
						
							|  |  |  | 						jit_x0_bfexts( | 
					
						
							|  |  |  | 							rd, rd, | 
					
						
							|  |  |  | 							UA_FIXUP_BFEXT_START(2), | 
					
						
							|  |  |  | 							UA_FIXUP_BFEXT_END(2)) | | 
					
						
							|  |  |  | 						jit_x1_fnop(); | 
					
						
							|  |  |  | 				else | 
					
						
							|  |  |  | 					frag.insn[n++] = | 
					
						
							|  |  |  | 						jit_x0_bfextu( | 
					
						
							|  |  |  | 							rd, rd, | 
					
						
							|  |  |  | 							UA_FIXUP_BFEXT_START(2), | 
					
						
							|  |  |  | 							UA_FIXUP_BFEXT_END(2)) | | 
					
						
							|  |  |  | 						jit_x1_fnop(); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_fnop()  | | 
					
						
							|  |  |  | 				jit_x1_iret(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else if (!load_n_store) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Generic memory store cases: use 3 clobber registers. | 
					
						
							|  |  |  | 		 * | 
					
						
							|  |  |  | 		 * Alloc space for saveing clob2,1,3 on user's stack. | 
					
						
							|  |  |  | 		 * register clob3 points to where clob2 saved, followed by | 
					
						
							|  |  |  | 		 * clob1 and 3 from high to low memory. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		frag.insn[n++] = | 
					
						
							|  |  |  | 			jit_x0_addi(TREG_SP, TREG_SP, -32)    | | 
					
						
							|  |  |  | 			jit_x1_fnop(); | 
					
						
							|  |  |  | 		frag.insn[n++] = | 
					
						
							|  |  |  | 			jit_x0_addi(clob3, TREG_SP, 16)  | | 
					
						
							|  |  |  | 			jit_x1_st_add(TREG_SP, clob3, 8); | 
					
						
							|  |  |  | #ifdef __LITTLE_ENDIAN
 | 
					
						
							|  |  |  | 		frag.insn[n++] = | 
					
						
							|  |  |  | 			jit_x0_addi(clob1, ra, 0)   | | 
					
						
							|  |  |  | 			jit_x1_st_add(TREG_SP, clob1, 8); | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 		frag.insn[n++] = | 
					
						
							|  |  |  | 			jit_x0_addi(clob1, ra, load_store_size - 1)   | | 
					
						
							|  |  |  | 			jit_x1_st_add(TREG_SP, clob1, 8); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 		if (load_store_size == 8) { | 
					
						
							|  |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * We save one byte a time, not for fast, but compact | 
					
						
							|  |  |  | 			 * code. After each store, data source register shift | 
					
						
							|  |  |  | 			 * right one byte. unchanged after 8 stores. | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_addi(clob2, TREG_ZERO, 7)     | | 
					
						
							|  |  |  | 				jit_x1_st_add(TREG_SP, clob2, 16); | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_rotli(rb, rb, 56)      | | 
					
						
							|  |  |  | 				jit_x1_st1_add(clob1, rb, UA_FIXUP_ADDR_DELTA); | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_addi(clob2, clob2, -1) | | 
					
						
							|  |  |  | 				jit_x1_bnezt(clob2, -1); | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_fnop()                 | | 
					
						
							|  |  |  | 				jit_x1_addi(clob2, y1_br_reg, 0); | 
					
						
							|  |  |  | 		} else if (load_store_size == 4) { | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_addi(clob2, TREG_ZERO, 3)     | | 
					
						
							|  |  |  | 				jit_x1_st_add(TREG_SP, clob2, 16); | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_rotli(rb, rb, 56)      | | 
					
						
							|  |  |  | 				jit_x1_st1_add(clob1, rb, UA_FIXUP_ADDR_DELTA); | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_addi(clob2, clob2, -1) | | 
					
						
							|  |  |  | 				jit_x1_bnezt(clob2, -1); | 
					
						
							|  |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * same as 8-byte case, but need shift another 4 | 
					
						
							|  |  |  | 			 * byte to recover rb for 4-byte store. | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			frag.insn[n++] = jit_x0_rotli(rb, rb, 32)      | | 
					
						
							|  |  |  | 				jit_x1_addi(clob2, y1_br_reg, 0); | 
					
						
							|  |  |  | 		} else { /* =2 */ | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_addi(clob2, rb, 0)     | | 
					
						
							|  |  |  | 				jit_x1_st_add(TREG_SP, clob2, 16); | 
					
						
							|  |  |  | 			for (k = 0; k < 2; k++) { | 
					
						
							|  |  |  | 				frag.insn[n++] = | 
					
						
							|  |  |  | 					jit_x0_shrui(rb, rb, 8)  | | 
					
						
							|  |  |  | 					jit_x1_st1_add(clob1, rb, | 
					
						
							|  |  |  | 						       UA_FIXUP_ADDR_DELTA); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_addi(rb, clob2, 0)       | | 
					
						
							|  |  |  | 				jit_x1_addi(clob2, y1_br_reg, 0); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (bundle_2_enable) | 
					
						
							|  |  |  | 			frag.insn[n++] = bundle_2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (y1_lr) { | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_fnop()                    | | 
					
						
							|  |  |  | 				jit_x1_mfspr(y1_lr_reg, | 
					
						
							|  |  |  | 					     SPR_EX_CONTEXT_0_0); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (y1_br) { | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_fnop()                    | | 
					
						
							|  |  |  | 				jit_x1_mtspr(SPR_EX_CONTEXT_0_0, | 
					
						
							|  |  |  | 					     clob2); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (x1_add) { | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_addi(ra, ra, x1_add_imm8) | | 
					
						
							|  |  |  | 				jit_x1_ld_add(clob2, clob3, -8); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_fnop()                    | | 
					
						
							|  |  |  | 				jit_x1_ld_add(clob2, clob3, -8); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		frag.insn[n++] = | 
					
						
							|  |  |  | 			jit_x0_fnop()   | | 
					
						
							|  |  |  | 			jit_x1_ld_add(clob1, clob3, -8); | 
					
						
							|  |  |  | 		frag.insn[n++] = jit_x0_fnop()   | jit_x1_ld(clob3, clob3); | 
					
						
							|  |  |  | 		frag.insn[n++] = jit_x0_fnop()   | jit_x1_iret(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Generic memory load cases. | 
					
						
							|  |  |  | 		 * | 
					
						
							|  |  |  | 		 * Alloc space for saveing clob1,2,3 on user's stack. | 
					
						
							|  |  |  | 		 * register clob3 points to where clob1 saved, followed | 
					
						
							|  |  |  | 		 * by clob2 and 3 from high to low memory. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		frag.insn[n++] = | 
					
						
							|  |  |  | 			jit_x0_addi(TREG_SP, TREG_SP, -32) | | 
					
						
							|  |  |  | 			jit_x1_fnop(); | 
					
						
							|  |  |  | 		frag.insn[n++] = | 
					
						
							|  |  |  | 			jit_x0_addi(clob3, TREG_SP, 16) | | 
					
						
							|  |  |  | 			jit_x1_st_add(TREG_SP, clob3, 8); | 
					
						
							|  |  |  | 		frag.insn[n++] = | 
					
						
							|  |  |  | 			jit_x0_addi(clob2, ra, 0) | | 
					
						
							|  |  |  | 			jit_x1_st_add(TREG_SP, clob2, 8); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (y1_br) { | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_addi(clob1, y1_br_reg, 0) | | 
					
						
							|  |  |  | 				jit_x1_st_add(TREG_SP, clob1, 16); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_fnop() | | 
					
						
							|  |  |  | 				jit_x1_st_add(TREG_SP, clob1, 16); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (bundle_2_enable) | 
					
						
							|  |  |  | 			frag.insn[n++] = bundle_2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (y1_lr) { | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_fnop()  | | 
					
						
							|  |  |  | 				jit_x1_mfspr(y1_lr_reg, | 
					
						
							|  |  |  | 					     SPR_EX_CONTEXT_0_0); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (y1_br) { | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_fnop() | | 
					
						
							|  |  |  | 				jit_x1_mtspr(SPR_EX_CONTEXT_0_0, | 
					
						
							|  |  |  | 					     clob1); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		frag.insn[n++] = | 
					
						
							|  |  |  | 			jit_x0_addi(clob1, clob2, 7)      | | 
					
						
							|  |  |  | 			jit_x1_ldna(rd, clob2); | 
					
						
							|  |  |  | 		frag.insn[n++] = | 
					
						
							|  |  |  | 			jit_x0_fnop()                     | | 
					
						
							|  |  |  | 			jit_x1_ldna(clob1, clob1); | 
					
						
							|  |  |  | 		frag.insn[n++] = | 
					
						
							|  |  |  | 			jit_x0_dblalign(rd, clob1, clob2) | | 
					
						
							|  |  |  | 			jit_x1_ld_add(clob1, clob3, -8); | 
					
						
							|  |  |  | 		if (x1_add) { | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_addi(ra, ra, x1_add_imm8) | | 
					
						
							|  |  |  | 				jit_x1_ld_add(clob2, clob3, -8); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			frag.insn[n++] = | 
					
						
							|  |  |  | 				jit_x0_fnop()  | | 
					
						
							|  |  |  | 				jit_x1_ld_add(clob2, clob3, -8); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		frag.insn[n++] = | 
					
						
							|  |  |  | 			jit_x0_fnop() | | 
					
						
							|  |  |  | 			jit_x1_ld(clob3, clob3); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (load_store_size == 4) { | 
					
						
							|  |  |  | 			if (load_store_signed) | 
					
						
							|  |  |  | 				frag.insn[n++] = | 
					
						
							|  |  |  | 					jit_x0_bfexts( | 
					
						
							|  |  |  | 						rd, rd, | 
					
						
							|  |  |  | 						UA_FIXUP_BFEXT_START(4), | 
					
						
							|  |  |  | 						UA_FIXUP_BFEXT_END(4)) | | 
					
						
							|  |  |  | 					jit_x1_fnop(); | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				frag.insn[n++] = | 
					
						
							|  |  |  | 					jit_x0_bfextu( | 
					
						
							|  |  |  | 						rd, rd, | 
					
						
							|  |  |  | 						UA_FIXUP_BFEXT_START(4), | 
					
						
							|  |  |  | 						UA_FIXUP_BFEXT_END(4)) | | 
					
						
							|  |  |  | 					jit_x1_fnop(); | 
					
						
							|  |  |  | 		} else if (load_store_size == 2) { | 
					
						
							|  |  |  | 			if (load_store_signed) | 
					
						
							|  |  |  | 				frag.insn[n++] = | 
					
						
							|  |  |  | 					jit_x0_bfexts( | 
					
						
							|  |  |  | 						rd, rd, | 
					
						
							|  |  |  | 						UA_FIXUP_BFEXT_START(2), | 
					
						
							|  |  |  | 						UA_FIXUP_BFEXT_END(2)) | | 
					
						
							|  |  |  | 					jit_x1_fnop(); | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				frag.insn[n++] = | 
					
						
							|  |  |  | 					jit_x0_bfextu( | 
					
						
							|  |  |  | 						rd, rd, | 
					
						
							|  |  |  | 						UA_FIXUP_BFEXT_START(2), | 
					
						
							|  |  |  | 						UA_FIXUP_BFEXT_END(2)) | | 
					
						
							|  |  |  | 					jit_x1_fnop(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		frag.insn[n++] = jit_x0_fnop() | jit_x1_iret(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Max JIT bundle count is 14. */ | 
					
						
							|  |  |  | 	WARN_ON(n > 14); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!unexpected) { | 
					
						
							|  |  |  | 		int status = 0; | 
					
						
							|  |  |  | 		int idx = (regs->pc >> 3) & | 
					
						
							|  |  |  | 			((1ULL << (PAGE_SHIFT - UNALIGN_JIT_SHIFT)) - 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		frag.pc = regs->pc; | 
					
						
							|  |  |  | 		frag.bundle = bundle; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (unaligned_printk) { | 
					
						
							| 
									
										
										
										
											2014-10-31 10:50:46 -07:00
										 |  |  | 			pr_info("%s/%d, Unalign fixup: pc=%lx bundle=%lx %d %d %d %d %d %d %d %d\n", | 
					
						
							| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  | 				current->comm, current->pid, | 
					
						
							|  |  |  | 				(unsigned long)frag.pc, | 
					
						
							|  |  |  | 				(unsigned long)frag.bundle, | 
					
						
							|  |  |  | 				(int)alias, (int)rd, (int)ra, | 
					
						
							|  |  |  | 				(int)rb, (int)bundle_2_enable, | 
					
						
							|  |  |  | 				(int)y1_lr, (int)y1_br, (int)x1_add); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			for (k = 0; k < n; k += 2) | 
					
						
							| 
									
										
										
										
											2014-10-31 10:50:46 -07:00
										 |  |  | 				pr_info("[%d] %016llx %016llx\n", | 
					
						
							|  |  |  | 					k, (unsigned long long)frag.insn[k], | 
					
						
							| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  | 					(unsigned long long)frag.insn[k+1]); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Swap bundle byte order for big endian sys. */ | 
					
						
							|  |  |  | #ifdef __BIG_ENDIAN
 | 
					
						
							|  |  |  | 		frag.bundle = GX_INSN_BSWAP(frag.bundle); | 
					
						
							|  |  |  | 		for (k = 0; k < n; k++) | 
					
						
							|  |  |  | 			frag.insn[k] = GX_INSN_BSWAP(frag.insn[k]); | 
					
						
							|  |  |  | #endif /* __BIG_ENDIAN */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		status = copy_to_user((void __user *)&jit_code_area[idx], | 
					
						
							|  |  |  | 				      &frag, sizeof(frag)); | 
					
						
							|  |  |  | 		if (status) { | 
					
						
							|  |  |  | 			/* Fail to copy JIT into user land. send SIGSEGV. */ | 
					
						
							|  |  |  | 			siginfo_t info = { | 
					
						
							|  |  |  | 				.si_signo = SIGSEGV, | 
					
						
							|  |  |  | 				.si_code = SEGV_MAPERR, | 
					
						
							|  |  |  | 				.si_addr = (void __user *)&jit_code_area[idx] | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-31 10:50:46 -07:00
										 |  |  | 			pr_warn("Unalign fixup: pid=%d %s jit_code_area=%llx\n", | 
					
						
							| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  | 				current->pid, current->comm, | 
					
						
							|  |  |  | 				(unsigned long long)&jit_code_area[idx]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			trace_unhandled_signal("segfault in unalign fixup", | 
					
						
							|  |  |  | 					       regs, | 
					
						
							|  |  |  | 					       (unsigned long)info.si_addr, | 
					
						
							|  |  |  | 					       SIGSEGV); | 
					
						
							|  |  |  | 			force_sig_info(info.si_signo, &info, current); | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Do a cheaper increment, not accurate. */ | 
					
						
							|  |  |  | 		unaligned_fixup_count++; | 
					
						
							|  |  |  | 		__flush_icache_range((unsigned long)&jit_code_area[idx], | 
					
						
							|  |  |  | 				     (unsigned long)&jit_code_area[idx] + | 
					
						
							|  |  |  | 				     sizeof(frag)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Setup SPR_EX_CONTEXT_0_0/1 for returning to user program.*/ | 
					
						
							|  |  |  | 		__insn_mtspr(SPR_EX_CONTEXT_0_0, regs->pc + 8); | 
					
						
							|  |  |  | 		__insn_mtspr(SPR_EX_CONTEXT_0_1, PL_ICS_EX1(USER_PL, 0)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Modify pc at the start of new JIT. */ | 
					
						
							|  |  |  | 		regs->pc = (unsigned long)&jit_code_area[idx].insn[0]; | 
					
						
							|  |  |  | 		/* Set ICS in SPR_EX_CONTEXT_K_1. */ | 
					
						
							|  |  |  | 		regs->ex1 = PL_ICS_EX1(USER_PL, 1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * C function to generate unalign data JIT. Called from unalign data | 
					
						
							|  |  |  |  * interrupt handler. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * First check if unalign fix is disabled or exception did not not come from | 
					
						
							|  |  |  |  * user space or sp register points to unalign address, if true, generate a | 
					
						
							|  |  |  |  * SIGBUS. Then map a page into user space as JIT area if it is not mapped | 
					
						
							|  |  |  |  * yet. Genenerate JIT code by calling jit_bundle_gen(). After that return | 
					
						
							|  |  |  |  * back to exception handler. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The exception handler will "iret" to new generated JIT code after | 
					
						
							|  |  |  |  * restoring caller saved registers. In theory, the JIT code will perform | 
					
						
							|  |  |  |  * another "iret" to resume user's program. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void do_unaligned(struct pt_regs *regs, int vecnum) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-03-23 14:23:58 -04:00
										 |  |  | 	enum ctx_state prev_state = exception_enter(); | 
					
						
							| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  | 	tilegx_bundle_bits __user  *pc; | 
					
						
							|  |  |  | 	tilegx_bundle_bits bundle; | 
					
						
							|  |  |  | 	struct thread_info *info = current_thread_info(); | 
					
						
							|  |  |  | 	int align_ctl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Checks the per-process unaligned JIT flags */ | 
					
						
							|  |  |  | 	align_ctl = unaligned_fixup; | 
					
						
							|  |  |  | 	switch (task_thread_info(current)->align_ctl) { | 
					
						
							|  |  |  | 	case PR_UNALIGN_NOPRINT: | 
					
						
							|  |  |  | 		align_ctl = 1; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case PR_UNALIGN_SIGBUS: | 
					
						
							|  |  |  | 		align_ctl = 0; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Enable iterrupt in order to access user land. */ | 
					
						
							|  |  |  | 	local_irq_enable(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * The fault came from kernel space. Two choices: | 
					
						
							|  |  |  | 	 * (a) unaligned_fixup < 1, we will first call get/put_user fixup | 
					
						
							|  |  |  | 	 *     to return -EFAULT. If no fixup, simply panic the kernel. | 
					
						
							|  |  |  | 	 * (b) unaligned_fixup >=1, we will try to fix the unaligned access | 
					
						
							|  |  |  | 	 *     if it was triggered by get_user/put_user() macros. Panic the | 
					
						
							|  |  |  | 	 *     kernel if it is not fixable. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (EX1_PL(regs->ex1) != USER_PL) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (align_ctl < 1) { | 
					
						
							|  |  |  | 			unaligned_fixup_count++; | 
					
						
							|  |  |  | 			/* If exception came from kernel, try fix it up. */ | 
					
						
							|  |  |  | 			if (fixup_exception(regs)) { | 
					
						
							|  |  |  | 				if (unaligned_printk) | 
					
						
							| 
									
										
										
										
											2014-10-31 10:50:46 -07:00
										 |  |  | 					pr_info("Unalign fixup: %d %llx @%llx\n", | 
					
						
							| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  | 						(int)unaligned_fixup, | 
					
						
							|  |  |  | 						(unsigned long long)regs->ex1, | 
					
						
							|  |  |  | 						(unsigned long long)regs->pc); | 
					
						
							| 
									
										
										
										
											2015-03-23 14:23:58 -04:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				/* Not fixable. Go panic. */ | 
					
						
							|  |  |  | 				panic("Unalign exception in Kernel. pc=%lx", | 
					
						
							|  |  |  | 				      regs->pc); | 
					
						
							| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * Try to fix the exception. If we can't, panic the | 
					
						
							|  |  |  | 			 * kernel. | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			bundle = GX_INSN_BSWAP( | 
					
						
							|  |  |  | 				*((tilegx_bundle_bits *)(regs->pc))); | 
					
						
							|  |  |  | 			jit_bundle_gen(regs, bundle, align_ctl); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-03-23 14:23:58 -04:00
										 |  |  | 		goto done; | 
					
						
							| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Fault came from user with ICS or stack is not aligned. | 
					
						
							|  |  |  | 	 * If so, we will trigger SIGBUS. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if ((regs->sp & 0x7) || (regs->ex1) || (align_ctl < 0)) { | 
					
						
							|  |  |  | 		siginfo_t info = { | 
					
						
							|  |  |  | 			.si_signo = SIGBUS, | 
					
						
							|  |  |  | 			.si_code = BUS_ADRALN, | 
					
						
							|  |  |  | 			.si_addr = (unsigned char __user *)0 | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (unaligned_printk) | 
					
						
							| 
									
										
										
										
											2014-10-31 10:50:46 -07:00
										 |  |  | 			pr_info("Unalign fixup: %d %llx @%llx\n", | 
					
						
							| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  | 				(int)unaligned_fixup, | 
					
						
							|  |  |  | 				(unsigned long long)regs->ex1, | 
					
						
							|  |  |  | 				(unsigned long long)regs->pc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		unaligned_fixup_count++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		trace_unhandled_signal("unaligned fixup trap", regs, 0, SIGBUS); | 
					
						
							|  |  |  | 		force_sig_info(info.si_signo, &info, current); | 
					
						
							| 
									
										
										
										
											2015-03-23 14:23:58 -04:00
										 |  |  | 		goto done; | 
					
						
							| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Read the bundle casued the exception! */ | 
					
						
							|  |  |  | 	pc = (tilegx_bundle_bits __user *)(regs->pc); | 
					
						
							|  |  |  | 	if (get_user(bundle, pc) != 0) { | 
					
						
							|  |  |  | 		/* Probably never be here since pc is valid user address.*/ | 
					
						
							|  |  |  | 		siginfo_t info = { | 
					
						
							|  |  |  | 			.si_signo = SIGSEGV, | 
					
						
							|  |  |  | 			.si_code = SEGV_MAPERR, | 
					
						
							|  |  |  | 			.si_addr = (void __user *)pc | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		pr_err("Couldn't read instruction at %p trying to step\n", pc); | 
					
						
							|  |  |  | 		trace_unhandled_signal("segfault in unalign fixup", regs, | 
					
						
							|  |  |  | 				       (unsigned long)info.si_addr, SIGSEGV); | 
					
						
							|  |  |  | 		force_sig_info(info.si_signo, &info, current); | 
					
						
							| 
									
										
										
										
											2015-03-23 14:23:58 -04:00
										 |  |  | 		goto done; | 
					
						
							| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!info->unalign_jit_base) { | 
					
						
							|  |  |  | 		void __user *user_page; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Allocate a page in userland. | 
					
						
							|  |  |  | 		 * For 64-bit processes we try to place the mapping far | 
					
						
							|  |  |  | 		 * from anything else that might be going on (specifically | 
					
						
							|  |  |  | 		 * 64 GB below the top of the user address space).  If it | 
					
						
							|  |  |  | 		 * happens not to be possible to put it there, it's OK; | 
					
						
							|  |  |  | 		 * the kernel will choose another location and we'll | 
					
						
							|  |  |  | 		 * remember it for later. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (is_compat_task()) | 
					
						
							|  |  |  | 			user_page = NULL; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			user_page = (void __user *)(TASK_SIZE - (1UL << 36)) + | 
					
						
							|  |  |  | 				(current->pid << PAGE_SHIFT); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		user_page = (void __user *) vm_mmap(NULL, | 
					
						
							|  |  |  | 						    (unsigned long)user_page, | 
					
						
							|  |  |  | 						    PAGE_SIZE, | 
					
						
							|  |  |  | 						    PROT_EXEC | PROT_READ | | 
					
						
							|  |  |  | 						    PROT_WRITE, | 
					
						
							|  |  |  | #ifdef CONFIG_HOMECACHE
 | 
					
						
							|  |  |  | 						    MAP_CACHE_HOME_TASK | | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 						    MAP_PRIVATE | | 
					
						
							|  |  |  | 						    MAP_ANONYMOUS, | 
					
						
							|  |  |  | 						    0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (IS_ERR((void __force *)user_page)) { | 
					
						
							| 
									
										
										
										
											2014-10-31 10:50:46 -07:00
										 |  |  | 			pr_err("Out of kernel pages trying do_mmap\n"); | 
					
						
							| 
									
										
										
										
											2015-03-23 14:23:58 -04:00
										 |  |  | 			goto done; | 
					
						
							| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Save the address in the thread_info struct */ | 
					
						
							|  |  |  | 		info->unalign_jit_base = user_page; | 
					
						
							|  |  |  | 		if (unaligned_printk) | 
					
						
							| 
									
										
										
										
											2014-10-31 10:50:46 -07:00
										 |  |  | 			pr_info("Unalign bundle: %d:%d, allocate page @%llx\n", | 
					
						
							| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  | 				raw_smp_processor_id(), current->pid, | 
					
						
							|  |  |  | 				(unsigned long long)user_page); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Generate unalign JIT */ | 
					
						
							|  |  |  | 	jit_bundle_gen(regs, GX_INSN_BSWAP(bundle), align_ctl); | 
					
						
							| 
									
										
										
										
											2015-03-23 14:23:58 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | done: | 
					
						
							|  |  |  | 	exception_exit(prev_state); | 
					
						
							| 
									
										
										
										
											2013-08-06 16:04:13 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #endif /* __tilegx__ */
 |