aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2026-05-08 01:20:25 +0000
committeriximeow <me@iximeow.net>2026-05-08 01:20:25 +0000
commit6f10e4663a04fcb86ac9d5b09dbc47ffb9b22151 (patch)
tree48ff9f289709af61b2c7d6d2d25a2eaa0dfe7aa6 /test
parent2378c1361c729da9f9cbd982142837b3d164106c (diff)
vex support done, starting on evex..
Diffstat (limited to 'test')
-rw-r--r--test/long_mode/behavior.rs292
1 files changed, 274 insertions, 18 deletions
diff --git a/test/long_mode/behavior.rs b/test/long_mode/behavior.rs
index 1a7c144..18894d2 100644
--- a/test/long_mode/behavior.rs
+++ b/test/long_mode/behavior.rs
@@ -1460,7 +1460,7 @@ mod kvm {
}
#[test]
- fn behavior_verify_kvm_0f_38_() {
+ fn behavior_verify_kvm_avx() {
use yaxpeax_arch::{Decoder, U8Reader};
use yaxpeax_x86::long_mode::Instruction;
@@ -1472,27 +1472,122 @@ mod kvm {
let mut buf = Instruction::default();
let initial_regs = vm.get_regs().unwrap();
- for word in 0x0000..u16::MAX {
- let inst = word.to_le_bytes();
- let bytes = [0x0f, 0x38, inst[0], inst[1]];
- let mut reader = U8Reader::new(&bytes);
- if decoder.decode_into(&mut buf, &mut reader).is_ok() {
- // two byte instructions were covered by `verify_kvm`, novel instructions are three
- // bytes (or longer..?)
- use yaxpeax_arch::LengthedInstruction;
- let inst_len = 0.wrapping_offset(buf.len()) as usize;
- if inst_len != 4 {
- continue;
- }
+ for opc in 0xf0..=255 {
+ for prefix in [0x00, 0x66, 0xf2, 0xf3] {
+ for map in 0..3 {
+ for operands in [0x01, 0xc1] {
+ let mut len = 0;
+ let mut bytes = [0; 8];
- if not_generic(&buf) {
- continue;
+ if prefix != 0x00 {
+ bytes[len] = prefix;
+ len += 1;
+ }
+
+ bytes[len] = 0x0f;
+ len += 1;
+
+ if map == 1 {
+ bytes[len] = 0x38;
+ len += 1;
+ } else if map == 2 {
+ bytes[len] = 0x3a;
+ len += 1;
+ }
+
+ bytes[len] = opc;
+ len += 1;
+
+ bytes[len] = operands;
+ len += 1;
+
+ let bytes = &bytes[..len];
+ let mut reader = U8Reader::new(&bytes);
+ if decoder.decode_into(&mut buf, &mut reader).is_ok() {
+ use yaxpeax_arch::LengthedInstruction;
+ let inst_len = 0.wrapping_offset(buf.len()) as usize;
+ if inst_len != bytes.len() {
+ continue;
+ }
+
+ if not_generic(&buf) {
+ continue;
+ }
+
+ vm.set_regs(&initial_regs).unwrap();
+ check_behavior(&mut vm, &bytes).expect("behavior check is ok");
+ }
+ }
}
+ }
+ }
+ }
- eprintln!("checking behavior of 0f 38 {:02x} {:02x}: {}", inst[0], inst[1], buf);
+ #[test]
+ fn behavior_verify_kvm_avx_imm8() {
+ use yaxpeax_arch::{Decoder, U8Reader};
+ use yaxpeax_x86::long_mode::Instruction;
- vm.set_regs(&initial_regs).unwrap();
- check_behavior(&mut vm, &bytes[..inst_len]).expect("behavior check is ok");
+ let mut vm = create_test_vm();
+ vm.set_single_step(true).expect("can enable single-step");
+
+ // TODO: happen to be testing on a zen 5 system, so i picked a zen 5 decoder.
+ let decoder = long_mode::uarch::amd::zen5();
+ let mut buf = Instruction::default();
+ let initial_regs = vm.get_regs().unwrap();
+
+ for opc in 0x10..=255 {
+ for prefix in [0x00, 0x66, 0xf2, 0xf3] {
+ for map in 0..3 {
+ for imm in [0u8, 1u8, 2u8, 4u8, 8u8, 16u8, 32u8, 64u8, 128u8, 255u8] {
+ for operands in [0x01, 0xc1] {
+ let mut len = 0;
+ let mut bytes = [0; 8];
+
+ if prefix != 0x00 {
+ bytes[len] = prefix;
+ len += 1;
+ }
+
+ bytes[len] = 0x0f;
+ len += 1;
+
+ if map == 1 {
+ bytes[len] = 0x38;
+ len += 1;
+ } else if map == 2 {
+ bytes[len] = 0x3a;
+ len += 1;
+ }
+
+ bytes[len] = opc;
+ len += 1;
+
+ bytes[len] = operands;
+ len += 1;
+
+ bytes[len] = imm;
+ len += 1;
+
+ let bytes = &bytes[..len];
+ let mut reader = U8Reader::new(&bytes);
+ if decoder.decode_into(&mut buf, &mut reader).is_ok() {
+ use yaxpeax_arch::LengthedInstruction;
+ let inst_len = 0.wrapping_offset(buf.len()) as usize;
+ if inst_len != bytes.len() {
+ continue;
+ }
+
+ if not_generic(&buf) {
+ continue;
+ }
+
+ vm.set_regs(&initial_regs).unwrap();
+ check_behavior(&mut vm, &bytes).expect("behavior check is ok");
+ }
+ }
+ }
+ }
}
}
}
@@ -1555,6 +1650,147 @@ mod kvm {
}
}
+ #[test]
+ fn behavior_verify_kvm_vex_imm8() {
+ use yaxpeax_arch::{Decoder, U8Reader};
+ use yaxpeax_x86::long_mode::Instruction;
+
+ let mut vm = create_test_vm();
+ vm.set_single_step(true).expect("can enable single-step");
+
+ // TODO: happen to be testing on a zen 5 system, so i picked a zen 5 decoder.
+ let decoder = long_mode::uarch::amd::zen5();
+ let mut buf = Instruction::default();
+ let initial_regs = vm.get_regs().unwrap();
+
+ #[allow(non_snake_case)]
+ for opcode in 0xc2..=u8::MAX {
+ for prefix_bits in 0x00..0x400u16 {
+ for imm in [0u8, 1u8, 2u8, 4u8, 8u8, 16u8, 32u8, 64u8, 128u8, 255u8] {
+ let mmmmm = prefix_bits & 0b11111;
+ let prefix_1 = (0xe0 | mmmmm) as u8;
+
+ let pp = (prefix_bits >> 5) & 0b11;
+ let W = (prefix_bits >> 7) & 1;
+ let L = (prefix_bits >> 8) & 1;
+ let prefix_2 = (0x78 | (W << 7) | (L << 2) | pp) as u8;
+
+ let operands = (prefix_bits >> 9) & 0b1;
+ static OPC_BYTE_TABLE: [u8; 2] = [0xc1, 0x01];
+
+ let bytes: [u8; 6] = [0xc4, prefix_1, prefix_2, opcode, OPC_BYTE_TABLE[operands as usize], imm];
+ let mut reader = U8Reader::new(&bytes);
+ if decoder.decode_into(&mut buf, &mut reader).is_ok() {
+ // two byte instructions were covered by `verify_kvm`, novel instructions are three
+ // bytes (or longer..?)
+ use yaxpeax_arch::LengthedInstruction;
+ let inst_len = 0.wrapping_offset(buf.len()) as usize;
+ if inst_len != bytes.len() {
+ continue;
+ }
+
+ if not_generic(&buf) {
+ continue;
+ }
+
+ vm.set_regs(&initial_regs).unwrap();
+ let res = check_behavior(&mut vm, &bytes[..inst_len]);
+ match res {
+ Ok(()) => {}
+ Err(CheckErr::ComplexOp(op)) => {
+ // uncheckable but not a failure
+ }
+ Err(e) => {
+ panic!("check error: {:?}", e);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ #[test]
+ fn behavior_verify_kvm_evex() {
+ use yaxpeax_arch::{Decoder, U8Reader};
+ use yaxpeax_x86::long_mode::Instruction;
+
+ let mut vm = create_test_vm();
+ vm.set_single_step(true).expect("can enable single-step");
+
+ // TODO: happen to be testing on a zen 5 system, so i picked a zen 5 decoder.
+ let decoder = long_mode::uarch::amd::zen5()
+// .with_avx512(); // TODO: need to refine isa_settings.rs/revise_instruction().
+ .with_avx512_f()
+ .with_avx512_dq()
+ .with_avx512_fma()
+ .with_avx512_cd()
+ .with_avx512_bw()
+ .with_avx512_vl()
+ .with_avx512_vbmi()
+ .with_avx512_vbmi2()
+ .with_avx512_vnni()
+ .with_avx512_bitalg()
+ .with_avx512_vpopcntdq();
+ /*
+ .with_avx512_vpopcntdq() // TODO: VP2INTERSECT
+ .with_avx512_bf16() // TODO: BF16
+ */
+ let mut buf = Instruction::default();
+ let initial_regs = vm.get_regs().unwrap();
+
+ #[allow(non_snake_case)]
+ for opcode in 0x7d..=u8::MAX {
+ for prefix_bits in 0x00..0x800u16 {
+ let mmm = prefix_bits & 0b111;
+ let prefix_1 = (0xf0 | mmm) as u8;
+
+ let pp = (prefix_bits >> 3) & 0b11;
+ let z = (prefix_bits >> 5) & 1;
+ let b = (prefix_bits >> 6) & 1;
+ let W = (prefix_bits >> 7) & 1;
+ let LL = (prefix_bits >> 8) & 0b11;
+ let k = (prefix_bits >> 10) & 1 != 0;
+
+ let prefix_2 = (0x7c | (W << 7) | pp) as u8;
+
+ let aaa = if k { 0b001 } else { 0b111 };
+ let prefix_3 = (0x08 | aaa | b << 4 | LL << 5 | z << 7) as u8;
+
+ let operands = (prefix_bits >> 9) & 0b1;
+ static OPC_BYTE_TABLE: [u8; 2] = [0xc1, 0x01];
+
+ let bytes: [u8; 6] = [0x62, prefix_1, prefix_2, prefix_3, opcode, OPC_BYTE_TABLE[operands as usize]];
+ let mut reader = U8Reader::new(&bytes);
+ if decoder.decode_into(&mut buf, &mut reader).is_ok() {
+ // two byte instructions were covered by `verify_kvm`, novel instructions are three
+ // bytes (or longer..?)
+ use yaxpeax_arch::LengthedInstruction;
+ let inst_len = 0.wrapping_offset(buf.len()) as usize;
+ if inst_len != bytes.len() {
+ continue;
+ }
+
+ if not_generic(&buf) {
+ continue;
+ }
+
+ vm.set_regs(&initial_regs).unwrap();
+ let res = check_behavior(&mut vm, &bytes[..inst_len]);
+ match res {
+ Ok(()) => {}
+ Err(CheckErr::ComplexOp(op)) => {
+ // uncheckable but not a failure
+ }
+ Err(e) => {
+ panic!("check error: {:?}", e);
+ }
+ }
+ }
+ }
+ }
+ }
+
// use the generic test harness for a handful of instructions that don't get covered in the
// general enumeration above
#[test]
@@ -1626,6 +1862,10 @@ mod kvm {
return true;
}
+ if enqcmd::OPCODES.contains(&instr.opcode()) {
+ return true;
+ }
+
static COMPLEX: &'static [Opcode] = &[
Opcode::SYSCALL,
Opcode::SYSRET,
@@ -1634,12 +1874,18 @@ mod kvm {
Opcode::PREFETCH2,
Opcode::PREFETCH1,
Opcode::PREFETCH0,
+ Opcode::MOVDIR64B,
];
if COMPLEX.contains(&instr.opcode()) {
return true;
}
+ if instr.opcode() == Opcode::INVPCID {
+ // this #UDs in the VM? is it because i'm not setting invpcid in cpuid..
+ return true;
+ }
+
if instr.opcode() == Opcode::RDPID {
// rdpid is a specialized rdmsr
return true;
@@ -1921,6 +2167,16 @@ mod kvm {
}
+ mod enqcmd {
+ use yaxpeax_x86::long_mode::Opcode;
+
+ pub static OPCODES: &'static [Opcode] = &[
+ Opcode::ENQCMD,
+ Opcode::ENQCMDS,
+ ];
+
+ }
+
// instructions related to operating VT-x/SVM virtual machines.
// TODO: these are not (yet) tested.
mod vm_instrs {