Re: [PATCH v2 07/42] Enforce VEX encoding restrictions

2022-04-27 Thread Paolo Bonzini

On 4/25/22 00:01, Paul Brook wrote:

+/* If a VEX prefix is used then it must have V=b */
+#define CHECK_AVX_V0(s) do { \
+CHECK_AVX(s); \
+if ((s->prefix & PREFIX_VEX) && (s->vex_v != 0)) \
+goto illegal_op; \
+} while (0)
+


What do you think about

#define CHECK_AVX(s, flags) \
do {
if ((s->prefix & PREFIX_VEX) && !(env->hflags & HF_AVX_EN_MASK)) {
goto illegal_op;
}
if ((flags) & SSE_OPF_AVX2) {
CHECK_AVX2(s);
}
if ((flags) & SSE_OPF_AVX_128) {
CHECK_AVX_128(s);
}
if ((flags) & SSE_OPF_V0) {
CHECK_V0(s);
}
}

Macros such as CHECK_AVX_V0_128(s) would become CHECK_AVX(s, SSE_OPF_V0 
| SSE_OPF_AVX_128); a bit longer but still bearable.  And here you would 
have:



  case 0x210: /* movss xmm, ea */
  if (mod != 3) {
+CHECK_AVX_V0_128(s);
  gen_lea_modrm(env, s, modrm);
  gen_op_ld_v(s, MO_32, s->T0, s->A0);
  tcg_gen_st32_tl(s->T0, cpu_env,
@@ -3379,6 +3432,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, 
int b,
  tcg_gen_st32_tl(s->T0, cpu_env,
  offsetof(CPUX86State, 
xmm_regs[reg].ZMM_L(3)));
  } else {
+CHECK_AVX_128(s);


CHECK_AVX(s, SSE_OPF_AVX_128);
if (mod != 3) {
CHECK_V0(s);
...
} else {
...
}

Another possibility is to add SSE_OPF_V0_MEM (i.e. V0 if mod != 3), and use

CHECK_AVX(s, SSE_OPF_AVX_128 | SSE_OPF_AVX_V0_MEM);


It's okay not to move _all_ flags checks in the macros, but for example 
here:



+if (op6.ext_mask == CPUID_EXT_AVX
+&& (s->prefix & PREFIX_VEX) == 0) {
+goto illegal_op;
+}
+if (op6.flags & SSE_OPF_AVX2) {
+CHECK_AVX2(s);
+}
+
  if (b1) {
+if (op6.flags & SSE_OPF_V0) {
+CHECK_AVX_V0(s);
+} else {
+CHECK_AVX(s);
+}
  op1_offset = offsetof(CPUX86State,xmm_regs[reg]);
+
+if (op6.flags & SSE_OPF_MMX) {
+CHECK_AVX2_256(s);
+}


there is a lot of room for using a flags-extended CHECK_AVX macro.


Also, SSE_OPF_V0 seems overloaded, because it means depending on the 
place in the code:


- always 2-operand

- 2-operand except if SCALAR && !CMP

- 2-operand except if SCALAR && !CMP && has REPZ/REPNZ prefixes

It is not clear to me if the former overlaps with the last (i.e. if 
there are any SCALAR && !CMP operations that are always 2-operand). If 
so, please use different constants for all three; if not, please use a 
different constant for the last, e.g. SSE_OPF_V0 and SSE_OPF_VEC_V0, so 
that the difference is visible in the flags-extended CHECK_AVX macro.


Also related to overloading, here and in patch 37 there is code like this:


+if (op7.flags & SSE_OPF_BLENDV && !(s->prefix & PREFIX_VEX)) {
+/* Only VEX encodings are valid for these blendv opcodes */
+goto illegal_op;
+}


If this is for all SSE_OPF_BLENDV operations, it can be handled in the 
flags-enabled CHECK_AVX() macro above.  If it is only for some, it 
should be a new flag SSE_OPF_VEX_ONLY.


Finally (replying here just to keep things together), patch 29 has "We 
abuse the SSE_OPF_SCALAR flag to select the memory operand width 
appropriately".  Please don't; use a separate function that takes in "b" 
and returns a bool, with just a switch statement in it.



+CHECK_AVX(s);
+scalar_op = (s->prefix & PREFIX_VEX)
+&& (op7.flags & SSE_OPF_SCALAR)
+&& !(op7.flags & SSE_OPF_CMP);
+if (is_xmm && (op7.flags & SSE_OPF_MMX)) {
+CHECK_AVX2_256(s);
+}


I think the is_xmm check is always true here (inside case 0x03a: case 
0x13a:, i.e. b is inside the 0x10..0x5f range)?


Paolo



Re: [PATCH v2 07/42] Enforce VEX encoding restrictions

2022-04-25 Thread Richard Henderson

On 4/24/22 15:01, Paul Brook wrote:

Add CHECK_AVX* macros, and use them to validate VEX encoded AVX instructions

All AVX instructions require both CPU and OS support, this is encapsulated
by HF_AVX_EN.

Some also require specific values in the VEX.L and VEX.V fields.
Some (mostly integer operations) also require AVX2

Signed-off-by: Paul Brook 
---
  target/i386/tcg/translate.c | 159 +---
  1 file changed, 149 insertions(+), 10 deletions(-)

diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c
index 66ba690b7d..2f5cc24e0c 100644
--- a/target/i386/tcg/translate.c
+++ b/target/i386/tcg/translate.c
@@ -3185,10 +3185,54 @@ static const struct SSEOpHelper_table7 
sse_op_table7[256] = {
  goto illegal_op; \
  } while (0)
  
+/*

+ * VEX encodings require AVX
+ * Allow legacy SSE encodings even if AVX not enabled
+ */
+#define CHECK_AVX(s) do { \
+if ((s->prefix & PREFIX_VEX) \
+&& !(env->hflags & HF_AVX_EN_MASK)) \
+goto illegal_op; \
+} while (0)
+
+/* If a VEX prefix is used then it must have V=b */
+#define CHECK_AVX_V0(s) do { \
+CHECK_AVX(s); \
+if ((s->prefix & PREFIX_VEX) && (s->vex_v != 0)) \
+goto illegal_op; \
+} while (0)
+
+/* If a VEX prefix is used then it must have L=0 */
+#define CHECK_AVX_128(s) do { \
+CHECK_AVX(s); \
+if ((s->prefix & PREFIX_VEX) && (s->vex_l != 0)) \
+goto illegal_op; \
+} while (0)
+
+/* If a VEX prefix is used then it must have V=b and L=0 */
+#define CHECK_AVX_V0_128(s) do { \
+CHECK_AVX(s); \
+if ((s->prefix & PREFIX_VEX) && (s->vex_v != 0 || s->vex_l != 0)) \
+goto illegal_op; \
+} while (0)


These predicates have some overlap, but awkwardly.  It leaves you with cases 
like


+if (op6.flags & SSE_OPF_V0) {
+CHECK_AVX_V0(s);
+} else {
+CHECK_AVX(s);
+}


this, where clearly the CHECK_AVX is common across the IF, and would be better 
written as

CHECK_AVX(s);
if (flags & SSE_OPF_V0) {
CHECK_V0(s);
}


+CHECK_AVX(s);
+scalar_op = (s->prefix & PREFIX_VEX)
+&& (op7.flags & SSE_OPF_SCALAR)
+&& !(op7.flags & SSE_OPF_CMP);
+if (is_xmm && (op7.flags & SSE_OPF_MMX)) {
+CHECK_AVX2_256(s);
+}
+if (op7.flags & SSE_OPF_AVX2) {
+CHECK_AVX2(s);
+}
+if ((op7.flags & SSE_OPF_V0) && !scalar_op) {
+CHECK_AVX_V0(s);
+}


And these.  Also, it would appear as if there's overlap between the AVX2 checks.  Is this 
clearer as


CHECK_AVX(s);
if (v0 && !scalar) {
   CHECK_V0(s);
}
if ((flags & AVX2) || ((flags & MMX) && s->vex_l)) {
CHECK_AVX2(s);
}

and perhaps these could be broken out into helpers, so that


  if (is_xmm) {
+scalar_op = (s->prefix & PREFIX_VEX)
+&& (sse_op.flags & SSE_OPF_SCALAR)
+&& !(sse_op.flags & SSE_OPF_CMP)
+&& (b1 == 2 || b1 == 3);
+/* VEX encoded scalar ops always have 3 operands! */
+if ((sse_op.flags & SSE_OPF_V0) && !scalar_op) {
+CHECK_AVX_V0(s);
+} else {
+CHECK_AVX(s);
+}
+if (sse_op.flags & SSE_OPF_MMX) {
+CHECK_AVX2_256(s);
+}


... you don't have to keep repeating stuff.  This is where a better decoder 
could really help.


r~



Re: [PATCH v2 07/42] Enforce VEX encoding restrictions

2022-04-25 Thread Richard Henderson

On 4/24/22 15:01, Paul Brook wrote:

+/*
+ * VEX encodings require AVX
+ * Allow legacy SSE encodings even if AVX not enabled
+ */
+#define CHECK_AVX(s) do { \
+if ((s->prefix & PREFIX_VEX) \
+&& !(env->hflags & HF_AVX_EN_MASK)) \
+goto illegal_op; \
+} while (0)


Likewise, fix coding style.


r~



[PATCH v2 07/42] Enforce VEX encoding restrictions

2022-04-24 Thread Paul Brook
Add CHECK_AVX* macros, and use them to validate VEX encoded AVX instructions

All AVX instructions require both CPU and OS support, this is encapsulated
by HF_AVX_EN.

Some also require specific values in the VEX.L and VEX.V fields.
Some (mostly integer operations) also require AVX2

Signed-off-by: Paul Brook 
---
 target/i386/tcg/translate.c | 159 +---
 1 file changed, 149 insertions(+), 10 deletions(-)

diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c
index 66ba690b7d..2f5cc24e0c 100644
--- a/target/i386/tcg/translate.c
+++ b/target/i386/tcg/translate.c
@@ -3185,10 +3185,54 @@ static const struct SSEOpHelper_table7 
sse_op_table7[256] = {
 goto illegal_op; \
 } while (0)
 
+/*
+ * VEX encodings require AVX
+ * Allow legacy SSE encodings even if AVX not enabled
+ */
+#define CHECK_AVX(s) do { \
+if ((s->prefix & PREFIX_VEX) \
+&& !(env->hflags & HF_AVX_EN_MASK)) \
+goto illegal_op; \
+} while (0)
+
+/* If a VEX prefix is used then it must have V=b */
+#define CHECK_AVX_V0(s) do { \
+CHECK_AVX(s); \
+if ((s->prefix & PREFIX_VEX) && (s->vex_v != 0)) \
+goto illegal_op; \
+} while (0)
+
+/* If a VEX prefix is used then it must have L=0 */
+#define CHECK_AVX_128(s) do { \
+CHECK_AVX(s); \
+if ((s->prefix & PREFIX_VEX) && (s->vex_l != 0)) \
+goto illegal_op; \
+} while (0)
+
+/* If a VEX prefix is used then it must have V=b and L=0 */
+#define CHECK_AVX_V0_128(s) do { \
+CHECK_AVX(s); \
+if ((s->prefix & PREFIX_VEX) && (s->vex_v != 0 || s->vex_l != 0)) \
+goto illegal_op; \
+} while (0)
+
+/* 256-bit (ymm) variants require AVX2 */
+#define CHECK_AVX2_256(s) do { \
+if (s->vex_l && !(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_AVX2)) \
+goto illegal_op; \
+} while (0)
+
+/* Requires AVX2 and VEX encoding */
+#define CHECK_AVX2(s) do { \
+if ((s->prefix & PREFIX_VEX) == 0 \
+|| !(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_AVX2)) \
+goto illegal_op; \
+} while (0)
+
 static void gen_sse(CPUX86State *env, DisasContext *s, int b,
 target_ulong pc_start)
 {
-int b1, op1_offset, op2_offset, is_xmm, val;
+int b1, op1_offset, op2_offset, is_xmm, val, scalar_op;
 int modrm, mod, rm, reg;
 struct SSEOpHelper_table1 sse_op;
 struct SSEOpHelper_table6 op6;
@@ -3228,15 +3272,18 @@ static void gen_sse(CPUX86State *env, DisasContext *s, 
int b,
 gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
 return;
 }
-if (s->flags & HF_EM_MASK) {
-illegal_op:
-gen_illegal_opcode(s);
-return;
-}
-if (is_xmm
-&& !(s->flags & HF_OSFXSR_MASK)
-&& (b != 0x38 && b != 0x3a)) {
-goto unknown_op;
+/* VEX encoded instuctions ignore EM bit. See also CHECK_AVX */
+if (!(s->prefix & PREFIX_VEX)) {
+if (s->flags & HF_EM_MASK) {
+illegal_op:
+gen_illegal_opcode(s);
+return;
+}
+if (is_xmm
+&& !(s->flags & HF_OSFXSR_MASK)
+&& (b != 0x38 && b != 0x3a)) {
+goto unknown_op;
+}
 }
 if (b == 0x0e) {
 if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW)) {
@@ -3278,12 +3325,14 @@ static void gen_sse(CPUX86State *env, DisasContext *s, 
int b,
 case 0x1e7: /* movntdq */
 case 0x02b: /* movntps */
 case 0x12b: /* movntps */
+CHECK_AVX_V0(s);
 if (mod == 3)
 goto illegal_op;
 gen_lea_modrm(env, s, modrm);
 gen_sto_env_A0(s, offsetof(CPUX86State, xmm_regs[reg]));
 break;
 case 0x3f0: /* lddqu */
+CHECK_AVX_V0(s);
 if (mod == 3)
 goto illegal_op;
 gen_lea_modrm(env, s, modrm);
@@ -3291,6 +3340,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, 
int b,
 break;
 case 0x22b: /* movntss */
 case 0x32b: /* movntsd */
+CHECK_AVX_V0_128(s);
 if (mod == 3)
 goto illegal_op;
 gen_lea_modrm(env, s, modrm);
@@ -3321,6 +3371,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, 
int b,
 }
 break;
 case 0x16e: /* movd xmm, ea */
+CHECK_AVX_V0_128(s);
 #ifdef TARGET_X86_64
 if (s->dflag == MO_64) {
 gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 0);
@@ -3356,6 +3407,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, 
int b,
 case 0x128: /* movapd */
 case 0x16f: /* movdqa xmm, ea */
 case 0x26f: /* movdqu xmm, ea */
+CHECK_AVX_V0(s);
 if (mod != 3) {
 gen_lea_modrm(env, s, modrm);
 gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg]));
@@ -3367,6 +3419,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, 
int b,