linux-headers/arch/e2k/include/asm/alternative.h

261 lines
8.0 KiB
C

#ifndef _ASM_E2K_ALTERNATIVE_H
#define _ASM_E2K_ALTERNATIVE_H
#ifndef __ASSEMBLY__
#include <linux/types.h>
struct alt_instr {
s32 instr_offset; /* original instruction */
s32 repl_offset; /* offset to replacement instruction */
u16 instrlen; /* length of original instruction */
u16 replacementlen; /* length of new instruction */
u16 facility; /* facility bit set for replacement */
} __aligned(4);
void apply_alternative_instructions(void);
void apply_alternatives(struct alt_instr *start, struct alt_instr *end);
/*
* An example when first alternative instruction is the biggest,
* and original instruction is the smallest.
*
* Original instruction is padded statically at compile time,
* while alternative instructions are padded if necessary in
* runtime when patching them in.
*
* |661: |662: |663:
* +-----------+---------+-----------------+
* | oldinstr | oldinstr_padding |
* | +---------+-----------------+
* | | ibranch if length >= 576 |
* | | 64-bytes NOPs otherwise |
* +-----------+---------+-----------------+
* ^^^^^^ static padding ^^^^^
*
* .altinstr_replacement section
* +-----------+---------+-----------------+
* |6641: |6651:
* | alternative instr 1 |
* +-----------+---------+- - - - - - - - -+
* |6642: |6652: |
* | alternative instr 2 | padding |
* +-----------+---------+- - - - - - - - -+
* ^runtime padding^
*
*
* 'struct alt_instr' holds details about how and when
* instructions must be replaced:
*
* .altinstructions section
* +----------------------------+
* | alt_instr entries for each |
* | alternative instruction |
* +----------------------------+
*/
#define b_altinstr(num) "664"#num
#define e_altinstr(num) "665"#num
#define e_oldinstr_pad_end "663"
#define oldinstr_len "662b-661b"
#define oldinstr_total_len e_oldinstr_pad_end"b-661b"
#define altinstr_len(num) e_altinstr(num)"b-"b_altinstr(num)"b"
#define oldinstr_pad_len(num) \
"-(((" altinstr_len(num) ")-(" oldinstr_len ")) > 0) * " \
"((" altinstr_len(num) ")-(" oldinstr_len "))"
#define INSTR_LEN_SANITY_CHECK(len) \
".if (" len ") %% 8\n" \
"\t.error \"cpu alternatives instructions length is not divisible by 8\"\n" \
".endif\n"
#define OLDINSTR_PAD_64_BYTES(num, check) \
".if " oldinstr_pad_len(num) " >= " __stringify(check) "\n" \
"\t.fill 1, 4, 0x00000070\n" \
"\t.fill 15, 4, 0\n" \
".endif\n"
#define OLDINSTR_PADDING(oldinstr, num) \
".if " oldinstr_pad_len(num) " >= 576\n" \
"\tibranch " e_oldinstr_pad_end "f\n" \
"6620:\n" \
"\t.fill (" oldinstr_pad_len(num) " - (6620b-662b)) / 8, 8, 0\n" \
".else\n" \
OLDINSTR_PAD_64_BYTES(num, 512) \
OLDINSTR_PAD_64_BYTES(num, 448) \
OLDINSTR_PAD_64_BYTES(num, 384) \
OLDINSTR_PAD_64_BYTES(num, 320) \
OLDINSTR_PAD_64_BYTES(num, 256) \
OLDINSTR_PAD_64_BYTES(num, 192) \
OLDINSTR_PAD_64_BYTES(num, 128) \
OLDINSTR_PAD_64_BYTES(num, 64) \
".if ( " oldinstr_pad_len(num) " %% 64 ) == 56\n" \
"\t.fill 1, 4, 0x00000060\n" \
"\t.fill 13, 4, 0\n" \
".endif\n" \
".if ( " oldinstr_pad_len(num) " %% 64 ) == 48\n" \
"\t.fill 1, 4, 0x00000050\n" \
"\t.fill 11, 4, 0\n" \
".endif\n" \
".if ( " oldinstr_pad_len(num) " %% 64 ) == 40\n" \
"\t.fill 1, 4, 0x00000040\n" \
"\t.fill 9, 4, 0\n" \
".endif\n" \
".if ( " oldinstr_pad_len(num) " %% 64 ) == 32\n" \
"\t.fill 1, 4, 0x00000030\n" \
"\t.fill 7, 4, 0\n" \
".endif\n" \
".if ( " oldinstr_pad_len(num) " %% 64 ) == 24\n" \
"\t.fill 1, 4, 0x00000020\n" \
"\t.fill 5, 4, 0\n" \
".endif\n" \
".if ( " oldinstr_pad_len(num) " %% 64 ) == 16\n" \
"\t.fill 1, 4, 0x00000010\n" \
"\t.fill 3, 4, 0\n" \
".endif\n" \
".if ( " oldinstr_pad_len(num) " %% 64 ) == 8\n" \
"\t.fill 2, 4, 0\n" \
".endif\n" \
".endif\n"
#define OLDINSTR(oldinstr, num) \
"661:\n\t" oldinstr "\n662:\n" \
OLDINSTR_PADDING(oldinstr, num) \
e_oldinstr_pad_end ":\n" \
INSTR_LEN_SANITY_CHECK(oldinstr_len)
#define OLDINSTR_2(oldinstr, num1, num2) \
"661:\n\t" oldinstr "\n662:\n" \
".if " altinstr_len(num1) " < " altinstr_len(num2) "\n" \
OLDINSTR_PADDING(oldinstr, num2) \
".else\n" \
OLDINSTR_PADDING(oldinstr, num1) \
".endif\n" \
e_oldinstr_pad_end ":\n" \
INSTR_LEN_SANITY_CHECK(oldinstr_len)
#define ALTINSTR_ENTRY(facility, num) \
"\t.align 4\n" \
"\t.word 661b - .\n" /* old instruction */ \
"\t.word " b_altinstr(num)"b - .\n" /* alt instruction */ \
"\t.short " oldinstr_total_len "\n" /* source len */ \
"\t.short " altinstr_len(num) "\n" /* alt instruction len */ \
"\t.short " __stringify(facility) "\n" /* facility bit */
#define ALTINSTR_REPLACEMENT(altinstr, num) /* replacement */ \
b_altinstr(num)":\n\t" altinstr "\n" e_altinstr(num) ":\n" \
INSTR_LEN_SANITY_CHECK(altinstr_len(num))
/* alternative assembly primitive: */
#define ALTERNATIVE(oldinstr, altinstr, facility) \
".pushsection .altinstr_replacement, \"ax\"\n" \
ALTINSTR_REPLACEMENT(altinstr, 1) \
".popsection\n" \
OLDINSTR(oldinstr, 1) \
".pushsection .altinstructions,\"a\"\n" \
ALTINSTR_ENTRY(facility, 1) \
".popsection\n"
#define ALTERNATIVE_2(oldinstr, altinstr1, facility1, altinstr2, facility2)\
".pushsection .altinstr_replacement, \"ax\"\n" \
ALTINSTR_REPLACEMENT(altinstr1, 1) \
ALTINSTR_REPLACEMENT(altinstr2, 2) \
".popsection\n" \
OLDINSTR_2(oldinstr, 1, 2) \
".pushsection .altinstructions,\"a\"\n" \
ALTINSTR_ENTRY(facility1, 1) \
ALTINSTR_ENTRY(facility2, 2) \
".popsection\n"
/*
* Alternative instructions for different CPU types or capabilities.
*
* This allows to use optimized instructions even on generic binary
* kernels.
*
* oldinstr is padded with jump and nops at compile time if altinstr is
* longer. altinstr is padded with jump and nops at run-time during patching.
*/
#define alternative(oldinstr, altinstr, facility, clobbers...) \
asm volatile (ALTERNATIVE(oldinstr, altinstr, facility) \
::: clobbers)
#define alternative_2(oldinstr, altinstr1, facility1, altinstr2, facility2) \
asm volatile (ALTERNATIVE_2(oldinstr, altinstr1, facility1, \
altinstr2, facility2) \
::: clobbers)
/*
* How to use:
*
* 1) There is one alternative
*
* asm volatile (
* ALTERNATIVE_1_ALTINSTR
* "< alt. instruction >"
* ALTERNATIVE_2_OLDINSTR
* "< initial instruction >"
* ALTERNATIVE_3_FEATURE(feature)
* )
*
* 2) There are two alternatives
*
* asm volatile (
* ALTERNATIVE_1_ALTINSTR
* "< first alt. instruction >"
* ALTERNATIVE_2_ALTINSTR2
* "< second alt. instruction >"
* ALTERNATIVE_3_OLDINSTR2
* "< initial instruction >"
* ALTERNATIVE_4_FEATURE2(feature1, feature2)
* )
*/
#define ALTERNATIVE_1_ALTINSTR \
".pushsection .altinstr_replacement, \"ax\"\n" \
b_altinstr(1)":\n"
#define ALTERNATIVE_2_OLDINSTR \
"\n" e_altinstr(1) ":\n" \
INSTR_LEN_SANITY_CHECK(altinstr_len(1)) \
".popsection\n" \
"661:\n"
#define ALTERNATIVE_3_FEATURE(facility) \
"\n662:\n" \
OLDINSTR_PADDING(oldinstr, 1) \
e_oldinstr_pad_end ":\n" \
INSTR_LEN_SANITY_CHECK(oldinstr_len) \
".pushsection .altinstructions,\"a\"\n" \
ALTINSTR_ENTRY(facility, 1) \
".popsection\n"
#define ALTERNATIVE_2_ALTINSTR2 \
"\n" e_altinstr(1) ":\n" \
INSTR_LEN_SANITY_CHECK(altinstr_len(1)) \
b_altinstr(2)":\n"
#define ALTERNATIVE_3_OLDINSTR2 \
"\n" e_altinstr(2) ":\n" \
INSTR_LEN_SANITY_CHECK(altinstr_len(2)) \
".popsection\n" \
"661:\n"
#define ALTERNATIVE_4_FEATURE2(facility1, facility2) \
"\n662:\n" \
".if " altinstr_len(1) " < " altinstr_len(2) "\n" \
OLDINSTR_PADDING(oldinstr, 2) \
".else\n" \
OLDINSTR_PADDING(oldinstr, 1) \
".endif\n" \
e_oldinstr_pad_end ":\n" \
INSTR_LEN_SANITY_CHECK(oldinstr_len) \
".pushsection .altinstructions,\"a\"\n" \
ALTINSTR_ENTRY(facility1, 1) \
ALTINSTR_ENTRY(facility2, 2) \
".popsection\n"
#endif /* __ASSEMBLY__ */
#endif /* _ASM_E2K_ALTERNATIVE_H */