x86/alternatives: Add nested alternatives macros
Instead of making increasingly complicated ALTERNATIVE_n()
implementations, use a nested alternative expression.
The only difference between:
ALTERNATIVE_2(oldinst, newinst1, flag1, newinst2, flag2)
and
ALTERNATIVE(ALTERNATIVE(oldinst, newinst1, flag1),
newinst2, flag2)
is that the outer alternative can add additional padding when the inner
alternative is the shorter one, which then results in
alt_instr::instrlen being inconsistent.
However, this is easily remedied since the alt_instr entries will be
consecutive and it is trivial to compute the max(alt_instr::instrlen) at
runtime while patching.
Specifically, after this the ALTERNATIVE_2 macro, after CPP expansion
(and manual layout), looks like this:
.macro ALTERNATIVE_2 oldinstr, newinstr1, ft_flags1, newinstr2, ft_flags2
740:
740: \oldinstr ;
741: .skip -(((744f-743f)-(741b-740b)) > 0) * ((744f-743f)-(741b-740b)),0x90 ;
742: .pushsection .altinstructions,"a" ;
altinstr_entry 740b,743f,\ft_flags1,742b-740b,744f-743f ;
.popsection ;
.pushsection .altinstr_replacement,"ax" ;
743: \newinstr1 ;
744: .popsection ; ;
741: .skip -(((744f-743f)-(741b-740b)) > 0) * ((744f-743f)-(741b-740b)),0x90 ;
742: .pushsection .altinstructions,"a" ;
altinstr_entry 740b,743f,\ft_flags2,742b-740b,744f-743f ;
.popsection ;
.pushsection .altinstr_replacement,"ax" ;
743: \newinstr2 ;
744: .popsection ;
.endm
The only label that is ambiguous is 740, however they all reference the
same spot, so that doesn't matter.
NOTE: obviously only @oldinstr may be an alternative; making @newinstr
an alternative would mean patching .altinstr_replacement which very
likely isn't what is intended, also the labels will be confused in that
case.
[ bp: Debug an issue where it would match the wrong two insns and
and consider them nested due to the same signed offsets in the
.alternative section and use instr_va() to compare the full virtual
addresses instead.
- Use new labels to denote that the new, nested
alternatives are being used when staring at preprocessed output.
- Use the %c constraint everywhere instead of %P and document the
difference for future reference. ]
Signed-off-by: Peter Zijlstra <peterz@infradead.org>
Co-developed-by: Borislav Petkov (AMD) <bp@alien8.de>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/20230628104952.GA2439977@hirez.programming.kicks-ass.net
This commit is contained in:
committed by
Borislav Petkov (AMD)
parent
9cbf2643b3
commit
d2a793dae2
@@ -9,6 +9,29 @@
|
||||
|
||||
void arch_handle_alternative(unsigned short feature, struct special_alt *alt)
|
||||
{
|
||||
static struct special_alt *group, *prev;
|
||||
|
||||
/*
|
||||
* Recompute orig_len for nested ALTERNATIVE()s.
|
||||
*/
|
||||
if (group && group->orig_sec == alt->orig_sec &&
|
||||
group->orig_off == alt->orig_off) {
|
||||
|
||||
struct special_alt *iter = group;
|
||||
for (;;) {
|
||||
unsigned int len = max(iter->orig_len, alt->orig_len);
|
||||
iter->orig_len = alt->orig_len = len;
|
||||
|
||||
if (iter == prev)
|
||||
break;
|
||||
|
||||
iter = list_next_entry(iter, list);
|
||||
}
|
||||
|
||||
} else group = alt;
|
||||
|
||||
prev = alt;
|
||||
|
||||
switch (feature) {
|
||||
case X86_FEATURE_SMAP:
|
||||
/*
|
||||
|
||||
@@ -84,6 +84,14 @@ static int get_alt_entry(struct elf *elf, const struct special_entry *entry,
|
||||
entry->new_len);
|
||||
}
|
||||
|
||||
orig_reloc = find_reloc_by_dest(elf, sec, offset + entry->orig);
|
||||
if (!orig_reloc) {
|
||||
WARN_FUNC("can't find orig reloc", sec, offset + entry->orig);
|
||||
return -1;
|
||||
}
|
||||
|
||||
reloc_to_sec_off(orig_reloc, &alt->orig_sec, &alt->orig_off);
|
||||
|
||||
if (entry->feature) {
|
||||
unsigned short feature;
|
||||
|
||||
@@ -94,14 +102,6 @@ static int get_alt_entry(struct elf *elf, const struct special_entry *entry,
|
||||
arch_handle_alternative(feature, alt);
|
||||
}
|
||||
|
||||
orig_reloc = find_reloc_by_dest(elf, sec, offset + entry->orig);
|
||||
if (!orig_reloc) {
|
||||
WARN_FUNC("can't find orig reloc", sec, offset + entry->orig);
|
||||
return -1;
|
||||
}
|
||||
|
||||
reloc_to_sec_off(orig_reloc, &alt->orig_sec, &alt->orig_off);
|
||||
|
||||
if (!entry->group || alt->new_len) {
|
||||
new_reloc = find_reloc_by_dest(elf, sec, offset + entry->new);
|
||||
if (!new_reloc) {
|
||||
|
||||
Reference in New Issue
Block a user