Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/insn: Add support for APX EVEX to the instruction decoder logic

Intel Advanced Performance Extensions (APX) extends the EVEX prefix to
support:

- extended general purpose registers (EGPRs) i.e. r16 to r31
- Push-Pop Acceleration (PPX) hints
- new data destination (NDD) register
- suppress status flags writes (NF) of common instructions
- new instructions

Refer to the Intel Advanced Performance Extensions (Intel APX) Architecture
Specification for details.

The extended EVEX prefix does not need amended instruction decoder logic,
except in one area. Some instructions are defined as SCALABLE which means
the EVEX.W bit and EVEX.pp bits are used to determine operand size.
Specifically, if an instruction is SCALABLE and EVEX.W is zero, then
EVEX.pp value 0 (representing no prefix NP) means default operand size,
whereas EVEX.pp value 1 (representing 66 prefix) means operand size
override i.e. 16 bits

Add an attribute (INAT_EVEX_SCALABLE) to identify such instructions, and
amend the logic appropriately.

Amend the awk script that generates the attribute tables from the opcode
map, to recognise "(es)" as attribute INAT_EVEX_SCALABLE.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Link: https://lore.kernel.org/r/20240502105853.5338-8-adrian.hunter@intel.com

authored by

Adrian Hunter and committed by
Ingo Molnar
87bbaf1a 159039af

+42
+6
arch/x86/include/asm/inat.h
··· 81 81 #define INAT_EVEXONLY (1 << (INAT_FLAG_OFFS + 7)) 82 82 #define INAT_NO_REX2 (1 << (INAT_FLAG_OFFS + 8)) 83 83 #define INAT_REX2_VARIANT (1 << (INAT_FLAG_OFFS + 9)) 84 + #define INAT_EVEX_SCALABLE (1 << (INAT_FLAG_OFFS + 10)) 84 85 /* Attribute making macros for attribute tables */ 85 86 #define INAT_MAKE_PREFIX(pfx) (pfx << INAT_PFX_OFFS) 86 87 #define INAT_MAKE_ESCAPE(esc) (esc << INAT_ESC_OFFS) ··· 236 235 static inline int inat_must_evex(insn_attr_t attr) 237 236 { 238 237 return attr & INAT_EVEXONLY; 238 + } 239 + 240 + static inline int inat_evex_scalable(insn_attr_t attr) 241 + { 242 + return attr & INAT_EVEX_SCALABLE; 239 243 } 240 244 #endif
+7
arch/x86/include/asm/insn.h
··· 215 215 return X86_VEX_P(insn->vex_prefix.bytes[2]); 216 216 } 217 217 218 + static inline insn_byte_t insn_vex_w_bit(struct insn *insn) 219 + { 220 + if (insn->vex_prefix.nbytes < 3) 221 + return 0; 222 + return X86_VEX_W(insn->vex_prefix.bytes[2]); 223 + } 224 + 218 225 /* Get the last prefix id from last prefix or VEX prefix */ 219 226 static inline int insn_last_prefix_id(struct insn *insn) 220 227 {
+4
arch/x86/lib/insn.c
··· 294 294 m = insn_vex_m_bits(insn); 295 295 p = insn_vex_p_bits(insn); 296 296 insn->attr = inat_get_avx_attribute(op, m, p); 297 + /* SCALABLE EVEX uses p bits to encode operand size */ 298 + if (inat_evex_scalable(insn->attr) && !insn_vex_w_bit(insn) && 299 + p == INAT_PFX_OPNDSZ) 300 + insn->opnd_bytes = 2; 297 301 if ((inat_must_evex(insn->attr) && !insn_is_evex(insn)) || 298 302 (!inat_accept_vex(insn->attr) && 299 303 !inat_is_group(insn->attr))) {
+4
arch/x86/tools/gen-insn-attr-x86.awk
··· 83 83 vexonly_expr = "\\(v\\)" 84 84 # All opcodes with (ev) superscript supports *only* EVEX prefix 85 85 evexonly_expr = "\\(ev\\)" 86 + # (es) is the same as (ev) but also "SCALABLE" i.e. W and pp determine operand size 87 + evex_scalable_expr = "\\(es\\)" 86 88 87 89 prefix_expr = "\\(Prefix\\)" 88 90 prefix_num["Operand-Size"] = "INAT_PFX_OPNDSZ" ··· 334 332 # check VEX codes 335 333 if (match(ext, evexonly_expr)) 336 334 flags = add_flags(flags, "INAT_VEXOK | INAT_EVEXONLY") 335 + else if (match(ext, evex_scalable_expr)) 336 + flags = add_flags(flags, "INAT_VEXOK | INAT_EVEXONLY | INAT_EVEX_SCALABLE") 337 337 else if (match(ext, vexonly_expr)) 338 338 flags = add_flags(flags, "INAT_VEXOK | INAT_VEXONLY") 339 339 else if (match(ext, vexok_expr) || match(opcode, vexok_opcode_expr))
+6
tools/arch/x86/include/asm/inat.h
··· 81 81 #define INAT_EVEXONLY (1 << (INAT_FLAG_OFFS + 7)) 82 82 #define INAT_NO_REX2 (1 << (INAT_FLAG_OFFS + 8)) 83 83 #define INAT_REX2_VARIANT (1 << (INAT_FLAG_OFFS + 9)) 84 + #define INAT_EVEX_SCALABLE (1 << (INAT_FLAG_OFFS + 10)) 84 85 /* Attribute making macros for attribute tables */ 85 86 #define INAT_MAKE_PREFIX(pfx) (pfx << INAT_PFX_OFFS) 86 87 #define INAT_MAKE_ESCAPE(esc) (esc << INAT_ESC_OFFS) ··· 236 235 static inline int inat_must_evex(insn_attr_t attr) 237 236 { 238 237 return attr & INAT_EVEXONLY; 238 + } 239 + 240 + static inline int inat_evex_scalable(insn_attr_t attr) 241 + { 242 + return attr & INAT_EVEX_SCALABLE; 239 243 } 240 244 #endif
+7
tools/arch/x86/include/asm/insn.h
··· 215 215 return X86_VEX_P(insn->vex_prefix.bytes[2]); 216 216 } 217 217 218 + static inline insn_byte_t insn_vex_w_bit(struct insn *insn) 219 + { 220 + if (insn->vex_prefix.nbytes < 3) 221 + return 0; 222 + return X86_VEX_W(insn->vex_prefix.bytes[2]); 223 + } 224 + 218 225 /* Get the last prefix id from last prefix or VEX prefix */ 219 226 static inline int insn_last_prefix_id(struct insn *insn) 220 227 {
+4
tools/arch/x86/lib/insn.c
··· 294 294 m = insn_vex_m_bits(insn); 295 295 p = insn_vex_p_bits(insn); 296 296 insn->attr = inat_get_avx_attribute(op, m, p); 297 + /* SCALABLE EVEX uses p bits to encode operand size */ 298 + if (inat_evex_scalable(insn->attr) && !insn_vex_w_bit(insn) && 299 + p == INAT_PFX_OPNDSZ) 300 + insn->opnd_bytes = 2; 297 301 if ((inat_must_evex(insn->attr) && !insn_is_evex(insn)) || 298 302 (!inat_accept_vex(insn->attr) && 299 303 !inat_is_group(insn->attr))) {
+4
tools/arch/x86/tools/gen-insn-attr-x86.awk
··· 83 83 vexonly_expr = "\\(v\\)" 84 84 # All opcodes with (ev) superscript supports *only* EVEX prefix 85 85 evexonly_expr = "\\(ev\\)" 86 + # (es) is the same as (ev) but also "SCALABLE" i.e. W and pp determine operand size 87 + evex_scalable_expr = "\\(es\\)" 86 88 87 89 prefix_expr = "\\(Prefix\\)" 88 90 prefix_num["Operand-Size"] = "INAT_PFX_OPNDSZ" ··· 334 332 # check VEX codes 335 333 if (match(ext, evexonly_expr)) 336 334 flags = add_flags(flags, "INAT_VEXOK | INAT_EVEXONLY") 335 + else if (match(ext, evex_scalable_expr)) 336 + flags = add_flags(flags, "INAT_VEXOK | INAT_EVEXONLY | INAT_EVEX_SCALABLE") 337 337 else if (match(ext, vexonly_expr)) 338 338 flags = add_flags(flags, "INAT_VEXOK | INAT_VEXONLY") 339 339 else if (match(ext, vexok_expr) || match(opcode, vexok_opcode_expr))