diff options
Diffstat (limited to 'src/protected_mode/mod.rs')
-rw-r--r-- | src/protected_mode/mod.rs | 505 |
1 files changed, 485 insertions, 20 deletions
diff --git a/src/protected_mode/mod.rs b/src/protected_mode/mod.rs index 58d7a85..89c485f 100644 --- a/src/protected_mode/mod.rs +++ b/src/protected_mode/mod.rs @@ -1,5 +1,6 @@ mod vex; mod display; +pub mod uarch; use core::hint::unreachable_unchecked; @@ -120,6 +121,14 @@ impl RegSpec { } #[inline] + pub fn sp() -> RegSpec { + RegSpec { + num: 4, + bank: RegisterBank::W + } + } + + #[inline] pub fn fs() -> RegSpec { RegSpec { bank: RegisterBank::S, num: 3 } } @@ -738,12 +747,17 @@ pub enum Opcode { XGETBV, XSETBV, VMFUNC, + XABORT, + XBEGIN, XEND, XTEST, ENCLU, RDPKRU, WRPKRU, + RDSEED, + RDRAND, + ADDPS, ADDPD, ANDNPS, @@ -1289,6 +1303,31 @@ pub enum Opcode { PHADDW, HSUBPD, HADDPD, + + SHA1RNDS4, + SHA1NEXTE, + SHA1MSG1, + SHA1MSG2, + SHA256RNDS2, + SHA256MSG1, + SHA256MSG2, + + LZCNT, + CLGI, + STGI, + SKINIT, + VMLOAD, + VMMCALL, + VMSAVE, + VMRUN, + INVLPGA, + + MOVBE, + + ADCX, + ADOX, + + PREFETCHW, } #[derive(Debug)] @@ -1457,6 +1496,14 @@ pub struct InstDecoder { // 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, } @@ -1543,6 +1590,12 @@ impl InstDecoder { self } + pub fn with_sse4(self) -> Self { + self + .with_sse4_1() + .with_sse4_2() + } + pub fn movbe(&self) -> bool { self.flags & (1 << 8) != 0 } @@ -1615,6 +1668,9 @@ impl InstDecoder { 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 } @@ -1975,6 +2031,94 @@ impl InstDecoder { 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): + /// ``` + /// 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 fn with_prefetchw(mut self) -> Self { + self.flags |= 1 << 61; + self + } + + pub fn tsx(&self) -> bool { + self.flags & (1 << 62) != 0 + } + + pub fn with_tsx(mut self) -> Self { + self.flags |= 1 << 62; + self + } + + pub fn lzcnt(&self) -> bool { + self.flags & (1 << 63) != 0 + } + + pub fn with_lzcnt(mut self) -> Self { + self.flags |= 1 << 63; + self + } + /// Optionally reject or reinterpret instruction according to the decoder's /// declared extensions. fn revise_instruction(&self, inst: &mut Instruction) -> Result<(), DecodeError> { @@ -2107,21 +2251,15 @@ impl InstDecoder { return Err(DecodeError::InvalidOpcode); } } - // AVX... - /* // TODO Opcode::XABORT | - Opcode::XACQUIRE | - Opcode::XRELEASE | Opcode::XBEGIN | Opcode::XEND | Opcode::XTEST => { if !self.tsx() { inst.opcode = Opcode::Invalid; - return Err(()); + return Err(DecodeError::InvalidOpcode); } } - */ - /* // TODO Opcode::SHA1MSG1 | Opcode::SHA1MSG2 | Opcode::SHA1NEXTE | @@ -2131,9 +2269,9 @@ impl InstDecoder { Opcode::SHA256RNDS2 => { if !self.sha() { inst.opcode = Opcode::Invalid; - return Err(()); + return Err(DecodeError::InvalidOpcode); } - }*/ + } Opcode::ENCLV | Opcode::ENCLS | Opcode::ENCLU => { @@ -2173,7 +2311,6 @@ impl InstDecoder { Opcode::VCVTDQ2PD | Opcode::VCVTDQ2PS | Opcode::VCVTPD2PS | - Opcode::VCVTPH2PS | Opcode::VCVTPS2DQ | Opcode::VCVTPS2PD | Opcode::VCVTSS2SD | @@ -2181,7 +2318,6 @@ impl InstDecoder { Opcode::VCVTSI2SD | Opcode::VCVTSD2SI | Opcode::VCVTSD2SS | - Opcode::VCVTPS2PH | Opcode::VCVTSS2SI | Opcode::VCVTTPD2DQ | Opcode::VCVTTPS2DQ | @@ -2484,6 +2620,124 @@ impl InstDecoder { return Err(DecodeError::InvalidOpcode); } } + Opcode::MOVBE => { + if !self.movbe() { + inst.opcode = Opcode::Invalid; + 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()) { + inst.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOpcode); + } 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. + * */ + inst.opcode = Opcode::Invalid; + 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() { + inst.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOpcode); + } else if !self.lzcnt() { + inst.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::ADCX | + Opcode::ADOX => { + if !self.adx() { + inst.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::VMRUN | + Opcode::VMLOAD | + Opcode::VMSAVE | + Opcode::CLGI | + Opcode::VMMCALL | + Opcode::INVLPGA => { + if !self.svm() { + inst.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::STGI | + Opcode::SKINIT => { + if !self.svm() || !self.skinit() { + inst.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::LAHF | + Opcode::SAHF => { + if !self.lahfsahf() { + inst.opcode = Opcode::Invalid; + 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() { + inst.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::RDRAND => { + if !self.rdrand() { + inst.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOpcode); + } + } + Opcode::RDSEED => { + if !self.rdseed() { + inst.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOpcode); + } + } other => { if !self.bmi1() { if BMI1.contains(&other) { @@ -2808,11 +3062,13 @@ impl Prefixes { pub enum OperandCode { ModRM_0x0f00, ModRM_0x0f01, + ModRM_0x0f0d, ModRM_0x0fae, ModRM_0x0fba, ModRM_0xf238, ModRM_0xf30fc7, ModRM_0x660f38, + ModRM_0xf30f38, ModRM_0x660f3a, CVT_AA, CVT_DA, @@ -3625,7 +3881,7 @@ const OPCODE_F30F_MAP: [OpcodeRecord; 256] = [ OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), - OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), + OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::ModRM_0xf30f38), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), @@ -3766,7 +4022,7 @@ const OPCODE_F30F_MAP: [OpcodeRecord; 256] = [ OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), - OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), + OpcodeRecord(Interpretation::Instruction(Opcode::LZCNT), OperandCode::Gv_Ev), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), // 0xc0 @@ -3889,7 +4145,7 @@ const OPCODE_0F_MAP: [OpcodeRecord; 256] = [ OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), OpcodeRecord(Interpretation::Instruction(Opcode::UD2), OperandCode::Nothing), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), - OpcodeRecord(Interpretation::Instruction(Opcode::NOP), OperandCode::Ev), + OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::ModRM_0x0f0d), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), // 0x10 @@ -5104,8 +5360,28 @@ fn read_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter: T, instruction.operands[1] = read_M(&mut bytes_iter, instruction, modrm, length)?; } }, - _op @ OperandCode::ModRM_0xc6_Eb_Ib | - _op @ OperandCode::ModRM_0xc7_Ev_Iv => { + op @ OperandCode::ModRM_0xc6_Eb_Ib | + op @ OperandCode::ModRM_0xc7_Ev_Iv => { + if modrm == 0xf8 { + if op == OperandCode::ModRM_0xc6_Eb_Ib { + instruction.opcode = Opcode::XABORT; + instruction.imm = read_imm_signed(&mut bytes_iter, 1, length)? as u32; + instruction.operands[0] = OperandSpec::ImmI8; + instruction.operand_count = 1; + return Ok(()); + } else { + instruction.opcode = Opcode::XBEGIN; + instruction.disp = if opwidth == 2 { + read_imm_signed(&mut bytes_iter, 2, length)? as i16 as i32 as u32 + } else { + read_imm_signed(&mut bytes_iter, 4, length)? as i32 as u32 + }; + instruction.modrm_mmm = RegSpec::eip(); + instruction.operands[0] = OperandSpec::RegDisp; + instruction.operand_count = 1; + return Ok(()); + } + } if (modrm & 0b00111000) != 0 { instruction.opcode = Opcode::Invalid; return Err(DecodeError::InvalidOperand); // Err("Invalid modr/m for opcode 0xc7".to_string()); @@ -5113,7 +5389,8 @@ fn read_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter: T, instruction.operands[0] = mem_oper; instruction.opcode = Opcode::MOV; - instruction.imm = read_imm_signed(&mut bytes_iter, opwidth, length)? as u32; + let numwidth = if opwidth == 8 { 4 } else { opwidth }; + instruction.imm = read_imm_signed(&mut bytes_iter, numwidth, length)? as u32; instruction.operands[1] = match opwidth { 1 => OperandSpec::ImmI8, 2 => OperandSpec::ImmI16, @@ -5468,6 +5745,113 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter instruction.operands[0] = OperandSpec::RegRRR; instruction.operands[1] = read_E_xmm(&mut bytes_iter, instruction, modrm, length)?; } + OperandCode::ModRM_0x0f0d => { + let modrm = read_modrm(&mut bytes_iter, length)?; + let r = modrm & 0b111; + + let opwidth = imm_width_from_prefixes(SizeCode::vd, instruction.prefixes); + + match r { + 1 => { + instruction.opcode = Opcode::PREFETCHW; + } + _ => { + instruction.opcode = Opcode::NOP; + } + } + instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, opwidth, length)?; + instruction.operand_count = 1; + } + OperandCode::ModRM_0x0f38 => { + let opcode = read_modrm(&mut bytes_iter, length)?; + + let high = opcode >> 4; + let low = opcode & 0xf; + + let operands = match high { + 0 => { + // PqQq + OperandCode::G_E_mm + }, + 1 => { + // PqQq + OperandCode::G_E_mm + }, + 0xc => { + // Vdq,Wdq + OperandCode::G_E_xmm + } + 0xf => { + match low { + 0 => OperandCode::Gv_Ev, + 1 => OperandCode::Ev_Gv, + _ => { + instruction.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOpcode); + } + } + } + _ => { + instruction.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOpcode); + } + }; + instruction.opcode = match opcode { + 0xc8 => Opcode::SHA1NEXTE, + 0xc9 => Opcode::SHA1MSG1, + 0xca => Opcode::SHA1MSG2, + 0xcb => Opcode::SHA256RNDS2, + 0xcc => Opcode::SHA256MSG1, + 0xcd => Opcode::SHA256MSG2, + 0xf0 | 0xf1 => Opcode::MOVBE, + _ => { + instruction.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOpcode); + } + }; + + return read_operands(decoder, bytes_iter, instruction, operands, length); + }, + OperandCode::ModRM_0x0f3a => { + }, + OperandCode::ModRM_0x0fc7 => { + let modrm = read_modrm(&mut bytes_iter, length)?; + if modrm >> 6 == 0b11 { + match (modrm >> 3) & 0b111 { + 0b111 => { + instruction.opcode = Opcode::RDSEED; + instruction.operand_count = 1; + instruction.operands[0] = OperandSpec::RegRRR; + let opwidth = imm_width_from_prefixes(SizeCode::vd, instruction.prefixes); + instruction.modrm_rrr = + RegSpec::from_parts(modrm & 7, match opwidth { + 4 => RegisterBank::D, + 2 => RegisterBank::W, + _ => unreachable!() + }); + } + 0b110 => { + instruction.opcode = Opcode::RDRAND; + instruction.operand_count = 1; + instruction.operands[0] = OperandSpec::RegRRR; + let opwidth = imm_width_from_prefixes(SizeCode::vd, instruction.prefixes); + instruction.modrm_rrr = + RegSpec::from_parts(modrm & 7, match opwidth { + 4 => RegisterBank::D, + 2 => RegisterBank::W, + _ => unreachable!() + }); + } + _ => { + instruction.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOpcode); + } + } + } else { + instruction.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOpcode); + } + }, OperandCode::ModRM_0x0f71 => { instruction.operand_count = 2; @@ -5582,6 +5966,19 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter instruction.operands[1] = read_E_xmm(&mut bytes_iter, instruction, modrm, length)?; instruction.operand_count = 2; } + OperandCode::ModRM_0xf30f38 => { + let op = bytes_iter.next().ok_or(DecodeError::ExhaustedInput).map(|b| { *length += 1; b })?; + match op { + 0xf6 => { + instruction.opcode = Opcode::ADOX; + return read_operands(decoder, bytes_iter, instruction, OperandCode::Gv_Ev, length); + } + _ => { + instruction.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOpcode); + } + }; + } OperandCode::ModRM_0x660f38 => { let op = bytes_iter.next().ok_or(DecodeError::ExhaustedInput).map(|b| { *length += 1; b })?; match op { @@ -5590,6 +5987,10 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter 0xdd => { instruction.opcode = Opcode::AESENCLAST; } 0xde => { instruction.opcode = Opcode::AESDEC; } 0xdf => { instruction.opcode = Opcode::AESDECLAST; } + 0xf6 => { + instruction.opcode = Opcode::ADCX; + return read_operands(decoder, bytes_iter, instruction, OperandCode::Gv_Ev, length); + } _ => { instruction.opcode = Opcode::Invalid; return Err(DecodeError::InvalidOpcode); @@ -5608,6 +6009,21 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter OperandCode::ModRM_0x660f3a => { let op = bytes_iter.next().ok_or(DecodeError::ExhaustedInput).map(|b| { *length += 1; b })?; match op { + 0xcc => { + instruction.opcode = Opcode::SHA1RNDS4; + + let modrm = read_modrm(&mut bytes_iter, length)?; + instruction.modrm_rrr = + RegSpec::from_parts((modrm >> 3) & 7, RegisterBank::X); + + + instruction.operands[0] = OperandSpec::RegRRR; + instruction.operands[1] = read_E_xmm(&mut bytes_iter, instruction, modrm, length)?; + instruction.imm = + read_imm_unsigned(&mut bytes_iter, 1, length)?; + instruction.operands[2] = OperandSpec::ImmU8; + instruction.operand_count = 3; + } 0xdf => { instruction.opcode = Opcode::AESKEYGENASSIST; // read operands right here right now @@ -6170,10 +6586,59 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter } } else if r == 3 { let mod_bits = modrm >> 6; + let m = modrm & 7; if mod_bits == 0b11 { - instruction.opcode = Opcode::Invalid; - instruction.operand_count = 0; - return Err(DecodeError::InvalidOperand); + match m { + 0b000 => { + instruction.opcode = Opcode::VMRUN; + instruction.operand_count = 1; + instruction.modrm_rrr = RegSpec::eax(); + instruction.operands[0] = OperandSpec::RegRRR; + }, + 0b001 => { + instruction.opcode = Opcode::VMMCALL; + instruction.operand_count = 0; + }, + 0b010 => { + instruction.opcode = Opcode::VMLOAD; + instruction.operand_count = 1; + instruction.modrm_rrr = RegSpec::eax(); + instruction.operands[0] = OperandSpec::RegRRR; + }, + 0b011 => { + instruction.opcode = Opcode::VMSAVE; + instruction.operand_count = 1; + instruction.modrm_rrr = RegSpec::eax(); + instruction.operands[0] = OperandSpec::RegRRR; + }, + 0b100 => { + instruction.opcode = Opcode::STGI; + instruction.operand_count = 0; + }, + 0b101 => { + instruction.opcode = Opcode::CLGI; + instruction.operand_count = 0; + }, + 0b110 => { + instruction.opcode = Opcode::SKINIT; + instruction.operand_count = 1; + instruction.operands[0] = OperandSpec::RegRRR; + instruction.modrm_rrr = RegSpec::eax(); + }, + 0b111 => { + instruction.opcode = Opcode::INVLPGA; + instruction.operand_count = 2; + instruction.operands[0] = OperandSpec::RegRRR; + instruction.operands[1] = OperandSpec::RegMMM; + instruction.modrm_rrr = RegSpec::eax(); + instruction.modrm_mmm = RegSpec::ecx(); + }, + _ => { + instruction.opcode = Opcode::Invalid; + instruction.operand_count = 0; + return Err(DecodeError::InvalidOperand); + } + } } else { instruction.opcode = Opcode::LIDT; instruction.operand_count = 1; |