diff options
Diffstat (limited to 'arch/x86/kernel/jump_label.c')
| -rw-r--r-- | arch/x86/kernel/jump_label.c | 108 | 
1 files changed, 101 insertions, 7 deletions
diff --git a/arch/x86/kernel/jump_label.c b/arch/x86/kernel/jump_label.c index 961b6b30ba9..26d5a55a273 100644 --- a/arch/x86/kernel/jump_label.c +++ b/arch/x86/kernel/jump_label.c @@ -24,27 +24,121 @@ union jump_code_union {  	} __attribute__((packed));  }; -void arch_jump_label_transform(struct jump_entry *entry, -			       enum jump_label_type type) +static void bug_at(unsigned char *ip, int line) +{ +	/* +	 * The location is not an op that we were expecting. +	 * Something went wrong. Crash the box, as something could be +	 * corrupting the kernel. +	 */ +	pr_warning("Unexpected op at %pS [%p] (%02x %02x %02x %02x %02x) %s:%d\n", +	       ip, ip, ip[0], ip[1], ip[2], ip[3], ip[4], __FILE__, line); +	BUG(); +} + +static void __jump_label_transform(struct jump_entry *entry, +				   enum jump_label_type type, +				   void *(*poker)(void *, const void *, size_t), +				   int init)  {  	union jump_code_union code; +	const unsigned char default_nop[] = { STATIC_KEY_INIT_NOP }; +	const unsigned char *ideal_nop = ideal_nops[NOP_ATOMIC5];  	if (type == JUMP_LABEL_ENABLE) { +		if (init) { +			/* +			 * Jump label is enabled for the first time. +			 * So we expect a default_nop... +			 */ +			if (unlikely(memcmp((void *)entry->code, default_nop, 5) +				     != 0)) +				bug_at((void *)entry->code, __LINE__); +		} else { +			/* +			 * ...otherwise expect an ideal_nop. Otherwise +			 * something went horribly wrong. +			 */ +			if (unlikely(memcmp((void *)entry->code, ideal_nop, 5) +				     != 0)) +				bug_at((void *)entry->code, __LINE__); +		} +  		code.jump = 0xe9;  		code.offset = entry->target -  				(entry->code + JUMP_LABEL_NOP_SIZE); -	} else -		memcpy(&code, ideal_nop5, JUMP_LABEL_NOP_SIZE); +	} else { +		/* +		 * We are disabling this jump label. If it is not what +		 * we think it is, then something must have gone wrong. +		 * If this is the first initialization call, then we +		 * are converting the default nop to the ideal nop. +		 */ +		if (init) { +			if (unlikely(memcmp((void *)entry->code, default_nop, 5) != 0)) +				bug_at((void *)entry->code, __LINE__); +		} else { +			code.jump = 0xe9; +			code.offset = entry->target - +				(entry->code + JUMP_LABEL_NOP_SIZE); +			if (unlikely(memcmp((void *)entry->code, &code, 5) != 0)) +				bug_at((void *)entry->code, __LINE__); +		} +		memcpy(&code, ideal_nops[NOP_ATOMIC5], JUMP_LABEL_NOP_SIZE); +	} + +	/* +	 * Make text_poke_bp() a default fallback poker. +	 * +	 * At the time the change is being done, just ignore whether we +	 * are doing nop -> jump or jump -> nop transition, and assume +	 * always nop being the 'currently valid' instruction +	 * +	 */ +	if (poker) +		(*poker)((void *)entry->code, &code, JUMP_LABEL_NOP_SIZE); +	else +		text_poke_bp((void *)entry->code, &code, JUMP_LABEL_NOP_SIZE, +			     (void *)entry->code + JUMP_LABEL_NOP_SIZE); +} + +void arch_jump_label_transform(struct jump_entry *entry, +			       enum jump_label_type type) +{  	get_online_cpus();  	mutex_lock(&text_mutex); -	text_poke_smp((void *)entry->code, &code, JUMP_LABEL_NOP_SIZE); +	__jump_label_transform(entry, type, NULL, 0);  	mutex_unlock(&text_mutex);  	put_online_cpus();  } -void arch_jump_label_text_poke_early(jump_label_t addr) +static enum { +	JL_STATE_START, +	JL_STATE_NO_UPDATE, +	JL_STATE_UPDATE, +} jlstate __initdata_or_module = JL_STATE_START; + +__init_or_module void arch_jump_label_transform_static(struct jump_entry *entry, +				      enum jump_label_type type)  { -	text_poke_early((void *)addr, ideal_nop5, JUMP_LABEL_NOP_SIZE); +	/* +	 * This function is called at boot up and when modules are +	 * first loaded. Check if the default nop, the one that is +	 * inserted at compile time, is the ideal nop. If it is, then +	 * we do not need to update the nop, and we can leave it as is. +	 * If it is not, then we need to update the nop to the ideal nop. +	 */ +	if (jlstate == JL_STATE_START) { +		const unsigned char default_nop[] = { STATIC_KEY_INIT_NOP }; +		const unsigned char *ideal_nop = ideal_nops[NOP_ATOMIC5]; + +		if (memcmp(ideal_nop, default_nop, 5) != 0) +			jlstate = JL_STATE_UPDATE; +		else +			jlstate = JL_STATE_NO_UPDATE; +	} +	if (jlstate == JL_STATE_UPDATE) +		__jump_label_transform(entry, type, text_poke_early, 1);  }  #endif  | 
