Skip to content

Commit d1953aa

Browse files
committed
Merge tag 'x86_alternatives_for_v7.0_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 alternatives update from Borislav Petkov: - Reorganize the alternatives patching mechanism to patch a single location only once instead of multiple times as it was the case with the two or three alternative options macros * tag 'x86_alternatives_for_v7.0_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/alternative: Patch a single alternative location only once x86/alternative: Use helper functions for patching alternatives
2 parents 14c357c + a4233c2 commit d1953aa

1 file changed

Lines changed: 92 additions & 57 deletions

File tree

arch/x86/kernel/alternative.c

Lines changed: 92 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -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)
599680
void __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

Comments
 (0)