@@ -124,6 +124,20 @@ const unsigned char * const x86_nops[ASM_NOP_MAX+1] =
124124#endif
125125};
126126
127+ /*
128+ * Nomenclature for variable names to simplify and clarify this code and ease
129+ * any potential staring at it:
130+ *
131+ * @instr: source address of the original instructions in the kernel text as
132+ * generated by the compiler.
133+ *
134+ * @buf: temporary buffer on which the patching operates. This buffer is
135+ * eventually text-poked into the kernel image.
136+ *
137+ * @replacement/@repl: pointer to the opcodes which are replacing @instr, located
138+ * in the .altinstr_replacement section.
139+ */
140+
127141/*
128142 * Fill the buffer with a single effective instruction of size @len.
129143 *
@@ -133,28 +147,28 @@ const unsigned char * const x86_nops[ASM_NOP_MAX+1] =
133147 * each single-byte NOPs). If @len to fill out is > ASM_NOP_MAX, pad with INT3 and
134148 * *jump* over instead of executing long and daft NOPs.
135149 */
136- static void add_nop (u8 * instr , unsigned int len )
150+ static void add_nop (u8 * buf , unsigned int len )
137151{
138- u8 * target = instr + len ;
152+ u8 * target = buf + len ;
139153
140154 if (!len )
141155 return ;
142156
143157 if (len <= ASM_NOP_MAX ) {
144- memcpy (instr , x86_nops [len ], len );
158+ memcpy (buf , x86_nops [len ], len );
145159 return ;
146160 }
147161
148162 if (len < 128 ) {
149- __text_gen_insn (instr , JMP8_INSN_OPCODE , instr , target , JMP8_INSN_SIZE );
150- instr += JMP8_INSN_SIZE ;
163+ __text_gen_insn (buf , JMP8_INSN_OPCODE , buf , target , JMP8_INSN_SIZE );
164+ buf += JMP8_INSN_SIZE ;
151165 } else {
152- __text_gen_insn (instr , JMP32_INSN_OPCODE , instr , target , JMP32_INSN_SIZE );
153- instr += JMP32_INSN_SIZE ;
166+ __text_gen_insn (buf , JMP32_INSN_OPCODE , buf , target , JMP32_INSN_SIZE );
167+ buf += JMP32_INSN_SIZE ;
154168 }
155169
156- for (;instr < target ; instr ++ )
157- * instr = INT3_INSN_OPCODE ;
170+ for (;buf < target ; buf ++ )
171+ * buf = INT3_INSN_OPCODE ;
158172}
159173
160174extern s32 __retpoline_sites [], __retpoline_sites_end [];
@@ -187,12 +201,12 @@ static bool insn_is_nop(struct insn *insn)
187201 * Find the offset of the first non-NOP instruction starting at @offset
188202 * but no further than @len.
189203 */
190- static int skip_nops (u8 * instr , int offset , int len )
204+ static int skip_nops (u8 * buf , int offset , int len )
191205{
192206 struct insn insn ;
193207
194208 for (; offset < len ; offset += insn .length ) {
195- if (insn_decode_kernel (& insn , & instr [offset ]))
209+ if (insn_decode_kernel (& insn , & buf [offset ]))
196210 break ;
197211
198212 if (!insn_is_nop (& insn ))
@@ -202,67 +216,33 @@ static int skip_nops(u8 *instr, int offset, int len)
202216 return offset ;
203217}
204218
205- /*
206- * Optimize a sequence of NOPs, possibly preceded by an unconditional jump
207- * to the end of the NOP sequence into a single NOP.
208- */
209- static bool
210- __optimize_nops (u8 * instr , size_t len , struct insn * insn , int * next , int * prev , int * target )
211- {
212- int i = * next - insn -> length ;
213-
214- switch (insn -> opcode .bytes [0 ]) {
215- case JMP8_INSN_OPCODE :
216- case JMP32_INSN_OPCODE :
217- * prev = i ;
218- * target = * next + insn -> immediate .value ;
219- return false;
220- }
221-
222- if (insn_is_nop (insn )) {
223- int nop = i ;
224-
225- * next = skip_nops (instr , * next , len );
226- if (* target && * next == * target )
227- nop = * prev ;
228-
229- add_nop (instr + nop , * next - nop );
230- DUMP_BYTES (ALT , instr , len , "%px: [%d:%d) optimized NOPs: " , instr , nop , * next );
231- return true;
232- }
233-
234- * target = 0 ;
235- return false;
236- }
237-
238219/*
239220 * "noinline" to cause control flow change and thus invalidate I$ and
240221 * cause refetch after modification.
241222 */
242- static void __init_or_module noinline optimize_nops (u8 * instr , size_t len )
223+ static void noinline optimize_nops (const u8 * const instr , u8 * buf , size_t len )
243224{
244- int prev , target = 0 ;
245-
246225 for (int next , i = 0 ; i < len ; i = next ) {
247226 struct insn insn ;
248227
249- if (insn_decode_kernel (& insn , & instr [i ]))
228+ if (insn_decode_kernel (& insn , & buf [i ]))
250229 return ;
251230
252231 next = i + insn .length ;
253232
254- __optimize_nops (instr , len , & insn , & next , & prev , & target );
255- }
256- }
233+ if (insn_is_nop (& insn )) {
234+ int nop = i ;
257235
258- static void __init_or_module noinline optimize_nops_inplace ( u8 * instr , size_t len )
259- {
260- unsigned long flags ;
236+ /* Has the NOP already been optimized? */
237+ if ( i + insn . length == len )
238+ return ;
261239
262- local_irq_save (flags );
263- optimize_nops (instr , len );
264- sync_core ();
265- local_irq_restore (flags );
240+ next = skip_nops (buf , next , len );
241+
242+ add_nop (buf + nop , next - nop );
243+ DUMP_BYTES (ALT , buf , len , "%px: [%d:%d) optimized NOPs: " , instr , nop , next );
244+ }
245+ }
266246}
267247
268248/*
@@ -335,21 +315,16 @@ bool need_reloc(unsigned long offset, u8 *src, size_t src_len)
335315 return (target < src || target > src + src_len );
336316}
337317
338- void apply_relocation (u8 * buf , size_t len , u8 * dest , u8 * src , size_t src_len )
318+ static void __apply_relocation (u8 * buf , const u8 * const instr , size_t instrlen , u8 * repl , size_t repl_len )
339319{
340- int prev , target = 0 ;
341-
342- for (int next , i = 0 ; i < len ; i = next ) {
320+ for (int next , i = 0 ; i < instrlen ; i = next ) {
343321 struct insn insn ;
344322
345323 if (WARN_ON_ONCE (insn_decode_kernel (& insn , & buf [i ])))
346324 return ;
347325
348326 next = i + insn .length ;
349327
350- if (__optimize_nops (buf , len , & insn , & next , & prev , & target ))
351- continue ;
352-
353328 switch (insn .opcode .bytes [0 ]) {
354329 case 0x0f :
355330 if (insn .opcode .bytes [1 ] < 0x80 ||
@@ -361,18 +336,18 @@ void apply_relocation(u8 *buf, size_t len, u8 *dest, u8 *src, size_t src_len)
361336 case JMP8_INSN_OPCODE :
362337 case JMP32_INSN_OPCODE :
363338 case CALL_INSN_OPCODE :
364- if (need_reloc (next + insn .immediate .value , src , src_len )) {
339+ if (need_reloc (next + insn .immediate .value , repl , repl_len )) {
365340 apply_reloc (insn .immediate .nbytes ,
366341 buf + i + insn_offset_immediate (& insn ),
367- src - dest );
342+ repl - instr );
368343 }
369344
370345 /*
371346 * Where possible, convert JMP.d32 into JMP.d8.
372347 */
373348 if (insn .opcode .bytes [0 ] == JMP32_INSN_OPCODE ) {
374349 s32 imm = insn .immediate .value ;
375- imm += src - dest ;
350+ imm += repl - instr ;
376351 imm += JMP32_INSN_SIZE - JMP8_INSN_SIZE ;
377352 if ((imm >> 31 ) == (imm >> 7 )) {
378353 buf [i + 0 ] = JMP8_INSN_OPCODE ;
@@ -385,15 +360,21 @@ void apply_relocation(u8 *buf, size_t len, u8 *dest, u8 *src, size_t src_len)
385360 }
386361
387362 if (insn_rip_relative (& insn )) {
388- if (need_reloc (next + insn .displacement .value , src , src_len )) {
363+ if (need_reloc (next + insn .displacement .value , repl , repl_len )) {
389364 apply_reloc (insn .displacement .nbytes ,
390365 buf + i + insn_offset_displacement (& insn ),
391- src - dest );
366+ repl - instr );
392367 }
393368 }
394369 }
395370}
396371
372+ void apply_relocation (u8 * buf , const u8 * const instr , size_t instrlen , u8 * repl , size_t repl_len )
373+ {
374+ __apply_relocation (buf , instr , instrlen , repl , repl_len );
375+ optimize_nops (instr , buf , repl_len );
376+ }
377+
397378/* Low-level backend functions usable from alternative code replacements. */
398379DEFINE_ASM_FUNC (nop_func , "" , .entry .text );
399380EXPORT_SYMBOL_GPL (nop_func );
@@ -464,9 +445,9 @@ static int alt_replace_call(u8 *instr, u8 *insn_buff, struct alt_instr *a)
464445void __init_or_module noinline apply_alternatives (struct alt_instr * start ,
465446 struct alt_instr * end )
466447{
467- struct alt_instr * a ;
468- u8 * instr , * replacement ;
469448 u8 insn_buff [MAX_PATCH_LEN ];
449+ u8 * instr , * replacement ;
450+ struct alt_instr * a ;
470451
471452 DPRINTK (ALT , "alt table %px, -> %px" , start , end );
472453
@@ -504,7 +485,9 @@ void __init_or_module noinline apply_alternatives(struct alt_instr *start,
504485 * patch if feature is *NOT* present.
505486 */
506487 if (!boot_cpu_has (a -> cpuid ) == !(a -> flags & ALT_FLAG_NOT )) {
507- optimize_nops_inplace (instr , a -> instrlen );
488+ memcpy (insn_buff , instr , a -> instrlen );
489+ optimize_nops (instr , insn_buff , a -> instrlen );
490+ text_poke_early (instr , insn_buff , a -> instrlen );
508491 continue ;
509492 }
510493
@@ -526,7 +509,7 @@ void __init_or_module noinline apply_alternatives(struct alt_instr *start,
526509 for (; insn_buff_sz < a -> instrlen ; insn_buff_sz ++ )
527510 insn_buff [insn_buff_sz ] = 0x90 ;
528511
529- apply_relocation (insn_buff , a -> instrlen , instr , replacement , a -> replacementlen );
512+ apply_relocation (insn_buff , instr , a -> instrlen , replacement , a -> replacementlen );
530513
531514 DUMP_BYTES (ALT , instr , a -> instrlen , "%px: old_insn: " , instr );
532515 DUMP_BYTES (ALT , replacement , a -> replacementlen , "%px: rpl_insn: " , replacement );
@@ -761,7 +744,7 @@ void __init_or_module noinline apply_retpolines(s32 *start, s32 *end)
761744
762745 len = patch_retpoline (addr , & insn , bytes );
763746 if (len == insn .length ) {
764- optimize_nops (bytes , len );
747+ optimize_nops (addr , bytes , len );
765748 DUMP_BYTES (RETPOLINE , ((u8 * )addr ), len , "%px: orig: " , addr );
766749 DUMP_BYTES (RETPOLINE , ((u8 * )bytes ), len , "%px: repl: " , addr );
767750 text_poke_early (addr , bytes , len );
0 commit comments