diff options
| author | iximeow <me@iximeow.net> | 2024-08-16 21:21:16 -0700 |
|---|---|---|
| committer | iximeow <me@iximeow.net> | 2025-06-01 09:21:37 +0000 |
| commit | 08eed360fea81ab9328fd0859b813ee01937b5b1 (patch) | |
| tree | c379847e969d479c13c29f46864465cf2969d9cb /src | |
| parent | 681262f4472ba4f452446e86012ce629b849d8d9 (diff) | |
expand isa feature selection to more bits
this is backed by the new IsaSettings trait. the existing InstDecoders
are unchanged, except that they implement this new trait.
also add new `DecodeEverything` structs with `IsaSettings` impls that
are unconditionally set to permit anything the decoder can be configured
to conditionally accept or reject.
in the process, add new `_3dnow` flag and stop accepting 3dnow
instructions in uarch-specific decoder settings that would not have
3dnow instructions.
update AMD microarchitectures and cross-ref chip directory
Diffstat (limited to 'src')
| -rw-r--r-- | src/long_mode/isa_settings.rs | 977 | ||||
| -rw-r--r-- | src/long_mode/mod.rs | 1444 | ||||
| -rw-r--r-- | src/long_mode/uarch.rs | 100 | ||||
| -rw-r--r-- | src/protected_mode/isa_settings.rs | 952 | ||||
| -rw-r--r-- | src/protected_mode/mod.rs | 1454 | ||||
| -rw-r--r-- | src/real_mode/isa_settings.rs | 952 | ||||
| -rw-r--r-- | src/real_mode/mod.rs | 1454 |
7 files changed, 3129 insertions, 4204 deletions
diff --git a/src/long_mode/isa_settings.rs b/src/long_mode/isa_settings.rs new file mode 100644 index 0000000..de3d6e7 --- /dev/null +++ b/src/long_mode/isa_settings.rs @@ -0,0 +1,977 @@ +use super::{BMI1, BMI2, DecodeError, DecodeEverything, InstDecoder, Instruction, Opcode}; + +macro_rules! gen_isa_settings { + ( + $( + $(#[$doc:meta])* + $feature:ident, + $(#[$set_doc:item])* + $set_feature:ident = $idx:expr; + )+ + + { + $( + $(#[$composite_doc:meta])* + $composite_feature:ident = { + $first_inner_feature:ident + $(,$($inner_feature:ident$(,)?)+)? + }$(,)? + )* + } + + { + $( + $(#[$composite_set_doc:meta])* + $composite_set_feature:ident = { + $set_first_inner_feature:ident + $(,$($set_inner_feature:ident$(,)?)+)? + }$(,)? + )* + } + ) => { + /// specific decode settings controlling how an x86 byte sequence is interpreted. + /// + /// this currently exists to specify which extensions are to be accepted or rejected. the two + /// implementations provided by `yaxpeax-x86` are: + /// * [`InstDecoder`], providing configurable enablement or disablement per-extension + /// * [`DecodeEverything`], which allows all extensions supported by `yaxpeax-x86` + /// + /// notably, `InstDecoder::default()` and `DecodeEverything` are functionally equivalent in that + /// they accept all extensions supported by the decoder. + /// + /// TODO: many additional extension support flags. + /// * extended MMX (see `sha256:daee4e23dac983f1744126352d40cc71d47b4a9283a2a1e473837728ca9c51ac`) + /// * lots of others... tile extensions... + pub trait IsaSettings { + $( + $(#[$doc])* + fn $feature(&self) -> bool; + )+ + + $( + $(#[$composite_doc])* + fn $composite_feature(&self) -> bool { + self.$first_inner_feature() + $($(&& self.$inner_feature())+)? + } + )* + + fn revise_instruction(&self, inst: &mut Instruction) -> Result<(), DecodeError> { + revise_instruction(self, inst) + } + } + + impl IsaSettings for DecodeEverything { + $(fn $feature(&self) -> bool { true })+ + + fn revise_instruction(&self, _inst: &mut Instruction) -> Result<(), DecodeError> { + Ok(()) + } + } + + impl IsaSettings for InstDecoder { + $( + fn $feature(&self) -> bool { + let i = $idx as usize; + self.flags[i / 64] & (1 << (i % 64)) != 0 + } + )+ + } + + impl InstDecoder { + $( + $(#[$set_doc])* + pub fn $set_feature(mut self) -> Self { + let i = $idx as usize; + self.flags[i / 64] |= 1 << (i % 64); + self + } + )+ + + $( + $(#[$composite_set_doc])* + pub fn $composite_set_feature(&self) -> Self { + self.$set_first_inner_feature() + $($(.$set_inner_feature())+)? + } + )* + + $( + $(#[$doc])* + pub fn $feature(&self) -> bool { + <Self as IsaSettings>::$feature(self) + } + )+ + + $( + $(#[$composite_doc])* + pub fn $composite_feature(&self) -> bool { + <Self as IsaSettings>::$composite_feature(self) + } + )* + } + } +} +gen_isa_settings!( + _3dnow, with_3dnow = 1; + _3dnowprefetch, with_3dnowprefetch = 2; + abm, with_abm = 3; + adx, with_adx = 4; + aesni, with_aesni = 5; + amd_quirks, with_amd_quirks = 6; + avx, with_avx = 7; + avx2, with_avx2 = 8; + avx512_4fmaps, with_avx512_4fmaps = 10; + avx512_4vnniw, with_avx512_4vnniw = 11; + avx512_bitalg, with_avx512_bitalg = 12; + avx512_bw, with_avx512_bw = 13; + avx512_cd, with_avx512_cd = 14; + avx512_dq, with_avx512_dq = 15; + avx512_er, with_avx512_er = 16; + avx512_f, with_avx512_f = 17; + avx512_fma, with_avx512_fma = 18; + avx512_pf, with_avx512_pf = 19; + avx512_vbmi, with_avx512_vbmi = 20; + avx512_vbmi2, with_avx512_vbmi2 = 21; + avx512_vl, with_avx512_vl = 22; + avx512_vpopcntdq, with_avx512_vpopcntdq = 23; + avx_vnni, with_avx_vnni = 24; + bmi1, with_bmi1 = 25; + #[doc="`bmi2` indicates support for the `BZHI`, `MULX`, `PDEP`, `PEXT`, `RORX`, `SARX`, `SHRX`, "] + #[doc="and `SHLX` instructions. `bmi2` is implemented in all x86_64 chips that implement `bmi`, "] + #[doc="except the amd `piledriver` and `steamroller` microarchitectures."] + bmi2, with_bmi2 = 26; + #[doc="from AMD APM Vol 3 `CPUID Fn0000_0007_EBX_x0`: \"CLFLUSHOPT instruction support.\""] + clflushopt, with_clflushopt = 27; + clwb, with_clwb = 28; + cmov, with_cmov = 29; + cmpxchg16b, with_cmpxchg16b = 30; + cx8, with_cx8 = 31; + f16c, with_f16c = 32; + fma3, with_fma3 = 33; + fma4, with_fma4 = 34; + gfni, with_gfni = 35; + intel_quirks, with_intel_quirks = 36; + invpcid, with_invpcid = 37; + #[doc="`lahfsahf` is only unset for early revisions of 64-bit amd and intel chips. unfortunately"] + #[doc="the clearest documentation on when these instructions were reintroduced into 64-bit"] + #[doc="architectures seems to be"] + #[doc="[wikipedia](https://en.wikipedia.org/wiki/X86-64#Older_implementations):"] + #[doc="```text"] + #[doc="Early AMD64 and Intel 64 CPUs lacked LAHF and SAHF instructions in 64-bit mode. AMD"] + #[doc="introduced these instructions (also in 64-bit mode) with their Athlon 64, Opteron and"] + #[doc="Turion 64 revision D processors in March 2005[48][49][50] while Intel introduced the"] + #[doc="instructions with the Pentium 4 G1 stepping in December 2005. The 64-bit version of Windows"] + #[doc="8.1 requires this feature.[47]"] + #[doc="```"] + #[doc=""] + #[doc="this puts reintroduction of these instructions somewhere in the middle of prescott and k8"] + #[doc="lifecycles, for intel and amd respectively. because there is no specific uarch where these"] + #[doc="features become enabled, prescott and k8 default to not supporting these instructions,"] + #[doc="where later uarches support these instructions."] + lahfsahf, with_lahfsahf = 38; + lzcnt, with_lzcnt = 39; + monitor, with_monitor = 40; + movbe, with_movbe = 41; + mpx, with_mpx = 42; + pclmulqdq, with_pclmulqdq = 43; + pcommit, with_pcommit = 44; + popcnt, with_popcnt = 45; + prefetchw, with_prefetchw = 46; + prefetchwt1, with_prefetchwt1 = 47; + rdrand, with_rdrand = 48; + #[doc="from AMD APM Vol 3 `CPUID Fn0000_0007_EBX_x0`: \"RDSEED instruction support.\""] + rdseed, with_rdseed = 49; + rdtscp, with_rdtscp = 50; + sgx, with_sgx = 51; + sha, with_sha = 52; + skinit, with_skinit = 53; + sse3, with_sse3 = 54; + sse4_1, with_sse4_1 = 55; + sse4_2, with_sse4_2 = 56; + sse4a, with_sse4a = 57; + ssse3, with_ssse3 = 58; + svm, with_svm = 59; + syscall, with_syscall = 60; + tbm, with_tbm = 61; + tsx, with_tsx = 62; + #[doc="from AMD APM Vol 3 `CPUID Fn0000_0007_ECX_x0`: \"VAES 256-bit instruction support.\""] + vaes, with_vaes = 63; + vmx, with_vmx = 64; + xop, with_xop = 65; + xsave, with_xsave = 66; + #[doc="from AMD APM Vol 3 `CPUID Fn0000_000D_EAX_x1`: \"XSAVEC and compact XRSTOR supported.\""] + xsavec, with_xsavec = 67; + #[doc="from AMD APM Vol 3 `CPUID Fn0000_000D_EAX_x1`: \"XSAVES, XRSTOR, and XSS are supported.\""] + xsaves, with_xsaves = 68; + #[doc="from AMD APM Vol 3 `CPUID Fn0000_000D_EAX_x1`: \"XSAVEOPT is available.\""] + xsaveopt, with_xsaveopt = 69; + #[doc="from AMD APM Vol 3 `CPUID Fn0000_0007_EAX_x0`: \"FS and GS base read/write instruction support.\""] + fsgsbase, with_fsgsbase = 70; + #[doc="from AMD APM Vol 3 `CPUID Fn0000_0001_ECX`: \"Support for MWAITX and MONITORX instructions.\""] + monitorx, with_monitorx = 71; + #[doc="from AMD APM Vol 3 `CPUID Fn0000_0008_EBX`: \"WBNOINVD instruction supported.\""] + wbnoinvd, with_wbnoinvd = 72; + #[doc="from AMD APM Vol 3 `CPUID Fn0000_0008_EBX`: \"CLZERO instruction supported.\""] + clzero, with_clzero = 72; + #[doc="from AMD APM Vol 3 `CPUID Fn0000_0007_ECX_x0`: \"RDPID instruction and TSC_AUX MSR support.\""] + rdpid, with_rdpid = 73; + #[doc="from AMD APM Vol 3 `CPUID Fn0000_0007_ECX_x0`: \"VPCLMULQDQ 256-bit instruction support.\""] + vpclmulqdq, with_vpclmulqdq = 74; + #[doc="supported in Zen 5, but not mentioned in the AMD APM as of revision 3.36."] + movdir64b, with_movdir64b = 75; + #[doc="supported in Zen 5, but not mentioned in the AMD APM as of revision 3.36."] + enqcmd, with_enqcmd = 76; + + { + sse4 = { + sse4_1, + sse4_2, + } + + #[doc = "returns `true` if this `InstDecoder` has **all** `avx512` features enabled."] + avx512 = { + avx512_4fmaps, + avx512_4vnniw, + avx512_bitalg, + avx512_bw, + avx512_cd, + avx512_dq, + avx512_er, + avx512_f, + avx512_fma, + avx512_pf, + avx512_vbmi, + avx512_vbmi2, + avx512_vl, + avx512_vpopcntdq, + }, + } + + { + with_sse4 = { + with_sse4_1, + with_sse4_2, + } + + with_avx512 = { + with_avx512_4fmaps, + with_avx512_4vnniw, + with_avx512_bitalg, + with_avx512_bw, + with_avx512_cd, + with_avx512_dq, + with_avx512_er, + with_avx512_f, + with_avx512_fma, + with_avx512_pf, + with_avx512_vbmi, + with_avx512_vbmi2, + with_avx512_vl, + with_avx512_vpopcntdq, + } + } +); + +/// optionally reject or reinterpret instruction according to settings for this decode +/// operation. +fn revise_instruction<D: IsaSettings + ?Sized>(settings: &D, inst: &mut Instruction) -> Result<(), DecodeError> { + if inst.prefixes.evex().is_some() { + if !settings.avx512() { + return Err(DecodeError::InvalidOpcode); + } else { + return Ok(()); + } + } + match inst.opcode { + // original 3dnow instructions. see also + // `3DNow-Technology-Manual.pdf` + // * sha256: daee4e23dac983f1744126352d40cc71d47b4a9283a2a1e473837728ca9c51ac + // * ref: https://www.amd.com/content/dam/amd/en/documents/archived-tech-docs/programmer-references/21928.pdf + // * order# 21928 + Opcode::FEMMS | + Opcode::PAVGUSB | + Opcode::PFADD | + Opcode::PFSUB | + Opcode::PFSUBR | + Opcode::PFACC | + Opcode::PFCMPGE | + Opcode::PFCMPGT | + Opcode::PFCMPEQ | + Opcode::PFMAX | + Opcode::PFMIN | + Opcode::PI2FD | + Opcode::PF2ID | + Opcode::PFRCP | + Opcode::PFRSQRT | + Opcode::PFMUL | + Opcode::PFRCPIT1 | + Opcode::PFRCPIT2 | + Opcode::PFRSQIT1 | + Opcode::PMULHRW => { + if !settings._3dnow() { + return Err(DecodeError::InvalidOpcode); + } + } + // later extension to 3dnow. see also + // `AMD-Extensions-to-the-3DNow-and-MMX-Instruction-Sets.pdf` + // * sha256: ad847bd6877a682296fc584b4bbee354bf84c57bb97ba57e9c9adfc63cc5f465 + // * ref: https://refspecs.linuxfoundation.org/AMD-extensions.pdf + // * order# 22466 + Opcode::PF2IW | + Opcode::PFNACC | + Opcode::PFPNACC | + Opcode::PI2FW | + Opcode::PSWAPD => { + if !settings._3dnow() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::TZCNT => { + if !settings.bmi1() { + // tzcnt is only supported if bmi1 is enabled. without bmi1, this decodes as + // bsf. + inst.opcode = Opcode::BSF; + } + } + Opcode::LDDQU | + Opcode::ADDSUBPS | + Opcode::ADDSUBPD | + Opcode::HADDPS | + Opcode::HSUBPS | + Opcode::HADDPD | + Opcode::HSUBPD | + Opcode::MOVSHDUP | + Opcode::MOVSLDUP | + Opcode::MOVDDUP | + Opcode::MONITOR | + Opcode::MWAIT => { + // via Intel section 5.7, SSE3 Instructions + if !settings.sse3() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::PHADDW | + Opcode::PHADDSW | + Opcode::PHADDD | + Opcode::PHSUBW | + Opcode::PHSUBSW | + Opcode::PHSUBD | + Opcode::PABSB | + Opcode::PABSW | + Opcode::PABSD | + Opcode::PMADDUBSW | + Opcode::PMULHRSW | + Opcode::PSHUFB | + Opcode::PSIGNB | + Opcode::PSIGNW | + Opcode::PSIGND | + Opcode::PALIGNR => { + // via Intel section 5.8, SSSE3 Instructions + if !settings.ssse3() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::PMULLD | + Opcode::PMULDQ | + Opcode::MOVNTDQA | + Opcode::BLENDPD | + Opcode::BLENDPS | + Opcode::BLENDVPD | + Opcode::BLENDVPS | + Opcode::PBLENDVB | + Opcode::BLENDW | + Opcode::PMINUW | + Opcode::PMINUD | + Opcode::PMINSB | + Opcode::PMINSD | + Opcode::PMAXUW | + Opcode::PMAXUD | + Opcode::PMAXSB | + Opcode::PMAXSD | + Opcode::ROUNDPS | + Opcode::ROUNDPD | + Opcode::ROUNDSS | + Opcode::ROUNDSD | + Opcode::PBLENDW | + Opcode::EXTRACTPS | + Opcode::INSERTPS | + Opcode::PINSRB | + Opcode::PINSRD | + Opcode::PINSRQ | + Opcode::PMOVSXBW | + Opcode::PMOVZXBW | + Opcode::PMOVSXBD | + Opcode::PMOVZXBD | + Opcode::PMOVSXWD | + Opcode::PMOVZXWD | + Opcode::PMOVSXBQ | + Opcode::PMOVZXBQ | + Opcode::PMOVSXWQ | + Opcode::PMOVZXWQ | + Opcode::PMOVSXDQ | + Opcode::PMOVZXDQ | + Opcode::DPPS | + Opcode::DPPD | + Opcode::MPSADBW | + Opcode::PHMINPOSUW | + Opcode::PTEST | + Opcode::PCMPEQQ | + Opcode::PEXTRB | + Opcode::PEXTRW | + Opcode::PEXTRD | + Opcode::PEXTRQ | + Opcode::PACKUSDW => { + // via Intel section 5.10, SSE4.1 Instructions + if !settings.sse4_1() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::EXTRQ | + Opcode::INSERTQ | + Opcode::MOVNTSS | + Opcode::MOVNTSD => { + if !settings.sse4a() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::CRC32 | + Opcode::PCMPESTRI | + Opcode::PCMPESTRM | + Opcode::PCMPISTRI | + Opcode::PCMPISTRM | + Opcode::PCMPGTQ => { + // via Intel section 5.11, SSE4.2 Instructions + if !settings.sse4_2() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::AESDEC | + Opcode::AESDECLAST | + Opcode::AESENC | + Opcode::AESENCLAST | + Opcode::AESIMC | + Opcode::AESKEYGENASSIST => { + // via Intel section 5.12. AESNI AND PCLMULQDQ + if !settings.aesni() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::PCLMULQDQ => { + // via Intel section 5.12. AESNI AND PCLMULQDQ + if !settings.pclmulqdq() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::XABORT | + Opcode::XBEGIN | + Opcode::XEND | + Opcode::XTEST => { + if !settings.tsx() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::SHA1MSG1 | + Opcode::SHA1MSG2 | + Opcode::SHA1NEXTE | + Opcode::SHA1RNDS4 | + Opcode::SHA256MSG1 | + Opcode::SHA256MSG2 | + Opcode::SHA256RNDS2 => { + if !settings.sha() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::ENCLV | + Opcode::ENCLS | + Opcode::ENCLU => { + if !settings.sgx() { + return Err(DecodeError::InvalidOpcode); + } + } + // AVX... + Opcode::VMOVDDUP | + Opcode::VPSHUFLW | + Opcode::VPSHUFHW | + Opcode::VHADDPS | + Opcode::VHSUBPS | + Opcode::VADDSUBPS | + Opcode::VCVTPD2DQ | + Opcode::VLDDQU | + Opcode::VCOMISD | + Opcode::VCOMISS | + Opcode::VUCOMISD | + Opcode::VUCOMISS | + Opcode::VADDPD | + Opcode::VADDPS | + Opcode::VADDSD | + Opcode::VADDSS | + Opcode::VADDSUBPD | + Opcode::VBLENDPD | + Opcode::VBLENDPS | + Opcode::VBLENDVPD | + Opcode::VBLENDVPS | + Opcode::VBROADCASTF128 | + Opcode::VBROADCASTI128 | + Opcode::VBROADCASTSD | + Opcode::VBROADCASTSS | + Opcode::VCMPSD | + Opcode::VCMPSS | + Opcode::VCMPPD | + Opcode::VCMPPS | + Opcode::VCVTDQ2PD | + Opcode::VCVTDQ2PS | + Opcode::VCVTPD2PS | + Opcode::VCVTPS2DQ | + Opcode::VCVTPS2PD | + Opcode::VCVTSS2SD | + Opcode::VCVTSI2SS | + Opcode::VCVTSI2SD | + Opcode::VCVTSD2SI | + Opcode::VCVTSD2SS | + Opcode::VCVTSS2SI | + Opcode::VCVTTPD2DQ | + Opcode::VCVTTPS2DQ | + Opcode::VCVTTSS2SI | + Opcode::VCVTTSD2SI | + Opcode::VDIVPD | + Opcode::VDIVPS | + Opcode::VDIVSD | + Opcode::VDIVSS | + Opcode::VDPPD | + Opcode::VDPPS | + Opcode::VEXTRACTF128 | + Opcode::VEXTRACTI128 | + Opcode::VEXTRACTPS | + Opcode::VFMADD132PD | + Opcode::VFMADD132PS | + Opcode::VFMADD132SD | + Opcode::VFMADD132SS | + Opcode::VFMADD213PD | + Opcode::VFMADD213PS | + Opcode::VFMADD213SD | + Opcode::VFMADD213SS | + Opcode::VFMADD231PD | + Opcode::VFMADD231PS | + Opcode::VFMADD231SD | + Opcode::VFMADD231SS | + Opcode::VFMADDSUB132PD | + Opcode::VFMADDSUB132PS | + Opcode::VFMADDSUB213PD | + Opcode::VFMADDSUB213PS | + Opcode::VFMADDSUB231PD | + Opcode::VFMADDSUB231PS | + Opcode::VFMSUB132PD | + Opcode::VFMSUB132PS | + Opcode::VFMSUB132SD | + Opcode::VFMSUB132SS | + Opcode::VFMSUB213PD | + Opcode::VFMSUB213PS | + Opcode::VFMSUB213SD | + Opcode::VFMSUB213SS | + Opcode::VFMSUB231PD | + Opcode::VFMSUB231PS | + Opcode::VFMSUB231SD | + Opcode::VFMSUB231SS | + Opcode::VFMSUBADD132PD | + Opcode::VFMSUBADD132PS | + Opcode::VFMSUBADD213PD | + Opcode::VFMSUBADD213PS | + Opcode::VFMSUBADD231PD | + Opcode::VFMSUBADD231PS | + Opcode::VFNMADD132PD | + Opcode::VFNMADD132PS | + Opcode::VFNMADD132SD | + Opcode::VFNMADD132SS | + Opcode::VFNMADD213PD | + Opcode::VFNMADD213PS | + Opcode::VFNMADD213SD | + Opcode::VFNMADD213SS | + Opcode::VFNMADD231PD | + Opcode::VFNMADD231PS | + Opcode::VFNMADD231SD | + Opcode::VFNMADD231SS | + Opcode::VFNMSUB132PD | + Opcode::VFNMSUB132PS | + Opcode::VFNMSUB132SD | + Opcode::VFNMSUB132SS | + Opcode::VFNMSUB213PD | + Opcode::VFNMSUB213PS | + Opcode::VFNMSUB213SD | + Opcode::VFNMSUB213SS | + Opcode::VFNMSUB231PD | + Opcode::VFNMSUB231PS | + Opcode::VFNMSUB231SD | + Opcode::VFNMSUB231SS | + Opcode::VGATHERDPD | + Opcode::VGATHERDPS | + Opcode::VGATHERQPD | + Opcode::VGATHERQPS | + Opcode::VHADDPD | + Opcode::VHSUBPD | + Opcode::VINSERTF128 | + Opcode::VINSERTI128 | + Opcode::VINSERTPS | + Opcode::VMASKMOVDQU | + Opcode::VMASKMOVPD | + Opcode::VMASKMOVPS | + Opcode::VMAXPD | + Opcode::VMAXPS | + Opcode::VMAXSD | + Opcode::VMAXSS | + Opcode::VMINPD | + Opcode::VMINPS | + Opcode::VMINSD | + Opcode::VMINSS | + Opcode::VMOVAPD | + Opcode::VMOVAPS | + Opcode::VMOVD | + Opcode::VMOVDQA | + Opcode::VMOVDQU | + Opcode::VMOVHLPS | + Opcode::VMOVHPD | + Opcode::VMOVHPS | + Opcode::VMOVLHPS | + Opcode::VMOVLPD | + Opcode::VMOVLPS | + Opcode::VMOVMSKPD | + Opcode::VMOVMSKPS | + Opcode::VMOVNTDQ | + Opcode::VMOVNTDQA | + Opcode::VMOVNTPD | + Opcode::VMOVNTPS | + Opcode::VMOVQ | + Opcode::VMOVSS | + Opcode::VMOVSD | + Opcode::VMOVSHDUP | + Opcode::VMOVSLDUP | + Opcode::VMOVUPD | + Opcode::VMOVUPS | + Opcode::VMPSADBW | + Opcode::VMULPD | + Opcode::VMULPS | + Opcode::VMULSD | + Opcode::VMULSS | + Opcode::VPABSB | + Opcode::VPABSD | + Opcode::VPABSW | + Opcode::VPACKSSDW | + Opcode::VPACKUSDW | + Opcode::VPACKSSWB | + Opcode::VPACKUSWB | + Opcode::VPADDB | + Opcode::VPADDD | + Opcode::VPADDQ | + Opcode::VPADDSB | + Opcode::VPADDSW | + Opcode::VPADDUSB | + Opcode::VPADDUSW | + Opcode::VPADDW | + Opcode::VPALIGNR | + Opcode::VPAND | + Opcode::VANDPD | + Opcode::VANDPS | + Opcode::VANDNPD | + Opcode::VANDNPS | + Opcode::VORPD | + Opcode::VORPS | + Opcode::VPANDN | + Opcode::VPAVGB | + Opcode::VPAVGW | + Opcode::VPBLENDD | + Opcode::VPBLENDVB | + Opcode::VPBLENDW | + Opcode::VPBROADCASTB | + Opcode::VPBROADCASTD | + Opcode::VPBROADCASTQ | + Opcode::VPBROADCASTW | + Opcode::VPCLMULQDQ | + Opcode::VPCMPEQB | + Opcode::VPCMPEQD | + Opcode::VPCMPEQQ | + Opcode::VPCMPEQW | + Opcode::VPCMPGTB | + Opcode::VPCMPGTD | + Opcode::VPCMPGTQ | + Opcode::VPCMPGTW | + Opcode::VPCMPESTRI | + Opcode::VPCMPESTRM | + Opcode::VPCMPISTRI | + Opcode::VPCMPISTRM | + Opcode::VPERM2F128 | + Opcode::VPERM2I128 | + Opcode::VPERMD | + Opcode::VPERMILPD | + Opcode::VPERMILPS | + Opcode::VPERMPD | + Opcode::VPERMPS | + Opcode::VPERMQ | + Opcode::VPEXTRB | + Opcode::VPEXTRD | + Opcode::VPEXTRQ | + Opcode::VPEXTRW | + Opcode::VPGATHERDD | + Opcode::VPGATHERDQ | + Opcode::VPGATHERQD | + Opcode::VPGATHERQQ | + Opcode::VPHADDD | + Opcode::VPHADDSW | + Opcode::VPHADDW | + Opcode::VPMADDUBSW | + Opcode::VPHMINPOSUW | + Opcode::VPHSUBD | + Opcode::VPHSUBSW | + Opcode::VPHSUBW | + Opcode::VPINSRB | + Opcode::VPINSRD | + Opcode::VPINSRQ | + Opcode::VPINSRW | + Opcode::VPMADDWD | + Opcode::VPMASKMOVD | + Opcode::VPMASKMOVQ | + Opcode::VPMAXSB | + Opcode::VPMAXSD | + Opcode::VPMAXSW | + Opcode::VPMAXUB | + Opcode::VPMAXUW | + Opcode::VPMAXUD | + Opcode::VPMINSB | + Opcode::VPMINSW | + Opcode::VPMINSD | + Opcode::VPMINUB | + Opcode::VPMINUW | + Opcode::VPMINUD | + Opcode::VPMOVMSKB | + Opcode::VPMOVSXBD | + Opcode::VPMOVSXBQ | + Opcode::VPMOVSXBW | + Opcode::VPMOVSXDQ | + Opcode::VPMOVSXWD | + Opcode::VPMOVSXWQ | + Opcode::VPMOVZXBD | + Opcode::VPMOVZXBQ | + Opcode::VPMOVZXBW | + Opcode::VPMOVZXDQ | + Opcode::VPMOVZXWD | + Opcode::VPMOVZXWQ | + Opcode::VPMULDQ | + Opcode::VPMULHRSW | + Opcode::VPMULHUW | + Opcode::VPMULHW | + Opcode::VPMULLQ | + Opcode::VPMULLD | + Opcode::VPMULLW | + Opcode::VPMULUDQ | + Opcode::VPOR | + Opcode::VPSADBW | + Opcode::VPSHUFB | + Opcode::VPSHUFD | + Opcode::VPSIGNB | + Opcode::VPSIGND | + Opcode::VPSIGNW | + Opcode::VPSLLD | + Opcode::VPSLLDQ | + Opcode::VPSLLQ | + Opcode::VPSLLVD | + Opcode::VPSLLVQ | + Opcode::VPSLLW | + Opcode::VPSRAD | + Opcode::VPSRAVD | + Opcode::VPSRAW | + Opcode::VPSRLD | + Opcode::VPSRLDQ | + Opcode::VPSRLQ | + Opcode::VPSRLVD | + Opcode::VPSRLVQ | + Opcode::VPSRLW | + Opcode::VPSUBB | + Opcode::VPSUBD | + Opcode::VPSUBQ | + Opcode::VPSUBSB | + Opcode::VPSUBSW | + Opcode::VPSUBUSB | + Opcode::VPSUBUSW | + Opcode::VPSUBW | + Opcode::VPTEST | + Opcode::VPUNPCKHBW | + Opcode::VPUNPCKHDQ | + Opcode::VPUNPCKHQDQ | + Opcode::VPUNPCKHWD | + Opcode::VPUNPCKLBW | + Opcode::VPUNPCKLDQ | + Opcode::VPUNPCKLQDQ | + Opcode::VPUNPCKLWD | + Opcode::VPXOR | + Opcode::VRCPPS | + Opcode::VROUNDPD | + Opcode::VROUNDPS | + Opcode::VROUNDSD | + Opcode::VROUNDSS | + Opcode::VRSQRTPS | + Opcode::VRSQRTSS | + Opcode::VRCPSS | + Opcode::VSHUFPD | + Opcode::VSHUFPS | + Opcode::VSQRTPD | + Opcode::VSQRTPS | + Opcode::VSQRTSS | + Opcode::VSQRTSD | + Opcode::VSUBPD | + Opcode::VSUBPS | + Opcode::VSUBSD | + Opcode::VSUBSS | + Opcode::VTESTPD | + Opcode::VTESTPS | + Opcode::VUNPCKHPD | + Opcode::VUNPCKHPS | + Opcode::VUNPCKLPD | + Opcode::VUNPCKLPS | + Opcode::VXORPD | + Opcode::VXORPS | + Opcode::VZEROUPPER | + Opcode::VZEROALL | + Opcode::VLDMXCSR | + Opcode::VSTMXCSR => { + // TODO: check a table for these + if !settings.avx() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::VAESDEC | + Opcode::VAESDECLAST | + Opcode::VAESENC | + Opcode::VAESENCLAST | + Opcode::VAESIMC | + Opcode::VAESKEYGENASSIST => { + // TODO: check a table for these + if !settings.avx() || !settings.aesni() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::MOVBE => { + if !settings.movbe() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::POPCNT => { + /* + * from the intel SDM: + * ``` + * Before an application attempts to use the POPCNT instruction, it must check that + * the processor supports SSE4.2 (if CPUID.01H:ECX.SSE4_2[bit 20] = 1) and POPCNT + * (if CPUID.01H:ECX.POPCNT[bit 23] = 1). + * ``` + */ + if settings.intel_quirks() && (settings.sse4_2() || settings.popcnt()) { + return Ok(()); + } else if !settings.popcnt() { + /* + * elsewhere from the amd APM: + * `Instruction Subsets and CPUID Feature Flags` on page 507 indicates that + * popcnt is present when the popcnt bit is reported by cpuid. this seems to be + * the less quirky default, so `intel_quirks` is considered the outlier, and + * before this default. + * */ + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::LZCNT => { + /* + * amd APM, `LZCNT` page 212: + * LZCNT is an Advanced Bit Manipulation (ABM) instruction. Support for the LZCNT + * instruction is indicated by CPUID Fn8000_0001_ECX[ABM] = 1. + * + * meanwhile the intel SDM simply states: + * ``` + * CPUID.EAX=80000001H:ECX.LZCNT[bit 5]: if 1 indicates the processor supports the + * LZCNT instruction. + * ``` + * + * so that's considered the less-quirky (default) case here. + * */ + if settings.amd_quirks() && !settings.abm() { + return Err(DecodeError::InvalidOpcode); + } else if !settings.lzcnt() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::ADCX | + Opcode::ADOX => { + if !settings.adx() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::VMRUN | + Opcode::VMLOAD | + Opcode::VMSAVE | + Opcode::CLGI | + Opcode::VMMCALL | + Opcode::INVLPGA => { + if !settings.svm() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::STGI | + Opcode::SKINIT => { + if !settings.svm() || !settings.skinit() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::LAHF | + Opcode::SAHF => { + if !settings.lahfsahf() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::VCVTPS2PH | + Opcode::VCVTPH2PS => { + /* + * from intel SDM: + * ``` + * 14.4.1 Detection of F16C Instructions Application using float 16 instruction + * must follow a detection sequence similar to AVX to ensure: • The OS has + * enabled YMM state management support, • The processor support AVX as + * indicated by the CPUID feature flag, i.e. CPUID.01H:ECX.AVX[bit 28] = 1. • + * The processor support 16-bit floating-point conversion instructions via a + * CPUID feature flag (CPUID.01H:ECX.F16C[bit 29] = 1). + * ``` + * + * TODO: only the VEX-coded variant of this instruction should be gated on `f16c`. + * the EVEX-coded variant should be gated on `avx512f` or `avx512vl` if not + * EVEX.512-coded. + */ + if !settings.avx() || !settings.f16c() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::RDRAND => { + if !settings.rdrand() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::RDSEED => { + if !settings.rdseed() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::MONITORX | Opcode::MWAITX | // these are gated on the `monitorx` and `mwaitx` cpuid bits, but are AMD-only. + Opcode::CLZERO | Opcode::RDPRU => { // again, gated on specific cpuid bits, but AMD-only. + if !settings.amd_quirks() { + return Err(DecodeError::InvalidOpcode); + } + } + other => { + if !settings.bmi1() { + if BMI1.contains(&other) { + return Err(DecodeError::InvalidOpcode); + } + } + if !settings.bmi2() { + if BMI2.contains(&other) { + return Err(DecodeError::InvalidOpcode); + } + } + } + } + Ok(()) +} diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs index 0ce6a58..0045236 100644 --- a/src/long_mode/mod.rs +++ b/src/long_mode/mod.rs @@ -3,6 +3,9 @@ mod evex; #[cfg(feature = "fmt")] mod display; pub mod uarch; +mod isa_settings; + +pub use isa_settings::IsaSettings; pub use crate::MemoryAccessSize; @@ -2791,73 +2794,7 @@ impl LengthedInstruction for Instruction { /// the design requiring that. #[derive(PartialEq, Copy, Clone, Eq, Hash, PartialOrd, Ord)] pub struct InstDecoder { - // extensions tracked here: - // 0. SSE3 - // 1. SSSE3 - // 2. monitor (intel-only?) - // 3. vmx (some atom chips still lack it) - // 4. fma3 (intel haswell/broadwell+, amd piledriver+) - // 5. cmpxchg16b (some amd are missing this one) - // 6. sse4.1 - // 7. sse4.2 - // 8. movbe - // 9. popcnt (independent of BMI) - // 10. aesni - // 11. xsave (xsave, xrestor, xsetbv, xgetbv) - // 12. rdrand (intel ivybridge+, amd ..??) - // 13. sgx (eadd, eblock, ecreate, edbgrd, edbgwr, einit, eldb, eldu, epa, eremove, etrace, - // ewb, eenter, eexit, egetkey, ereport, eresume) - // 14. bmi1 (intel haswell+, amd jaguar+) - // 15. avx2 (intel haswell+, amd excavator+) - // 16. bmi2 (intel ?, amd ?) - // 17. invpcid - // 18. mpx - // 19. avx512_f - // 20. avx512_dq - // 21. rdseed - // 22. adx - // 23. avx512_fma - // 24. pcommit - // 25. clflushopt - // 26. clwb - // 27. avx512_pf - // 28. avx512_er - // 29. avx512_cd - // 30. sha - // 31. avx512_bw - // 32. avx512_vl - // 33. prefetchwt1 - // 34. avx512_vbmi - // 35. avx512_vbmi2 - // 36. gfni (galois field instructions) - // 37. vaes - // 38. pclmulqdq - // 39. avx_vnni - // 40. avx512_bitalg - // 41. avx512_vpopcntdq - // 42. avx512_4vnniw - // 43. avx512_4fmaps - // 44. cx8 // cmpxchg8 - is this actually optional in x86_64? - // 45. syscall // syscall/sysret - actually optional in x86_64? - // 46. rdtscp // actually optional in x86_64? - // 47. abm (lzcnt, popcnt) - // 48. sse4a - // 49. 3dnowprefetch // actually optional? - // 50. xop - // 51. skinit - // 52. tbm - // 53. intel quirks - // 54. amd quirks - // 55. avx (intel ?, amd ?) - // 56. amd-v/svm - // 57. lahfsahf - // 58. cmov - // 59. f16c - // 60. fma4 - // 61. prefetchw - // 62. tsx - // 63. lzcnt - flags: u64, + flags: [u64; 2], } impl InstDecoder { @@ -2867,7 +2804,7 @@ impl InstDecoder { /// and any instructions defined by extensions. pub fn minimal() -> Self { InstDecoder { - flags: 0, + flags: [0, 0], } } @@ -2879,1311 +2816,32 @@ impl InstDecoder { let mut reader = yaxpeax_arch::U8Reader::new(data); self.decode(&mut reader) } +} - pub fn sse3(&self) -> bool { - self.flags & (1 << 0) != 0 - } - - pub fn with_sse3(mut self) -> Self { - self.flags |= 1 << 0; - self - } - - pub fn ssse3(&self) -> bool { - self.flags & (1 << 1) != 0 - } - - pub fn with_ssse3(mut self) -> Self { - self.flags |= 1 << 1; - self - } - - pub fn monitor(&self) -> bool { - self.flags & (1 << 2) != 0 - } - - pub fn with_monitor(mut self) -> Self { - self.flags |= 1 << 2; - self - } - - pub fn vmx(&self) -> bool { - self.flags & (1 << 3) != 0 - } - - pub fn with_vmx(mut self) -> Self { - self.flags |= 1 << 3; - self - } - - pub fn fma3(&self) -> bool { - self.flags & (1 << 4) != 0 - } - - pub fn with_fma3(mut self) -> Self { - self.flags |= 1 << 4; - self - } - - pub fn cmpxchg16b(&self) -> bool { - self.flags & (1 << 5) != 0 - } - - pub fn with_cmpxchg16b(mut self) -> Self { - self.flags |= 1 << 5; - self - } - - pub fn sse4_1(&self) -> bool { - self.flags & (1 << 6) != 0 - } - - pub fn with_sse4_1(mut self) -> Self { - self.flags |= 1 << 6; - self - } - - pub fn sse4_2(&self) -> bool { - self.flags & (1 << 7) != 0 - } - - pub fn with_sse4_2(mut self) -> Self { - self.flags |= 1 << 7; - self - } - - pub fn with_sse4(self) -> Self { - self - .with_sse4_1() - .with_sse4_2() - } - - pub fn movbe(&self) -> bool { - self.flags & (1 << 8) != 0 - } - - pub fn with_movbe(mut self) -> Self { - self.flags |= 1 << 8; - self - } - - pub fn popcnt(&self) -> bool { - self.flags & (1 << 9) != 0 - } - - pub fn with_popcnt(mut self) -> Self { - self.flags |= 1 << 9; - self - } - - pub fn aesni(&self) -> bool { - self.flags & (1 << 10) != 0 - } - - pub fn with_aesni(mut self) -> Self { - self.flags |= 1 << 10; - self - } - - pub fn xsave(&self) -> bool { - self.flags & (1 << 11) != 0 - } - - pub fn with_xsave(mut self) -> Self { - self.flags |= 1 << 11; - self - } - - pub fn rdrand(&self) -> bool { - self.flags & (1 << 12) != 0 - } - - pub fn with_rdrand(mut self) -> Self { - self.flags |= 1 << 12; - self - } - - pub fn sgx(&self) -> bool { - self.flags & (1 << 13) != 0 - } - - pub fn with_sgx(mut self) -> Self { - self.flags |= 1 << 13; - self - } - - pub fn bmi1(&self) -> bool { - self.flags & (1 << 14) != 0 - } - - pub fn with_bmi1(mut self) -> Self { - self.flags |= 1 << 14; - self - } - - pub fn avx2(&self) -> bool { - self.flags & (1 << 15) != 0 - } - - pub fn with_avx2(mut self) -> Self { - self.flags |= 1 << 15; - self - } - - /// `bmi2` indicates support for the `BZHI`, `MULX`, `PDEP`, `PEXT`, `RORX`, `SARX`, `SHRX`, - /// and `SHLX` instructions. `bmi2` is implemented in all x86_64 chips that implement `bmi`, - /// except the amd `piledriver` and `steamroller` microarchitectures. - pub fn bmi2(&self) -> bool { - self.flags & (1 << 16) != 0 - } - - pub fn with_bmi2(mut self) -> Self { - self.flags |= 1 << 16; - self - } - - pub fn invpcid(&self) -> bool { - self.flags & (1 << 17) != 0 - } - - pub fn with_invpcid(mut self) -> Self { - self.flags |= 1 << 17; - self - } - - pub fn mpx(&self) -> bool { - self.flags & (1 << 18) != 0 - } - - pub fn with_mpx(mut self) -> Self { - self.flags |= 1 << 18; - self - } - - pub fn avx512_f(&self) -> bool { - self.flags & (1 << 19) != 0 - } - - pub fn with_avx512_f(mut self) -> Self { - self.flags |= 1 << 19; - self - } - - pub fn avx512_dq(&self) -> bool { - self.flags & (1 << 20) != 0 - } - - pub fn with_avx512_dq(mut self) -> Self { - self.flags |= 1 << 20; - self - } - - pub fn rdseed(&self) -> bool { - self.flags & (1 << 21) != 0 - } - - pub fn with_rdseed(mut self) -> Self { - self.flags |= 1 << 21; - self - } - - pub fn adx(&self) -> bool { - self.flags & (1 << 22) != 0 - } - - pub fn with_adx(mut self) -> Self { - self.flags |= 1 << 22; - self - } - - pub fn avx512_fma(&self) -> bool { - self.flags & (1 << 23) != 0 - } - - pub fn with_avx512_fma(mut self) -> Self { - self.flags |= 1 << 23; - self - } - - pub fn pcommit(&self) -> bool { - self.flags & (1 << 24) != 0 - } - - pub fn with_pcommit(mut self) -> Self { - self.flags |= 1 << 24; - self - } - - pub fn clflushopt(&self) -> bool { - self.flags & (1 << 25) != 0 - } - - pub fn with_clflushopt(mut self) -> Self { - self.flags |= 1 << 25; - self - } - - pub fn clwb(&self) -> bool { - self.flags & (1 << 26) != 0 - } - - pub fn with_clwb(mut self) -> Self { - self.flags |= 1 << 26; - self - } - - pub fn avx512_pf(&self) -> bool { - self.flags & (1 << 27) != 0 - } - - pub fn with_avx512_pf(mut self) -> Self { - self.flags |= 1 << 27; - self - } - - pub fn avx512_er(&self) -> bool { - self.flags & (1 << 28) != 0 - } - - pub fn with_avx512_er(mut self) -> Self { - self.flags |= 1 << 28; - self - } - - pub fn avx512_cd(&self) -> bool { - self.flags & (1 << 29) != 0 - } - - pub fn with_avx512_cd(mut self) -> Self { - self.flags |= 1 << 29; - self - } - - pub fn sha(&self) -> bool { - self.flags & (1 << 30) != 0 - } - - pub fn with_sha(mut self) -> Self { - self.flags |= 1 << 30; - self - } - - pub fn avx512_bw(&self) -> bool { - self.flags & (1 << 31) != 0 - } - - pub fn with_avx512_bw(mut self) -> Self { - self.flags |= 1 << 31; - self - } - - pub fn avx512_vl(&self) -> bool { - self.flags & (1 << 32) != 0 - } - - pub fn with_avx512_vl(mut self) -> Self { - self.flags |= 1 << 32; - self - } - - pub fn prefetchwt1(&self) -> bool { - self.flags & (1 << 33) != 0 - } - - pub fn with_prefetchwt1(mut self) -> Self { - self.flags |= 1 << 33; - self - } - - pub fn avx512_vbmi(&self) -> bool { - self.flags & (1 << 34) != 0 - } - - pub fn with_avx512_vbmi(mut self) -> Self { - self.flags |= 1 << 34; - self - } - - pub fn avx512_vbmi2(&self) -> bool { - self.flags & (1 << 35) != 0 - } - - pub fn with_avx512_vbmi2(mut self) -> Self { - self.flags |= 1 << 35; - self - } - - pub fn gfni(&self) -> bool { - self.flags & (1 << 36) != 0 - } - - pub fn with_gfni(mut self) -> Self { - self.flags |= 1 << 36; - self - } - - pub fn vaes(&self) -> bool { - self.flags & (1 << 37) != 0 - } - - pub fn with_vaes(mut self) -> Self { - self.flags |= 1 << 37; - self - } - - pub fn pclmulqdq(&self) -> bool { - self.flags & (1 << 38) != 0 - } - - pub fn with_pclmulqdq(mut self) -> Self { - self.flags |= 1 << 38; - self - } - - pub fn avx_vnni(&self) -> bool { - self.flags & (1 << 39) != 0 - } - - pub fn with_avx_vnni(mut self) -> Self { - self.flags |= 1 << 39; - self - } - - pub fn avx512_bitalg(&self) -> bool { - self.flags & (1 << 40) != 0 - } - - pub fn with_avx512_bitalg(mut self) -> Self { - self.flags |= 1 << 40; - self - } - - pub fn avx512_vpopcntdq(&self) -> bool { - self.flags & (1 << 41) != 0 - } - - pub fn with_avx512_vpopcntdq(mut self) -> Self { - self.flags |= 1 << 41; - self - } - - pub fn avx512_4vnniw(&self) -> bool { - self.flags & (1 << 42) != 0 - } - - pub fn with_avx512_4vnniw(mut self) -> Self { - self.flags |= 1 << 42; - self - } - - pub fn avx512_4fmaps(&self) -> bool { - self.flags & (1 << 43) != 0 - } - - pub fn with_avx512_4fmaps(mut self) -> Self { - self.flags |= 1 << 43; - self - } - - /// returns `true` if this `InstDecoder` has **all** `avx512` features enabled. - pub fn avx512(&self) -> bool { - let avx512_mask = - (1 << 19) | - (1 << 20) | - (1 << 23) | - (1 << 27) | - (1 << 28) | - (1 << 29) | - (1 << 31) | - (1 << 32) | - (1 << 34) | - (1 << 35) | - (1 << 40) | - (1 << 41) | - (1 << 42) | - (1 << 43); - - (self.flags & avx512_mask) == avx512_mask - } - - /// enable all `avx512` features on this `InstDecoder`. no real CPU, at time of writing, - /// actually has such a feature combination, but this is a useful overestimate for `avx512` - /// generally. - pub fn with_avx512(mut self) -> Self { - let avx512_mask = - (1 << 19) | - (1 << 20) | - (1 << 23) | - (1 << 27) | - (1 << 28) | - (1 << 29) | - (1 << 31) | - (1 << 32) | - (1 << 34) | - (1 << 35) | - (1 << 40) | - (1 << 41) | - (1 << 42) | - (1 << 43); - - self.flags |= avx512_mask; - self - } - - pub fn cx8(&self) -> bool { - self.flags & (1 << 44) != 0 - } - - pub fn with_cx8(mut self) -> Self { - self.flags |= 1 << 44; - self - } - - pub fn syscall(&self) -> bool { - self.flags & (1 << 45) != 0 - } - - pub fn with_syscall(mut self) -> Self { - self.flags |= 1 << 45; - self - } - - pub fn rdtscp(&self) -> bool { - self.flags & (1 << 46) != 0 - } - - pub fn with_rdtscp(mut self) -> Self { - self.flags |= 1 << 46; - self - } - - pub fn abm(&self) -> bool { - self.flags & (1 << 47) != 0 - } - - pub fn with_abm(mut self) -> Self { - self.flags |= 1 << 47; - self - } - - pub fn sse4a(&self) -> bool { - self.flags & (1 << 48) != 0 - } - - pub fn with_sse4a(mut self) -> Self { - self.flags |= 1 << 48; - self - } - - pub fn _3dnowprefetch(&self) -> bool { - self.flags & (1 << 49) != 0 - } - - pub fn with_3dnowprefetch(mut self) -> Self { - self.flags |= 1 << 49; - self - } - - pub fn xop(&self) -> bool { - self.flags & (1 << 50) != 0 - } - - pub fn with_xop(mut self) -> Self { - self.flags |= 1 << 50; - self - } - - pub fn skinit(&self) -> bool { - self.flags & (1 << 51) != 0 - } - - pub fn with_skinit(mut self) -> Self { - self.flags |= 1 << 51; - self - } - - pub fn tbm(&self) -> bool { - self.flags & (1 << 52) != 0 - } - - pub fn with_tbm(mut self) -> Self { - self.flags |= 1 << 52; - self - } - - pub fn intel_quirks(&self) -> bool { - self.flags & (1 << 53) != 0 - } - - pub fn with_intel_quirks(mut self) -> Self { - self.flags |= 1 << 53; - self - } - - pub fn amd_quirks(&self) -> bool { - self.flags & (1 << 54) != 0 - } - - pub fn with_amd_quirks(mut self) -> Self { - self.flags |= 1 << 54; - self - } - - pub fn avx(&self) -> bool { - self.flags & (1 << 55) != 0 - } - - pub fn with_avx(mut self) -> Self { - self.flags |= 1 << 55; - self - } - - pub fn svm(&self) -> bool { - self.flags & (1 << 56) != 0 - } - - pub fn with_svm(mut self) -> Self { - self.flags |= 1 << 56; - self - } - - /// `lahfsahf` is only unset for early revisions of 64-bit amd and intel chips. unfortunately - /// the clearest documentation on when these instructions were reintroduced into 64-bit - /// architectures seems to be - /// [wikipedia](https://en.wikipedia.org/wiki/X86-64#Older_implementations): - /// ```text - /// Early AMD64 and Intel 64 CPUs lacked LAHF and SAHF instructions in 64-bit mode. AMD - /// introduced these instructions (also in 64-bit mode) with their Athlon 64, Opteron and - /// Turion 64 revision D processors in March 2005[48][49][50] while Intel introduced the - /// instructions with the Pentium 4 G1 stepping in December 2005. The 64-bit version of Windows - /// 8.1 requires this feature.[47] - /// ``` - /// - /// this puts reintroduction of these instructions somewhere in the middle of prescott and k8 - /// lifecycles, for intel and amd respectively. because there is no specific uarch where these - /// features become enabled, prescott and k8 default to not supporting these instructions, - /// where later uarches support these instructions. - pub fn lahfsahf(&self) -> bool { - self.flags & (1 << 57) != 0 - } - - pub fn with_lahfsahf(mut self) -> Self { - self.flags |= 1 << 57; - self - } - - pub fn cmov(&self) -> bool { - self.flags & (1 << 58) != 0 - } - - pub fn with_cmov(mut self) -> Self { - self.flags |= 1 << 58; - self - } - - pub fn f16c(&self) -> bool { - self.flags & (1 << 59) != 0 - } - - pub fn with_f16c(mut self) -> Self { - self.flags |= 1 << 59; - self - } - - pub fn fma4(&self) -> bool { - self.flags & (1 << 60) != 0 - } - - pub fn with_fma4(mut self) -> Self { - self.flags |= 1 << 60; - self - } - - pub fn prefetchw(&self) -> bool { - self.flags & (1 << 61) != 0 - } +pub struct DecodeEverything {} - pub fn with_prefetchw(mut self) -> Self { - self.flags |= 1 << 61; - self - } - - pub fn tsx(&self) -> bool { - self.flags & (1 << 62) != 0 - } +impl Decoder<Arch> for DecodeEverything { + fn decode<T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>>(&self, words: &mut T) -> Result<Instruction, <Arch as yaxpeax_arch::Arch>::DecodeError> { + let mut instr = Instruction::invalid(); + self.decode_into(&mut instr, words)?; - pub fn with_tsx(mut self) -> Self { - self.flags |= 1 << 62; - self + Ok(instr) } - - pub fn lzcnt(&self) -> bool { - self.flags & (1 << 63) != 0 + #[inline(always)] + fn decode_into<T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>>(&self, instr: &mut Instruction, words: &mut T) -> Result<(), <Arch as yaxpeax_arch::Arch>::DecodeError> { + self.decode_with_annotation(instr, words, &mut NullSink) } +} - pub fn with_lzcnt(mut self) -> Self { - self.flags |= 1 << 63; - self - } +impl AnnotatingDecoder<Arch> for DecodeEverything { + type FieldDescription = FieldDescription; - /// optionally reject or reinterpret instruction according to the decoder's - /// declared extensions. - fn revise_instruction(&self, inst: &mut Instruction) -> Result<(), DecodeError> { - if inst.prefixes.evex().is_some() { - if !self.avx512() { - return Err(DecodeError::InvalidOpcode); - } else { - return Ok(()); - } - } - match inst.opcode { - Opcode::TZCNT => { - if !self.bmi1() { - // tzcnt is only supported if bmi1 is enabled. without bmi1, this decodes as - // bsf. - inst.opcode = Opcode::BSF; - } - } - Opcode::LDDQU | - Opcode::ADDSUBPS | - Opcode::ADDSUBPD | - Opcode::HADDPS | - Opcode::HSUBPS | - Opcode::HADDPD | - Opcode::HSUBPD | - Opcode::MOVSHDUP | - Opcode::MOVSLDUP | - Opcode::MOVDDUP | - Opcode::MONITOR | - Opcode::MWAIT => { - // via Intel section 5.7, SSE3 Instructions - if !self.sse3() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::PHADDW | - Opcode::PHADDSW | - Opcode::PHADDD | - Opcode::PHSUBW | - Opcode::PHSUBSW | - Opcode::PHSUBD | - Opcode::PABSB | - Opcode::PABSW | - Opcode::PABSD | - Opcode::PMADDUBSW | - Opcode::PMULHRSW | - Opcode::PSHUFB | - Opcode::PSIGNB | - Opcode::PSIGNW | - Opcode::PSIGND | - Opcode::PALIGNR => { - // via Intel section 5.8, SSSE3 Instructions - if !self.ssse3() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::PMULLD | - Opcode::PMULDQ | - Opcode::MOVNTDQA | - Opcode::BLENDPD | - Opcode::BLENDPS | - Opcode::BLENDVPD | - Opcode::BLENDVPS | - Opcode::PBLENDVB | - Opcode::BLENDW | - Opcode::PMINUW | - Opcode::PMINUD | - Opcode::PMINSB | - Opcode::PMINSD | - Opcode::PMAXUW | - Opcode::PMAXUD | - Opcode::PMAXSB | - Opcode::PMAXSD | - Opcode::ROUNDPS | - Opcode::ROUNDPD | - Opcode::ROUNDSS | - Opcode::ROUNDSD | - Opcode::PBLENDW | - Opcode::EXTRACTPS | - Opcode::INSERTPS | - Opcode::PINSRB | - Opcode::PINSRD | - Opcode::PINSRQ | - Opcode::PMOVSXBW | - Opcode::PMOVZXBW | - Opcode::PMOVSXBD | - Opcode::PMOVZXBD | - Opcode::PMOVSXWD | - Opcode::PMOVZXWD | - Opcode::PMOVSXBQ | - Opcode::PMOVZXBQ | - Opcode::PMOVSXWQ | - Opcode::PMOVZXWQ | - Opcode::PMOVSXDQ | - Opcode::PMOVZXDQ | - Opcode::DPPS | - Opcode::DPPD | - Opcode::MPSADBW | - Opcode::PHMINPOSUW | - Opcode::PTEST | - Opcode::PCMPEQQ | - Opcode::PEXTRB | - Opcode::PEXTRW | - Opcode::PEXTRD | - Opcode::PEXTRQ | - Opcode::PACKUSDW => { - // via Intel section 5.10, SSE4.1 Instructions - if !self.sse4_1() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::EXTRQ | - Opcode::INSERTQ | - Opcode::MOVNTSS | - Opcode::MOVNTSD => { - if !self.sse4a() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::CRC32 | - Opcode::PCMPESTRI | - Opcode::PCMPESTRM | - Opcode::PCMPISTRI | - Opcode::PCMPISTRM | - Opcode::PCMPGTQ => { - // via Intel section 5.11, SSE4.2 Instructions - if !self.sse4_2() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::AESDEC | - Opcode::AESDECLAST | - Opcode::AESENC | - Opcode::AESENCLAST | - Opcode::AESIMC | - Opcode::AESKEYGENASSIST => { - // via Intel section 5.12. AESNI AND PCLMULQDQ - if !self.aesni() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::PCLMULQDQ => { - // via Intel section 5.12. AESNI AND PCLMULQDQ - if !self.pclmulqdq() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::XABORT | - Opcode::XBEGIN | - Opcode::XEND | - Opcode::XTEST => { - if !self.tsx() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::SHA1MSG1 | - Opcode::SHA1MSG2 | - Opcode::SHA1NEXTE | - Opcode::SHA1RNDS4 | - Opcode::SHA256MSG1 | - Opcode::SHA256MSG2 | - Opcode::SHA256RNDS2 => { - if !self.sha() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::ENCLV | - Opcode::ENCLS | - Opcode::ENCLU => { - if !self.sgx() { - return Err(DecodeError::InvalidOpcode); - } - } - // AVX... - Opcode::VMOVDDUP | - Opcode::VPSHUFLW | - Opcode::VPSHUFHW | - Opcode::VHADDPS | - Opcode::VHSUBPS | - Opcode::VADDSUBPS | - Opcode::VCVTPD2DQ | - Opcode::VLDDQU | - Opcode::VCOMISD | - Opcode::VCOMISS | - Opcode::VUCOMISD | - Opcode::VUCOMISS | - Opcode::VADDPD | - Opcode::VADDPS | - Opcode::VADDSD | - Opcode::VADDSS | - Opcode::VADDSUBPD | - Opcode::VBLENDPD | - Opcode::VBLENDPS | - Opcode::VBLENDVPD | - Opcode::VBLENDVPS | - Opcode::VBROADCASTF128 | - Opcode::VBROADCASTI128 | - Opcode::VBROADCASTSD | - Opcode::VBROADCASTSS | - Opcode::VCMPSD | - Opcode::VCMPSS | - Opcode::VCMPPD | - Opcode::VCMPPS | - Opcode::VCVTDQ2PD | - Opcode::VCVTDQ2PS | - Opcode::VCVTPD2PS | - Opcode::VCVTPS2DQ | - Opcode::VCVTPS2PD | - Opcode::VCVTSS2SD | - Opcode::VCVTSI2SS | - Opcode::VCVTSI2SD | - Opcode::VCVTSD2SI | - Opcode::VCVTSD2SS | - Opcode::VCVTSS2SI | - Opcode::VCVTTPD2DQ | - Opcode::VCVTTPS2DQ | - Opcode::VCVTTSS2SI | - Opcode::VCVTTSD2SI | - Opcode::VDIVPD | - Opcode::VDIVPS | - Opcode::VDIVSD | - Opcode::VDIVSS | - Opcode::VDPPD | - Opcode::VDPPS | - Opcode::VEXTRACTF128 | - Opcode::VEXTRACTI128 | - Opcode::VEXTRACTPS | - Opcode::VFMADD132PD | - Opcode::VFMADD132PS | - Opcode::VFMADD132SD | - Opcode::VFMADD132SS | - Opcode::VFMADD213PD | - Opcode::VFMADD213PS | - Opcode::VFMADD213SD | - Opcode::VFMADD213SS | - Opcode::VFMADD231PD | - Opcode::VFMADD231PS | - Opcode::VFMADD231SD | - Opcode::VFMADD231SS | - Opcode::VFMADDSUB132PD | - Opcode::VFMADDSUB132PS | - Opcode::VFMADDSUB213PD | - Opcode::VFMADDSUB213PS | - Opcode::VFMADDSUB231PD | - Opcode::VFMADDSUB231PS | - Opcode::VFMSUB132PD | - Opcode::VFMSUB132PS | - Opcode::VFMSUB132SD | - Opcode::VFMSUB132SS | - Opcode::VFMSUB213PD | - Opcode::VFMSUB213PS | - Opcode::VFMSUB213SD | - Opcode::VFMSUB213SS | - Opcode::VFMSUB231PD | - Opcode::VFMSUB231PS | - Opcode::VFMSUB231SD | - Opcode::VFMSUB231SS | - Opcode::VFMSUBADD132PD | - Opcode::VFMSUBADD132PS | - Opcode::VFMSUBADD213PD | - Opcode::VFMSUBADD213PS | - Opcode::VFMSUBADD231PD | - Opcode::VFMSUBADD231PS | - Opcode::VFNMADD132PD | - Opcode::VFNMADD132PS | - Opcode::VFNMADD132SD | - Opcode::VFNMADD132SS | - Opcode::VFNMADD213PD | - Opcode::VFNMADD213PS | - Opcode::VFNMADD213SD | - Opcode::VFNMADD213SS | - Opcode::VFNMADD231PD | - Opcode::VFNMADD231PS | - Opcode::VFNMADD231SD | - Opcode::VFNMADD231SS | - Opcode::VFNMSUB132PD | - Opcode::VFNMSUB132PS | - Opcode::VFNMSUB132SD | - Opcode::VFNMSUB132SS | - Opcode::VFNMSUB213PD | - Opcode::VFNMSUB213PS | - Opcode::VFNMSUB213SD | - Opcode::VFNMSUB213SS | - Opcode::VFNMSUB231PD | - Opcode::VFNMSUB231PS | - Opcode::VFNMSUB231SD | - Opcode::VFNMSUB231SS | - Opcode::VGATHERDPD | - Opcode::VGATHERDPS | - Opcode::VGATHERQPD | - Opcode::VGATHERQPS | - Opcode::VHADDPD | - Opcode::VHSUBPD | - Opcode::VINSERTF128 | - Opcode::VINSERTI128 | - Opcode::VINSERTPS | - Opcode::VMASKMOVDQU | - Opcode::VMASKMOVPD | - Opcode::VMASKMOVPS | - Opcode::VMAXPD | - Opcode::VMAXPS | - Opcode::VMAXSD | - Opcode::VMAXSS | - Opcode::VMINPD | - Opcode::VMINPS | - Opcode::VMINSD | - Opcode::VMINSS | - Opcode::VMOVAPD | - Opcode::VMOVAPS | - Opcode::VMOVD | - Opcode::VMOVDQA | - Opcode::VMOVDQU | - Opcode::VMOVHLPS | - Opcode::VMOVHPD | - Opcode::VMOVHPS | - Opcode::VMOVLHPS | - Opcode::VMOVLPD | - Opcode::VMOVLPS | - Opcode::VMOVMSKPD | - Opcode::VMOVMSKPS | - Opcode::VMOVNTDQ | - Opcode::VMOVNTDQA | - Opcode::VMOVNTPD | - Opcode::VMOVNTPS | - Opcode::VMOVQ | - Opcode::VMOVSS | - Opcode::VMOVSD | - Opcode::VMOVSHDUP | - Opcode::VMOVSLDUP | - Opcode::VMOVUPD | - Opcode::VMOVUPS | - Opcode::VMPSADBW | - Opcode::VMULPD | - Opcode::VMULPS | - Opcode::VMULSD | - Opcode::VMULSS | - Opcode::VPABSB | - Opcode::VPABSD | - Opcode::VPABSW | - Opcode::VPACKSSDW | - Opcode::VPACKUSDW | - Opcode::VPACKSSWB | - Opcode::VPACKUSWB | - Opcode::VPADDB | - Opcode::VPADDD | - Opcode::VPADDQ | - Opcode::VPADDSB | - Opcode::VPADDSW | - Opcode::VPADDUSB | - Opcode::VPADDUSW | - Opcode::VPADDW | - Opcode::VPALIGNR | - Opcode::VPAND | - Opcode::VANDPD | - Opcode::VANDPS | - Opcode::VANDNPD | - Opcode::VANDNPS | - Opcode::VORPD | - Opcode::VORPS | - Opcode::VPANDN | - Opcode::VPAVGB | - Opcode::VPAVGW | - Opcode::VPBLENDD | - Opcode::VPBLENDVB | - Opcode::VPBLENDW | - Opcode::VPBROADCASTB | - Opcode::VPBROADCASTD | - Opcode::VPBROADCASTQ | - Opcode::VPBROADCASTW | - Opcode::VPCLMULQDQ | - Opcode::VPCMPEQB | - Opcode::VPCMPEQD | - Opcode::VPCMPEQQ | - Opcode::VPCMPEQW | - Opcode::VPCMPGTB | - Opcode::VPCMPGTD | - Opcode::VPCMPGTQ | - Opcode::VPCMPGTW | - Opcode::VPCMPESTRI | - Opcode::VPCMPESTRM | - Opcode::VPCMPISTRI | - Opcode::VPCMPISTRM | - Opcode::VPERM2F128 | - Opcode::VPERM2I128 | - Opcode::VPERMD | - Opcode::VPERMILPD | - Opcode::VPERMILPS | - Opcode::VPERMPD | - Opcode::VPERMPS | - Opcode::VPERMQ | - Opcode::VPEXTRB | - Opcode::VPEXTRD | - Opcode::VPEXTRQ | - Opcode::VPEXTRW | - Opcode::VPGATHERDD | - Opcode::VPGATHERDQ | - Opcode::VPGATHERQD | - Opcode::VPGATHERQQ | - Opcode::VPHADDD | - Opcode::VPHADDSW | - Opcode::VPHADDW | - Opcode::VPMADDUBSW | - Opcode::VPHMINPOSUW | - Opcode::VPHSUBD | - Opcode::VPHSUBSW | - Opcode::VPHSUBW | - Opcode::VPINSRB | - Opcode::VPINSRD | - Opcode::VPINSRQ | - Opcode::VPINSRW | - Opcode::VPMADDWD | - Opcode::VPMASKMOVD | - Opcode::VPMASKMOVQ | - Opcode::VPMAXSB | - Opcode::VPMAXSD | - Opcode::VPMAXSW | - Opcode::VPMAXUB | - Opcode::VPMAXUW | - Opcode::VPMAXUD | - Opcode::VPMINSB | - Opcode::VPMINSW | - Opcode::VPMINSD | - Opcode::VPMINUB | - Opcode::VPMINUW | - Opcode::VPMINUD | - Opcode::VPMOVMSKB | - Opcode::VPMOVSXBD | - Opcode::VPMOVSXBQ | - Opcode::VPMOVSXBW | - Opcode::VPMOVSXDQ | - Opcode::VPMOVSXWD | - Opcode::VPMOVSXWQ | - Opcode::VPMOVZXBD | - Opcode::VPMOVZXBQ | - Opcode::VPMOVZXBW | - Opcode::VPMOVZXDQ | - Opcode::VPMOVZXWD | - Opcode::VPMOVZXWQ | - Opcode::VPMULDQ | - Opcode::VPMULHRSW | - Opcode::VPMULHUW | - Opcode::VPMULHW | - Opcode::VPMULLQ | - Opcode::VPMULLD | - Opcode::VPMULLW | - Opcode::VPMULUDQ | - Opcode::VPOR | - Opcode::VPSADBW | - Opcode::VPSHUFB | - Opcode::VPSHUFD | - Opcode::VPSIGNB | - Opcode::VPSIGND | - Opcode::VPSIGNW | - Opcode::VPSLLD | - Opcode::VPSLLDQ | - Opcode::VPSLLQ | - Opcode::VPSLLVD | - Opcode::VPSLLVQ | - Opcode::VPSLLW | - Opcode::VPSRAD | - Opcode::VPSRAVD | - Opcode::VPSRAW | - Opcode::VPSRLD | - Opcode::VPSRLDQ | - Opcode::VPSRLQ | - Opcode::VPSRLVD | - Opcode::VPSRLVQ | - Opcode::VPSRLW | - Opcode::VPSUBB | - Opcode::VPSUBD | - Opcode::VPSUBQ | - Opcode::VPSUBSB | - Opcode::VPSUBSW | - Opcode::VPSUBUSB | - Opcode::VPSUBUSW | - Opcode::VPSUBW | - Opcode::VPTEST | - Opcode::VPUNPCKHBW | - Opcode::VPUNPCKHDQ | - Opcode::VPUNPCKHQDQ | - Opcode::VPUNPCKHWD | - Opcode::VPUNPCKLBW | - Opcode::VPUNPCKLDQ | - Opcode::VPUNPCKLQDQ | - Opcode::VPUNPCKLWD | - Opcode::VPXOR | - Opcode::VRCPPS | - Opcode::VROUNDPD | - Opcode::VROUNDPS | - Opcode::VROUNDSD | - Opcode::VROUNDSS | - Opcode::VRSQRTPS | - Opcode::VRSQRTSS | - Opcode::VRCPSS | - Opcode::VSHUFPD | - Opcode::VSHUFPS | - Opcode::VSQRTPD | - Opcode::VSQRTPS | - Opcode::VSQRTSS | - Opcode::VSQRTSD | - Opcode::VSUBPD | - Opcode::VSUBPS | - Opcode::VSUBSD | - Opcode::VSUBSS | - Opcode::VTESTPD | - Opcode::VTESTPS | - Opcode::VUNPCKHPD | - Opcode::VUNPCKHPS | - Opcode::VUNPCKLPD | - Opcode::VUNPCKLPS | - Opcode::VXORPD | - Opcode::VXORPS | - Opcode::VZEROUPPER | - Opcode::VZEROALL | - Opcode::VLDMXCSR | - Opcode::VSTMXCSR => { - // TODO: check a table for these - if !self.avx() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::VAESDEC | - Opcode::VAESDECLAST | - Opcode::VAESENC | - Opcode::VAESENCLAST | - Opcode::VAESIMC | - Opcode::VAESKEYGENASSIST => { - // TODO: check a table for these - if !self.avx() || !self.aesni() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::MOVBE => { - if !self.movbe() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::POPCNT => { - /* - * from the intel SDM: - * ``` - * Before an application attempts to use the POPCNT instruction, it must check that - * the processor supports SSE4.2 (if CPUID.01H:ECX.SSE4_2[bit 20] = 1) and POPCNT - * (if CPUID.01H:ECX.POPCNT[bit 23] = 1). - * ``` - */ - if self.intel_quirks() && (self.sse4_2() || self.popcnt()) { - return Ok(()); - } else if !self.popcnt() { - /* - * elsewhere from the amd APM: - * `Instruction Subsets and CPUID Feature Flags` on page 507 indicates that - * popcnt is present when the popcnt bit is reported by cpuid. this seems to be - * the less quirky default, so `intel_quirks` is considered the outlier, and - * before this default. - * */ - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::LZCNT => { - /* - * amd APM, `LZCNT` page 212: - * LZCNT is an Advanced Bit Manipulation (ABM) instruction. Support for the LZCNT - * instruction is indicated by CPUID Fn8000_0001_ECX[ABM] = 1. - * - * meanwhile the intel SDM simply states: - * ``` - * CPUID.EAX=80000001H:ECX.LZCNT[bit 5]: if 1 indicates the processor supports the - * LZCNT instruction. - * ``` - * - * so that's considered the less-quirky (default) case here. - * */ - if self.amd_quirks() && !self.abm() { - return Err(DecodeError::InvalidOpcode); - } else if !self.lzcnt() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::ADCX | - Opcode::ADOX => { - if !self.adx() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::VMRUN | - Opcode::VMLOAD | - Opcode::VMSAVE | - Opcode::CLGI | - Opcode::VMMCALL | - Opcode::INVLPGA => { - if !self.svm() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::STGI | - Opcode::SKINIT => { - if !self.svm() || !self.skinit() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::LAHF | - Opcode::SAHF => { - if !self.lahfsahf() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::VCVTPS2PH | - Opcode::VCVTPH2PS => { - /* - * from intel SDM: - * ``` - * 14.4.1 Detection of F16C Instructions Application using float 16 instruction - * must follow a detection sequence similar to AVX to ensure: • The OS has - * enabled YMM state management support, • The processor support AVX as - * indicated by the CPUID feature flag, i.e. CPUID.01H:ECX.AVX[bit 28] = 1. • - * The processor support 16-bit floating-point conversion instructions via a - * CPUID feature flag (CPUID.01H:ECX.F16C[bit 29] = 1). - * ``` - * - * TODO: only the VEX-coded variant of this instruction should be gated on `f16c`. - * the EVEX-coded variant should be gated on `avx512f` or `avx512vl` if not - * EVEX.512-coded. - */ - if !self.avx() || !self.f16c() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::RDRAND => { - if !self.rdrand() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::RDSEED => { - if !self.rdseed() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::MONITORX | Opcode::MWAITX | // these are gated on the `monitorx` and `mwaitx` cpuid bits, but are AMD-only. - Opcode::CLZERO | Opcode::RDPRU => { // again, gated on specific cpuid bits, but AMD-only. - if !self.amd_quirks() { - return Err(DecodeError::InvalidOpcode); - } - } - other => { - if !self.bmi1() { - if BMI1.contains(&other) { - return Err(DecodeError::InvalidOpcode); - } - } - if !self.bmi2() { - if BMI2.contains(&other) { - return Err(DecodeError::InvalidOpcode); - } - } - } - } - Ok(()) + #[inline(always)] + fn decode_with_annotation< + T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>, + S: DescriptionSink<Self::FieldDescription> + >(&self, instr: &mut Instruction, words: &mut T, sink: &mut S) -> Result<(), <Arch as yaxpeax_arch::Arch>::DecodeError> { + decode_with_annotation(self, instr, words, sink) } } @@ -4194,7 +2852,7 @@ impl Default for InstDecoder { /// instruction defined in any extension. fn default() -> Self { Self { - flags: 0xffffffff_ffffffff, + flags: [0xffffffff_ffffffff, 0xffffffff_ffffffff], } } } @@ -4202,16 +2860,7 @@ impl Default for InstDecoder { impl Decoder<Arch> for InstDecoder { fn decode<T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>>(&self, words: &mut T) -> Result<Instruction, <Arch as yaxpeax_arch::Arch>::DecodeError> { let mut instr = Instruction::invalid(); - DecodeCtx::new().read_with_annotations(self, words, &mut instr, &mut NullSink)?; - - instr.length = words.offset() as u8; - if words.offset() > 15 { - return Err(DecodeError::TooLong); - } - - if self != &InstDecoder::default() { - self.revise_instruction(&mut instr)?; - } + self.decode_into(&mut instr, words)?; Ok(instr) } @@ -4229,19 +2878,26 @@ impl AnnotatingDecoder<Arch> for InstDecoder { T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>, S: DescriptionSink<Self::FieldDescription> >(&self, instr: &mut Instruction, words: &mut T, sink: &mut S) -> Result<(), <Arch as yaxpeax_arch::Arch>::DecodeError> { - DecodeCtx::new().read_with_annotations(self, words, instr, sink)?; - - instr.length = words.offset() as u8; - if words.offset() > 15 { - return Err(DecodeError::TooLong); - } + decode_with_annotation(self, instr, words, sink) + } +} - if self != &InstDecoder::default() { - self.revise_instruction(instr)?; - } +#[inline(always)] +fn decode_with_annotation< + D: IsaSettings, + T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>, + S: DescriptionSink<FieldDescription> +>(isa_settings: &D, instr: &mut Instruction, words: &mut T, sink: &mut S) -> Result<(), <Arch as yaxpeax_arch::Arch>::DecodeError> { + DecodeCtx::new().read_with_annotations(isa_settings, words, instr, sink)?; - Ok(()) + instr.length = words.offset() as u8; + if words.offset() > 15 { + return Err(DecodeError::TooLong); } + + isa_settings.revise_instruction(instr)?; + + Ok(()) } impl Opcode { @@ -6725,9 +5381,10 @@ fn read_opc_hotpath< #[cfg_attr(feature="profiling", inline(never))] #[cfg_attr(not(feature="profiling"), inline(always))] fn read_with_annotations< + D: IsaSettings, T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>, S: DescriptionSink<FieldDescription>, ->(mut self, decoder: &InstDecoder, words: &mut T, instruction: &mut Instruction, sink: &mut S) -> Result<(), DecodeError> { +>(mut self, isa_settings: &D, words: &mut T, instruction: &mut Instruction, sink: &mut S) -> Result<(), DecodeError> { words.mark(); let mut nextb = words.next().ok().ok_or(DecodeError::ExhaustedInput)?; let mut next_rec = OPCODES[nextb as usize]; @@ -6875,7 +5532,7 @@ fn read_with_annotations< record.operand() }; - self.read_operands(decoder, words, instruction, record, sink)?; + self.read_operands(isa_settings, words, instruction, record, sink)?; if self.check_lock { if !instruction.opcode.can_lock() || !instruction.operands[0].is_memory() { @@ -6944,9 +5601,10 @@ fn read_avx_prefixed< #[cfg_attr(feature="profiling", inline(never))] #[cfg_attr(not(feature="profiling"), inline(always))] fn read_operands< + D: IsaSettings, T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>, S: DescriptionSink<FieldDescription> ->(&mut self, decoder: &InstDecoder, words: &mut T, instruction: &mut Instruction, operand_code: OperandCode, sink: &mut S) -> Result<(), DecodeError> { +>(&mut self, isa_settings: &D, words: &mut T, instruction: &mut Instruction, operand_code: OperandCode, sink: &mut S) -> Result<(), DecodeError> { sink.record( words.offset() as u32 * 8 - 1, words.offset() as u32 * 8 - 1, InnerDescription::Boundary("opcode ends/operands begin (typically)") @@ -10070,7 +8728,7 @@ fn read_operands< instruction.opcode = Opcode::LFENCE; // Intel's manual accepts m != 0, AMD supports m != 0 though the manual // doesn't say (tested on threadripper) - if !decoder.amd_quirks() && !decoder.intel_quirks() { + if !isa_settings.amd_quirks() && !isa_settings.intel_quirks() { if m != 0 { return Err(DecodeError::InvalidOperand); } @@ -10080,7 +8738,7 @@ fn read_operands< instruction.opcode = Opcode::MFENCE; // Intel's manual accepts m != 0, AMD supports m != 0 though the manual // doesn't say (tested on threadripper) - if !decoder.amd_quirks() && !decoder.intel_quirks() { + if !isa_settings.amd_quirks() && !isa_settings.intel_quirks() { if m != 0 { return Err(DecodeError::InvalidOperand); } @@ -10090,7 +8748,7 @@ fn read_operands< instruction.opcode = Opcode::SFENCE; // Intel's manual accepts m != 0, AMD supports m != 0 though the manual // doesn't say (tested on threadripper) - if !decoder.amd_quirks() && !decoder.intel_quirks() { + if !isa_settings.amd_quirks() && !isa_settings.intel_quirks() { if m != 0 { return Err(DecodeError::InvalidOperand); } diff --git a/src/long_mode/uarch.rs b/src/long_mode/uarch.rs index bfd4887..5af0175 100644 --- a/src/long_mode/uarch.rs +++ b/src/long_mode/uarch.rs @@ -1,12 +1,24 @@ +//! information for AMD and Intel microarchitectures in the modules below is sourced from a +//! combination of Wikipedia (especially for dates), one-off research for particular +//! microarchitectures, and `InstLatx64`'s CPUID dumps via [chip directory](https://github.com/iximeow/chip_directory). +//! +//! these microarchitecture-specific decoders are relatively rarely used, but generally should be +//! accurate. + pub mod amd { - //! most information about instruction set extensions for microarchitectures here was sourced - //! from - //! [https://en.wikipedia.org/wiki/AMD_Accelerated_Processing_Unit#Feature_overview](https://docs.rs/yaxpeax-x86/0.0.12/yaxpeax_x86/protected_mode/uarch/intel/index.html) + //! initial information for the mircoarchitecture (families) described here came from a + //! combination of the Wikipedia pages + //! [https://en.wikipedia.org/wiki/AMD_Accelerated_Processing_Unit#Feature_overview](https://en.wikipedia.org/wiki/AMD_Accelerated_Processing_Unit#Feature_overview) //! and - //! [https://en.wikipedia.org/wiki/Template:AMD_x86_CPU_features](https://docs.rs/yaxpeax-x86/0.0.12/yaxpeax_x86/protected_mode/uarch/intel/index.html). - //! these mappings are best-effort but fairly unused, so a critical eye should be kept towards - //! these decoders rejecting instructions they should not, or incorrectly accepting - //! instructions. + //! [https://en.wikipedia.org/wiki/Template:AMD_x86_CPU_features](https://en.wikipedia.org/wiki/Template:AMD_x86_CPU_features). + //! it has been since "augmented" by the CPUID dumps from InstLatx64, via [chip + //! directory](https://github.com/iximeow/chip_directory/tree/no-gods-no-/x86). scare quotes + //! because in several cases CPUID measurement error adds, rather than removes, ambiguity. + //! additionally, for some CPU features, InstLatx64 has CPUID dumps of early engineering + //! samples where features are not present. later production steppings of those parts do + //! universally have the corresponding feature, which makes it less obvious which features are + //! universally present in a family, standardized in a following architecture, unevenly present + //! due to market segmentation, and so on. //! //! microarchitectures as defined here are with respect to flags reported by CPUID. notably, //! `Zen` does not report `FMA4` support by `CPUID`, but instructions in that extension @@ -35,9 +47,6 @@ pub mod amd { .with_abm() .with_lahfsahf() .with_sse3() - .with_ssse3() - .with_sse4() - .with_sse4_2() .with_sse4a() } @@ -45,6 +54,9 @@ pub mod amd { /// support among other extensions, and are notable for including `AESNI`. pub fn bulldozer() -> InstDecoder { k10() + .with_ssse3() + .with_sse4() + .with_sse4_2() .with_bmi1() .with_aesni() .with_pclmulqdq() @@ -52,6 +64,8 @@ pub mod amd { .with_avx() .with_fma4() .with_xop() + .with_xsave() + .with_skinit() } /// `Piledriver` was the successor to `Bulldozer`, launched in 2012. @@ -86,21 +100,81 @@ pub mod amd { /// instructions to AVX2 and discarded FMA4, TBM, and XOP extensions. they also gained ADX, /// SHA, RDSEED, and other extensions. pub fn zen() -> InstDecoder { + // no nice way to *un*set feature bits, but several extensions were dropped. + // so, start again from K10. k10() + // first, bundle all the K10->Bulldozer features.. + .with_ssse3() + .with_sse4() + .with_sse4_2() + .with_bmi1() + .with_aesni() + .with_pclmulqdq() + .with_f16c() .with_avx() + .with_xsave() + .with_skinit() + // now all the Bulldozer (/Piledriver/Steamroller/Excavator)->Zen features .with_avx2() - .with_bmi1() .with_aesni() .with_pclmulqdq() .with_f16c() .with_movbe() .with_bmi2() - .with_rdrand() .with_adx() .with_sha() + .with_rdrand() .with_rdseed() .with_fma3() - // TODO: XSAVEC, XSAVES, XRSTORS, CLFLUSHOPT, CLZERO? + + .with_xsavec() + .with_xsaves() + .with_xsaveopt() + .with_clflushopt() + .with_clwb() + .with_fsgsbase() + .with_monitorx() + } + + /// `Zen 2`, launched in 2019, succeeded `Zen`/`Zen+`. there aren't many instruction set + /// extensions here, but `clwb`, `rdpid`, and `wbnoinvd` show up here. + pub fn zen2() -> InstDecoder { + zen() + .with_clwb() + .with_rdpid() + .with_wbnoinvd() + } + + /// `Zen 3`, launched in 2020, succeeded `Zen 2`. like `Zen 2`, there aren't many instruction + /// set extensions here. + pub fn zen3() -> InstDecoder { + zen2() + .with_invpcid() + .with_vaes() + .with_vpclmulqdq() + } + + /// `Zen 4`, launched in 2022, succeeded `Zen 3`. `Zen 4` is notable for being the first AMD + /// processor family supporting AVX-512. + pub fn zen4() -> InstDecoder { + zen3() + .with_avx512_f() + .with_avx512_vl() + .with_avx512_bw() + .with_avx512_cd() + .with_avx512_cd() + .with_avx512_vbmi() + .with_avx512_vbmi2() + .with_avx512_vpopcntdq() + .with_gfni() + } + + /// `Zen 5`, launched in 2024, succeeded `Zen 4`. `Zen 5` adds only a few additional + /// instructions; some AVX-512 features, `enqcmd`, and `movdir64b`. + pub fn zen5() -> InstDecoder { + zen4() + .with_movdir64b() + .with_enqcmd() } } diff --git a/src/protected_mode/isa_settings.rs b/src/protected_mode/isa_settings.rs new file mode 100644 index 0000000..55d18a7 --- /dev/null +++ b/src/protected_mode/isa_settings.rs @@ -0,0 +1,952 @@ +use super::{BMI1, BMI2, DecodeError, DecodeEverything, InstDecoder, Instruction, Opcode}; + +macro_rules! gen_isa_settings { + ( + $( + $(#[$doc:meta])* + $feature:ident, + $(#[$set_doc:item])* + $set_feature:ident = $idx:expr; + )+ + + { + $( + $(#[$composite_doc:meta])* + $composite_feature:ident = { + $first_inner_feature:ident + $(,$($inner_feature:ident$(,)?)+)? + }$(,)? + )* + } + + { + $( + $(#[$composite_set_doc:meta])* + $composite_set_feature:ident = { + $set_first_inner_feature:ident + $(,$($set_inner_feature:ident$(,)?)+)? + }$(,)? + )* + } + ) => { + /// specific decode settings controlling how an x86 byte sequence is interpreted. + /// + /// this currently exists to specify which extensions are to be accepted or rejected. the two + /// implementations provided by `yaxpeax-x86` are: + /// * [`InstDecoder`], providing configurable enablement or disablement per-extension + /// * [`DecodeEverything`], which allows all extensions supported by `yaxpeax-x86` + /// + /// notably, `InstDecoder::default()` and `DecodeEverything` are functionally equivalent in that + /// they accept all extensions supported by the decoder. + /// + /// TODO: many additional extension support flags. + /// * extended MMX (see `sha256:daee4e23dac983f1744126352d40cc71d47b4a9283a2a1e473837728ca9c51ac`) + /// * lots of others... tile extensions... + pub trait IsaSettings { + $( + $(#[$doc])* + fn $feature(&self) -> bool; + )+ + + $( + $(#[$composite_doc])* + fn $composite_feature(&self) -> bool { + self.$first_inner_feature() + $($(&& self.$inner_feature())+)? + } + )* + + fn revise_instruction(&self, inst: &mut Instruction) -> Result<(), DecodeError> { + revise_instruction(self, inst) + } + } + + impl IsaSettings for DecodeEverything { + $(fn $feature(&self) -> bool { true })+ + + fn revise_instruction(&self, _inst: &mut Instruction) -> Result<(), DecodeError> { + Ok(()) + } + } + + impl IsaSettings for InstDecoder { + $( + fn $feature(&self) -> bool { + let i = $idx as usize; + self.flags[i / 64] & (1 << (i % 64)) != 0 + } + )+ + } + + impl InstDecoder { + $( + $(#[$set_doc])* + pub fn $set_feature(mut self) -> Self { + let i = $idx as usize; + self.flags[i / 64] |= 1 << (i % 64); + self + } + )+ + + $( + $(#[$composite_set_doc])* + pub fn $composite_set_feature(&self) -> Self { + self.$set_first_inner_feature() + $($(.$set_inner_feature())+)? + } + )* + + $( + $(#[$doc])* + pub fn $feature(&self) -> bool { + <Self as IsaSettings>::$feature(self) + } + )+ + + $( + $(#[$composite_doc])* + pub fn $composite_feature(&self) -> bool { + <Self as IsaSettings>::$composite_feature(self) + } + )* + } + } +} +gen_isa_settings!( + _3dnow, with_3dnow = 1; + _3dnowprefetch, with_3dnowprefetch = 2; + abm, with_abm = 3; + adx, with_adx = 4; + aesni, with_aesni = 5; + amd_quirks, with_amd_quirks = 6; + avx, with_avx = 7; + avx2, with_avx2 = 8; + avx512_4fmaps, with_avx512_4fmaps = 10; + avx512_4vnniw, with_avx512_4vnniw = 11; + avx512_bitalg, with_avx512_bitalg = 12; + avx512_bw, with_avx512_bw = 13; + avx512_cd, with_avx512_cd = 14; + avx512_dq, with_avx512_dq = 15; + avx512_er, with_avx512_er = 16; + avx512_f, with_avx512_f = 17; + avx512_fma, with_avx512_fma = 18; + avx512_pf, with_avx512_pf = 19; + avx512_vbmi, with_avx512_vbmi = 20; + avx512_vbmi2, with_avx512_vbmi2 = 21; + avx512_vl, with_avx512_vl = 22; + avx512_vpopcntdq, with_avx512_vpopcntdq = 23; + avx_vnni, with_avx_vnni = 24; + bmi1, with_bmi1 = 25; + #[doc="`bmi2` indicates support for the `BZHI`, `MULX`, `PDEP`, `PEXT`, `RORX`, `SARX`, `SHRX`, "] + #[doc="and `SHLX` instructions. `bmi2` is implemented in all x86_64 chips that implement `bmi`, "] + #[doc="except the amd `piledriver` and `steamroller` microarchitectures."] + bmi2, with_bmi2 = 26; + clflushopt, with_clflushopt = 27; + clwb, with_clwb = 28; + cmov, with_cmov = 29; + cmpxchg16b, with_cmpxchg16b = 30; + cx8, with_cx8 = 31; + f16c, with_f16c = 32; + fma3, with_fma3 = 33; + fma4, with_fma4 = 34; + gfni, with_gfni = 35; + intel_quirks, with_intel_quirks = 36; + invpcid, with_invpcid = 37; + #[doc="`lahfsahf` is only unset for early revisions of 64-bit amd and intel chips. unfortunately"] + #[doc="the clearest documentation on when these instructions were reintroduced into 64-bit"] + #[doc="architectures seems to be"] + #[doc="[wikipedia](https://en.wikipedia.org/wiki/X86-64#Older_implementations):"] + #[doc="```text"] + #[doc="Early AMD64 and Intel 64 CPUs lacked LAHF and SAHF instructions in 64-bit mode. AMD"] + #[doc="introduced these instructions (also in 64-bit mode) with their Athlon 64, Opteron and"] + #[doc="Turion 64 revision D processors in March 2005[48][49][50] while Intel introduced the"] + #[doc="instructions with the Pentium 4 G1 stepping in December 2005. The 64-bit version of Windows"] + #[doc="8.1 requires this feature.[47]"] + #[doc="```"] + #[doc=""] + #[doc="this puts reintroduction of these instructions somewhere in the middle of prescott and k8"] + #[doc="lifecycles, for intel and amd respectively. because there is no specific uarch where these"] + #[doc="features become enabled, prescott and k8 default to not supporting these instructions,"] + #[doc="where later uarches support these instructions."] + lahfsahf, with_lahfsahf = 38; + lzcnt, with_lzcnt = 39; + monitor, with_monitor = 40; + movbe, with_movbe = 41; + mpx, with_mpx = 42; + pclmulqdq, with_pclmulqdq = 43; + pcommit, with_pcommit = 44; + popcnt, with_popcnt = 45; + prefetchw, with_prefetchw = 46; + prefetchwt1, with_prefetchwt1 = 47; + rdrand, with_rdrand = 48; + rdseed, with_rdseed = 49; + rdtscp, with_rdtscp = 50; + sgx, with_sgx = 51; + sha, with_sha = 52; + skinit, with_skinit = 53; + sse3, with_sse3 = 54; + sse4_1, with_sse4_1 = 55; + sse4_2, with_sse4_2 = 56; + sse4a, with_sse4a = 57; + ssse3, with_ssse3 = 58; + svm, with_svm = 59; + syscall, with_syscall = 60; + tbm, with_tbm = 61; + tsx, with_tsx = 62; + vaes, with_vaes = 63; + vmx, with_vmx = 64; + xop, with_xop = 65; + xsave, with_xsave = 66; + + { + sse4 = { + sse4_1, + sse4_2, + } + + #[doc = "returns `true` if this `InstDecoder` has **all** `avx512` features enabled."] + avx512 = { + avx512_4fmaps, + avx512_4vnniw, + avx512_bitalg, + avx512_bw, + avx512_cd, + avx512_dq, + avx512_er, + avx512_f, + avx512_fma, + avx512_pf, + avx512_vbmi, + avx512_vbmi2, + avx512_vl, + avx512_vpopcntdq, + }, + } + + { + with_sse4 = { + with_sse4_1, + with_sse4_2, + } + + with_avx512 = { + with_avx512_4fmaps, + with_avx512_4vnniw, + with_avx512_bitalg, + with_avx512_bw, + with_avx512_cd, + with_avx512_dq, + with_avx512_er, + with_avx512_f, + with_avx512_fma, + with_avx512_pf, + with_avx512_vbmi, + with_avx512_vbmi2, + with_avx512_vl, + with_avx512_vpopcntdq, + } + } +); + +/// optionally reject or reinterpret instruction according to settings for this decode +/// operation. +fn revise_instruction<D: IsaSettings + ?Sized>(settings: &D, inst: &mut Instruction) -> Result<(), DecodeError> { + if inst.prefixes.evex().is_some() { + if !settings.avx512() { + return Err(DecodeError::InvalidOpcode); + } else { + return Ok(()); + } + } + match inst.opcode { + // original 3dnow instructions. see also + // `3DNow-Technology-Manual.pdf` + // * sha256: daee4e23dac983f1744126352d40cc71d47b4a9283a2a1e473837728ca9c51ac + // * ref: https://www.amd.com/content/dam/amd/en/documents/archived-tech-docs/programmer-references/21928.pdf + // * order# 21928 + Opcode::FEMMS | + Opcode::PAVGUSB | + Opcode::PFADD | + Opcode::PFSUB | + Opcode::PFSUBR | + Opcode::PFACC | + Opcode::PFCMPGE | + Opcode::PFCMPGT | + Opcode::PFCMPEQ | + Opcode::PFMAX | + Opcode::PFMIN | + Opcode::PI2FD | + Opcode::PF2ID | + Opcode::PFRCP | + Opcode::PFRSQRT | + Opcode::PFMUL | + Opcode::PFRCPIT1 | + Opcode::PFRCPIT2 | + Opcode::PFRSQIT1 | + Opcode::PMULHRW => { + if !settings._3dnow() { + return Err(DecodeError::InvalidOpcode); + } + } + // later extension to 3dnow. see also + // `AMD-Extensions-to-the-3DNow-and-MMX-Instruction-Sets.pdf` + // * sha256: ad847bd6877a682296fc584b4bbee354bf84c57bb97ba57e9c9adfc63cc5f465 + // * ref: https://refspecs.linuxfoundation.org/AMD-extensions.pdf + // * order# 22466 + Opcode::PF2IW | + Opcode::PFNACC | + Opcode::PFPNACC | + Opcode::PI2FW | + Opcode::PSWAPD => { + if !settings._3dnow() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::TZCNT => { + if !settings.bmi1() { + // tzcnt is only supported if bmi1 is enabled. without bmi1, this decodes as + // bsf. + inst.opcode = Opcode::BSF; + } + } + Opcode::LDDQU | + Opcode::ADDSUBPS | + Opcode::ADDSUBPD | + Opcode::HADDPS | + Opcode::HSUBPS | + Opcode::HADDPD | + Opcode::HSUBPD | + Opcode::MOVSHDUP | + Opcode::MOVSLDUP | + Opcode::MOVDDUP | + Opcode::MONITOR | + Opcode::MWAIT => { + // via Intel section 5.7, SSE3 Instructions + if !settings.sse3() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::PHADDW | + Opcode::PHADDSW | + Opcode::PHADDD | + Opcode::PHSUBW | + Opcode::PHSUBSW | + Opcode::PHSUBD | + Opcode::PABSB | + Opcode::PABSW | + Opcode::PABSD | + Opcode::PMADDUBSW | + Opcode::PMULHRSW | + Opcode::PSHUFB | + Opcode::PSIGNB | + Opcode::PSIGNW | + Opcode::PSIGND | + Opcode::PALIGNR => { + // via Intel section 5.8, SSSE3 Instructions + if !settings.ssse3() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::PMULLD | + Opcode::PMULDQ | + Opcode::MOVNTDQA | + Opcode::BLENDPD | + Opcode::BLENDPS | + Opcode::BLENDVPD | + Opcode::BLENDVPS | + Opcode::PBLENDVB | + Opcode::BLENDW | + Opcode::PMINUW | + Opcode::PMINUD | + Opcode::PMINSB | + Opcode::PMINSD | + Opcode::PMAXUW | + Opcode::PMAXUD | + Opcode::PMAXSB | + Opcode::PMAXSD | + Opcode::ROUNDPS | + Opcode::ROUNDPD | + Opcode::ROUNDSS | + Opcode::ROUNDSD | + Opcode::PBLENDW | + Opcode::EXTRACTPS | + Opcode::INSERTPS | + Opcode::PINSRB | + Opcode::PINSRD | + Opcode::PINSRQ | + Opcode::PMOVSXBW | + Opcode::PMOVZXBW | + Opcode::PMOVSXBD | + Opcode::PMOVZXBD | + Opcode::PMOVSXWD | + Opcode::PMOVZXWD | + Opcode::PMOVSXBQ | + Opcode::PMOVZXBQ | + Opcode::PMOVSXWQ | + Opcode::PMOVZXWQ | + Opcode::PMOVSXDQ | + Opcode::PMOVZXDQ | + Opcode::DPPS | + Opcode::DPPD | + Opcode::MPSADBW | + Opcode::PHMINPOSUW | + Opcode::PTEST | + Opcode::PCMPEQQ | + Opcode::PEXTRB | + Opcode::PEXTRW | + Opcode::PEXTRD | + Opcode::PEXTRQ | + Opcode::PACKUSDW => { + // via Intel section 5.10, SSE4.1 Instructions + if !settings.sse4_1() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::EXTRQ | + Opcode::INSERTQ | + Opcode::MOVNTSS | + Opcode::MOVNTSD => { + if !settings.sse4a() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::CRC32 | + Opcode::PCMPESTRI | + Opcode::PCMPESTRM | + Opcode::PCMPISTRI | + Opcode::PCMPISTRM | + Opcode::PCMPGTQ => { + // via Intel section 5.11, SSE4.2 Instructions + if !settings.sse4_2() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::AESDEC | + Opcode::AESDECLAST | + Opcode::AESENC | + Opcode::AESENCLAST | + Opcode::AESIMC | + Opcode::AESKEYGENASSIST => { + // via Intel section 5.12. AESNI AND PCLMULQDQ + if !settings.aesni() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::PCLMULQDQ => { + // via Intel section 5.12. AESNI AND PCLMULQDQ + if !settings.pclmulqdq() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::XABORT | + Opcode::XBEGIN | + Opcode::XEND | + Opcode::XTEST => { + if !settings.tsx() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::SHA1MSG1 | + Opcode::SHA1MSG2 | + Opcode::SHA1NEXTE | + Opcode::SHA1RNDS4 | + Opcode::SHA256MSG1 | + Opcode::SHA256MSG2 | + Opcode::SHA256RNDS2 => { + if !settings.sha() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::ENCLV | + Opcode::ENCLS | + Opcode::ENCLU => { + if !settings.sgx() { + return Err(DecodeError::InvalidOpcode); + } + } + // AVX... + Opcode::VMOVDDUP | + Opcode::VPSHUFLW | + Opcode::VPSHUFHW | + Opcode::VHADDPS | + Opcode::VHSUBPS | + Opcode::VADDSUBPS | + Opcode::VCVTPD2DQ | + Opcode::VLDDQU | + Opcode::VCOMISD | + Opcode::VCOMISS | + Opcode::VUCOMISD | + Opcode::VUCOMISS | + Opcode::VADDPD | + Opcode::VADDPS | + Opcode::VADDSD | + Opcode::VADDSS | + Opcode::VADDSUBPD | + Opcode::VBLENDPD | + Opcode::VBLENDPS | + Opcode::VBLENDVPD | + Opcode::VBLENDVPS | + Opcode::VBROADCASTF128 | + Opcode::VBROADCASTI128 | + Opcode::VBROADCASTSD | + Opcode::VBROADCASTSS | + Opcode::VCMPSD | + Opcode::VCMPSS | + Opcode::VCMPPD | + Opcode::VCMPPS | + Opcode::VCVTDQ2PD | + Opcode::VCVTDQ2PS | + Opcode::VCVTPD2PS | + Opcode::VCVTPS2DQ | + Opcode::VCVTPS2PD | + Opcode::VCVTSS2SD | + Opcode::VCVTSI2SS | + Opcode::VCVTSI2SD | + Opcode::VCVTSD2SI | + Opcode::VCVTSD2SS | + Opcode::VCVTSS2SI | + Opcode::VCVTTPD2DQ | + Opcode::VCVTTPS2DQ | + Opcode::VCVTTSS2SI | + Opcode::VCVTTSD2SI | + Opcode::VDIVPD | + Opcode::VDIVPS | + Opcode::VDIVSD | + Opcode::VDIVSS | + Opcode::VDPPD | + Opcode::VDPPS | + Opcode::VEXTRACTF128 | + Opcode::VEXTRACTI128 | + Opcode::VEXTRACTPS | + Opcode::VFMADD132PD | + Opcode::VFMADD132PS | + Opcode::VFMADD132SD | + Opcode::VFMADD132SS | + Opcode::VFMADD213PD | + Opcode::VFMADD213PS | + Opcode::VFMADD213SD | + Opcode::VFMADD213SS | + Opcode::VFMADD231PD | + Opcode::VFMADD231PS | + Opcode::VFMADD231SD | + Opcode::VFMADD231SS | + Opcode::VFMADDSUB132PD | + Opcode::VFMADDSUB132PS | + Opcode::VFMADDSUB213PD | + Opcode::VFMADDSUB213PS | + Opcode::VFMADDSUB231PD | + Opcode::VFMADDSUB231PS | + Opcode::VFMSUB132PD | + Opcode::VFMSUB132PS | + Opcode::VFMSUB132SD | + Opcode::VFMSUB132SS | + Opcode::VFMSUB213PD | + Opcode::VFMSUB213PS | + Opcode::VFMSUB213SD | + Opcode::VFMSUB213SS | + Opcode::VFMSUB231PD | + Opcode::VFMSUB231PS | + Opcode::VFMSUB231SD | + Opcode::VFMSUB231SS | + Opcode::VFMSUBADD132PD | + Opcode::VFMSUBADD132PS | + Opcode::VFMSUBADD213PD | + Opcode::VFMSUBADD213PS | + Opcode::VFMSUBADD231PD | + Opcode::VFMSUBADD231PS | + Opcode::VFNMADD132PD | + Opcode::VFNMADD132PS | + Opcode::VFNMADD132SD | + Opcode::VFNMADD132SS | + Opcode::VFNMADD213PD | + Opcode::VFNMADD213PS | + Opcode::VFNMADD213SD | + Opcode::VFNMADD213SS | + Opcode::VFNMADD231PD | + Opcode::VFNMADD231PS | + Opcode::VFNMADD231SD | + Opcode::VFNMADD231SS | + Opcode::VFNMSUB132PD | + Opcode::VFNMSUB132PS | + Opcode::VFNMSUB132SD | + Opcode::VFNMSUB132SS | + Opcode::VFNMSUB213PD | + Opcode::VFNMSUB213PS | + Opcode::VFNMSUB213SD | + Opcode::VFNMSUB213SS | + Opcode::VFNMSUB231PD | + Opcode::VFNMSUB231PS | + Opcode::VFNMSUB231SD | + Opcode::VFNMSUB231SS | + Opcode::VGATHERDPD | + Opcode::VGATHERDPS | + Opcode::VGATHERQPD | + Opcode::VGATHERQPS | + Opcode::VHADDPD | + Opcode::VHSUBPD | + Opcode::VINSERTF128 | + Opcode::VINSERTI128 | + Opcode::VINSERTPS | + Opcode::VMASKMOVDQU | + Opcode::VMASKMOVPD | + Opcode::VMASKMOVPS | + Opcode::VMAXPD | + Opcode::VMAXPS | + Opcode::VMAXSD | + Opcode::VMAXSS | + Opcode::VMINPD | + Opcode::VMINPS | + Opcode::VMINSD | + Opcode::VMINSS | + Opcode::VMOVAPD | + Opcode::VMOVAPS | + Opcode::VMOVD | + Opcode::VMOVDQA | + Opcode::VMOVDQU | + Opcode::VMOVHLPS | + Opcode::VMOVHPD | + Opcode::VMOVHPS | + Opcode::VMOVLHPS | + Opcode::VMOVLPD | + Opcode::VMOVLPS | + Opcode::VMOVMSKPD | + Opcode::VMOVMSKPS | + Opcode::VMOVNTDQ | + Opcode::VMOVNTDQA | + Opcode::VMOVNTPD | + Opcode::VMOVNTPS | + Opcode::VMOVQ | + Opcode::VMOVSS | + Opcode::VMOVSD | + Opcode::VMOVSHDUP | + Opcode::VMOVSLDUP | + Opcode::VMOVUPD | + Opcode::VMOVUPS | + Opcode::VMPSADBW | + Opcode::VMULPD | + Opcode::VMULPS | + Opcode::VMULSD | + Opcode::VMULSS | + Opcode::VPABSB | + Opcode::VPABSD | + Opcode::VPABSW | + Opcode::VPACKSSDW | + Opcode::VPACKUSDW | + Opcode::VPACKSSWB | + Opcode::VPACKUSWB | + Opcode::VPADDB | + Opcode::VPADDD | + Opcode::VPADDQ | + Opcode::VPADDSB | + Opcode::VPADDSW | + Opcode::VPADDUSB | + Opcode::VPADDUSW | + Opcode::VPADDW | + Opcode::VPALIGNR | + Opcode::VPAND | + Opcode::VANDPD | + Opcode::VANDPS | + Opcode::VANDNPD | + Opcode::VANDNPS | + Opcode::VORPD | + Opcode::VORPS | + Opcode::VPANDN | + Opcode::VPAVGB | + Opcode::VPAVGW | + Opcode::VPBLENDD | + Opcode::VPBLENDVB | + Opcode::VPBLENDW | + Opcode::VPBROADCASTB | + Opcode::VPBROADCASTD | + Opcode::VPBROADCASTQ | + Opcode::VPBROADCASTW | + Opcode::VPCLMULQDQ | + Opcode::VPCMPEQB | + Opcode::VPCMPEQD | + Opcode::VPCMPEQQ | + Opcode::VPCMPEQW | + Opcode::VPCMPGTB | + Opcode::VPCMPGTD | + Opcode::VPCMPGTQ | + Opcode::VPCMPGTW | + Opcode::VPCMPESTRI | + Opcode::VPCMPESTRM | + Opcode::VPCMPISTRI | + Opcode::VPCMPISTRM | + Opcode::VPERM2F128 | + Opcode::VPERM2I128 | + Opcode::VPERMD | + Opcode::VPERMILPD | + Opcode::VPERMILPS | + Opcode::VPERMPD | + Opcode::VPERMPS | + Opcode::VPERMQ | + Opcode::VPEXTRB | + Opcode::VPEXTRD | + Opcode::VPEXTRQ | + Opcode::VPEXTRW | + Opcode::VPGATHERDD | + Opcode::VPGATHERDQ | + Opcode::VPGATHERQD | + Opcode::VPGATHERQQ | + Opcode::VPHADDD | + Opcode::VPHADDSW | + Opcode::VPHADDW | + Opcode::VPMADDUBSW | + Opcode::VPHMINPOSUW | + Opcode::VPHSUBD | + Opcode::VPHSUBSW | + Opcode::VPHSUBW | + Opcode::VPINSRB | + Opcode::VPINSRD | + Opcode::VPINSRQ | + Opcode::VPINSRW | + Opcode::VPMADDWD | + Opcode::VPMASKMOVD | + Opcode::VPMASKMOVQ | + Opcode::VPMAXSB | + Opcode::VPMAXSD | + Opcode::VPMAXSW | + Opcode::VPMAXUB | + Opcode::VPMAXUW | + Opcode::VPMAXUD | + Opcode::VPMINSB | + Opcode::VPMINSW | + Opcode::VPMINSD | + Opcode::VPMINUB | + Opcode::VPMINUW | + Opcode::VPMINUD | + Opcode::VPMOVMSKB | + Opcode::VPMOVSXBD | + Opcode::VPMOVSXBQ | + Opcode::VPMOVSXBW | + Opcode::VPMOVSXDQ | + Opcode::VPMOVSXWD | + Opcode::VPMOVSXWQ | + Opcode::VPMOVZXBD | + Opcode::VPMOVZXBQ | + Opcode::VPMOVZXBW | + Opcode::VPMOVZXDQ | + Opcode::VPMOVZXWD | + Opcode::VPMOVZXWQ | + Opcode::VPMULDQ | + Opcode::VPMULHRSW | + Opcode::VPMULHUW | + Opcode::VPMULHW | + Opcode::VPMULLQ | + Opcode::VPMULLD | + Opcode::VPMULLW | + Opcode::VPMULUDQ | + Opcode::VPOR | + Opcode::VPSADBW | + Opcode::VPSHUFB | + Opcode::VPSHUFD | + Opcode::VPSIGNB | + Opcode::VPSIGND | + Opcode::VPSIGNW | + Opcode::VPSLLD | + Opcode::VPSLLDQ | + Opcode::VPSLLQ | + Opcode::VPSLLVD | + Opcode::VPSLLVQ | + Opcode::VPSLLW | + Opcode::VPSRAD | + Opcode::VPSRAVD | + Opcode::VPSRAW | + Opcode::VPSRLD | + Opcode::VPSRLDQ | + Opcode::VPSRLQ | + Opcode::VPSRLVD | + Opcode::VPSRLVQ | + Opcode::VPSRLW | + Opcode::VPSUBB | + Opcode::VPSUBD | + Opcode::VPSUBQ | + Opcode::VPSUBSB | + Opcode::VPSUBSW | + Opcode::VPSUBUSB | + Opcode::VPSUBUSW | + Opcode::VPSUBW | + Opcode::VPTEST | + Opcode::VPUNPCKHBW | + Opcode::VPUNPCKHDQ | + Opcode::VPUNPCKHQDQ | + Opcode::VPUNPCKHWD | + Opcode::VPUNPCKLBW | + Opcode::VPUNPCKLDQ | + Opcode::VPUNPCKLQDQ | + Opcode::VPUNPCKLWD | + Opcode::VPXOR | + Opcode::VRCPPS | + Opcode::VROUNDPD | + Opcode::VROUNDPS | + Opcode::VROUNDSD | + Opcode::VROUNDSS | + Opcode::VRSQRTPS | + Opcode::VRSQRTSS | + Opcode::VRCPSS | + Opcode::VSHUFPD | + Opcode::VSHUFPS | + Opcode::VSQRTPD | + Opcode::VSQRTPS | + Opcode::VSQRTSS | + Opcode::VSQRTSD | + Opcode::VSUBPD | + Opcode::VSUBPS | + Opcode::VSUBSD | + Opcode::VSUBSS | + Opcode::VTESTPD | + Opcode::VTESTPS | + Opcode::VUNPCKHPD | + Opcode::VUNPCKHPS | + Opcode::VUNPCKLPD | + Opcode::VUNPCKLPS | + Opcode::VXORPD | + Opcode::VXORPS | + Opcode::VZEROUPPER | + Opcode::VZEROALL | + Opcode::VLDMXCSR | + Opcode::VSTMXCSR => { + // TODO: check a table for these + if !settings.avx() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::VAESDEC | + Opcode::VAESDECLAST | + Opcode::VAESENC | + Opcode::VAESENCLAST | + Opcode::VAESIMC | + Opcode::VAESKEYGENASSIST => { + // TODO: check a table for these + if !settings.avx() || !settings.aesni() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::MOVBE => { + if !settings.movbe() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::POPCNT => { + /* + * from the intel SDM: + * ``` + * Before an application attempts to use the POPCNT instruction, it must check that + * the processor supports SSE4.2 (if CPUID.01H:ECX.SSE4_2[bit 20] = 1) and POPCNT + * (if CPUID.01H:ECX.POPCNT[bit 23] = 1). + * ``` + */ + if settings.intel_quirks() && (settings.sse4_2() || settings.popcnt()) { + return Ok(()); + } else if !settings.popcnt() { + /* + * elsewhere from the amd APM: + * `Instruction Subsets and CPUID Feature Flags` on page 507 indicates that + * popcnt is present when the popcnt bit is reported by cpuid. this seems to be + * the less quirky default, so `intel_quirks` is considered the outlier, and + * before this default. + * */ + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::LZCNT => { + /* + * amd APM, `LZCNT` page 212: + * LZCNT is an Advanced Bit Manipulation (ABM) instruction. Support for the LZCNT + * instruction is indicated by CPUID Fn8000_0001_ECX[ABM] = 1. + * + * meanwhile the intel SDM simply states: + * ``` + * CPUID.EAX=80000001H:ECX.LZCNT[bit 5]: if 1 indicates the processor supports the + * LZCNT instruction. + * ``` + * + * so that's considered the less-quirky (default) case here. + * */ + if settings.amd_quirks() && !settings.abm() { + return Err(DecodeError::InvalidOpcode); + } else if !settings.lzcnt() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::ADCX | + Opcode::ADOX => { + if !settings.adx() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::VMRUN | + Opcode::VMLOAD | + Opcode::VMSAVE | + Opcode::CLGI | + Opcode::VMMCALL | + Opcode::INVLPGA => { + if !settings.svm() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::STGI | + Opcode::SKINIT => { + if !settings.svm() || !settings.skinit() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::LAHF | + Opcode::SAHF => { + if !settings.lahfsahf() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::VCVTPS2PH | + Opcode::VCVTPH2PS => { + /* + * from intel SDM: + * ``` + * 14.4.1 Detection of F16C Instructions Application using float 16 instruction + * must follow a detection sequence similar to AVX to ensure: • The OS has + * enabled YMM state management support, • The processor support AVX as + * indicated by the CPUID feature flag, i.e. CPUID.01H:ECX.AVX[bit 28] = 1. • + * The processor support 16-bit floating-point conversion instructions via a + * CPUID feature flag (CPUID.01H:ECX.F16C[bit 29] = 1). + * ``` + * + * TODO: only the VEX-coded variant of this instruction should be gated on `f16c`. + * the EVEX-coded variant should be gated on `avx512f` or `avx512vl` if not + * EVEX.512-coded. + */ + if !settings.avx() || !settings.f16c() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::RDRAND => { + if !settings.rdrand() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::RDSEED => { + if !settings.rdseed() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::MONITORX | Opcode::MWAITX | // these are gated on the `monitorx` and `mwaitx` cpuid bits, but are AMD-only. + Opcode::CLZERO | Opcode::RDPRU => { // again, gated on specific cpuid bits, but AMD-only. + if !settings.amd_quirks() { + return Err(DecodeError::InvalidOpcode); + } + } + other => { + if !settings.bmi1() { + if BMI1.contains(&other) { + return Err(DecodeError::InvalidOpcode); + } + } + if !settings.bmi2() { + if BMI2.contains(&other) { + return Err(DecodeError::InvalidOpcode); + } + } + } + } + Ok(()) +} diff --git a/src/protected_mode/mod.rs b/src/protected_mode/mod.rs index 081be20..2ee3e38 100644 --- a/src/protected_mode/mod.rs +++ b/src/protected_mode/mod.rs @@ -3,6 +3,9 @@ mod evex; #[cfg(feature = "fmt")] mod display; pub mod uarch; +mod isa_settings; + +pub use isa_settings::IsaSettings; pub use crate::MemoryAccessSize; @@ -2707,73 +2710,7 @@ impl LengthedInstruction for Instruction { /// the design requiring that. #[derive(PartialEq, Copy, Clone, Eq, Hash, PartialOrd, Ord)] pub struct InstDecoder { - // extensions tracked here: - // 0. SSE3 - // 1. SSSE3 - // 2. monitor (intel-only?) - // 3. vmx (some atom chips still lack it) - // 4. fma3 (intel haswell/broadwell+, amd piledriver+) - // 5. cmpxchg16b (some amd are missing this one) - // 6. sse4.1 - // 7. sse4.2 - // 8. movbe - // 9. popcnt (independent of BMI) - // 10. aesni - // 11. xsave (xsave, xrestor, xsetbv, xgetbv) - // 12. rdrand (intel ivybridge+, amd ..??) - // 13. sgx (eadd, eblock, ecreate, edbgrd, edbgwr, einit, eldb, eldu, epa, eremove, etrace, - // ewb, eenter, eexit, egetkey, ereport, eresume) - // 14. bmi1 (intel haswell+, amd jaguar+) - // 15. avx2 (intel haswell+, amd excavator+) - // 16. bmi2 (intel ?, amd ?) - // 17. invpcid - // 18. mpx - // 19. avx512_f - // 20. avx512_dq - // 21. rdseed - // 22. adx - // 23. avx512_fma - // 24. pcommit - // 25. clflushopt - // 26. clwb - // 27. avx512_pf - // 28. avx512_er - // 29. avx512_cd - // 30. sha - // 31. avx512_bw - // 32. avx512_vl - // 33. prefetchwt1 - // 34. avx512_vbmi - // 35. avx512_vbmi2 - // 36. gfni (galois field instructions) - // 37. vaes - // 38. pclmulqdq - // 39. avx_vnni - // 40. avx512_bitalg - // 41. avx512_vpopcntdq - // 42. avx512_4vnniw - // 43. avx512_4fmaps - // 44. cx8 // cmpxchg8 - is this actually optional in x86? - // 45. syscall // syscall/sysret - actually optional in x86? - // 46. rdtscp // actually optional in x86? - // 47. abm (lzcnt, popcnt) - // 48. sse4a - // 49. 3dnowprefetch // actually optional? - // 50. xop - // 51. skinit - // 52. tbm - // 53. intel quirks - // 54. amd quirks - // 55. avx (intel ?, amd ?) - // 56. amd-v/svm - // 57. lahfsahf - // 58. cmov - // 59. f16c - // 60. fma4 - // 61. prefetchw - // 62. tsx - // 63. lzcnt - flags: u64, + flags: [u64; 2], } impl InstDecoder { @@ -2783,7 +2720,7 @@ impl InstDecoder { /// and any instructions defined by extensions. pub fn minimal() -> Self { InstDecoder { - flags: 0, + flags: [0, 0], } } @@ -2795,1311 +2732,32 @@ impl InstDecoder { let mut reader = yaxpeax_arch::U8Reader::new(data); self.decode(&mut reader) } +} - pub fn sse3(&self) -> bool { - self.flags & (1 << 0) != 0 - } - - pub fn with_sse3(mut self) -> Self { - self.flags |= 1 << 0; - self - } - - pub fn ssse3(&self) -> bool { - self.flags & (1 << 1) != 0 - } - - pub fn with_ssse3(mut self) -> Self { - self.flags |= 1 << 1; - self - } - - pub fn monitor(&self) -> bool { - self.flags & (1 << 2) != 0 - } - - pub fn with_monitor(mut self) -> Self { - self.flags |= 1 << 2; - self - } - - pub fn vmx(&self) -> bool { - self.flags & (1 << 3) != 0 - } - - pub fn with_vmx(mut self) -> Self { - self.flags |= 1 << 3; - self - } - - pub fn fma3(&self) -> bool { - self.flags & (1 << 4) != 0 - } - - pub fn with_fma3(mut self) -> Self { - self.flags |= 1 << 4; - self - } - - pub fn cmpxchg16b(&self) -> bool { - self.flags & (1 << 5) != 0 - } - - pub fn with_cmpxchg16b(mut self) -> Self { - self.flags |= 1 << 5; - self - } - - pub fn sse4_1(&self) -> bool { - self.flags & (1 << 6) != 0 - } - - pub fn with_sse4_1(mut self) -> Self { - self.flags |= 1 << 6; - self - } - - pub fn sse4_2(&self) -> bool { - self.flags & (1 << 7) != 0 - } - - pub fn with_sse4_2(mut self) -> Self { - self.flags |= 1 << 7; - self - } - - pub fn with_sse4(self) -> Self { - self - .with_sse4_1() - .with_sse4_2() - } - - pub fn movbe(&self) -> bool { - self.flags & (1 << 8) != 0 - } - - pub fn with_movbe(mut self) -> Self { - self.flags |= 1 << 8; - self - } - - pub fn popcnt(&self) -> bool { - self.flags & (1 << 9) != 0 - } - - pub fn with_popcnt(mut self) -> Self { - self.flags |= 1 << 9; - self - } - - pub fn aesni(&self) -> bool { - self.flags & (1 << 10) != 0 - } - - pub fn with_aesni(mut self) -> Self { - self.flags |= 1 << 10; - self - } - - pub fn xsave(&self) -> bool { - self.flags & (1 << 11) != 0 - } - - pub fn with_xsave(mut self) -> Self { - self.flags |= 1 << 11; - self - } - - pub fn rdrand(&self) -> bool { - self.flags & (1 << 12) != 0 - } - - pub fn with_rdrand(mut self) -> Self { - self.flags |= 1 << 12; - self - } - - pub fn sgx(&self) -> bool { - self.flags & (1 << 13) != 0 - } - - pub fn with_sgx(mut self) -> Self { - self.flags |= 1 << 13; - self - } - - pub fn bmi1(&self) -> bool { - self.flags & (1 << 14) != 0 - } - - pub fn with_bmi1(mut self) -> Self { - self.flags |= 1 << 14; - self - } - - pub fn avx2(&self) -> bool { - self.flags & (1 << 15) != 0 - } - - pub fn with_avx2(mut self) -> Self { - self.flags |= 1 << 15; - self - } - - /// `bmi2` indicates support for the `BZHI`, `MULX`, `PDEP`, `PEXT`, `RORX`, `SARX`, `SHRX`, - /// and `SHLX` instructions. `bmi2` is implemented in all x86 chips that implement `bmi`, - /// except the amd `piledriver` and `steamroller` microarchitectures. - pub fn bmi2(&self) -> bool { - self.flags & (1 << 16) != 0 - } - - pub fn with_bmi2(mut self) -> Self { - self.flags |= 1 << 16; - self - } - - pub fn invpcid(&self) -> bool { - self.flags & (1 << 17) != 0 - } - - pub fn with_invpcid(mut self) -> Self { - self.flags |= 1 << 17; - self - } - - pub fn mpx(&self) -> bool { - self.flags & (1 << 18) != 0 - } - - pub fn with_mpx(mut self) -> Self { - self.flags |= 1 << 18; - self - } - - pub fn avx512_f(&self) -> bool { - self.flags & (1 << 19) != 0 - } - - pub fn with_avx512_f(mut self) -> Self { - self.flags |= 1 << 19; - self - } - - pub fn avx512_dq(&self) -> bool { - self.flags & (1 << 20) != 0 - } - - pub fn with_avx512_dq(mut self) -> Self { - self.flags |= 1 << 20; - self - } - - pub fn rdseed(&self) -> bool { - self.flags & (1 << 21) != 0 - } - - pub fn with_rdseed(mut self) -> Self { - self.flags |= 1 << 21; - self - } - - pub fn adx(&self) -> bool { - self.flags & (1 << 22) != 0 - } - - pub fn with_adx(mut self) -> Self { - self.flags |= 1 << 22; - self - } - - pub fn avx512_fma(&self) -> bool { - self.flags & (1 << 23) != 0 - } - - pub fn with_avx512_fma(mut self) -> Self { - self.flags |= 1 << 23; - self - } - - pub fn pcommit(&self) -> bool { - self.flags & (1 << 24) != 0 - } - - pub fn with_pcommit(mut self) -> Self { - self.flags |= 1 << 24; - self - } - - pub fn clflushopt(&self) -> bool { - self.flags & (1 << 25) != 0 - } - - pub fn with_clflushopt(mut self) -> Self { - self.flags |= 1 << 25; - self - } - - pub fn clwb(&self) -> bool { - self.flags & (1 << 26) != 0 - } - - pub fn with_clwb(mut self) -> Self { - self.flags |= 1 << 26; - self - } - - pub fn avx512_pf(&self) -> bool { - self.flags & (1 << 27) != 0 - } - - pub fn with_avx512_pf(mut self) -> Self { - self.flags |= 1 << 27; - self - } - - pub fn avx512_er(&self) -> bool { - self.flags & (1 << 28) != 0 - } - - pub fn with_avx512_er(mut self) -> Self { - self.flags |= 1 << 28; - self - } - - pub fn avx512_cd(&self) -> bool { - self.flags & (1 << 29) != 0 - } - - pub fn with_avx512_cd(mut self) -> Self { - self.flags |= 1 << 29; - self - } - - pub fn sha(&self) -> bool { - self.flags & (1 << 30) != 0 - } - - pub fn with_sha(mut self) -> Self { - self.flags |= 1 << 30; - self - } - - pub fn avx512_bw(&self) -> bool { - self.flags & (1 << 31) != 0 - } - - pub fn with_avx512_bw(mut self) -> Self { - self.flags |= 1 << 31; - self - } - - pub fn avx512_vl(&self) -> bool { - self.flags & (1 << 32) != 0 - } - - pub fn with_avx512_vl(mut self) -> Self { - self.flags |= 1 << 32; - self - } - - pub fn prefetchwt1(&self) -> bool { - self.flags & (1 << 33) != 0 - } - - pub fn with_prefetchwt1(mut self) -> Self { - self.flags |= 1 << 33; - self - } - - pub fn avx512_vbmi(&self) -> bool { - self.flags & (1 << 34) != 0 - } - - pub fn with_avx512_vbmi(mut self) -> Self { - self.flags |= 1 << 34; - self - } - - pub fn avx512_vbmi2(&self) -> bool { - self.flags & (1 << 35) != 0 - } - - pub fn with_avx512_vbmi2(mut self) -> Self { - self.flags |= 1 << 35; - self - } - - pub fn gfni(&self) -> bool { - self.flags & (1 << 36) != 0 - } - - pub fn with_gfni(mut self) -> Self { - self.flags |= 1 << 36; - self - } - - pub fn vaes(&self) -> bool { - self.flags & (1 << 37) != 0 - } - - pub fn with_vaes(mut self) -> Self { - self.flags |= 1 << 37; - self - } - - pub fn pclmulqdq(&self) -> bool { - self.flags & (1 << 38) != 0 - } - - pub fn with_pclmulqdq(mut self) -> Self { - self.flags |= 1 << 38; - self - } - - pub fn avx_vnni(&self) -> bool { - self.flags & (1 << 39) != 0 - } - - pub fn with_avx_vnni(mut self) -> Self { - self.flags |= 1 << 39; - self - } - - pub fn avx512_bitalg(&self) -> bool { - self.flags & (1 << 40) != 0 - } - - pub fn with_avx512_bitalg(mut self) -> Self { - self.flags |= 1 << 40; - self - } - - pub fn avx512_vpopcntdq(&self) -> bool { - self.flags & (1 << 41) != 0 - } - - pub fn with_avx512_vpopcntdq(mut self) -> Self { - self.flags |= 1 << 41; - self - } - - pub fn avx512_4vnniw(&self) -> bool { - self.flags & (1 << 42) != 0 - } - - pub fn with_avx512_4vnniw(mut self) -> Self { - self.flags |= 1 << 42; - self - } - - pub fn avx512_4fmaps(&self) -> bool { - self.flags & (1 << 43) != 0 - } - - pub fn with_avx512_4fmaps(mut self) -> Self { - self.flags |= 1 << 43; - self - } - - /// returns `true` if this `InstDecoder` has **all** `avx512` features enabled. - pub fn avx512(&self) -> bool { - let avx512_mask = - (1 << 19) | - (1 << 20) | - (1 << 23) | - (1 << 27) | - (1 << 28) | - (1 << 29) | - (1 << 31) | - (1 << 32) | - (1 << 34) | - (1 << 35) | - (1 << 40) | - (1 << 41) | - (1 << 42) | - (1 << 43); - - (self.flags & avx512_mask) == avx512_mask - } - - /// enable all `avx512` features on this `InstDecoder`. no real CPU, at time of writing, - /// actually has such a feature combination, but this is a useful overestimate for `avx512` - /// generally. - pub fn with_avx512(mut self) -> Self { - let avx512_mask = - (1 << 19) | - (1 << 20) | - (1 << 23) | - (1 << 27) | - (1 << 28) | - (1 << 29) | - (1 << 31) | - (1 << 32) | - (1 << 34) | - (1 << 35) | - (1 << 40) | - (1 << 41) | - (1 << 42) | - (1 << 43); - - self.flags |= avx512_mask; - self - } - - pub fn cx8(&self) -> bool { - self.flags & (1 << 44) != 0 - } - - pub fn with_cx8(mut self) -> Self { - self.flags |= 1 << 44; - self - } - - pub fn syscall(&self) -> bool { - self.flags & (1 << 45) != 0 - } - - pub fn with_syscall(mut self) -> Self { - self.flags |= 1 << 45; - self - } - - pub fn rdtscp(&self) -> bool { - self.flags & (1 << 46) != 0 - } - - pub fn with_rdtscp(mut self) -> Self { - self.flags |= 1 << 46; - self - } - - pub fn abm(&self) -> bool { - self.flags & (1 << 47) != 0 - } - - pub fn with_abm(mut self) -> Self { - self.flags |= 1 << 47; - self - } - - pub fn sse4a(&self) -> bool { - self.flags & (1 << 48) != 0 - } - - pub fn with_sse4a(mut self) -> Self { - self.flags |= 1 << 48; - self - } - - pub fn _3dnowprefetch(&self) -> bool { - self.flags & (1 << 49) != 0 - } - - pub fn with_3dnowprefetch(mut self) -> Self { - self.flags |= 1 << 49; - self - } - - pub fn xop(&self) -> bool { - self.flags & (1 << 50) != 0 - } - - pub fn with_xop(mut self) -> Self { - self.flags |= 1 << 50; - self - } - - pub fn skinit(&self) -> bool { - self.flags & (1 << 51) != 0 - } - - pub fn with_skinit(mut self) -> Self { - self.flags |= 1 << 51; - self - } - - pub fn tbm(&self) -> bool { - self.flags & (1 << 52) != 0 - } - - pub fn with_tbm(mut self) -> Self { - self.flags |= 1 << 52; - self - } - - pub fn intel_quirks(&self) -> bool { - self.flags & (1 << 53) != 0 - } - - pub fn with_intel_quirks(mut self) -> Self { - self.flags |= 1 << 53; - self - } - - pub fn amd_quirks(&self) -> bool { - self.flags & (1 << 54) != 0 - } - - pub fn with_amd_quirks(mut self) -> Self { - self.flags |= 1 << 54; - self - } - - pub fn avx(&self) -> bool { - self.flags & (1 << 55) != 0 - } - - pub fn with_avx(mut self) -> Self { - self.flags |= 1 << 55; - self - } - - pub fn svm(&self) -> bool { - self.flags & (1 << 56) != 0 - } - - pub fn with_svm(mut self) -> Self { - self.flags |= 1 << 56; - self - } - - /// `lahfsahf` is only unset for early revisions of 64-bit amd and intel chips. unfortunately - /// the clearest documentation on when these instructions were reintroduced into 64-bit - /// architectures seems to be - /// [wikipedia](https://en.wikipedia.org/wiki/X86-64#Older_implementations): - /// ```text - /// Early AMD64 and Intel 64 CPUs lacked LAHF and SAHF instructions in 64-bit mode. AMD - /// introduced these instructions (also in 64-bit mode) with their Athlon 64, Opteron and - /// Turion 64 revision D processors in March 2005[48][49][50] while Intel introduced the - /// instructions with the Pentium 4 G1 stepping in December 2005. The 64-bit version of Windows - /// 8.1 requires this feature.[47] - /// ``` - /// - /// this puts reintroduction of these instructions somewhere in the middle of prescott and k8 - /// lifecycles, for intel and amd respectively. because there is no specific uarch where these - /// features become enabled, prescott and k8 default to not supporting these instructions, - /// where later uarches support these instructions. - pub fn lahfsahf(&self) -> bool { - self.flags & (1 << 57) != 0 - } - - pub fn with_lahfsahf(mut self) -> Self { - self.flags |= 1 << 57; - self - } - - pub fn cmov(&self) -> bool { - self.flags & (1 << 58) != 0 - } - - pub fn with_cmov(mut self) -> Self { - self.flags |= 1 << 58; - self - } - - pub fn f16c(&self) -> bool { - self.flags & (1 << 59) != 0 - } - - pub fn with_f16c(mut self) -> Self { - self.flags |= 1 << 59; - self - } - - pub fn fma4(&self) -> bool { - self.flags & (1 << 60) != 0 - } - - pub fn with_fma4(mut self) -> Self { - self.flags |= 1 << 60; - self - } - - pub fn prefetchw(&self) -> bool { - self.flags & (1 << 61) != 0 - } +pub struct DecodeEverything {} - pub fn with_prefetchw(mut self) -> Self { - self.flags |= 1 << 61; - self - } - - pub fn tsx(&self) -> bool { - self.flags & (1 << 62) != 0 - } +impl Decoder<Arch> for DecodeEverything { + fn decode<T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>>(&self, words: &mut T) -> Result<Instruction, <Arch as yaxpeax_arch::Arch>::DecodeError> { + let mut instr = Instruction::invalid(); + self.decode_into(&mut instr, words)?; - pub fn with_tsx(mut self) -> Self { - self.flags |= 1 << 62; - self + Ok(instr) } - - pub fn lzcnt(&self) -> bool { - self.flags & (1 << 63) != 0 + #[inline(always)] + fn decode_into<T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>>(&self, instr: &mut Instruction, words: &mut T) -> Result<(), <Arch as yaxpeax_arch::Arch>::DecodeError> { + self.decode_with_annotation(instr, words, &mut NullSink) } +} - pub fn with_lzcnt(mut self) -> Self { - self.flags |= 1 << 63; - self - } +impl AnnotatingDecoder<Arch> for DecodeEverything { + type FieldDescription = FieldDescription; - /// Optionally reject or reinterpret instruction according to the decoder's - /// declared extensions. - fn revise_instruction(&self, inst: &mut Instruction) -> Result<(), DecodeError> { - if inst.prefixes.evex().is_some() { - if !self.avx512() { - return Err(DecodeError::InvalidOpcode); - } else { - return Ok(()); - } - } - match inst.opcode { - Opcode::TZCNT => { - if !self.bmi1() { - // tzcnt is only supported if bmi1 is enabled. without bmi1, this decodes as - // bsf. - inst.opcode = Opcode::BSF; - } - } - Opcode::LDDQU | - Opcode::ADDSUBPS | - Opcode::ADDSUBPD | - Opcode::HADDPS | - Opcode::HSUBPS | - Opcode::HADDPD | - Opcode::HSUBPD | - Opcode::MOVSHDUP | - Opcode::MOVSLDUP | - Opcode::MOVDDUP | - Opcode::MONITOR | - Opcode::MWAIT => { - // via Intel section 5.7, SSE3 Instructions - if !self.sse3() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::PHADDW | - Opcode::PHADDSW | - Opcode::PHADDD | - Opcode::PHSUBW | - Opcode::PHSUBSW | - Opcode::PHSUBD | - Opcode::PABSB | - Opcode::PABSW | - Opcode::PABSD | - Opcode::PMADDUBSW | - Opcode::PMULHRSW | - Opcode::PSHUFB | - Opcode::PSIGNB | - Opcode::PSIGNW | - Opcode::PSIGND | - Opcode::PALIGNR => { - // via Intel section 5.8, SSSE3 Instructions - if !self.ssse3() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::PMULLD | - Opcode::PMULDQ | - Opcode::MOVNTDQA | - Opcode::BLENDPD | - Opcode::BLENDPS | - Opcode::BLENDVPD | - Opcode::BLENDVPS | - Opcode::PBLENDVB | - Opcode::BLENDW | - Opcode::PMINUW | - Opcode::PMINUD | - Opcode::PMINSB | - Opcode::PMINSD | - Opcode::PMAXUW | - Opcode::PMAXUD | - Opcode::PMAXSB | - Opcode::PMAXSD | - Opcode::ROUNDPS | - Opcode::ROUNDPD | - Opcode::ROUNDSS | - Opcode::ROUNDSD | - Opcode::PBLENDW | - Opcode::EXTRACTPS | - Opcode::INSERTPS | - Opcode::PINSRB | - Opcode::PINSRD | - Opcode::PINSRQ | - Opcode::PMOVSXBW | - Opcode::PMOVZXBW | - Opcode::PMOVSXBD | - Opcode::PMOVZXBD | - Opcode::PMOVSXWD | - Opcode::PMOVZXWD | - Opcode::PMOVSXBQ | - Opcode::PMOVZXBQ | - Opcode::PMOVSXWQ | - Opcode::PMOVZXWQ | - Opcode::PMOVSXDQ | - Opcode::PMOVZXDQ | - Opcode::DPPS | - Opcode::DPPD | - Opcode::MPSADBW | - Opcode::PHMINPOSUW | - Opcode::PTEST | - Opcode::PCMPEQQ | - Opcode::PEXTRB | - Opcode::PEXTRW | - Opcode::PEXTRD | - Opcode::PEXTRQ | - Opcode::PACKUSDW => { - // via Intel section 5.10, SSE4.1 Instructions - if !self.sse4_1() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::EXTRQ | - Opcode::INSERTQ | - Opcode::MOVNTSS | - Opcode::MOVNTSD => { - if !self.sse4a() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::CRC32 | - Opcode::PCMPESTRI | - Opcode::PCMPESTRM | - Opcode::PCMPISTRI | - Opcode::PCMPISTRM | - Opcode::PCMPGTQ => { - // via Intel section 5.11, SSE4.2 Instructions - if !self.sse4_2() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::AESDEC | - Opcode::AESDECLAST | - Opcode::AESENC | - Opcode::AESENCLAST | - Opcode::AESIMC | - Opcode::AESKEYGENASSIST => { - // via Intel section 5.12. AESNI AND PCLMULQDQ - if !self.aesni() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::PCLMULQDQ => { - // via Intel section 5.12. AESNI AND PCLMULQDQ - if !self.pclmulqdq() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::XABORT | - Opcode::XBEGIN | - Opcode::XEND | - Opcode::XTEST => { - if !self.tsx() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::SHA1MSG1 | - Opcode::SHA1MSG2 | - Opcode::SHA1NEXTE | - Opcode::SHA1RNDS4 | - Opcode::SHA256MSG1 | - Opcode::SHA256MSG2 | - Opcode::SHA256RNDS2 => { - if !self.sha() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::ENCLV | - Opcode::ENCLS | - Opcode::ENCLU => { - if !self.sgx() { - return Err(DecodeError::InvalidOpcode); - } - } - // AVX... - Opcode::VMOVDDUP | - Opcode::VPSHUFLW | - Opcode::VPSHUFHW | - Opcode::VHADDPS | - Opcode::VHSUBPS | - Opcode::VADDSUBPS | - Opcode::VCVTPD2DQ | - Opcode::VLDDQU | - Opcode::VCOMISD | - Opcode::VCOMISS | - Opcode::VUCOMISD | - Opcode::VUCOMISS | - Opcode::VADDPD | - Opcode::VADDPS | - Opcode::VADDSD | - Opcode::VADDSS | - Opcode::VADDSUBPD | - Opcode::VBLENDPD | - Opcode::VBLENDPS | - Opcode::VBLENDVPD | - Opcode::VBLENDVPS | - Opcode::VBROADCASTF128 | - Opcode::VBROADCASTI128 | - Opcode::VBROADCASTSD | - Opcode::VBROADCASTSS | - Opcode::VCMPSD | - Opcode::VCMPSS | - Opcode::VCMPPD | - Opcode::VCMPPS | - Opcode::VCVTDQ2PD | - Opcode::VCVTDQ2PS | - Opcode::VCVTPD2PS | - Opcode::VCVTPS2DQ | - Opcode::VCVTPS2PD | - Opcode::VCVTSS2SD | - Opcode::VCVTSI2SS | - Opcode::VCVTSI2SD | - Opcode::VCVTSD2SI | - Opcode::VCVTSD2SS | - Opcode::VCVTSS2SI | - Opcode::VCVTTPD2DQ | - Opcode::VCVTTPS2DQ | - Opcode::VCVTTSS2SI | - Opcode::VCVTTSD2SI | - Opcode::VDIVPD | - Opcode::VDIVPS | - Opcode::VDIVSD | - Opcode::VDIVSS | - Opcode::VDPPD | - Opcode::VDPPS | - Opcode::VEXTRACTF128 | - Opcode::VEXTRACTI128 | - Opcode::VEXTRACTPS | - Opcode::VFMADD132PD | - Opcode::VFMADD132PS | - Opcode::VFMADD132SD | - Opcode::VFMADD132SS | - Opcode::VFMADD213PD | - Opcode::VFMADD213PS | - Opcode::VFMADD213SD | - Opcode::VFMADD213SS | - Opcode::VFMADD231PD | - Opcode::VFMADD231PS | - Opcode::VFMADD231SD | - Opcode::VFMADD231SS | - Opcode::VFMADDSUB132PD | - Opcode::VFMADDSUB132PS | - Opcode::VFMADDSUB213PD | - Opcode::VFMADDSUB213PS | - Opcode::VFMADDSUB231PD | - Opcode::VFMADDSUB231PS | - Opcode::VFMSUB132PD | - Opcode::VFMSUB132PS | - Opcode::VFMSUB132SD | - Opcode::VFMSUB132SS | - Opcode::VFMSUB213PD | - Opcode::VFMSUB213PS | - Opcode::VFMSUB213SD | - Opcode::VFMSUB213SS | - Opcode::VFMSUB231PD | - Opcode::VFMSUB231PS | - Opcode::VFMSUB231SD | - Opcode::VFMSUB231SS | - Opcode::VFMSUBADD132PD | - Opcode::VFMSUBADD132PS | - Opcode::VFMSUBADD213PD | - Opcode::VFMSUBADD213PS | - Opcode::VFMSUBADD231PD | - Opcode::VFMSUBADD231PS | - Opcode::VFNMADD132PD | - Opcode::VFNMADD132PS | - Opcode::VFNMADD132SD | - Opcode::VFNMADD132SS | - Opcode::VFNMADD213PD | - Opcode::VFNMADD213PS | - Opcode::VFNMADD213SD | - Opcode::VFNMADD213SS | - Opcode::VFNMADD231PD | - Opcode::VFNMADD231PS | - Opcode::VFNMADD231SD | - Opcode::VFNMADD231SS | - Opcode::VFNMSUB132PD | - Opcode::VFNMSUB132PS | - Opcode::VFNMSUB132SD | - Opcode::VFNMSUB132SS | - Opcode::VFNMSUB213PD | - Opcode::VFNMSUB213PS | - Opcode::VFNMSUB213SD | - Opcode::VFNMSUB213SS | - Opcode::VFNMSUB231PD | - Opcode::VFNMSUB231PS | - Opcode::VFNMSUB231SD | - Opcode::VFNMSUB231SS | - Opcode::VGATHERDPD | - Opcode::VGATHERDPS | - Opcode::VGATHERQPD | - Opcode::VGATHERQPS | - Opcode::VHADDPD | - Opcode::VHSUBPD | - Opcode::VINSERTF128 | - Opcode::VINSERTI128 | - Opcode::VINSERTPS | - Opcode::VMASKMOVDQU | - Opcode::VMASKMOVPD | - Opcode::VMASKMOVPS | - Opcode::VMAXPD | - Opcode::VMAXPS | - Opcode::VMAXSD | - Opcode::VMAXSS | - Opcode::VMINPD | - Opcode::VMINPS | - Opcode::VMINSD | - Opcode::VMINSS | - Opcode::VMOVAPD | - Opcode::VMOVAPS | - Opcode::VMOVD | - Opcode::VMOVDQA | - Opcode::VMOVDQU | - Opcode::VMOVHLPS | - Opcode::VMOVHPD | - Opcode::VMOVHPS | - Opcode::VMOVLHPS | - Opcode::VMOVLPD | - Opcode::VMOVLPS | - Opcode::VMOVMSKPD | - Opcode::VMOVMSKPS | - Opcode::VMOVNTDQ | - Opcode::VMOVNTDQA | - Opcode::VMOVNTPD | - Opcode::VMOVNTPS | - Opcode::VMOVQ | - Opcode::VMOVSS | - Opcode::VMOVSD | - Opcode::VMOVSHDUP | - Opcode::VMOVSLDUP | - Opcode::VMOVUPD | - Opcode::VMOVUPS | - Opcode::VMPSADBW | - Opcode::VMULPD | - Opcode::VMULPS | - Opcode::VMULSD | - Opcode::VMULSS | - Opcode::VPABSB | - Opcode::VPABSD | - Opcode::VPABSW | - Opcode::VPACKSSDW | - Opcode::VPACKUSDW | - Opcode::VPACKSSWB | - Opcode::VPACKUSWB | - Opcode::VPADDB | - Opcode::VPADDD | - Opcode::VPADDQ | - Opcode::VPADDSB | - Opcode::VPADDSW | - Opcode::VPADDUSB | - Opcode::VPADDUSW | - Opcode::VPADDW | - Opcode::VPALIGNR | - Opcode::VPAND | - Opcode::VANDPD | - Opcode::VANDPS | - Opcode::VANDNPD | - Opcode::VANDNPS | - Opcode::VORPD | - Opcode::VORPS | - Opcode::VPANDN | - Opcode::VPAVGB | - Opcode::VPAVGW | - Opcode::VPBLENDD | - Opcode::VPBLENDVB | - Opcode::VPBLENDW | - Opcode::VPBROADCASTB | - Opcode::VPBROADCASTD | - Opcode::VPBROADCASTQ | - Opcode::VPBROADCASTW | - Opcode::VPCLMULQDQ | - Opcode::VPCMPEQB | - Opcode::VPCMPEQD | - Opcode::VPCMPEQQ | - Opcode::VPCMPEQW | - Opcode::VPCMPGTB | - Opcode::VPCMPGTD | - Opcode::VPCMPGTQ | - Opcode::VPCMPGTW | - Opcode::VPCMPESTRI | - Opcode::VPCMPESTRM | - Opcode::VPCMPISTRI | - Opcode::VPCMPISTRM | - Opcode::VPERM2F128 | - Opcode::VPERM2I128 | - Opcode::VPERMD | - Opcode::VPERMILPD | - Opcode::VPERMILPS | - Opcode::VPERMPD | - Opcode::VPERMPS | - Opcode::VPERMQ | - Opcode::VPEXTRB | - Opcode::VPEXTRD | - Opcode::VPEXTRQ | - Opcode::VPEXTRW | - Opcode::VPGATHERDD | - Opcode::VPGATHERDQ | - Opcode::VPGATHERQD | - Opcode::VPGATHERQQ | - Opcode::VPHADDD | - Opcode::VPHADDSW | - Opcode::VPHADDW | - Opcode::VPMADDUBSW | - Opcode::VPHMINPOSUW | - Opcode::VPHSUBD | - Opcode::VPHSUBSW | - Opcode::VPHSUBW | - Opcode::VPINSRB | - Opcode::VPINSRD | - Opcode::VPINSRQ | - Opcode::VPINSRW | - Opcode::VPMADDWD | - Opcode::VPMASKMOVD | - Opcode::VPMASKMOVQ | - Opcode::VPMAXSB | - Opcode::VPMAXSD | - Opcode::VPMAXSW | - Opcode::VPMAXUB | - Opcode::VPMAXUW | - Opcode::VPMAXUD | - Opcode::VPMINSB | - Opcode::VPMINSW | - Opcode::VPMINSD | - Opcode::VPMINUB | - Opcode::VPMINUW | - Opcode::VPMINUD | - Opcode::VPMOVMSKB | - Opcode::VPMOVSXBD | - Opcode::VPMOVSXBQ | - Opcode::VPMOVSXBW | - Opcode::VPMOVSXDQ | - Opcode::VPMOVSXWD | - Opcode::VPMOVSXWQ | - Opcode::VPMOVZXBD | - Opcode::VPMOVZXBQ | - Opcode::VPMOVZXBW | - Opcode::VPMOVZXDQ | - Opcode::VPMOVZXWD | - Opcode::VPMOVZXWQ | - Opcode::VPMULDQ | - Opcode::VPMULHRSW | - Opcode::VPMULHUW | - Opcode::VPMULHW | - Opcode::VPMULLQ | - Opcode::VPMULLD | - Opcode::VPMULLW | - Opcode::VPMULUDQ | - Opcode::VPOR | - Opcode::VPSADBW | - Opcode::VPSHUFB | - Opcode::VPSHUFD | - Opcode::VPSIGNB | - Opcode::VPSIGND | - Opcode::VPSIGNW | - Opcode::VPSLLD | - Opcode::VPSLLDQ | - Opcode::VPSLLQ | - Opcode::VPSLLVD | - Opcode::VPSLLVQ | - Opcode::VPSLLW | - Opcode::VPSRAD | - Opcode::VPSRAVD | - Opcode::VPSRAW | - Opcode::VPSRLD | - Opcode::VPSRLDQ | - Opcode::VPSRLQ | - Opcode::VPSRLVD | - Opcode::VPSRLVQ | - Opcode::VPSRLW | - Opcode::VPSUBB | - Opcode::VPSUBD | - Opcode::VPSUBQ | - Opcode::VPSUBSB | - Opcode::VPSUBSW | - Opcode::VPSUBUSB | - Opcode::VPSUBUSW | - Opcode::VPSUBW | - Opcode::VPTEST | - Opcode::VPUNPCKHBW | - Opcode::VPUNPCKHDQ | - Opcode::VPUNPCKHQDQ | - Opcode::VPUNPCKHWD | - Opcode::VPUNPCKLBW | - Opcode::VPUNPCKLDQ | - Opcode::VPUNPCKLQDQ | - Opcode::VPUNPCKLWD | - Opcode::VPXOR | - Opcode::VRCPPS | - Opcode::VROUNDPD | - Opcode::VROUNDPS | - Opcode::VROUNDSD | - Opcode::VROUNDSS | - Opcode::VRSQRTPS | - Opcode::VRSQRTSS | - Opcode::VRCPSS | - Opcode::VSHUFPD | - Opcode::VSHUFPS | - Opcode::VSQRTPD | - Opcode::VSQRTPS | - Opcode::VSQRTSS | - Opcode::VSQRTSD | - Opcode::VSUBPD | - Opcode::VSUBPS | - Opcode::VSUBSD | - Opcode::VSUBSS | - Opcode::VTESTPD | - Opcode::VTESTPS | - Opcode::VUNPCKHPD | - Opcode::VUNPCKHPS | - Opcode::VUNPCKLPD | - Opcode::VUNPCKLPS | - Opcode::VXORPD | - Opcode::VXORPS | - Opcode::VZEROUPPER | - Opcode::VZEROALL | - Opcode::VLDMXCSR | - Opcode::VSTMXCSR => { - // TODO: check a table for these - if !self.avx() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::VAESDEC | - Opcode::VAESDECLAST | - Opcode::VAESENC | - Opcode::VAESENCLAST | - Opcode::VAESIMC | - Opcode::VAESKEYGENASSIST => { - // TODO: check a table for these - if !self.avx() || !self.aesni() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::MOVBE => { - if !self.movbe() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::POPCNT => { - /* - * from the intel SDM: - * ``` - * Before an application attempts to use the POPCNT instruction, it must check that - * the processor supports SSE4.2 (if CPUID.01H:ECX.SSE4_2[bit 20] = 1) and POPCNT - * (if CPUID.01H:ECX.POPCNT[bit 23] = 1). - * ``` - */ - if self.intel_quirks() && (self.sse4_2() || self.popcnt()) { - return Ok(()); - } else if !self.popcnt() { - /* - * elsewhere from the amd APM: - * `Instruction Subsets and CPUID Feature Flags` on page 507 indicates that - * popcnt is present when the popcnt bit is reported by cpuid. this seems to be - * the less quirky default, so `intel_quirks` is considered the outlier, and - * before this default. - * */ - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::LZCNT => { - /* - * amd APM, `LZCNT` page 212: - * LZCNT is an Advanced Bit Manipulation (ABM) instruction. Support for the LZCNT - * instruction is indicated by CPUID Fn8000_0001_ECX[ABM] = 1. - * - * meanwhile the intel SDM simply states: - * ``` - * CPUID.EAX=80000001H:ECX.LZCNT[bit 5]: if 1 indicates the processor supports the - * LZCNT instruction. - * ``` - * - * so that's considered the less-quirky (default) case here. - * */ - if self.amd_quirks() && !self.abm() { - return Err(DecodeError::InvalidOpcode); - } else if !self.lzcnt() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::ADCX | - Opcode::ADOX => { - if !self.adx() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::VMRUN | - Opcode::VMLOAD | - Opcode::VMSAVE | - Opcode::CLGI | - Opcode::VMMCALL | - Opcode::INVLPGA => { - if !self.svm() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::STGI | - Opcode::SKINIT => { - if !self.svm() || !self.skinit() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::LAHF | - Opcode::SAHF => { - if !self.lahfsahf() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::VCVTPS2PH | - Opcode::VCVTPH2PS => { - /* - * from intel SDM: - * ``` - * 14.4.1 Detection of F16C Instructions Application using float 16 instruction - * must follow a detection sequence similar to AVX to ensure: • The OS has - * enabled YMM state management support, • The processor support AVX as - * indicated by the CPUID feature flag, i.e. CPUID.01H:ECX.AVX[bit 28] = 1. • - * The processor support 16-bit floating-point conversion instructions via a - * CPUID feature flag (CPUID.01H:ECX.F16C[bit 29] = 1). - * ``` - * - * TODO: only the VEX-coded variant of this instruction should be gated on `f16c`. - * the EVEX-coded variant should be gated on `avx512f` or `avx512vl` if not - * EVEX.512-coded. - */ - if !self.avx() || !self.f16c() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::RDRAND => { - if !self.rdrand() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::RDSEED => { - if !self.rdseed() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::MONITORX | Opcode::MWAITX | // these are gated on the `monitorx` and `mwaitx` cpuid bits, but are AMD-only. - Opcode::CLZERO | Opcode::RDPRU => { // again, gated on specific cpuid bits, but AMD-only. - if !self.amd_quirks() { - return Err(DecodeError::InvalidOpcode); - } - } - other => { - if !self.bmi1() { - if BMI1.contains(&other) { - return Err(DecodeError::InvalidOpcode); - } - } - if !self.bmi2() { - if BMI2.contains(&other) { - return Err(DecodeError::InvalidOpcode); - } - } - } - } - Ok(()) + #[inline(always)] + fn decode_with_annotation< + T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>, + S: DescriptionSink<Self::FieldDescription> + >(&self, instr: &mut Instruction, words: &mut T, sink: &mut S) -> Result<(), <Arch as yaxpeax_arch::Arch>::DecodeError> { + decode_with_annotation(self, instr, words, sink) } } @@ -4110,7 +2768,7 @@ impl Default for InstDecoder { /// instruction defined in any extension. fn default() -> Self { Self { - flags: 0xffffffff_ffffffff, + flags: [0xffffffff_ffffffff, 0xffffffff_ffffffff], } } } @@ -4118,16 +2776,7 @@ impl Default for InstDecoder { impl Decoder<Arch> for InstDecoder { fn decode<T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>>(&self, words: &mut T) -> Result<Instruction, <Arch as yaxpeax_arch::Arch>::DecodeError> { let mut instr = Instruction::invalid(); - DecodeCtx::new().read_with_annotations(self, words, &mut instr, &mut NullSink)?; - - instr.length = words.offset() as u8; - if words.offset() > 15 { - return Err(DecodeError::TooLong); - } - - if self != &InstDecoder::default() { - self.revise_instruction(&mut instr)?; - } + self.decode_into(&mut instr, words)?; Ok(instr) } @@ -4143,19 +2792,26 @@ impl AnnotatingDecoder<Arch> for InstDecoder { T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>, S: DescriptionSink<Self::FieldDescription> >(&self, instr: &mut Instruction, words: &mut T, sink: &mut S) -> Result<(), <Arch as yaxpeax_arch::Arch>::DecodeError> { - DecodeCtx::new().read_with_annotations(self, words, instr, sink)?; - - instr.length = words.offset() as u8; - if words.offset() > 15 { - return Err(DecodeError::TooLong); - } + decode_with_annotation(self, instr, words, sink) + } +} - if self != &InstDecoder::default() { - self.revise_instruction(instr)?; - } +#[inline(always)] +fn decode_with_annotation< + D: IsaSettings, + T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>, + S: DescriptionSink<FieldDescription> +>(isa_settings: &D, instr: &mut Instruction, words: &mut T, sink: &mut S) -> Result<(), <Arch as yaxpeax_arch::Arch>::DecodeError> { + DecodeCtx::new().read_with_annotations(isa_settings, words, instr, sink)?; - Ok(()) + instr.length = words.offset() as u8; + if words.offset() > 15 { + return Err(DecodeError::TooLong); } + + isa_settings.revise_instruction(instr)?; + + Ok(()) } impl Opcode { @@ -6738,9 +5394,10 @@ fn read_opc_hotpath< #[cfg_attr(feature="profiling", inline(never))] #[cfg_attr(not(feature="profiling"), inline(always))] fn read_with_annotations< + D: IsaSettings, T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>, S: DescriptionSink<FieldDescription>, ->(&mut self, decoder: &InstDecoder, words: &mut T, instruction: &mut Instruction, sink: &mut S) -> Result<(), DecodeError> { +>(&mut self, isa_settings: &D, words: &mut T, instruction: &mut Instruction, sink: &mut S) -> Result<(), DecodeError> { words.mark(); let mut nextb = words.next().ok().ok_or(DecodeError::ExhaustedInput)?; let mut next_rec = OPCODES[nextb as usize]; @@ -6886,7 +5543,7 @@ fn read_with_annotations< record.operand() }; - self.read_operands(decoder, words, instruction, record, sink)?; + self.read_operands(isa_settings, words, instruction, record, sink)?; if self.check_lock { if !instruction.opcode.can_lock() || !instruction.operands[0].is_memory() { @@ -6900,9 +5557,10 @@ fn read_with_annotations< #[cfg_attr(feature="profiling", inline(never))] #[cfg_attr(not(feature="profiling"), inline(always))] fn read_operands< + D: IsaSettings, T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>, S: DescriptionSink<FieldDescription> ->(&mut self, decoder: &InstDecoder, words: &mut T, instruction: &mut Instruction, operand_code: OperandCode, sink: &mut S) -> Result<(), DecodeError> { +>(&mut self, isa_settings: &D, words: &mut T, instruction: &mut Instruction, operand_code: OperandCode, sink: &mut S) -> Result<(), DecodeError> { sink.record( words.offset() as u32 * 8 - 1, words.offset() as u32 * 8 - 1, InnerDescription::Boundary("opcode ends/operands begin (typically)") @@ -7920,9 +6578,8 @@ fn read_operands< ); vex::three_byte_vex(words, modrm, instruction, sink)?; - if decoder != &InstDecoder::default() { - decoder.revise_instruction(instruction)?; - } + isa_settings.revise_instruction(instruction)?; + return Ok(()); } } else { @@ -7953,9 +6610,8 @@ fn read_operands< ); vex::two_byte_vex(words, modrm, instruction, sink)?; - if decoder != &InstDecoder::default() { - decoder.revise_instruction(instruction)?; - } + isa_settings.revise_instruction(instruction)?; + return Ok(()); } } else { @@ -9859,7 +8515,7 @@ fn read_operands< instruction.opcode = Opcode::LFENCE; // Intel's manual accepts m != 0, AMD supports m != 0 though the manual // doesn't say (tested on threadripper) - if !decoder.amd_quirks() && !decoder.intel_quirks() { + if !isa_settings.amd_quirks() && !isa_settings.intel_quirks() { if m != 0 { return Err(DecodeError::InvalidOperand); } @@ -9869,7 +8525,7 @@ fn read_operands< instruction.opcode = Opcode::MFENCE; // Intel's manual accepts m != 0, AMD supports m != 0 though the manual // doesn't say (tested on threadripper) - if !decoder.amd_quirks() && !decoder.intel_quirks() { + if !isa_settings.amd_quirks() && !isa_settings.intel_quirks() { if m != 0 { return Err(DecodeError::InvalidOperand); } @@ -9879,7 +8535,7 @@ fn read_operands< instruction.opcode = Opcode::SFENCE; // Intel's manual accepts m != 0, AMD supports m != 0 though the manual // doesn't say (tested on threadripper) - if !decoder.amd_quirks() && !decoder.intel_quirks() { + if !isa_settings.amd_quirks() && !isa_settings.intel_quirks() { if m != 0 { return Err(DecodeError::InvalidOperand); } diff --git a/src/real_mode/isa_settings.rs b/src/real_mode/isa_settings.rs new file mode 100644 index 0000000..55d18a7 --- /dev/null +++ b/src/real_mode/isa_settings.rs @@ -0,0 +1,952 @@ +use super::{BMI1, BMI2, DecodeError, DecodeEverything, InstDecoder, Instruction, Opcode}; + +macro_rules! gen_isa_settings { + ( + $( + $(#[$doc:meta])* + $feature:ident, + $(#[$set_doc:item])* + $set_feature:ident = $idx:expr; + )+ + + { + $( + $(#[$composite_doc:meta])* + $composite_feature:ident = { + $first_inner_feature:ident + $(,$($inner_feature:ident$(,)?)+)? + }$(,)? + )* + } + + { + $( + $(#[$composite_set_doc:meta])* + $composite_set_feature:ident = { + $set_first_inner_feature:ident + $(,$($set_inner_feature:ident$(,)?)+)? + }$(,)? + )* + } + ) => { + /// specific decode settings controlling how an x86 byte sequence is interpreted. + /// + /// this currently exists to specify which extensions are to be accepted or rejected. the two + /// implementations provided by `yaxpeax-x86` are: + /// * [`InstDecoder`], providing configurable enablement or disablement per-extension + /// * [`DecodeEverything`], which allows all extensions supported by `yaxpeax-x86` + /// + /// notably, `InstDecoder::default()` and `DecodeEverything` are functionally equivalent in that + /// they accept all extensions supported by the decoder. + /// + /// TODO: many additional extension support flags. + /// * extended MMX (see `sha256:daee4e23dac983f1744126352d40cc71d47b4a9283a2a1e473837728ca9c51ac`) + /// * lots of others... tile extensions... + pub trait IsaSettings { + $( + $(#[$doc])* + fn $feature(&self) -> bool; + )+ + + $( + $(#[$composite_doc])* + fn $composite_feature(&self) -> bool { + self.$first_inner_feature() + $($(&& self.$inner_feature())+)? + } + )* + + fn revise_instruction(&self, inst: &mut Instruction) -> Result<(), DecodeError> { + revise_instruction(self, inst) + } + } + + impl IsaSettings for DecodeEverything { + $(fn $feature(&self) -> bool { true })+ + + fn revise_instruction(&self, _inst: &mut Instruction) -> Result<(), DecodeError> { + Ok(()) + } + } + + impl IsaSettings for InstDecoder { + $( + fn $feature(&self) -> bool { + let i = $idx as usize; + self.flags[i / 64] & (1 << (i % 64)) != 0 + } + )+ + } + + impl InstDecoder { + $( + $(#[$set_doc])* + pub fn $set_feature(mut self) -> Self { + let i = $idx as usize; + self.flags[i / 64] |= 1 << (i % 64); + self + } + )+ + + $( + $(#[$composite_set_doc])* + pub fn $composite_set_feature(&self) -> Self { + self.$set_first_inner_feature() + $($(.$set_inner_feature())+)? + } + )* + + $( + $(#[$doc])* + pub fn $feature(&self) -> bool { + <Self as IsaSettings>::$feature(self) + } + )+ + + $( + $(#[$composite_doc])* + pub fn $composite_feature(&self) -> bool { + <Self as IsaSettings>::$composite_feature(self) + } + )* + } + } +} +gen_isa_settings!( + _3dnow, with_3dnow = 1; + _3dnowprefetch, with_3dnowprefetch = 2; + abm, with_abm = 3; + adx, with_adx = 4; + aesni, with_aesni = 5; + amd_quirks, with_amd_quirks = 6; + avx, with_avx = 7; + avx2, with_avx2 = 8; + avx512_4fmaps, with_avx512_4fmaps = 10; + avx512_4vnniw, with_avx512_4vnniw = 11; + avx512_bitalg, with_avx512_bitalg = 12; + avx512_bw, with_avx512_bw = 13; + avx512_cd, with_avx512_cd = 14; + avx512_dq, with_avx512_dq = 15; + avx512_er, with_avx512_er = 16; + avx512_f, with_avx512_f = 17; + avx512_fma, with_avx512_fma = 18; + avx512_pf, with_avx512_pf = 19; + avx512_vbmi, with_avx512_vbmi = 20; + avx512_vbmi2, with_avx512_vbmi2 = 21; + avx512_vl, with_avx512_vl = 22; + avx512_vpopcntdq, with_avx512_vpopcntdq = 23; + avx_vnni, with_avx_vnni = 24; + bmi1, with_bmi1 = 25; + #[doc="`bmi2` indicates support for the `BZHI`, `MULX`, `PDEP`, `PEXT`, `RORX`, `SARX`, `SHRX`, "] + #[doc="and `SHLX` instructions. `bmi2` is implemented in all x86_64 chips that implement `bmi`, "] + #[doc="except the amd `piledriver` and `steamroller` microarchitectures."] + bmi2, with_bmi2 = 26; + clflushopt, with_clflushopt = 27; + clwb, with_clwb = 28; + cmov, with_cmov = 29; + cmpxchg16b, with_cmpxchg16b = 30; + cx8, with_cx8 = 31; + f16c, with_f16c = 32; + fma3, with_fma3 = 33; + fma4, with_fma4 = 34; + gfni, with_gfni = 35; + intel_quirks, with_intel_quirks = 36; + invpcid, with_invpcid = 37; + #[doc="`lahfsahf` is only unset for early revisions of 64-bit amd and intel chips. unfortunately"] + #[doc="the clearest documentation on when these instructions were reintroduced into 64-bit"] + #[doc="architectures seems to be"] + #[doc="[wikipedia](https://en.wikipedia.org/wiki/X86-64#Older_implementations):"] + #[doc="```text"] + #[doc="Early AMD64 and Intel 64 CPUs lacked LAHF and SAHF instructions in 64-bit mode. AMD"] + #[doc="introduced these instructions (also in 64-bit mode) with their Athlon 64, Opteron and"] + #[doc="Turion 64 revision D processors in March 2005[48][49][50] while Intel introduced the"] + #[doc="instructions with the Pentium 4 G1 stepping in December 2005. The 64-bit version of Windows"] + #[doc="8.1 requires this feature.[47]"] + #[doc="```"] + #[doc=""] + #[doc="this puts reintroduction of these instructions somewhere in the middle of prescott and k8"] + #[doc="lifecycles, for intel and amd respectively. because there is no specific uarch where these"] + #[doc="features become enabled, prescott and k8 default to not supporting these instructions,"] + #[doc="where later uarches support these instructions."] + lahfsahf, with_lahfsahf = 38; + lzcnt, with_lzcnt = 39; + monitor, with_monitor = 40; + movbe, with_movbe = 41; + mpx, with_mpx = 42; + pclmulqdq, with_pclmulqdq = 43; + pcommit, with_pcommit = 44; + popcnt, with_popcnt = 45; + prefetchw, with_prefetchw = 46; + prefetchwt1, with_prefetchwt1 = 47; + rdrand, with_rdrand = 48; + rdseed, with_rdseed = 49; + rdtscp, with_rdtscp = 50; + sgx, with_sgx = 51; + sha, with_sha = 52; + skinit, with_skinit = 53; + sse3, with_sse3 = 54; + sse4_1, with_sse4_1 = 55; + sse4_2, with_sse4_2 = 56; + sse4a, with_sse4a = 57; + ssse3, with_ssse3 = 58; + svm, with_svm = 59; + syscall, with_syscall = 60; + tbm, with_tbm = 61; + tsx, with_tsx = 62; + vaes, with_vaes = 63; + vmx, with_vmx = 64; + xop, with_xop = 65; + xsave, with_xsave = 66; + + { + sse4 = { + sse4_1, + sse4_2, + } + + #[doc = "returns `true` if this `InstDecoder` has **all** `avx512` features enabled."] + avx512 = { + avx512_4fmaps, + avx512_4vnniw, + avx512_bitalg, + avx512_bw, + avx512_cd, + avx512_dq, + avx512_er, + avx512_f, + avx512_fma, + avx512_pf, + avx512_vbmi, + avx512_vbmi2, + avx512_vl, + avx512_vpopcntdq, + }, + } + + { + with_sse4 = { + with_sse4_1, + with_sse4_2, + } + + with_avx512 = { + with_avx512_4fmaps, + with_avx512_4vnniw, + with_avx512_bitalg, + with_avx512_bw, + with_avx512_cd, + with_avx512_dq, + with_avx512_er, + with_avx512_f, + with_avx512_fma, + with_avx512_pf, + with_avx512_vbmi, + with_avx512_vbmi2, + with_avx512_vl, + with_avx512_vpopcntdq, + } + } +); + +/// optionally reject or reinterpret instruction according to settings for this decode +/// operation. +fn revise_instruction<D: IsaSettings + ?Sized>(settings: &D, inst: &mut Instruction) -> Result<(), DecodeError> { + if inst.prefixes.evex().is_some() { + if !settings.avx512() { + return Err(DecodeError::InvalidOpcode); + } else { + return Ok(()); + } + } + match inst.opcode { + // original 3dnow instructions. see also + // `3DNow-Technology-Manual.pdf` + // * sha256: daee4e23dac983f1744126352d40cc71d47b4a9283a2a1e473837728ca9c51ac + // * ref: https://www.amd.com/content/dam/amd/en/documents/archived-tech-docs/programmer-references/21928.pdf + // * order# 21928 + Opcode::FEMMS | + Opcode::PAVGUSB | + Opcode::PFADD | + Opcode::PFSUB | + Opcode::PFSUBR | + Opcode::PFACC | + Opcode::PFCMPGE | + Opcode::PFCMPGT | + Opcode::PFCMPEQ | + Opcode::PFMAX | + Opcode::PFMIN | + Opcode::PI2FD | + Opcode::PF2ID | + Opcode::PFRCP | + Opcode::PFRSQRT | + Opcode::PFMUL | + Opcode::PFRCPIT1 | + Opcode::PFRCPIT2 | + Opcode::PFRSQIT1 | + Opcode::PMULHRW => { + if !settings._3dnow() { + return Err(DecodeError::InvalidOpcode); + } + } + // later extension to 3dnow. see also + // `AMD-Extensions-to-the-3DNow-and-MMX-Instruction-Sets.pdf` + // * sha256: ad847bd6877a682296fc584b4bbee354bf84c57bb97ba57e9c9adfc63cc5f465 + // * ref: https://refspecs.linuxfoundation.org/AMD-extensions.pdf + // * order# 22466 + Opcode::PF2IW | + Opcode::PFNACC | + Opcode::PFPNACC | + Opcode::PI2FW | + Opcode::PSWAPD => { + if !settings._3dnow() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::TZCNT => { + if !settings.bmi1() { + // tzcnt is only supported if bmi1 is enabled. without bmi1, this decodes as + // bsf. + inst.opcode = Opcode::BSF; + } + } + Opcode::LDDQU | + Opcode::ADDSUBPS | + Opcode::ADDSUBPD | + Opcode::HADDPS | + Opcode::HSUBPS | + Opcode::HADDPD | + Opcode::HSUBPD | + Opcode::MOVSHDUP | + Opcode::MOVSLDUP | + Opcode::MOVDDUP | + Opcode::MONITOR | + Opcode::MWAIT => { + // via Intel section 5.7, SSE3 Instructions + if !settings.sse3() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::PHADDW | + Opcode::PHADDSW | + Opcode::PHADDD | + Opcode::PHSUBW | + Opcode::PHSUBSW | + Opcode::PHSUBD | + Opcode::PABSB | + Opcode::PABSW | + Opcode::PABSD | + Opcode::PMADDUBSW | + Opcode::PMULHRSW | + Opcode::PSHUFB | + Opcode::PSIGNB | + Opcode::PSIGNW | + Opcode::PSIGND | + Opcode::PALIGNR => { + // via Intel section 5.8, SSSE3 Instructions + if !settings.ssse3() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::PMULLD | + Opcode::PMULDQ | + Opcode::MOVNTDQA | + Opcode::BLENDPD | + Opcode::BLENDPS | + Opcode::BLENDVPD | + Opcode::BLENDVPS | + Opcode::PBLENDVB | + Opcode::BLENDW | + Opcode::PMINUW | + Opcode::PMINUD | + Opcode::PMINSB | + Opcode::PMINSD | + Opcode::PMAXUW | + Opcode::PMAXUD | + Opcode::PMAXSB | + Opcode::PMAXSD | + Opcode::ROUNDPS | + Opcode::ROUNDPD | + Opcode::ROUNDSS | + Opcode::ROUNDSD | + Opcode::PBLENDW | + Opcode::EXTRACTPS | + Opcode::INSERTPS | + Opcode::PINSRB | + Opcode::PINSRD | + Opcode::PINSRQ | + Opcode::PMOVSXBW | + Opcode::PMOVZXBW | + Opcode::PMOVSXBD | + Opcode::PMOVZXBD | + Opcode::PMOVSXWD | + Opcode::PMOVZXWD | + Opcode::PMOVSXBQ | + Opcode::PMOVZXBQ | + Opcode::PMOVSXWQ | + Opcode::PMOVZXWQ | + Opcode::PMOVSXDQ | + Opcode::PMOVZXDQ | + Opcode::DPPS | + Opcode::DPPD | + Opcode::MPSADBW | + Opcode::PHMINPOSUW | + Opcode::PTEST | + Opcode::PCMPEQQ | + Opcode::PEXTRB | + Opcode::PEXTRW | + Opcode::PEXTRD | + Opcode::PEXTRQ | + Opcode::PACKUSDW => { + // via Intel section 5.10, SSE4.1 Instructions + if !settings.sse4_1() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::EXTRQ | + Opcode::INSERTQ | + Opcode::MOVNTSS | + Opcode::MOVNTSD => { + if !settings.sse4a() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::CRC32 | + Opcode::PCMPESTRI | + Opcode::PCMPESTRM | + Opcode::PCMPISTRI | + Opcode::PCMPISTRM | + Opcode::PCMPGTQ => { + // via Intel section 5.11, SSE4.2 Instructions + if !settings.sse4_2() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::AESDEC | + Opcode::AESDECLAST | + Opcode::AESENC | + Opcode::AESENCLAST | + Opcode::AESIMC | + Opcode::AESKEYGENASSIST => { + // via Intel section 5.12. AESNI AND PCLMULQDQ + if !settings.aesni() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::PCLMULQDQ => { + // via Intel section 5.12. AESNI AND PCLMULQDQ + if !settings.pclmulqdq() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::XABORT | + Opcode::XBEGIN | + Opcode::XEND | + Opcode::XTEST => { + if !settings.tsx() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::SHA1MSG1 | + Opcode::SHA1MSG2 | + Opcode::SHA1NEXTE | + Opcode::SHA1RNDS4 | + Opcode::SHA256MSG1 | + Opcode::SHA256MSG2 | + Opcode::SHA256RNDS2 => { + if !settings.sha() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::ENCLV | + Opcode::ENCLS | + Opcode::ENCLU => { + if !settings.sgx() { + return Err(DecodeError::InvalidOpcode); + } + } + // AVX... + Opcode::VMOVDDUP | + Opcode::VPSHUFLW | + Opcode::VPSHUFHW | + Opcode::VHADDPS | + Opcode::VHSUBPS | + Opcode::VADDSUBPS | + Opcode::VCVTPD2DQ | + Opcode::VLDDQU | + Opcode::VCOMISD | + Opcode::VCOMISS | + Opcode::VUCOMISD | + Opcode::VUCOMISS | + Opcode::VADDPD | + Opcode::VADDPS | + Opcode::VADDSD | + Opcode::VADDSS | + Opcode::VADDSUBPD | + Opcode::VBLENDPD | + Opcode::VBLENDPS | + Opcode::VBLENDVPD | + Opcode::VBLENDVPS | + Opcode::VBROADCASTF128 | + Opcode::VBROADCASTI128 | + Opcode::VBROADCASTSD | + Opcode::VBROADCASTSS | + Opcode::VCMPSD | + Opcode::VCMPSS | + Opcode::VCMPPD | + Opcode::VCMPPS | + Opcode::VCVTDQ2PD | + Opcode::VCVTDQ2PS | + Opcode::VCVTPD2PS | + Opcode::VCVTPS2DQ | + Opcode::VCVTPS2PD | + Opcode::VCVTSS2SD | + Opcode::VCVTSI2SS | + Opcode::VCVTSI2SD | + Opcode::VCVTSD2SI | + Opcode::VCVTSD2SS | + Opcode::VCVTSS2SI | + Opcode::VCVTTPD2DQ | + Opcode::VCVTTPS2DQ | + Opcode::VCVTTSS2SI | + Opcode::VCVTTSD2SI | + Opcode::VDIVPD | + Opcode::VDIVPS | + Opcode::VDIVSD | + Opcode::VDIVSS | + Opcode::VDPPD | + Opcode::VDPPS | + Opcode::VEXTRACTF128 | + Opcode::VEXTRACTI128 | + Opcode::VEXTRACTPS | + Opcode::VFMADD132PD | + Opcode::VFMADD132PS | + Opcode::VFMADD132SD | + Opcode::VFMADD132SS | + Opcode::VFMADD213PD | + Opcode::VFMADD213PS | + Opcode::VFMADD213SD | + Opcode::VFMADD213SS | + Opcode::VFMADD231PD | + Opcode::VFMADD231PS | + Opcode::VFMADD231SD | + Opcode::VFMADD231SS | + Opcode::VFMADDSUB132PD | + Opcode::VFMADDSUB132PS | + Opcode::VFMADDSUB213PD | + Opcode::VFMADDSUB213PS | + Opcode::VFMADDSUB231PD | + Opcode::VFMADDSUB231PS | + Opcode::VFMSUB132PD | + Opcode::VFMSUB132PS | + Opcode::VFMSUB132SD | + Opcode::VFMSUB132SS | + Opcode::VFMSUB213PD | + Opcode::VFMSUB213PS | + Opcode::VFMSUB213SD | + Opcode::VFMSUB213SS | + Opcode::VFMSUB231PD | + Opcode::VFMSUB231PS | + Opcode::VFMSUB231SD | + Opcode::VFMSUB231SS | + Opcode::VFMSUBADD132PD | + Opcode::VFMSUBADD132PS | + Opcode::VFMSUBADD213PD | + Opcode::VFMSUBADD213PS | + Opcode::VFMSUBADD231PD | + Opcode::VFMSUBADD231PS | + Opcode::VFNMADD132PD | + Opcode::VFNMADD132PS | + Opcode::VFNMADD132SD | + Opcode::VFNMADD132SS | + Opcode::VFNMADD213PD | + Opcode::VFNMADD213PS | + Opcode::VFNMADD213SD | + Opcode::VFNMADD213SS | + Opcode::VFNMADD231PD | + Opcode::VFNMADD231PS | + Opcode::VFNMADD231SD | + Opcode::VFNMADD231SS | + Opcode::VFNMSUB132PD | + Opcode::VFNMSUB132PS | + Opcode::VFNMSUB132SD | + Opcode::VFNMSUB132SS | + Opcode::VFNMSUB213PD | + Opcode::VFNMSUB213PS | + Opcode::VFNMSUB213SD | + Opcode::VFNMSUB213SS | + Opcode::VFNMSUB231PD | + Opcode::VFNMSUB231PS | + Opcode::VFNMSUB231SD | + Opcode::VFNMSUB231SS | + Opcode::VGATHERDPD | + Opcode::VGATHERDPS | + Opcode::VGATHERQPD | + Opcode::VGATHERQPS | + Opcode::VHADDPD | + Opcode::VHSUBPD | + Opcode::VINSERTF128 | + Opcode::VINSERTI128 | + Opcode::VINSERTPS | + Opcode::VMASKMOVDQU | + Opcode::VMASKMOVPD | + Opcode::VMASKMOVPS | + Opcode::VMAXPD | + Opcode::VMAXPS | + Opcode::VMAXSD | + Opcode::VMAXSS | + Opcode::VMINPD | + Opcode::VMINPS | + Opcode::VMINSD | + Opcode::VMINSS | + Opcode::VMOVAPD | + Opcode::VMOVAPS | + Opcode::VMOVD | + Opcode::VMOVDQA | + Opcode::VMOVDQU | + Opcode::VMOVHLPS | + Opcode::VMOVHPD | + Opcode::VMOVHPS | + Opcode::VMOVLHPS | + Opcode::VMOVLPD | + Opcode::VMOVLPS | + Opcode::VMOVMSKPD | + Opcode::VMOVMSKPS | + Opcode::VMOVNTDQ | + Opcode::VMOVNTDQA | + Opcode::VMOVNTPD | + Opcode::VMOVNTPS | + Opcode::VMOVQ | + Opcode::VMOVSS | + Opcode::VMOVSD | + Opcode::VMOVSHDUP | + Opcode::VMOVSLDUP | + Opcode::VMOVUPD | + Opcode::VMOVUPS | + Opcode::VMPSADBW | + Opcode::VMULPD | + Opcode::VMULPS | + Opcode::VMULSD | + Opcode::VMULSS | + Opcode::VPABSB | + Opcode::VPABSD | + Opcode::VPABSW | + Opcode::VPACKSSDW | + Opcode::VPACKUSDW | + Opcode::VPACKSSWB | + Opcode::VPACKUSWB | + Opcode::VPADDB | + Opcode::VPADDD | + Opcode::VPADDQ | + Opcode::VPADDSB | + Opcode::VPADDSW | + Opcode::VPADDUSB | + Opcode::VPADDUSW | + Opcode::VPADDW | + Opcode::VPALIGNR | + Opcode::VPAND | + Opcode::VANDPD | + Opcode::VANDPS | + Opcode::VANDNPD | + Opcode::VANDNPS | + Opcode::VORPD | + Opcode::VORPS | + Opcode::VPANDN | + Opcode::VPAVGB | + Opcode::VPAVGW | + Opcode::VPBLENDD | + Opcode::VPBLENDVB | + Opcode::VPBLENDW | + Opcode::VPBROADCASTB | + Opcode::VPBROADCASTD | + Opcode::VPBROADCASTQ | + Opcode::VPBROADCASTW | + Opcode::VPCLMULQDQ | + Opcode::VPCMPEQB | + Opcode::VPCMPEQD | + Opcode::VPCMPEQQ | + Opcode::VPCMPEQW | + Opcode::VPCMPGTB | + Opcode::VPCMPGTD | + Opcode::VPCMPGTQ | + Opcode::VPCMPGTW | + Opcode::VPCMPESTRI | + Opcode::VPCMPESTRM | + Opcode::VPCMPISTRI | + Opcode::VPCMPISTRM | + Opcode::VPERM2F128 | + Opcode::VPERM2I128 | + Opcode::VPERMD | + Opcode::VPERMILPD | + Opcode::VPERMILPS | + Opcode::VPERMPD | + Opcode::VPERMPS | + Opcode::VPERMQ | + Opcode::VPEXTRB | + Opcode::VPEXTRD | + Opcode::VPEXTRQ | + Opcode::VPEXTRW | + Opcode::VPGATHERDD | + Opcode::VPGATHERDQ | + Opcode::VPGATHERQD | + Opcode::VPGATHERQQ | + Opcode::VPHADDD | + Opcode::VPHADDSW | + Opcode::VPHADDW | + Opcode::VPMADDUBSW | + Opcode::VPHMINPOSUW | + Opcode::VPHSUBD | + Opcode::VPHSUBSW | + Opcode::VPHSUBW | + Opcode::VPINSRB | + Opcode::VPINSRD | + Opcode::VPINSRQ | + Opcode::VPINSRW | + Opcode::VPMADDWD | + Opcode::VPMASKMOVD | + Opcode::VPMASKMOVQ | + Opcode::VPMAXSB | + Opcode::VPMAXSD | + Opcode::VPMAXSW | + Opcode::VPMAXUB | + Opcode::VPMAXUW | + Opcode::VPMAXUD | + Opcode::VPMINSB | + Opcode::VPMINSW | + Opcode::VPMINSD | + Opcode::VPMINUB | + Opcode::VPMINUW | + Opcode::VPMINUD | + Opcode::VPMOVMSKB | + Opcode::VPMOVSXBD | + Opcode::VPMOVSXBQ | + Opcode::VPMOVSXBW | + Opcode::VPMOVSXDQ | + Opcode::VPMOVSXWD | + Opcode::VPMOVSXWQ | + Opcode::VPMOVZXBD | + Opcode::VPMOVZXBQ | + Opcode::VPMOVZXBW | + Opcode::VPMOVZXDQ | + Opcode::VPMOVZXWD | + Opcode::VPMOVZXWQ | + Opcode::VPMULDQ | + Opcode::VPMULHRSW | + Opcode::VPMULHUW | + Opcode::VPMULHW | + Opcode::VPMULLQ | + Opcode::VPMULLD | + Opcode::VPMULLW | + Opcode::VPMULUDQ | + Opcode::VPOR | + Opcode::VPSADBW | + Opcode::VPSHUFB | + Opcode::VPSHUFD | + Opcode::VPSIGNB | + Opcode::VPSIGND | + Opcode::VPSIGNW | + Opcode::VPSLLD | + Opcode::VPSLLDQ | + Opcode::VPSLLQ | + Opcode::VPSLLVD | + Opcode::VPSLLVQ | + Opcode::VPSLLW | + Opcode::VPSRAD | + Opcode::VPSRAVD | + Opcode::VPSRAW | + Opcode::VPSRLD | + Opcode::VPSRLDQ | + Opcode::VPSRLQ | + Opcode::VPSRLVD | + Opcode::VPSRLVQ | + Opcode::VPSRLW | + Opcode::VPSUBB | + Opcode::VPSUBD | + Opcode::VPSUBQ | + Opcode::VPSUBSB | + Opcode::VPSUBSW | + Opcode::VPSUBUSB | + Opcode::VPSUBUSW | + Opcode::VPSUBW | + Opcode::VPTEST | + Opcode::VPUNPCKHBW | + Opcode::VPUNPCKHDQ | + Opcode::VPUNPCKHQDQ | + Opcode::VPUNPCKHWD | + Opcode::VPUNPCKLBW | + Opcode::VPUNPCKLDQ | + Opcode::VPUNPCKLQDQ | + Opcode::VPUNPCKLWD | + Opcode::VPXOR | + Opcode::VRCPPS | + Opcode::VROUNDPD | + Opcode::VROUNDPS | + Opcode::VROUNDSD | + Opcode::VROUNDSS | + Opcode::VRSQRTPS | + Opcode::VRSQRTSS | + Opcode::VRCPSS | + Opcode::VSHUFPD | + Opcode::VSHUFPS | + Opcode::VSQRTPD | + Opcode::VSQRTPS | + Opcode::VSQRTSS | + Opcode::VSQRTSD | + Opcode::VSUBPD | + Opcode::VSUBPS | + Opcode::VSUBSD | + Opcode::VSUBSS | + Opcode::VTESTPD | + Opcode::VTESTPS | + Opcode::VUNPCKHPD | + Opcode::VUNPCKHPS | + Opcode::VUNPCKLPD | + Opcode::VUNPCKLPS | + Opcode::VXORPD | + Opcode::VXORPS | + Opcode::VZEROUPPER | + Opcode::VZEROALL | + Opcode::VLDMXCSR | + Opcode::VSTMXCSR => { + // TODO: check a table for these + if !settings.avx() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::VAESDEC | + Opcode::VAESDECLAST | + Opcode::VAESENC | + Opcode::VAESENCLAST | + Opcode::VAESIMC | + Opcode::VAESKEYGENASSIST => { + // TODO: check a table for these + if !settings.avx() || !settings.aesni() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::MOVBE => { + if !settings.movbe() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::POPCNT => { + /* + * from the intel SDM: + * ``` + * Before an application attempts to use the POPCNT instruction, it must check that + * the processor supports SSE4.2 (if CPUID.01H:ECX.SSE4_2[bit 20] = 1) and POPCNT + * (if CPUID.01H:ECX.POPCNT[bit 23] = 1). + * ``` + */ + if settings.intel_quirks() && (settings.sse4_2() || settings.popcnt()) { + return Ok(()); + } else if !settings.popcnt() { + /* + * elsewhere from the amd APM: + * `Instruction Subsets and CPUID Feature Flags` on page 507 indicates that + * popcnt is present when the popcnt bit is reported by cpuid. this seems to be + * the less quirky default, so `intel_quirks` is considered the outlier, and + * before this default. + * */ + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::LZCNT => { + /* + * amd APM, `LZCNT` page 212: + * LZCNT is an Advanced Bit Manipulation (ABM) instruction. Support for the LZCNT + * instruction is indicated by CPUID Fn8000_0001_ECX[ABM] = 1. + * + * meanwhile the intel SDM simply states: + * ``` + * CPUID.EAX=80000001H:ECX.LZCNT[bit 5]: if 1 indicates the processor supports the + * LZCNT instruction. + * ``` + * + * so that's considered the less-quirky (default) case here. + * */ + if settings.amd_quirks() && !settings.abm() { + return Err(DecodeError::InvalidOpcode); + } else if !settings.lzcnt() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::ADCX | + Opcode::ADOX => { + if !settings.adx() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::VMRUN | + Opcode::VMLOAD | + Opcode::VMSAVE | + Opcode::CLGI | + Opcode::VMMCALL | + Opcode::INVLPGA => { + if !settings.svm() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::STGI | + Opcode::SKINIT => { + if !settings.svm() || !settings.skinit() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::LAHF | + Opcode::SAHF => { + if !settings.lahfsahf() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::VCVTPS2PH | + Opcode::VCVTPH2PS => { + /* + * from intel SDM: + * ``` + * 14.4.1 Detection of F16C Instructions Application using float 16 instruction + * must follow a detection sequence similar to AVX to ensure: • The OS has + * enabled YMM state management support, • The processor support AVX as + * indicated by the CPUID feature flag, i.e. CPUID.01H:ECX.AVX[bit 28] = 1. • + * The processor support 16-bit floating-point conversion instructions via a + * CPUID feature flag (CPUID.01H:ECX.F16C[bit 29] = 1). + * ``` + * + * TODO: only the VEX-coded variant of this instruction should be gated on `f16c`. + * the EVEX-coded variant should be gated on `avx512f` or `avx512vl` if not + * EVEX.512-coded. + */ + if !settings.avx() || !settings.f16c() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::RDRAND => { + if !settings.rdrand() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::RDSEED => { + if !settings.rdseed() { + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::MONITORX | Opcode::MWAITX | // these are gated on the `monitorx` and `mwaitx` cpuid bits, but are AMD-only. + Opcode::CLZERO | Opcode::RDPRU => { // again, gated on specific cpuid bits, but AMD-only. + if !settings.amd_quirks() { + return Err(DecodeError::InvalidOpcode); + } + } + other => { + if !settings.bmi1() { + if BMI1.contains(&other) { + return Err(DecodeError::InvalidOpcode); + } + } + if !settings.bmi2() { + if BMI2.contains(&other) { + return Err(DecodeError::InvalidOpcode); + } + } + } + } + Ok(()) +} diff --git a/src/real_mode/mod.rs b/src/real_mode/mod.rs index b06a525..e62333b 100644 --- a/src/real_mode/mod.rs +++ b/src/real_mode/mod.rs @@ -3,6 +3,9 @@ mod evex; #[cfg(feature = "fmt")] mod display; pub mod uarch; +mod isa_settings; + +pub use isa_settings::IsaSettings; pub use crate::MemoryAccessSize; @@ -2707,73 +2710,7 @@ impl LengthedInstruction for Instruction { /// the design requiring that. #[derive(PartialEq, Copy, Clone, Eq, Hash, PartialOrd, Ord)] pub struct InstDecoder { - // extensions tracked here: - // 0. SSE3 - // 1. SSSE3 - // 2. monitor (intel-only?) - // 3. vmx (some atom chips still lack it) - // 4. fma3 (intel haswell/broadwell+, amd piledriver+) - // 5. cmpxchg16b (some amd are missing this one) - // 6. sse4.1 - // 7. sse4.2 - // 8. movbe - // 9. popcnt (independent of BMI) - // 10. aesni - // 11. xsave (xsave, xrestor, xsetbv, xgetbv) - // 12. rdrand (intel ivybridge+, amd ..??) - // 13. sgx (eadd, eblock, ecreate, edbgrd, edbgwr, einit, eldb, eldu, epa, eremove, etrace, - // ewb, eenter, eexit, egetkey, ereport, eresume) - // 14. bmi1 (intel haswell+, amd jaguar+) - // 15. avx2 (intel haswell+, amd excavator+) - // 16. bmi2 (intel ?, amd ?) - // 17. invpcid - // 18. mpx - // 19. avx512_f - // 20. avx512_dq - // 21. rdseed - // 22. adx - // 23. avx512_fma - // 24. pcommit - // 25. clflushopt - // 26. clwb - // 27. avx512_pf - // 28. avx512_er - // 29. avx512_cd - // 30. sha - // 31. avx512_bw - // 32. avx512_vl - // 33. prefetchwt1 - // 34. avx512_vbmi - // 35. avx512_vbmi2 - // 36. gfni (galois field instructions) - // 37. vaes - // 38. pclmulqdq - // 39. avx_vnni - // 40. avx512_bitalg - // 41. avx512_vpopcntdq - // 42. avx512_4vnniw - // 43. avx512_4fmaps - // 44. cx8 // cmpxchg8 - is this actually optional in x86? - // 45. syscall // syscall/sysret - actually optional in x86? - // 46. rdtscp // actually optional in x86? - // 47. abm (lzcnt, popcnt) - // 48. sse4a - // 49. 3dnowprefetch // actually optional? - // 50. xop - // 51. skinit - // 52. tbm - // 53. intel quirks - // 54. amd quirks - // 55. avx (intel ?, amd ?) - // 56. amd-v/svm - // 57. lahfsahf - // 58. cmov - // 59. f16c - // 60. fma4 - // 61. prefetchw - // 62. tsx - // 63. lzcnt - flags: u64, + flags: [u64; 2], } impl InstDecoder { @@ -2783,7 +2720,7 @@ impl InstDecoder { /// and any instructions defined by extensions. pub fn minimal() -> Self { InstDecoder { - flags: 0, + flags: [0, 0], } } @@ -2795,1311 +2732,32 @@ impl InstDecoder { let mut reader = yaxpeax_arch::U8Reader::new(data); self.decode(&mut reader) } +} - pub fn sse3(&self) -> bool { - self.flags & (1 << 0) != 0 - } - - pub fn with_sse3(mut self) -> Self { - self.flags |= 1 << 0; - self - } - - pub fn ssse3(&self) -> bool { - self.flags & (1 << 1) != 0 - } - - pub fn with_ssse3(mut self) -> Self { - self.flags |= 1 << 1; - self - } - - pub fn monitor(&self) -> bool { - self.flags & (1 << 2) != 0 - } - - pub fn with_monitor(mut self) -> Self { - self.flags |= 1 << 2; - self - } - - pub fn vmx(&self) -> bool { - self.flags & (1 << 3) != 0 - } - - pub fn with_vmx(mut self) -> Self { - self.flags |= 1 << 3; - self - } - - pub fn fma3(&self) -> bool { - self.flags & (1 << 4) != 0 - } - - pub fn with_fma3(mut self) -> Self { - self.flags |= 1 << 4; - self - } - - pub fn cmpxchg16b(&self) -> bool { - self.flags & (1 << 5) != 0 - } - - pub fn with_cmpxchg16b(mut self) -> Self { - self.flags |= 1 << 5; - self - } - - pub fn sse4_1(&self) -> bool { - self.flags & (1 << 6) != 0 - } - - pub fn with_sse4_1(mut self) -> Self { - self.flags |= 1 << 6; - self - } - - pub fn sse4_2(&self) -> bool { - self.flags & (1 << 7) != 0 - } - - pub fn with_sse4_2(mut self) -> Self { - self.flags |= 1 << 7; - self - } - - pub fn with_sse4(self) -> Self { - self - .with_sse4_1() - .with_sse4_2() - } - - pub fn movbe(&self) -> bool { - self.flags & (1 << 8) != 0 - } - - pub fn with_movbe(mut self) -> Self { - self.flags |= 1 << 8; - self - } - - pub fn popcnt(&self) -> bool { - self.flags & (1 << 9) != 0 - } - - pub fn with_popcnt(mut self) -> Self { - self.flags |= 1 << 9; - self - } - - pub fn aesni(&self) -> bool { - self.flags & (1 << 10) != 0 - } - - pub fn with_aesni(mut self) -> Self { - self.flags |= 1 << 10; - self - } - - pub fn xsave(&self) -> bool { - self.flags & (1 << 11) != 0 - } - - pub fn with_xsave(mut self) -> Self { - self.flags |= 1 << 11; - self - } - - pub fn rdrand(&self) -> bool { - self.flags & (1 << 12) != 0 - } - - pub fn with_rdrand(mut self) -> Self { - self.flags |= 1 << 12; - self - } - - pub fn sgx(&self) -> bool { - self.flags & (1 << 13) != 0 - } - - pub fn with_sgx(mut self) -> Self { - self.flags |= 1 << 13; - self - } - - pub fn bmi1(&self) -> bool { - self.flags & (1 << 14) != 0 - } - - pub fn with_bmi1(mut self) -> Self { - self.flags |= 1 << 14; - self - } - - pub fn avx2(&self) -> bool { - self.flags & (1 << 15) != 0 - } - - pub fn with_avx2(mut self) -> Self { - self.flags |= 1 << 15; - self - } - - /// `bmi2` indicates support for the `BZHI`, `MULX`, `PDEP`, `PEXT`, `RORX`, `SARX`, `SHRX`, - /// and `SHLX` instructions. `bmi2` is implemented in all x86 chips that implement `bmi`, - /// except the amd `piledriver` and `steamroller` microarchitectures. - pub fn bmi2(&self) -> bool { - self.flags & (1 << 16) != 0 - } - - pub fn with_bmi2(mut self) -> Self { - self.flags |= 1 << 16; - self - } - - pub fn invpcid(&self) -> bool { - self.flags & (1 << 17) != 0 - } - - pub fn with_invpcid(mut self) -> Self { - self.flags |= 1 << 17; - self - } - - pub fn mpx(&self) -> bool { - self.flags & (1 << 18) != 0 - } - - pub fn with_mpx(mut self) -> Self { - self.flags |= 1 << 18; - self - } - - pub fn avx512_f(&self) -> bool { - self.flags & (1 << 19) != 0 - } - - pub fn with_avx512_f(mut self) -> Self { - self.flags |= 1 << 19; - self - } - - pub fn avx512_dq(&self) -> bool { - self.flags & (1 << 20) != 0 - } - - pub fn with_avx512_dq(mut self) -> Self { - self.flags |= 1 << 20; - self - } - - pub fn rdseed(&self) -> bool { - self.flags & (1 << 21) != 0 - } - - pub fn with_rdseed(mut self) -> Self { - self.flags |= 1 << 21; - self - } - - pub fn adx(&self) -> bool { - self.flags & (1 << 22) != 0 - } - - pub fn with_adx(mut self) -> Self { - self.flags |= 1 << 22; - self - } - - pub fn avx512_fma(&self) -> bool { - self.flags & (1 << 23) != 0 - } - - pub fn with_avx512_fma(mut self) -> Self { - self.flags |= 1 << 23; - self - } - - pub fn pcommit(&self) -> bool { - self.flags & (1 << 24) != 0 - } - - pub fn with_pcommit(mut self) -> Self { - self.flags |= 1 << 24; - self - } - - pub fn clflushopt(&self) -> bool { - self.flags & (1 << 25) != 0 - } - - pub fn with_clflushopt(mut self) -> Self { - self.flags |= 1 << 25; - self - } - - pub fn clwb(&self) -> bool { - self.flags & (1 << 26) != 0 - } - - pub fn with_clwb(mut self) -> Self { - self.flags |= 1 << 26; - self - } - - pub fn avx512_pf(&self) -> bool { - self.flags & (1 << 27) != 0 - } - - pub fn with_avx512_pf(mut self) -> Self { - self.flags |= 1 << 27; - self - } - - pub fn avx512_er(&self) -> bool { - self.flags & (1 << 28) != 0 - } - - pub fn with_avx512_er(mut self) -> Self { - self.flags |= 1 << 28; - self - } - - pub fn avx512_cd(&self) -> bool { - self.flags & (1 << 29) != 0 - } - - pub fn with_avx512_cd(mut self) -> Self { - self.flags |= 1 << 29; - self - } - - pub fn sha(&self) -> bool { - self.flags & (1 << 30) != 0 - } - - pub fn with_sha(mut self) -> Self { - self.flags |= 1 << 30; - self - } - - pub fn avx512_bw(&self) -> bool { - self.flags & (1 << 31) != 0 - } - - pub fn with_avx512_bw(mut self) -> Self { - self.flags |= 1 << 31; - self - } - - pub fn avx512_vl(&self) -> bool { - self.flags & (1 << 32) != 0 - } - - pub fn with_avx512_vl(mut self) -> Self { - self.flags |= 1 << 32; - self - } - - pub fn prefetchwt1(&self) -> bool { - self.flags & (1 << 33) != 0 - } - - pub fn with_prefetchwt1(mut self) -> Self { - self.flags |= 1 << 33; - self - } - - pub fn avx512_vbmi(&self) -> bool { - self.flags & (1 << 34) != 0 - } - - pub fn with_avx512_vbmi(mut self) -> Self { - self.flags |= 1 << 34; - self - } - - pub fn avx512_vbmi2(&self) -> bool { - self.flags & (1 << 35) != 0 - } - - pub fn with_avx512_vbmi2(mut self) -> Self { - self.flags |= 1 << 35; - self - } - - pub fn gfni(&self) -> bool { - self.flags & (1 << 36) != 0 - } - - pub fn with_gfni(mut self) -> Self { - self.flags |= 1 << 36; - self - } - - pub fn vaes(&self) -> bool { - self.flags & (1 << 37) != 0 - } - - pub fn with_vaes(mut self) -> Self { - self.flags |= 1 << 37; - self - } - - pub fn pclmulqdq(&self) -> bool { - self.flags & (1 << 38) != 0 - } - - pub fn with_pclmulqdq(mut self) -> Self { - self.flags |= 1 << 38; - self - } - - pub fn avx_vnni(&self) -> bool { - self.flags & (1 << 39) != 0 - } - - pub fn with_avx_vnni(mut self) -> Self { - self.flags |= 1 << 39; - self - } - - pub fn avx512_bitalg(&self) -> bool { - self.flags & (1 << 40) != 0 - } - - pub fn with_avx512_bitalg(mut self) -> Self { - self.flags |= 1 << 40; - self - } - - pub fn avx512_vpopcntdq(&self) -> bool { - self.flags & (1 << 41) != 0 - } - - pub fn with_avx512_vpopcntdq(mut self) -> Self { - self.flags |= 1 << 41; - self - } - - pub fn avx512_4vnniw(&self) -> bool { - self.flags & (1 << 42) != 0 - } - - pub fn with_avx512_4vnniw(mut self) -> Self { - self.flags |= 1 << 42; - self - } - - pub fn avx512_4fmaps(&self) -> bool { - self.flags & (1 << 43) != 0 - } - - pub fn with_avx512_4fmaps(mut self) -> Self { - self.flags |= 1 << 43; - self - } - - /// returns `true` if this `InstDecoder` has **all** `avx512` features enabled. - pub fn avx512(&self) -> bool { - let avx512_mask = - (1 << 19) | - (1 << 20) | - (1 << 23) | - (1 << 27) | - (1 << 28) | - (1 << 29) | - (1 << 31) | - (1 << 32) | - (1 << 34) | - (1 << 35) | - (1 << 40) | - (1 << 41) | - (1 << 42) | - (1 << 43); - - (self.flags & avx512_mask) == avx512_mask - } - - /// enable all `avx512` features on this `InstDecoder`. no real CPU, at time of writing, - /// actually has such a feature combination, but this is a useful overestimate for `avx512` - /// generally. - pub fn with_avx512(mut self) -> Self { - let avx512_mask = - (1 << 19) | - (1 << 20) | - (1 << 23) | - (1 << 27) | - (1 << 28) | - (1 << 29) | - (1 << 31) | - (1 << 32) | - (1 << 34) | - (1 << 35) | - (1 << 40) | - (1 << 41) | - (1 << 42) | - (1 << 43); - - self.flags |= avx512_mask; - self - } - - pub fn cx8(&self) -> bool { - self.flags & (1 << 44) != 0 - } - - pub fn with_cx8(mut self) -> Self { - self.flags |= 1 << 44; - self - } - - pub fn syscall(&self) -> bool { - self.flags & (1 << 45) != 0 - } - - pub fn with_syscall(mut self) -> Self { - self.flags |= 1 << 45; - self - } - - pub fn rdtscp(&self) -> bool { - self.flags & (1 << 46) != 0 - } - - pub fn with_rdtscp(mut self) -> Self { - self.flags |= 1 << 46; - self - } - - pub fn abm(&self) -> bool { - self.flags & (1 << 47) != 0 - } - - pub fn with_abm(mut self) -> Self { - self.flags |= 1 << 47; - self - } - - pub fn sse4a(&self) -> bool { - self.flags & (1 << 48) != 0 - } - - pub fn with_sse4a(mut self) -> Self { - self.flags |= 1 << 48; - self - } - - pub fn _3dnowprefetch(&self) -> bool { - self.flags & (1 << 49) != 0 - } - - pub fn with_3dnowprefetch(mut self) -> Self { - self.flags |= 1 << 49; - self - } - - pub fn xop(&self) -> bool { - self.flags & (1 << 50) != 0 - } - - pub fn with_xop(mut self) -> Self { - self.flags |= 1 << 50; - self - } - - pub fn skinit(&self) -> bool { - self.flags & (1 << 51) != 0 - } - - pub fn with_skinit(mut self) -> Self { - self.flags |= 1 << 51; - self - } - - pub fn tbm(&self) -> bool { - self.flags & (1 << 52) != 0 - } - - pub fn with_tbm(mut self) -> Self { - self.flags |= 1 << 52; - self - } - - pub fn intel_quirks(&self) -> bool { - self.flags & (1 << 53) != 0 - } - - pub fn with_intel_quirks(mut self) -> Self { - self.flags |= 1 << 53; - self - } - - pub fn amd_quirks(&self) -> bool { - self.flags & (1 << 54) != 0 - } - - pub fn with_amd_quirks(mut self) -> Self { - self.flags |= 1 << 54; - self - } - - pub fn avx(&self) -> bool { - self.flags & (1 << 55) != 0 - } - - pub fn with_avx(mut self) -> Self { - self.flags |= 1 << 55; - self - } - - pub fn svm(&self) -> bool { - self.flags & (1 << 56) != 0 - } - - pub fn with_svm(mut self) -> Self { - self.flags |= 1 << 56; - self - } - - /// `lahfsahf` is only unset for early revisions of 64-bit amd and intel chips. unfortunately - /// the clearest documentation on when these instructions were reintroduced into 64-bit - /// architectures seems to be - /// [wikipedia](https://en.wikipedia.org/wiki/X86-64#Older_implementations): - /// ```text - /// Early AMD64 and Intel 64 CPUs lacked LAHF and SAHF instructions in 64-bit mode. AMD - /// introduced these instructions (also in 64-bit mode) with their Athlon 64, Opteron and - /// Turion 64 revision D processors in March 2005[48][49][50] while Intel introduced the - /// instructions with the Pentium 4 G1 stepping in December 2005. The 64-bit version of Windows - /// 8.1 requires this feature.[47] - /// ``` - /// - /// this puts reintroduction of these instructions somewhere in the middle of prescott and k8 - /// lifecycles, for intel and amd respectively. because there is no specific uarch where these - /// features become enabled, prescott and k8 default to not supporting these instructions, - /// where later uarches support these instructions. - pub fn lahfsahf(&self) -> bool { - self.flags & (1 << 57) != 0 - } - - pub fn with_lahfsahf(mut self) -> Self { - self.flags |= 1 << 57; - self - } - - pub fn cmov(&self) -> bool { - self.flags & (1 << 58) != 0 - } - - pub fn with_cmov(mut self) -> Self { - self.flags |= 1 << 58; - self - } - - pub fn f16c(&self) -> bool { - self.flags & (1 << 59) != 0 - } - - pub fn with_f16c(mut self) -> Self { - self.flags |= 1 << 59; - self - } - - pub fn fma4(&self) -> bool { - self.flags & (1 << 60) != 0 - } - - pub fn with_fma4(mut self) -> Self { - self.flags |= 1 << 60; - self - } - - pub fn prefetchw(&self) -> bool { - self.flags & (1 << 61) != 0 - } +pub struct DecodeEverything {} - pub fn with_prefetchw(mut self) -> Self { - self.flags |= 1 << 61; - self - } - - pub fn tsx(&self) -> bool { - self.flags & (1 << 62) != 0 - } +impl Decoder<Arch> for DecodeEverything { + fn decode<T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>>(&self, words: &mut T) -> Result<Instruction, <Arch as yaxpeax_arch::Arch>::DecodeError> { + let mut instr = Instruction::invalid(); + self.decode_into(&mut instr, words)?; - pub fn with_tsx(mut self) -> Self { - self.flags |= 1 << 62; - self + Ok(instr) } - - pub fn lzcnt(&self) -> bool { - self.flags & (1 << 63) != 0 + #[inline(always)] + fn decode_into<T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>>(&self, instr: &mut Instruction, words: &mut T) -> Result<(), <Arch as yaxpeax_arch::Arch>::DecodeError> { + self.decode_with_annotation(instr, words, &mut NullSink) } +} - pub fn with_lzcnt(mut self) -> Self { - self.flags |= 1 << 63; - self - } +impl AnnotatingDecoder<Arch> for DecodeEverything { + type FieldDescription = FieldDescription; - /// Optionally reject or reinterpret instruction according to the decoder's - /// declared extensions. - fn revise_instruction(&self, inst: &mut Instruction) -> Result<(), DecodeError> { - if inst.prefixes.evex().is_some() { - if !self.avx512() { - return Err(DecodeError::InvalidOpcode); - } else { - return Ok(()); - } - } - match inst.opcode { - Opcode::TZCNT => { - if !self.bmi1() { - // tzcnt is only supported if bmi1 is enabled. without bmi1, this decodes as - // bsf. - inst.opcode = Opcode::BSF; - } - } - Opcode::LDDQU | - Opcode::ADDSUBPS | - Opcode::ADDSUBPD | - Opcode::HADDPS | - Opcode::HSUBPS | - Opcode::HADDPD | - Opcode::HSUBPD | - Opcode::MOVSHDUP | - Opcode::MOVSLDUP | - Opcode::MOVDDUP | - Opcode::MONITOR | - Opcode::MWAIT => { - // via Intel section 5.7, SSE3 Instructions - if !self.sse3() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::PHADDW | - Opcode::PHADDSW | - Opcode::PHADDD | - Opcode::PHSUBW | - Opcode::PHSUBSW | - Opcode::PHSUBD | - Opcode::PABSB | - Opcode::PABSW | - Opcode::PABSD | - Opcode::PMADDUBSW | - Opcode::PMULHRSW | - Opcode::PSHUFB | - Opcode::PSIGNB | - Opcode::PSIGNW | - Opcode::PSIGND | - Opcode::PALIGNR => { - // via Intel section 5.8, SSSE3 Instructions - if !self.ssse3() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::PMULLD | - Opcode::PMULDQ | - Opcode::MOVNTDQA | - Opcode::BLENDPD | - Opcode::BLENDPS | - Opcode::BLENDVPD | - Opcode::BLENDVPS | - Opcode::PBLENDVB | - Opcode::BLENDW | - Opcode::PMINUW | - Opcode::PMINUD | - Opcode::PMINSB | - Opcode::PMINSD | - Opcode::PMAXUW | - Opcode::PMAXUD | - Opcode::PMAXSB | - Opcode::PMAXSD | - Opcode::ROUNDPS | - Opcode::ROUNDPD | - Opcode::ROUNDSS | - Opcode::ROUNDSD | - Opcode::PBLENDW | - Opcode::EXTRACTPS | - Opcode::INSERTPS | - Opcode::PINSRB | - Opcode::PINSRD | - Opcode::PINSRQ | - Opcode::PMOVSXBW | - Opcode::PMOVZXBW | - Opcode::PMOVSXBD | - Opcode::PMOVZXBD | - Opcode::PMOVSXWD | - Opcode::PMOVZXWD | - Opcode::PMOVSXBQ | - Opcode::PMOVZXBQ | - Opcode::PMOVSXWQ | - Opcode::PMOVZXWQ | - Opcode::PMOVSXDQ | - Opcode::PMOVZXDQ | - Opcode::DPPS | - Opcode::DPPD | - Opcode::MPSADBW | - Opcode::PHMINPOSUW | - Opcode::PTEST | - Opcode::PCMPEQQ | - Opcode::PEXTRB | - Opcode::PEXTRW | - Opcode::PEXTRD | - Opcode::PEXTRQ | - Opcode::PACKUSDW => { - // via Intel section 5.10, SSE4.1 Instructions - if !self.sse4_1() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::EXTRQ | - Opcode::INSERTQ | - Opcode::MOVNTSS | - Opcode::MOVNTSD => { - if !self.sse4a() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::CRC32 | - Opcode::PCMPESTRI | - Opcode::PCMPESTRM | - Opcode::PCMPISTRI | - Opcode::PCMPISTRM | - Opcode::PCMPGTQ => { - // via Intel section 5.11, SSE4.2 Instructions - if !self.sse4_2() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::AESDEC | - Opcode::AESDECLAST | - Opcode::AESENC | - Opcode::AESENCLAST | - Opcode::AESIMC | - Opcode::AESKEYGENASSIST => { - // via Intel section 5.12. AESNI AND PCLMULQDQ - if !self.aesni() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::PCLMULQDQ => { - // via Intel section 5.12. AESNI AND PCLMULQDQ - if !self.pclmulqdq() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::XABORT | - Opcode::XBEGIN | - Opcode::XEND | - Opcode::XTEST => { - if !self.tsx() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::SHA1MSG1 | - Opcode::SHA1MSG2 | - Opcode::SHA1NEXTE | - Opcode::SHA1RNDS4 | - Opcode::SHA256MSG1 | - Opcode::SHA256MSG2 | - Opcode::SHA256RNDS2 => { - if !self.sha() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::ENCLV | - Opcode::ENCLS | - Opcode::ENCLU => { - if !self.sgx() { - return Err(DecodeError::InvalidOpcode); - } - } - // AVX... - Opcode::VMOVDDUP | - Opcode::VPSHUFLW | - Opcode::VPSHUFHW | - Opcode::VHADDPS | - Opcode::VHSUBPS | - Opcode::VADDSUBPS | - Opcode::VCVTPD2DQ | - Opcode::VLDDQU | - Opcode::VCOMISD | - Opcode::VCOMISS | - Opcode::VUCOMISD | - Opcode::VUCOMISS | - Opcode::VADDPD | - Opcode::VADDPS | - Opcode::VADDSD | - Opcode::VADDSS | - Opcode::VADDSUBPD | - Opcode::VBLENDPD | - Opcode::VBLENDPS | - Opcode::VBLENDVPD | - Opcode::VBLENDVPS | - Opcode::VBROADCASTF128 | - Opcode::VBROADCASTI128 | - Opcode::VBROADCASTSD | - Opcode::VBROADCASTSS | - Opcode::VCMPSD | - Opcode::VCMPSS | - Opcode::VCMPPD | - Opcode::VCMPPS | - Opcode::VCVTDQ2PD | - Opcode::VCVTDQ2PS | - Opcode::VCVTPD2PS | - Opcode::VCVTPS2DQ | - Opcode::VCVTPS2PD | - Opcode::VCVTSS2SD | - Opcode::VCVTSI2SS | - Opcode::VCVTSI2SD | - Opcode::VCVTSD2SI | - Opcode::VCVTSD2SS | - Opcode::VCVTSS2SI | - Opcode::VCVTTPD2DQ | - Opcode::VCVTTPS2DQ | - Opcode::VCVTTSS2SI | - Opcode::VCVTTSD2SI | - Opcode::VDIVPD | - Opcode::VDIVPS | - Opcode::VDIVSD | - Opcode::VDIVSS | - Opcode::VDPPD | - Opcode::VDPPS | - Opcode::VEXTRACTF128 | - Opcode::VEXTRACTI128 | - Opcode::VEXTRACTPS | - Opcode::VFMADD132PD | - Opcode::VFMADD132PS | - Opcode::VFMADD132SD | - Opcode::VFMADD132SS | - Opcode::VFMADD213PD | - Opcode::VFMADD213PS | - Opcode::VFMADD213SD | - Opcode::VFMADD213SS | - Opcode::VFMADD231PD | - Opcode::VFMADD231PS | - Opcode::VFMADD231SD | - Opcode::VFMADD231SS | - Opcode::VFMADDSUB132PD | - Opcode::VFMADDSUB132PS | - Opcode::VFMADDSUB213PD | - Opcode::VFMADDSUB213PS | - Opcode::VFMADDSUB231PD | - Opcode::VFMADDSUB231PS | - Opcode::VFMSUB132PD | - Opcode::VFMSUB132PS | - Opcode::VFMSUB132SD | - Opcode::VFMSUB132SS | - Opcode::VFMSUB213PD | - Opcode::VFMSUB213PS | - Opcode::VFMSUB213SD | - Opcode::VFMSUB213SS | - Opcode::VFMSUB231PD | - Opcode::VFMSUB231PS | - Opcode::VFMSUB231SD | - Opcode::VFMSUB231SS | - Opcode::VFMSUBADD132PD | - Opcode::VFMSUBADD132PS | - Opcode::VFMSUBADD213PD | - Opcode::VFMSUBADD213PS | - Opcode::VFMSUBADD231PD | - Opcode::VFMSUBADD231PS | - Opcode::VFNMADD132PD | - Opcode::VFNMADD132PS | - Opcode::VFNMADD132SD | - Opcode::VFNMADD132SS | - Opcode::VFNMADD213PD | - Opcode::VFNMADD213PS | - Opcode::VFNMADD213SD | - Opcode::VFNMADD213SS | - Opcode::VFNMADD231PD | - Opcode::VFNMADD231PS | - Opcode::VFNMADD231SD | - Opcode::VFNMADD231SS | - Opcode::VFNMSUB132PD | - Opcode::VFNMSUB132PS | - Opcode::VFNMSUB132SD | - Opcode::VFNMSUB132SS | - Opcode::VFNMSUB213PD | - Opcode::VFNMSUB213PS | - Opcode::VFNMSUB213SD | - Opcode::VFNMSUB213SS | - Opcode::VFNMSUB231PD | - Opcode::VFNMSUB231PS | - Opcode::VFNMSUB231SD | - Opcode::VFNMSUB231SS | - Opcode::VGATHERDPD | - Opcode::VGATHERDPS | - Opcode::VGATHERQPD | - Opcode::VGATHERQPS | - Opcode::VHADDPD | - Opcode::VHSUBPD | - Opcode::VINSERTF128 | - Opcode::VINSERTI128 | - Opcode::VINSERTPS | - Opcode::VMASKMOVDQU | - Opcode::VMASKMOVPD | - Opcode::VMASKMOVPS | - Opcode::VMAXPD | - Opcode::VMAXPS | - Opcode::VMAXSD | - Opcode::VMAXSS | - Opcode::VMINPD | - Opcode::VMINPS | - Opcode::VMINSD | - Opcode::VMINSS | - Opcode::VMOVAPD | - Opcode::VMOVAPS | - Opcode::VMOVD | - Opcode::VMOVDQA | - Opcode::VMOVDQU | - Opcode::VMOVHLPS | - Opcode::VMOVHPD | - Opcode::VMOVHPS | - Opcode::VMOVLHPS | - Opcode::VMOVLPD | - Opcode::VMOVLPS | - Opcode::VMOVMSKPD | - Opcode::VMOVMSKPS | - Opcode::VMOVNTDQ | - Opcode::VMOVNTDQA | - Opcode::VMOVNTPD | - Opcode::VMOVNTPS | - Opcode::VMOVQ | - Opcode::VMOVSS | - Opcode::VMOVSD | - Opcode::VMOVSHDUP | - Opcode::VMOVSLDUP | - Opcode::VMOVUPD | - Opcode::VMOVUPS | - Opcode::VMPSADBW | - Opcode::VMULPD | - Opcode::VMULPS | - Opcode::VMULSD | - Opcode::VMULSS | - Opcode::VPABSB | - Opcode::VPABSD | - Opcode::VPABSW | - Opcode::VPACKSSDW | - Opcode::VPACKUSDW | - Opcode::VPACKSSWB | - Opcode::VPACKUSWB | - Opcode::VPADDB | - Opcode::VPADDD | - Opcode::VPADDQ | - Opcode::VPADDSB | - Opcode::VPADDSW | - Opcode::VPADDUSB | - Opcode::VPADDUSW | - Opcode::VPADDW | - Opcode::VPALIGNR | - Opcode::VPAND | - Opcode::VANDPD | - Opcode::VANDPS | - Opcode::VANDNPD | - Opcode::VANDNPS | - Opcode::VORPD | - Opcode::VORPS | - Opcode::VPANDN | - Opcode::VPAVGB | - Opcode::VPAVGW | - Opcode::VPBLENDD | - Opcode::VPBLENDVB | - Opcode::VPBLENDW | - Opcode::VPBROADCASTB | - Opcode::VPBROADCASTD | - Opcode::VPBROADCASTQ | - Opcode::VPBROADCASTW | - Opcode::VPCLMULQDQ | - Opcode::VPCMPEQB | - Opcode::VPCMPEQD | - Opcode::VPCMPEQQ | - Opcode::VPCMPEQW | - Opcode::VPCMPGTB | - Opcode::VPCMPGTD | - Opcode::VPCMPGTQ | - Opcode::VPCMPGTW | - Opcode::VPCMPESTRI | - Opcode::VPCMPESTRM | - Opcode::VPCMPISTRI | - Opcode::VPCMPISTRM | - Opcode::VPERM2F128 | - Opcode::VPERM2I128 | - Opcode::VPERMD | - Opcode::VPERMILPD | - Opcode::VPERMILPS | - Opcode::VPERMPD | - Opcode::VPERMPS | - Opcode::VPERMQ | - Opcode::VPEXTRB | - Opcode::VPEXTRD | - Opcode::VPEXTRQ | - Opcode::VPEXTRW | - Opcode::VPGATHERDD | - Opcode::VPGATHERDQ | - Opcode::VPGATHERQD | - Opcode::VPGATHERQQ | - Opcode::VPHADDD | - Opcode::VPHADDSW | - Opcode::VPHADDW | - Opcode::VPMADDUBSW | - Opcode::VPHMINPOSUW | - Opcode::VPHSUBD | - Opcode::VPHSUBSW | - Opcode::VPHSUBW | - Opcode::VPINSRB | - Opcode::VPINSRD | - Opcode::VPINSRQ | - Opcode::VPINSRW | - Opcode::VPMADDWD | - Opcode::VPMASKMOVD | - Opcode::VPMASKMOVQ | - Opcode::VPMAXSB | - Opcode::VPMAXSD | - Opcode::VPMAXSW | - Opcode::VPMAXUB | - Opcode::VPMAXUW | - Opcode::VPMAXUD | - Opcode::VPMINSB | - Opcode::VPMINSW | - Opcode::VPMINSD | - Opcode::VPMINUB | - Opcode::VPMINUW | - Opcode::VPMINUD | - Opcode::VPMOVMSKB | - Opcode::VPMOVSXBD | - Opcode::VPMOVSXBQ | - Opcode::VPMOVSXBW | - Opcode::VPMOVSXDQ | - Opcode::VPMOVSXWD | - Opcode::VPMOVSXWQ | - Opcode::VPMOVZXBD | - Opcode::VPMOVZXBQ | - Opcode::VPMOVZXBW | - Opcode::VPMOVZXDQ | - Opcode::VPMOVZXWD | - Opcode::VPMOVZXWQ | - Opcode::VPMULDQ | - Opcode::VPMULHRSW | - Opcode::VPMULHUW | - Opcode::VPMULHW | - Opcode::VPMULLQ | - Opcode::VPMULLD | - Opcode::VPMULLW | - Opcode::VPMULUDQ | - Opcode::VPOR | - Opcode::VPSADBW | - Opcode::VPSHUFB | - Opcode::VPSHUFD | - Opcode::VPSIGNB | - Opcode::VPSIGND | - Opcode::VPSIGNW | - Opcode::VPSLLD | - Opcode::VPSLLDQ | - Opcode::VPSLLQ | - Opcode::VPSLLVD | - Opcode::VPSLLVQ | - Opcode::VPSLLW | - Opcode::VPSRAD | - Opcode::VPSRAVD | - Opcode::VPSRAW | - Opcode::VPSRLD | - Opcode::VPSRLDQ | - Opcode::VPSRLQ | - Opcode::VPSRLVD | - Opcode::VPSRLVQ | - Opcode::VPSRLW | - Opcode::VPSUBB | - Opcode::VPSUBD | - Opcode::VPSUBQ | - Opcode::VPSUBSB | - Opcode::VPSUBSW | - Opcode::VPSUBUSB | - Opcode::VPSUBUSW | - Opcode::VPSUBW | - Opcode::VPTEST | - Opcode::VPUNPCKHBW | - Opcode::VPUNPCKHDQ | - Opcode::VPUNPCKHQDQ | - Opcode::VPUNPCKHWD | - Opcode::VPUNPCKLBW | - Opcode::VPUNPCKLDQ | - Opcode::VPUNPCKLQDQ | - Opcode::VPUNPCKLWD | - Opcode::VPXOR | - Opcode::VRCPPS | - Opcode::VROUNDPD | - Opcode::VROUNDPS | - Opcode::VROUNDSD | - Opcode::VROUNDSS | - Opcode::VRSQRTPS | - Opcode::VRSQRTSS | - Opcode::VRCPSS | - Opcode::VSHUFPD | - Opcode::VSHUFPS | - Opcode::VSQRTPD | - Opcode::VSQRTPS | - Opcode::VSQRTSS | - Opcode::VSQRTSD | - Opcode::VSUBPD | - Opcode::VSUBPS | - Opcode::VSUBSD | - Opcode::VSUBSS | - Opcode::VTESTPD | - Opcode::VTESTPS | - Opcode::VUNPCKHPD | - Opcode::VUNPCKHPS | - Opcode::VUNPCKLPD | - Opcode::VUNPCKLPS | - Opcode::VXORPD | - Opcode::VXORPS | - Opcode::VZEROUPPER | - Opcode::VZEROALL | - Opcode::VLDMXCSR | - Opcode::VSTMXCSR => { - // TODO: check a table for these - if !self.avx() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::VAESDEC | - Opcode::VAESDECLAST | - Opcode::VAESENC | - Opcode::VAESENCLAST | - Opcode::VAESIMC | - Opcode::VAESKEYGENASSIST => { - // TODO: check a table for these - if !self.avx() || !self.aesni() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::MOVBE => { - if !self.movbe() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::POPCNT => { - /* - * from the intel SDM: - * ``` - * Before an application attempts to use the POPCNT instruction, it must check that - * the processor supports SSE4.2 (if CPUID.01H:ECX.SSE4_2[bit 20] = 1) and POPCNT - * (if CPUID.01H:ECX.POPCNT[bit 23] = 1). - * ``` - */ - if self.intel_quirks() && (self.sse4_2() || self.popcnt()) { - return Ok(()); - } else if !self.popcnt() { - /* - * elsewhere from the amd APM: - * `Instruction Subsets and CPUID Feature Flags` on page 507 indicates that - * popcnt is present when the popcnt bit is reported by cpuid. this seems to be - * the less quirky default, so `intel_quirks` is considered the outlier, and - * before this default. - * */ - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::LZCNT => { - /* - * amd APM, `LZCNT` page 212: - * LZCNT is an Advanced Bit Manipulation (ABM) instruction. Support for the LZCNT - * instruction is indicated by CPUID Fn8000_0001_ECX[ABM] = 1. - * - * meanwhile the intel SDM simply states: - * ``` - * CPUID.EAX=80000001H:ECX.LZCNT[bit 5]: if 1 indicates the processor supports the - * LZCNT instruction. - * ``` - * - * so that's considered the less-quirky (default) case here. - * */ - if self.amd_quirks() && !self.abm() { - return Err(DecodeError::InvalidOpcode); - } else if !self.lzcnt() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::ADCX | - Opcode::ADOX => { - if !self.adx() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::VMRUN | - Opcode::VMLOAD | - Opcode::VMSAVE | - Opcode::CLGI | - Opcode::VMMCALL | - Opcode::INVLPGA => { - if !self.svm() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::STGI | - Opcode::SKINIT => { - if !self.svm() || !self.skinit() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::LAHF | - Opcode::SAHF => { - if !self.lahfsahf() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::VCVTPS2PH | - Opcode::VCVTPH2PS => { - /* - * from intel SDM: - * ``` - * 14.4.1 Detection of F16C Instructions Application using float 16 instruction - * must follow a detection sequence similar to AVX to ensure: • The OS has - * enabled YMM state management support, • The processor support AVX as - * indicated by the CPUID feature flag, i.e. CPUID.01H:ECX.AVX[bit 28] = 1. • - * The processor support 16-bit floating-point conversion instructions via a - * CPUID feature flag (CPUID.01H:ECX.F16C[bit 29] = 1). - * ``` - * - * TODO: only the VEX-coded variant of this instruction should be gated on `f16c`. - * the EVEX-coded variant should be gated on `avx512f` or `avx512vl` if not - * EVEX.512-coded. - */ - if !self.avx() || !self.f16c() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::RDRAND => { - if !self.rdrand() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::RDSEED => { - if !self.rdseed() { - return Err(DecodeError::InvalidOpcode); - } - } - Opcode::MONITORX | Opcode::MWAITX | // these are gated on the `monitorx` and `mwaitx` cpuid bits, but are AMD-only. - Opcode::CLZERO | Opcode::RDPRU => { // again, gated on specific cpuid bits, but AMD-only. - if !self.amd_quirks() { - return Err(DecodeError::InvalidOpcode); - } - } - other => { - if !self.bmi1() { - if BMI1.contains(&other) { - return Err(DecodeError::InvalidOpcode); - } - } - if !self.bmi2() { - if BMI2.contains(&other) { - return Err(DecodeError::InvalidOpcode); - } - } - } - } - Ok(()) + #[inline(always)] + fn decode_with_annotation< + T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>, + S: DescriptionSink<Self::FieldDescription> + >(&self, instr: &mut Instruction, words: &mut T, sink: &mut S) -> Result<(), <Arch as yaxpeax_arch::Arch>::DecodeError> { + decode_with_annotation(self, instr, words, sink) } } @@ -4110,7 +2768,7 @@ impl Default for InstDecoder { /// instruction defined in any extension. fn default() -> Self { Self { - flags: 0xffffffff_ffffffff, + flags: [0xffffffff_ffffffff, 0xffffffff_ffffffff], } } } @@ -4118,16 +2776,7 @@ impl Default for InstDecoder { impl Decoder<Arch> for InstDecoder { fn decode<T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>>(&self, words: &mut T) -> Result<Instruction, <Arch as yaxpeax_arch::Arch>::DecodeError> { let mut instr = Instruction::invalid(); - DecodeCtx::new().read_with_annotations(self, words, &mut instr, &mut NullSink)?; - - instr.length = words.offset() as u8; - if words.offset() > 15 { - return Err(DecodeError::TooLong); - } - - if self != &InstDecoder::default() { - self.revise_instruction(&mut instr)?; - } + self.decode_into(&mut instr, words)?; Ok(instr) } @@ -4143,19 +2792,26 @@ impl AnnotatingDecoder<Arch> for InstDecoder { T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>, S: DescriptionSink<Self::FieldDescription> >(&self, instr: &mut Instruction, words: &mut T, sink: &mut S) -> Result<(), <Arch as yaxpeax_arch::Arch>::DecodeError> { - DecodeCtx::new().read_with_annotations(self, words, instr, sink)?; - - instr.length = words.offset() as u8; - if words.offset() > 15 { - return Err(DecodeError::TooLong); - } + decode_with_annotation(self, instr, words, sink) + } +} - if self != &InstDecoder::default() { - self.revise_instruction(instr)?; - } +#[inline(always)] +fn decode_with_annotation< + D: IsaSettings, + T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>, + S: DescriptionSink<FieldDescription> +>(isa_settings: &D, instr: &mut Instruction, words: &mut T, sink: &mut S) -> Result<(), <Arch as yaxpeax_arch::Arch>::DecodeError> { + DecodeCtx::new().read_with_annotations(isa_settings, words, instr, sink)?; - Ok(()) + instr.length = words.offset() as u8; + if words.offset() > 15 { + return Err(DecodeError::TooLong); } + + isa_settings.revise_instruction(instr)?; + + Ok(()) } impl Opcode { @@ -6739,9 +5395,10 @@ fn read_opc_hotpath< #[cfg_attr(feature="profiling", inline(never))] #[cfg_attr(not(feature="profiling"), inline(always))] fn read_with_annotations< + D: IsaSettings, T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>, S: DescriptionSink<FieldDescription>, ->(&mut self, decoder: &InstDecoder, words: &mut T, instruction: &mut Instruction, sink: &mut S) -> Result<(), DecodeError> { +>(mut self, isa_settings: &D, words: &mut T, instruction: &mut Instruction, sink: &mut S) -> Result<(), DecodeError> { words.mark(); let mut nextb = words.next().ok().ok_or(DecodeError::ExhaustedInput)?; let mut next_rec = OPCODES[nextb as usize]; @@ -6887,7 +5544,7 @@ fn read_with_annotations< record.operand() }; - self.read_operands(decoder, words, instruction, record, sink)?; + self.read_operands(isa_settings, words, instruction, record, sink)?; if self.check_lock { if !instruction.opcode.can_lock() || !instruction.operands[0].is_memory() { @@ -6901,9 +5558,10 @@ fn read_with_annotations< #[cfg_attr(feature="profiling", inline(never))] #[cfg_attr(not(feature="profiling"), inline(always))] fn read_operands< + D: IsaSettings, T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>, S: DescriptionSink<FieldDescription> ->(&mut self, decoder: &InstDecoder, words: &mut T, instruction: &mut Instruction, operand_code: OperandCode, sink: &mut S) -> Result<(), DecodeError> { +>(&mut self, isa_settings: &D, words: &mut T, instruction: &mut Instruction, operand_code: OperandCode, sink: &mut S) -> Result<(), DecodeError> { sink.record( words.offset() as u32 * 8 - 1, words.offset() as u32 * 8 - 1, InnerDescription::Boundary("opcode ends/operands begin (typically)") @@ -7928,9 +6586,8 @@ fn read_operands< ); vex::three_byte_vex(words, modrm, instruction, sink)?; - if decoder != &InstDecoder::default() { - decoder.revise_instruction(instruction)?; - } + isa_settings.revise_instruction(instruction)?; + return Ok(()); } } else { @@ -7961,9 +6618,8 @@ fn read_operands< ); vex::two_byte_vex(words, modrm, instruction, sink)?; - if decoder != &InstDecoder::default() { - decoder.revise_instruction(instruction)?; - } + isa_settings.revise_instruction(instruction)?; + return Ok(()); } } else { @@ -9874,7 +8530,7 @@ fn read_operands< instruction.opcode = Opcode::LFENCE; // Intel's manual accepts m != 0, AMD supports m != 0 though the manual // doesn't say (tested on threadripper) - if !decoder.amd_quirks() && !decoder.intel_quirks() { + if !isa_settings.amd_quirks() && !isa_settings.intel_quirks() { if m != 0 { return Err(DecodeError::InvalidOperand); } @@ -9884,7 +8540,7 @@ fn read_operands< instruction.opcode = Opcode::MFENCE; // Intel's manual accepts m != 0, AMD supports m != 0 though the manual // doesn't say (tested on threadripper) - if !decoder.amd_quirks() && !decoder.intel_quirks() { + if !isa_settings.amd_quirks() && !isa_settings.intel_quirks() { if m != 0 { return Err(DecodeError::InvalidOperand); } @@ -9894,7 +8550,7 @@ fn read_operands< instruction.opcode = Opcode::SFENCE; // Intel's manual accepts m != 0, AMD supports m != 0 though the manual // doesn't say (tested on threadripper) - if !decoder.amd_quirks() && !decoder.intel_quirks() { + if !isa_settings.amd_quirks() && !isa_settings.intel_quirks() { if m != 0 { return Err(DecodeError::InvalidOperand); } |
