@@ -586,6 +586,87 @@ static inline u8 * instr_va(struct alt_instr *i)
586586 return (u8 * )& i -> instr_offset + i -> instr_offset ;
587587}
588588
589+ struct patch_site {
590+ u8 * instr ;
591+ struct alt_instr * alt ;
592+ u8 buff [MAX_PATCH_LEN ];
593+ u8 len ;
594+ };
595+
596+ static struct alt_instr * __init_or_module analyze_patch_site (struct patch_site * ps ,
597+ struct alt_instr * start ,
598+ struct alt_instr * end )
599+ {
600+ struct alt_instr * alt = start ;
601+
602+ ps -> instr = instr_va (start );
603+
604+ /*
605+ * In case of nested ALTERNATIVE()s the outer alternative might add
606+ * more padding. To ensure consistent patching find the max padding for
607+ * all alt_instr entries for this site (nested alternatives result in
608+ * consecutive entries).
609+ * Find the last alt_instr eligible for patching at the site.
610+ */
611+ for (; alt < end && instr_va (alt ) == ps -> instr ; alt ++ ) {
612+ ps -> len = max (ps -> len , alt -> instrlen );
613+
614+ BUG_ON (alt -> cpuid >= (NCAPINTS + NBUGINTS ) * 32 );
615+ /*
616+ * Patch if either:
617+ * - feature is present
618+ * - feature not present but ALT_FLAG_NOT is set to mean,
619+ * patch if feature is *NOT* present.
620+ */
621+ if (!boot_cpu_has (alt -> cpuid ) != !(alt -> flags & ALT_FLAG_NOT ))
622+ ps -> alt = alt ;
623+ }
624+
625+ BUG_ON (ps -> len > sizeof (ps -> buff ));
626+
627+ return alt ;
628+ }
629+
630+ static void __init_or_module prep_patch_site (struct patch_site * ps )
631+ {
632+ struct alt_instr * alt = ps -> alt ;
633+ u8 buff_sz ;
634+ u8 * repl ;
635+
636+ if (!alt ) {
637+ /* Nothing to patch, use original instruction. */
638+ memcpy (ps -> buff , ps -> instr , ps -> len );
639+ return ;
640+ }
641+
642+ repl = (u8 * )& alt -> repl_offset + alt -> repl_offset ;
643+ DPRINTK (ALT , "feat: %d*32+%d, old: (%pS (%px) len: %d), repl: (%px, len: %d) flags: 0x%x" ,
644+ alt -> cpuid >> 5 , alt -> cpuid & 0x1f ,
645+ ps -> instr , ps -> instr , ps -> len ,
646+ repl , alt -> replacementlen , alt -> flags );
647+
648+ memcpy (ps -> buff , repl , alt -> replacementlen );
649+ buff_sz = alt -> replacementlen ;
650+
651+ if (alt -> flags & ALT_FLAG_DIRECT_CALL )
652+ buff_sz = alt_replace_call (ps -> instr , ps -> buff , alt );
653+
654+ for (; buff_sz < ps -> len ; buff_sz ++ )
655+ ps -> buff [buff_sz ] = 0x90 ;
656+
657+ __apply_relocation (ps -> buff , ps -> instr , ps -> len , repl , alt -> replacementlen );
658+
659+ DUMP_BYTES (ALT , ps -> instr , ps -> len , "%px: old_insn: " , ps -> instr );
660+ DUMP_BYTES (ALT , repl , alt -> replacementlen , "%px: rpl_insn: " , repl );
661+ DUMP_BYTES (ALT , ps -> buff , ps -> len , "%px: final_insn: " , ps -> instr );
662+ }
663+
664+ static void __init_or_module patch_site (struct patch_site * ps )
665+ {
666+ optimize_nops (ps -> instr , ps -> buff , ps -> len );
667+ text_poke_early (ps -> instr , ps -> buff , ps -> len );
668+ }
669+
589670/*
590671 * Replace instructions with better alternatives for this CPU type. This runs
591672 * before SMP is initialized to avoid SMP problems with self modifying code.
@@ -599,9 +680,7 @@ static inline u8 * instr_va(struct alt_instr *i)
599680void __init_or_module noinline apply_alternatives (struct alt_instr * start ,
600681 struct alt_instr * end )
601682{
602- u8 insn_buff [MAX_PATCH_LEN ];
603- u8 * instr , * replacement ;
604- struct alt_instr * a , * b ;
683+ struct alt_instr * a ;
605684
606685 DPRINTK (ALT , "alt table %px, -> %px" , start , end );
607686
@@ -624,60 +703,16 @@ void __init_or_module noinline apply_alternatives(struct alt_instr *start,
624703 * So be careful if you want to change the scan order to any other
625704 * order.
626705 */
627- for (a = start ; a < end ; a ++ ) {
628- unsigned int insn_buff_sz = 0 ;
629-
630- /*
631- * In case of nested ALTERNATIVE()s the outer alternative might
632- * add more padding. To ensure consistent patching find the max
633- * padding for all alt_instr entries for this site (nested
634- * alternatives result in consecutive entries).
635- */
636- for (b = a + 1 ; b < end && instr_va (b ) == instr_va (a ); b ++ ) {
637- u8 len = max (a -> instrlen , b -> instrlen );
638- a -> instrlen = b -> instrlen = len ;
639- }
640-
641- instr = instr_va (a );
642- replacement = (u8 * )& a -> repl_offset + a -> repl_offset ;
643- BUG_ON (a -> instrlen > sizeof (insn_buff ));
644- BUG_ON (a -> cpuid >= (NCAPINTS + NBUGINTS ) * 32 );
645-
646- /*
647- * Patch if either:
648- * - feature is present
649- * - feature not present but ALT_FLAG_NOT is set to mean,
650- * patch if feature is *NOT* present.
651- */
652- if (!boot_cpu_has (a -> cpuid ) == !(a -> flags & ALT_FLAG_NOT )) {
653- memcpy (insn_buff , instr , a -> instrlen );
654- optimize_nops (instr , insn_buff , a -> instrlen );
655- text_poke_early (instr , insn_buff , a -> instrlen );
656- continue ;
657- }
658-
659- DPRINTK (ALT , "feat: %d*32+%d, old: (%pS (%px) len: %d), repl: (%px, len: %d) flags: 0x%x" ,
660- a -> cpuid >> 5 ,
661- a -> cpuid & 0x1f ,
662- instr , instr , a -> instrlen ,
663- replacement , a -> replacementlen , a -> flags );
664-
665- memcpy (insn_buff , replacement , a -> replacementlen );
666- insn_buff_sz = a -> replacementlen ;
667-
668- if (a -> flags & ALT_FLAG_DIRECT_CALL )
669- insn_buff_sz = alt_replace_call (instr , insn_buff , a );
670-
671- for (; insn_buff_sz < a -> instrlen ; insn_buff_sz ++ )
672- insn_buff [insn_buff_sz ] = 0x90 ;
673-
674- text_poke_apply_relocation (insn_buff , instr , a -> instrlen , replacement , a -> replacementlen );
675-
676- DUMP_BYTES (ALT , instr , a -> instrlen , "%px: old_insn: " , instr );
677- DUMP_BYTES (ALT , replacement , a -> replacementlen , "%px: rpl_insn: " , replacement );
678- DUMP_BYTES (ALT , insn_buff , insn_buff_sz , "%px: final_insn: " , instr );
679-
680- text_poke_early (instr , insn_buff , insn_buff_sz );
706+ a = start ;
707+ while (a < end ) {
708+ struct patch_site ps = {
709+ .alt = NULL ,
710+ .len = 0
711+ };
712+
713+ a = analyze_patch_site (& ps , a , end );
714+ prep_patch_site (& ps );
715+ patch_site (& ps );
681716 }
682717
683718 kasan_enable_current ();
0 commit comments