diff options
| author | iximeow <me@iximeow.net> | 2026-05-11 02:01:16 +0000 |
|---|---|---|
| committer | iximeow <me@iximeow.net> | 2026-05-11 02:01:16 +0000 |
| commit | 6f0426bc30ae65aac2f08cbf90aafa823f4554bc (patch) | |
| tree | b05fd42a25efc45b923108154e5150ecf9578d96 /test/long_mode | |
| parent | 5d9f17ddd807bc9917adbb498d734d5f95554909 (diff) | |
the rest of x86-64 behaviors. so far.
Diffstat (limited to 'test/long_mode')
| -rw-r--r-- | test/long_mode/behavior.rs | 282 |
1 files changed, 188 insertions, 94 deletions
diff --git a/test/long_mode/behavior.rs b/test/long_mode/behavior.rs index 18894d2..8c643c4 100644 --- a/test/long_mode/behavior.rs +++ b/test/long_mode/behavior.rs @@ -1346,7 +1346,7 @@ mod kvm { } #[test] - fn behavior_verify_kvm_0f__() { + fn behavior_verify_kvm_0f_() { use yaxpeax_arch::{Decoder, U8Reader}; use yaxpeax_x86::long_mode::Instruction; @@ -1358,103 +1358,90 @@ 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, 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 == 2 { - continue; - } - - if not_generic(&buf) { - continue; + for opc in 0xf0..=u8::MAX { + for bits in 0..=0x7f { + let mut instlen = 0; + let suffix = bits & 3; + let prefix = (bits >> 2) & 3; + let imm = (bits >> 4) & 3; + let opers = (bits >> 6) & 1; + + let mut bytes = [0; 6]; // 0x66, 0x0f, inst[0], inst[1]]; + + match prefix { + 0b00 => { }, + 0b01 => { + bytes[instlen] = 0x66; + instlen += 1; + } + 0b10 => { + bytes[instlen] = 0xf2; + instlen += 1; + } + 0b11 => { + bytes[instlen] = 0xf3; + instlen += 1; + } + _ => {} } - eprintln!("checking behavior of 0f {:02x} {:02x}: {}", inst[0], inst[1], buf); - - vm.set_regs(&initial_regs).unwrap(); - check_behavior(&mut vm, &bytes[..inst_len]).expect("behavior check is ok"); - } - } - } - - #[test] - fn behavior_verify_kvm_66_0f__() { - use yaxpeax_arch::{Decoder, U8Reader}; - use yaxpeax_x86::long_mode::Instruction; + bytes[instlen] = 0x0f; + instlen += 1; - 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 word in 0x0000..u16::MAX { - let inst = word.to_le_bytes(); - let bytes = [0x66, 0x0f, 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; + match suffix { + 0b00 => { }, + 0b01 => { + bytes[instlen] = 0x38; + instlen += 1; + } + 0b10 => { + bytes[instlen] = 0x3a; + instlen += 1; + } + _ => {} } - if not_generic(&buf) { - continue; + bytes[instlen] = opc; + bytes[instlen + 1] = if opers == 0 { + 0x01 + } else { + 0xc1 + }; + instlen += 2; + + match imm { + 0b00 => { }, + 0b01 => { + bytes[instlen] = 0x00; + instlen += 1; + }, + 0b10 => { + bytes[instlen] = 0x01; + instlen += 1; + }, + _ => { + bytes[instlen] = 0xff; + instlen += 1; + }, } - eprintln!("checking behavior of 66 0f {:02x} {:02x}: {}", inst[0], inst[1], buf); - - vm.set_regs(&initial_regs).unwrap(); - check_behavior(&mut vm, &bytes[..inst_len]).expect("behavior check is ok"); - } - } - } - - #[test] - fn behavior_verify_kvm_f3_0f__() { - 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(); + 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 decoded_len = 0.wrapping_offset(buf.len()) as usize; + if decoded_len != instlen { + continue; + } - for word in 0xec00..u16::MAX { - let inst = word.to_le_bytes(); - let bytes = [0xf3, 0x0f, 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; - } + if not_generic(&buf) { + continue; + } - if not_generic(&buf) { - continue; + vm.set_regs(&initial_regs).unwrap(); + check_behavior(&mut vm, &bytes[..instlen]).expect("behavior check is ok"); } - - eprintln!("checking behavior of f3 0f {:02x} {:02x}: {}", inst[0], inst[1], buf); - - vm.set_regs(&initial_regs).unwrap(); - check_behavior(&mut vm, &bytes[..inst_len]).expect("behavior check is ok"); } } } @@ -1606,7 +1593,7 @@ mod kvm { let initial_regs = vm.get_regs().unwrap(); #[allow(non_snake_case)] - for opcode in 0x90..=u8::MAX { + for opcode in 0x00..=u8::MAX { for prefix_bits in 0x00..0x400u16 { let mmmmm = prefix_bits & 0b11111; let prefix_1 = (0xe0 | mmmmm) as u8; @@ -1711,7 +1698,7 @@ mod kvm { } #[test] - fn behavior_verify_kvm_evex() { + fn behavior_verify_kvm_evex_noimm() { use yaxpeax_arch::{Decoder, U8Reader}; use yaxpeax_x86::long_mode::Instruction; @@ -1740,8 +1727,8 @@ mod kvm { let initial_regs = vm.get_regs().unwrap(); #[allow(non_snake_case)] - for opcode in 0x7d..=u8::MAX { - for prefix_bits in 0x00..0x800u16 { + for opcode in 0x00..=u8::MAX { + for prefix_bits in 0x00..0x1000u16 { let mmm = prefix_bits & 0b111; let prefix_1 = (0xf0 | mmm) as u8; @@ -1750,11 +1737,11 @@ mod kvm { 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 k = (prefix_bits >> 10) & 11; let prefix_2 = (0x7c | (W << 7) | pp) as u8; - let aaa = if k { 0b001 } else { 0b111 }; + let aaa = [0b000, 0b001, 0b010, 0b111][k as usize]; let prefix_3 = (0x08 | aaa | b << 4 | LL << 5 | z << 7) as u8; let operands = (prefix_bits >> 9) & 0b1; @@ -1791,6 +1778,89 @@ mod kvm { } } + #[test] + fn behavior_verify_kvm_evex_imm() { + 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 0x00..=u8::MAX { + for imm in [0x00, 0x01, 0x80, 0xff] { + for prefix_bits in 0x00..0x1000u16 { + 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) & 11; + + let prefix_2 = (0x7c | (W << 7) | pp) as u8; + + let aaa = [0b000, 0b001, 0b010, 0b111][k as usize]; + 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; 7] = [0x62, prefix_1, prefix_2, prefix_3, 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); + } + } + } + } + } + } + } + // use the generic test harness for a handful of instructions that don't get covered in the // general enumeration above #[test] @@ -1842,6 +1912,11 @@ mod kvm { use yaxpeax_x86::long_mode::Opcode; use yaxpeax_x86::long_mode::Operand; fn not_generic(instr: &Instruction) -> bool { + if sse_gpr::OPCODES.contains(&instr.opcode()) { + // not having reads for xmm registers yet, these don't fit well with the test harness.. + return true; + } + if table_instrs::OPCODES.contains(&instr.opcode()) { // tested under `mod table_instrs`. return true; @@ -2203,6 +2278,25 @@ mod kvm { ]; } + mod sse_gpr { + use super::create_test_vm; + use super::test_prelude; + use super::MemPatch; + use super::check_behavior; + use super::check_behavior_with_regs; + + use yaxpeax_arch::{Decoder, U8Reader}; + use yaxpeax_x86::long_mode::{Instruction, Opcode}; + use yaxpeax_x86::long_mode; + + pub static OPCODES: &'static [Opcode] = &[ + Opcode::PEXTRB, + Opcode::PEXTRW, + Opcode::PEXTRD, + Opcode::EXTRACTPS, + ]; + } + // check the collection of {l,s}{g,i,l}dt. these instructions are at the combination of // "interesting memory size" and "interesting [non]interaction with prefixes" // |
