|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 5/7] x86/alt: Support for automatic padding calculations
The correct amount of padding in an origin patch site can be calculated
automatically, based on the relative lengths of the replacements.
This requires a bit of trickery to calculate correctly, especially in the
ALTENRATIVE_2 case where a branchless max() calculation in needed. The
calculation is further complicated because GAS's idea of true is -1 rather
than 1, which is why the extra negations are required.
Additionally, have apply_alternatives() attempt to optimise the padding nops.
Signed-off-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
---
CC: Jan Beulich <JBeulich@xxxxxxxx>
CC: Konrad Rzeszutek Wilk <konrad.wilk@xxxxxxxxxx>
CC: Roger Pau Monné <roger.pau@xxxxxxxxxx>
CC: Wei Liu <wei.liu2@xxxxxxxxxx>
---
xen/arch/x86/alternative.c | 32 ++++++++++++++++++++++++----
xen/include/asm-x86/alternative-asm.h | 40 +++++++++++++++++++++++++++--------
xen/include/asm-x86/alternative.h | 39 ++++++++++++++++++++++++++--------
3 files changed, 89 insertions(+), 22 deletions(-)
diff --git a/xen/arch/x86/alternative.c b/xen/arch/x86/alternative.c
index f8ddab5..ec87ff4 100644
--- a/xen/arch/x86/alternative.c
+++ b/xen/arch/x86/alternative.c
@@ -180,13 +180,37 @@ void init_or_livepatch apply_alternatives(const struct
alt_instr *start,
uint8_t *orig = ALT_ORIG_PTR(a);
uint8_t *repl = ALT_REPL_PTR(a);
uint8_t buf[MAX_PATCH_LEN];
+ unsigned int total_len = a->orig_len + a->pad_len;
- BUG_ON(a->repl_len > a->orig_len);
- BUG_ON(a->orig_len > sizeof(buf));
+ BUG_ON(a->repl_len > total_len);
+ BUG_ON(total_len > sizeof(buf));
BUG_ON(a->cpuid >= NCAPINTS * 32);
if ( !boot_cpu_has(a->cpuid) )
+ {
+ unsigned int i;
+
+ /* No replacement to make, but try to optimise any padding. */
+ if ( a->pad_len <= 1 )
+ continue;
+
+ /* Search the padding area for any byte which isn't a nop. */
+ for ( i = a->orig_len; i < total_len; ++i )
+ if ( orig[i] != 0x90 )
+ break;
+
+ /*
+ * Only make any changes if all padding bytes are unoptimised
+ * nops. With multiple alternatives over the same origin site, we
+ * may have already made a replacement, or optimised the nops.
+ */
+ if ( i != total_len )
+ continue;
+
+ add_nops(buf, a->pad_len);
+ text_poke(orig + a->orig_len, buf, a->pad_len);
continue;
+ }
memcpy(buf, repl, a->repl_len);
@@ -194,8 +218,8 @@ void init_or_livepatch apply_alternatives(const struct
alt_instr *start,
if ( a->repl_len >= 5 && (*buf & 0xfe) == 0xe8 )
*(s32 *)(buf + 1) += repl - orig;
- add_nops(buf + a->repl_len, a->orig_len - a->repl_len);
- text_poke(orig, buf, a->orig_len);
+ add_nops(buf + a->repl_len, total_len - a->repl_len);
+ text_poke(orig, buf, total_len);
}
}
diff --git a/xen/include/asm-x86/alternative-asm.h
b/xen/include/asm-x86/alternative-asm.h
index 150bd1a..f7e37cb 100644
--- a/xen/include/asm-x86/alternative-asm.h
+++ b/xen/include/asm-x86/alternative-asm.h
@@ -9,30 +9,41 @@
* enough information for the alternatives patching code to patch an
* instruction. See apply_alternatives().
*/
-.macro altinstruction_entry orig repl feature orig_len repl_len
+.macro altinstruction_entry orig repl feature orig_len repl_len pad_len
.long \orig - .
.long \repl - .
.word \feature
.byte \orig_len
.byte \repl_len
+ .byte \pad_len
.endm
#define orig_len (.L\@_orig_e - .L\@_orig_s)
+#define pad_len (.L\@_orig_p - .L\@_orig_e)
+#define total_len (.L\@_orig_p - .L\@_orig_s)
#define repl_len(nr) (.L\@_repl_e\()nr - .L\@_repl_s\()nr)
#define decl_repl(insn, nr) .L\@_repl_s\()nr: insn; .L\@_repl_e\()nr:
+#define gas_max(a, b) ((a) ^ (((a) ^ (b)) & -(-((a) < (b)))))
.macro ALTERNATIVE oldinstr, newinstr, feature
.L\@_orig_s:
\oldinstr
.L\@_orig_e:
+ .skip (-((repl_len(1) - orig_len) > 0) * (repl_len(1) - orig_len)), 0x90
+.L\@_orig_p:
.pushsection .altinstructions, "a", @progbits
altinstruction_entry .L\@_orig_s, .L\@_repl_s1, \feature, \
- orig_len, repl_len(1)
+ orig_len, repl_len(1), pad_len
.section .discard, "a", @progbits
- /* Assembler-time check that \newinstr isn't longer than \oldinstr. */
- .byte 0xff + repl_len(1) - orig_len
+ /*
+ * Assembler-time checks:
+ * - total_len <= 255
+ * - \newinstr <= total_len
+ */
+ .byte total_len
+ .byte 0xff + repl_len(1) - total_len
.section .altinstr_replacement, "ax", @progbits
@@ -45,18 +56,26 @@
.L\@_orig_s:
\oldinstr
.L\@_orig_e:
+ .skip (-((gas_max(repl_len(1), repl_len(2)) - orig_len) > 0) * \
+ (gas_max(repl_len(1), repl_len(2)) - orig_len)), 0x90
+.L\@_orig_p:
.pushsection .altinstructions, "a", @progbits
altinstruction_entry .L\@_orig_s, .L\@_repl_s1, \feature1, \
- orig_len, repl_len(1)
+ orig_len, repl_len(1), pad_len
altinstruction_entry .L\@_orig_s, .L\@_repl_s2, \feature2, \
- orig_len, repl_len(2)
+ orig_len, repl_len(2), pad_len
.section .discard, "a", @progbits
- /* Assembler-time check that \newinstr{1,2} aren't longer than \oldinstr.
*/
- .byte 0xff + repl_len(1) - orig_len
- .byte 0xff + repl_len(2) - orig_len
+ /*
+ * Assembler-time checks:
+ * - total_len <= 255
+ * - \newinstr* <= total_len
+ */
+ .byte total_len
+ .byte 0xff + repl_len(1) - total_len
+ .byte 0xff + repl_len(2) - total_len
.section .altinstr_replacement, "ax", @progbits
@@ -66,8 +85,11 @@
.popsection
.endm
+#undef gas_max
#undef decl_repl
#undef repl_len
+#undef total_len
+#undef pad_len
#undef orig_len
#endif /* __ASSEMBLY__ */
diff --git a/xen/include/asm-x86/alternative.h
b/xen/include/asm-x86/alternative.h
index 1e5cfbd..20dea22 100644
--- a/xen/include/asm-x86/alternative.h
+++ b/xen/include/asm-x86/alternative.h
@@ -8,12 +8,13 @@
#include <xen/stringify.h>
#include <xen/types.h>
-struct alt_instr {
+struct __packed alt_instr {
int32_t orig_offset; /* original instruction */
int32_t repl_offset; /* offset to replacement instruction */
uint16_t cpuid; /* cpuid bit set for replacement */
uint8_t orig_len; /* length of original instruction */
- uint8_t repl_len; /* length of new instruction, <= instrlen */
+ uint8_t repl_len; /* length of new instruction */
+ uint8_t pad_len; /* length of build-time padding */
};
#define __ALT_PTR(a,f) ((u8 *)((void *)&(a)->f + (a)->f))
@@ -26,44 +27,64 @@ extern void apply_alternatives(const struct alt_instr
*start,
const struct alt_instr *end);
extern void alternative_instructions(void);
-#define OLDINSTR(oldinstr) ".L%=_orig_s:\n\t" oldinstr "\n.L%=_orig_e:\n"
-
#define repl_s(num) ".L%=_repl_s"#num
#define repl_e(num) ".L%=_repl_e"#num
#define alt_orig_len "(.L%=_orig_e - .L%=_orig_s)"
+#define alt_pad_len "(.L%=_orig_p - .L%=_orig_e)"
+#define alt_total_len "(.L%=_orig_p - .L%=_orig_s)"
#define alt_repl_len(num) "(" repl_e(num) " - " repl_s(num) ")"
+#define gas_max(a, b) \
+ "((" a ") ^ (((" a ") ^ (" b ")) & -(-((" a ") < (" b ")))))"
+
+#define OLDINSTR_1(oldinstr, n1) \
+ ".L%=_orig_s:\n\t" oldinstr "\n .L%=_orig_e:\n\t" \
+ ".skip (-(("alt_repl_len(n1)"-"alt_orig_len") > 0) * " \
+ "("alt_repl_len(n1)"-"alt_orig_len")), 0x90\n\t" \
+ ".L%=_orig_p:\n\t"
+
+#define ALT_PADDING_LEN(n1, n2) \
+ gas_max((alt_repl_len(n1), alt_repl_len(n2))"-"alt_orig_len
+
+#define OLDINSTR_2(oldinstr, n1, n2) \
+ ".L%=_orig_s:\n\t" oldinstr "\n .L%=_orig_e:\n\t" \
+ ".skip (-(("ALT_PADDING_LEN(n1, n2)") > 0) * " \
+ "("ALT_PADDING_LEN(n1, n2)")), 0x90\n\t" \
+ ".L%=_orig_p:\n\t"
#define ALTINSTR_ENTRY(feature, num) \
" .long .L%=_orig_s - .\n" /* label */ \
" .long " repl_s(num)" - .\n" /* new instruction */ \
" .word " __stringify(feature) "\n" /* feature bit */ \
" .byte " alt_orig_len "\n" /* source len */ \
- " .byte " alt_repl_len(num) "\n" /* replacement len */
+ " .byte " alt_repl_len(num) "\n" /* replacement len */ \
+ " .byte " alt_pad_len "\n" /* padding len */
-#define DISCARD_ENTRY(num) /* repl <= orig */ \
- " .byte 0xff + (" alt_repl_len(num) ") - (" alt_orig_len ")\n"
+#define DISCARD_ENTRY(num) /* repl <= total */ \
+ " .byte 0xff + (" alt_repl_len(num) ") - (" alt_total_len ")\n"
#define ALTINSTR_REPLACEMENT(newinstr, num) /* replacement */ \
repl_s(num)":\n\t" newinstr "\n" repl_e(num) ":\n\t"
/* alternative assembly primitive: */
#define ALTERNATIVE(oldinstr, newinstr, feature) \
- OLDINSTR(oldinstr) \
+ OLDINSTR_1(oldinstr, 1) \
".pushsection .altinstructions, \"a\", @progbits\n" \
ALTINSTR_ENTRY(feature, 1) \
".section .discard, \"a\", @progbits\n" \
+ ".byte " alt_total_len "\n" /* total_len <= 255 */ \
DISCARD_ENTRY(1) \
".section .altinstr_replacement, \"ax\", @progbits\n" \
ALTINSTR_REPLACEMENT(newinstr, 1) \
".popsection\n"
#define ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2) \
- OLDINSTR(oldinstr) \
+ OLDINSTR_2(oldinstr, 1, 2) \
".pushsection .altinstructions, \"a\", @progbits\n" \
ALTINSTR_ENTRY(feature1, 1) \
ALTINSTR_ENTRY(feature2, 2) \
".section .discard, \"a\", @progbits\n" \
+ ".byte " alt_total_len "\n" /* total_len <= 255 */ \
DISCARD_ENTRY(1) \
DISCARD_ENTRY(2) \
".section .altinstr_replacement, \"ax\", @progbits\n" \
--
2.1.4
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |