diff options
Diffstat (limited to 'arch/arm64/kernel/module.c')
| -rw-r--r-- | arch/arm64/kernel/module.c | 158 | 
1 files changed, 49 insertions, 109 deletions
diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c index ca0e3d55da9..1eb1cc95513 100644 --- a/arch/arm64/kernel/module.c +++ b/arch/arm64/kernel/module.c @@ -25,11 +25,15 @@  #include <linux/mm.h>  #include <linux/moduleloader.h>  #include <linux/vmalloc.h> +#include <asm/insn.h> + +#define	AARCH64_INSN_IMM_MOVNZ		AARCH64_INSN_IMM_MAX +#define	AARCH64_INSN_IMM_MOVK		AARCH64_INSN_IMM_16  void *module_alloc(unsigned long size)  {  	return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END, -				    GFP_KERNEL, PAGE_KERNEL_EXEC, -1, +				    GFP_KERNEL, PAGE_KERNEL_EXEC, NUMA_NO_NODE,  				    __builtin_return_address(0));  } @@ -94,25 +98,18 @@ static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len)  	return 0;  } -enum aarch64_imm_type { -	INSN_IMM_MOVNZ, -	INSN_IMM_MOVK, -	INSN_IMM_ADR, -	INSN_IMM_26, -	INSN_IMM_19, -	INSN_IMM_16, -	INSN_IMM_14, -	INSN_IMM_12, -	INSN_IMM_9, -}; - -static u32 encode_insn_immediate(enum aarch64_imm_type type, u32 insn, u64 imm) +static int reloc_insn_movw(enum aarch64_reloc_op op, void *place, u64 val, +			   int lsb, enum aarch64_insn_imm_type imm_type)  { -	u32 immlo, immhi, lomask, himask, mask; -	int shift; +	u64 imm, limit = 0; +	s64 sval; +	u32 insn = le32_to_cpu(*(u32 *)place); + +	sval = do_reloc(op, place, val); +	sval >>= lsb; +	imm = sval & 0xffff; -	switch (type) { -	case INSN_IMM_MOVNZ: +	if (imm_type == AARCH64_INSN_IMM_MOVNZ) {  		/*  		 * For signed MOVW relocations, we have to manipulate the  		 * instruction encoding depending on whether or not the @@ -131,70 +128,12 @@ static u32 encode_insn_immediate(enum aarch64_imm_type type, u32 insn, u64 imm)  			 */  			imm = ~imm;  		} -	case INSN_IMM_MOVK: -		mask = BIT(16) - 1; -		shift = 5; -		break; -	case INSN_IMM_ADR: -		lomask = 0x3; -		himask = 0x7ffff; -		immlo = imm & lomask; -		imm >>= 2; -		immhi = imm & himask; -		imm = (immlo << 24) | (immhi); -		mask = (lomask << 24) | (himask); -		shift = 5; -		break; -	case INSN_IMM_26: -		mask = BIT(26) - 1; -		shift = 0; -		break; -	case INSN_IMM_19: -		mask = BIT(19) - 1; -		shift = 5; -		break; -	case INSN_IMM_16: -		mask = BIT(16) - 1; -		shift = 5; -		break; -	case INSN_IMM_14: -		mask = BIT(14) - 1; -		shift = 5; -		break; -	case INSN_IMM_12: -		mask = BIT(12) - 1; -		shift = 10; -		break; -	case INSN_IMM_9: -		mask = BIT(9) - 1; -		shift = 12; -		break; -	default: -		pr_err("encode_insn_immediate: unknown immediate encoding %d\n", -			type); -		return 0; +		imm_type = AARCH64_INSN_IMM_MOVK;  	} -	/* Update the immediate field. */ -	insn &= ~(mask << shift); -	insn |= (imm & mask) << shift; - -	return insn; -} - -static int reloc_insn_movw(enum aarch64_reloc_op op, void *place, u64 val, -			   int lsb, enum aarch64_imm_type imm_type) -{ -	u64 imm, limit = 0; -	s64 sval; -	u32 insn = *(u32 *)place; - -	sval = do_reloc(op, place, val); -	sval >>= lsb; -	imm = sval & 0xffff; -  	/* Update the instruction with the new encoding. */ -	*(u32 *)place = encode_insn_immediate(imm_type, insn, imm); +	insn = aarch64_insn_encode_immediate(imm_type, insn, imm); +	*(u32 *)place = cpu_to_le32(insn);  	/* Shift out the immediate field. */  	sval >>= 16; @@ -203,9 +142,9 @@ static int reloc_insn_movw(enum aarch64_reloc_op op, void *place, u64 val,  	 * For unsigned immediates, the overflow check is straightforward.  	 * For signed immediates, the sign bit is actually the bit past the  	 * most significant bit of the field. -	 * The INSN_IMM_16 immediate type is unsigned. +	 * The AARCH64_INSN_IMM_16 immediate type is unsigned.  	 */ -	if (imm_type != INSN_IMM_16) { +	if (imm_type != AARCH64_INSN_IMM_16) {  		sval++;  		limit++;  	} @@ -218,11 +157,11 @@ static int reloc_insn_movw(enum aarch64_reloc_op op, void *place, u64 val,  }  static int reloc_insn_imm(enum aarch64_reloc_op op, void *place, u64 val, -			  int lsb, int len, enum aarch64_imm_type imm_type) +			  int lsb, int len, enum aarch64_insn_imm_type imm_type)  {  	u64 imm, imm_mask;  	s64 sval; -	u32 insn = *(u32 *)place; +	u32 insn = le32_to_cpu(*(u32 *)place);  	/* Calculate the relocation value. */  	sval = do_reloc(op, place, val); @@ -233,7 +172,8 @@ static int reloc_insn_imm(enum aarch64_reloc_op op, void *place, u64 val,  	imm = sval & imm_mask;  	/* Update the instruction's immediate field. */ -	*(u32 *)place = encode_insn_immediate(imm_type, insn, imm); +	insn = aarch64_insn_encode_immediate(imm_type, insn, imm); +	*(u32 *)place = cpu_to_le32(insn);  	/*  	 * Extract the upper value bits (including the sign bit) and @@ -315,125 +255,125 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,  			overflow_check = false;  		case R_AARCH64_MOVW_UABS_G0:  			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0, -					      INSN_IMM_16); +					      AARCH64_INSN_IMM_16);  			break;  		case R_AARCH64_MOVW_UABS_G1_NC:  			overflow_check = false;  		case R_AARCH64_MOVW_UABS_G1:  			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16, -					      INSN_IMM_16); +					      AARCH64_INSN_IMM_16);  			break;  		case R_AARCH64_MOVW_UABS_G2_NC:  			overflow_check = false;  		case R_AARCH64_MOVW_UABS_G2:  			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32, -					      INSN_IMM_16); +					      AARCH64_INSN_IMM_16);  			break;  		case R_AARCH64_MOVW_UABS_G3:  			/* We're using the top bits so we can't overflow. */  			overflow_check = false;  			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 48, -					      INSN_IMM_16); +					      AARCH64_INSN_IMM_16);  			break;  		case R_AARCH64_MOVW_SABS_G0:  			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0, -					      INSN_IMM_MOVNZ); +					      AARCH64_INSN_IMM_MOVNZ);  			break;  		case R_AARCH64_MOVW_SABS_G1:  			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16, -					      INSN_IMM_MOVNZ); +					      AARCH64_INSN_IMM_MOVNZ);  			break;  		case R_AARCH64_MOVW_SABS_G2:  			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32, -					      INSN_IMM_MOVNZ); +					      AARCH64_INSN_IMM_MOVNZ);  			break;  		case R_AARCH64_MOVW_PREL_G0_NC:  			overflow_check = false;  			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0, -					      INSN_IMM_MOVK); +					      AARCH64_INSN_IMM_MOVK);  			break;  		case R_AARCH64_MOVW_PREL_G0:  			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0, -					      INSN_IMM_MOVNZ); +					      AARCH64_INSN_IMM_MOVNZ);  			break;  		case R_AARCH64_MOVW_PREL_G1_NC:  			overflow_check = false;  			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16, -					      INSN_IMM_MOVK); +					      AARCH64_INSN_IMM_MOVK);  			break;  		case R_AARCH64_MOVW_PREL_G1:  			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16, -					      INSN_IMM_MOVNZ); +					      AARCH64_INSN_IMM_MOVNZ);  			break;  		case R_AARCH64_MOVW_PREL_G2_NC:  			overflow_check = false;  			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32, -					      INSN_IMM_MOVK); +					      AARCH64_INSN_IMM_MOVK);  			break;  		case R_AARCH64_MOVW_PREL_G2:  			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32, -					      INSN_IMM_MOVNZ); +					      AARCH64_INSN_IMM_MOVNZ);  			break;  		case R_AARCH64_MOVW_PREL_G3:  			/* We're using the top bits so we can't overflow. */  			overflow_check = false;  			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 48, -					      INSN_IMM_MOVNZ); +					      AARCH64_INSN_IMM_MOVNZ);  			break;  		/* Immediate instruction relocations. */  		case R_AARCH64_LD_PREL_LO19:  			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19, -					     INSN_IMM_19); +					     AARCH64_INSN_IMM_19);  			break;  		case R_AARCH64_ADR_PREL_LO21:  			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 0, 21, -					     INSN_IMM_ADR); +					     AARCH64_INSN_IMM_ADR);  			break;  		case R_AARCH64_ADR_PREL_PG_HI21_NC:  			overflow_check = false;  		case R_AARCH64_ADR_PREL_PG_HI21:  			ovf = reloc_insn_imm(RELOC_OP_PAGE, loc, val, 12, 21, -					     INSN_IMM_ADR); +					     AARCH64_INSN_IMM_ADR);  			break;  		case R_AARCH64_ADD_ABS_LO12_NC:  		case R_AARCH64_LDST8_ABS_LO12_NC:  			overflow_check = false;  			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 0, 12, -					     INSN_IMM_12); +					     AARCH64_INSN_IMM_12);  			break;  		case R_AARCH64_LDST16_ABS_LO12_NC:  			overflow_check = false;  			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 1, 11, -					     INSN_IMM_12); +					     AARCH64_INSN_IMM_12);  			break;  		case R_AARCH64_LDST32_ABS_LO12_NC:  			overflow_check = false;  			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 2, 10, -					     INSN_IMM_12); +					     AARCH64_INSN_IMM_12);  			break;  		case R_AARCH64_LDST64_ABS_LO12_NC:  			overflow_check = false;  			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 3, 9, -					     INSN_IMM_12); +					     AARCH64_INSN_IMM_12);  			break;  		case R_AARCH64_LDST128_ABS_LO12_NC:  			overflow_check = false;  			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 4, 8, -					     INSN_IMM_12); +					     AARCH64_INSN_IMM_12);  			break;  		case R_AARCH64_TSTBR14:  			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 14, -					     INSN_IMM_14); +					     AARCH64_INSN_IMM_14);  			break;  		case R_AARCH64_CONDBR19:  			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19, -					     INSN_IMM_19); +					     AARCH64_INSN_IMM_19);  			break;  		case R_AARCH64_JUMP26:  		case R_AARCH64_CALL26:  			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26, -					     INSN_IMM_26); +					     AARCH64_INSN_IMM_26);  			break;  		default:  | 
