diff options
author | iximeow <me@iximeow.net> | 2020-05-23 19:37:58 -0700 |
---|---|---|
committer | iximeow <me@iximeow.net> | 2020-05-23 19:37:58 -0700 |
commit | 9b965525afced37e99543e3a412218207a41e793 (patch) | |
tree | d82cb429eb2b7f230249769f935bc24d60a6158f /src/long_mode/mod.rs | |
parent | a0fd5a24cb0aa0b697f680c451d928cefe8323b4 (diff) |
add SHA, BMI1, and BMI2, complete XSAVE extension support
additionally: cmpcxchg{8,16}b, rdrand, rdseed, rdpid, {rd,wr}{fs,gs}base
Diffstat (limited to 'src/long_mode/mod.rs')
-rw-r--r-- | src/long_mode/mod.rs | 316 |
1 files changed, 265 insertions, 51 deletions
diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs index 20abe1f..58d4c57 100644 --- a/src/long_mode/mod.rs +++ b/src/long_mode/mod.rs @@ -528,17 +528,36 @@ const BMI1: [Opcode; 6] = [ Opcode::TZCNT, ]; +const BMI2: [Opcode; 8] = [ + Opcode::BZHI, + Opcode::MULX, + Opcode::PDEP, + Opcode::PEXT, + Opcode::RORX, + Opcode::SARX, + Opcode::SHRX, + Opcode::SHLX, +]; + +#[allow(dead_code)] +const XSAVE: [Opcode; 10] = [ + Opcode::XGETBV, + Opcode::XRSTOR, + Opcode::XRSTORS, + Opcode::XSAVE, + Opcode::XSAVEC, + Opcode::XSAVEC64, + Opcode::XSAVEOPT, + Opcode::XSAVES, + Opcode::XSAVES64, + Opcode::XSETBV, +]; + // TODO: // PTWRITE -// RDFSBASE -// RDGSBASE -// WRFSBASE -// WRGSBASE // TPAUSE // UMONITOR // UMWAIT -// CLFLUSHOPT -// CLWB #[allow(non_camel_case_types)] #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum Opcode { @@ -740,6 +759,8 @@ pub enum Opcode { MFENCE, SFENCE, CLFLUSH, + CLFLUSHOPT, + CLWB, WRMSR, RDTSC, RDMSR, @@ -1376,6 +1397,32 @@ pub enum Opcode { ADOX, PREFETCHW, + + RDPID, + CMPXCHG8B, + CMPXCHG16B, + VMPTRLD, + VMPTRST, + + BZHI, + MULX, + SHLX, + SHRX, + SARX, + PDEP, + PEXT, + RORX, + XRSTORS, + XRSTORS64, + XSAVEC, + XSAVEC64, + XSAVES, + XSAVES64, + + RDFSBASE, + RDGSBASE, + WRFSBASE, + WRGSBASE, } #[derive(Debug)] @@ -2094,7 +2141,7 @@ impl InstDecoder { /// 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 @@ -2686,9 +2733,8 @@ impl InstDecoder { * (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); + if self.intel_quirks() && (self.sse4_2() || self.popcnt()) { + return Ok(()); } else if !self.popcnt() { /* * elsewhere from the amd APM: @@ -2795,6 +2841,11 @@ impl InstDecoder { return Err(DecodeError::InvalidOpcode); } } + if !self.bmi2() { + if BMI2.contains(&other) { + return Err(DecodeError::InvalidOpcode); + } + } } } Ok(()) @@ -3137,6 +3188,8 @@ pub enum OperandCode { ModRM_0x0fae, ModRM_0x0fba, ModRM_0xf238, + ModRM_0xf30fae, + ModRM_0x660fae, ModRM_0xf30fc7, ModRM_0x660f38, ModRM_0xf30f38, @@ -3513,7 +3566,7 @@ const OPCODE_660F_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_0x660fae), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), // 0xb0 OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), @@ -4075,7 +4128,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_0xf30fae), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), // 0xb0 OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), @@ -5379,13 +5432,12 @@ fn read_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter: T, return Ok(()); } else { instruction.opcode = Opcode::XBEGIN; - instruction.disp = if opwidth == 2 { + instruction.imm = if opwidth == 2 { read_imm_signed(&mut bytes_iter, 2, length)? as i16 as i64 as u64 } else { read_imm_signed(&mut bytes_iter, 4, length)? as i32 as i64 as u64 }; - instruction.modrm_mmm = RegSpec::rip(); - instruction.operands[0] = OperandSpec::RegDisp; + instruction.operands[0] = OperandSpec::ImmI32; instruction.operand_count = 1; return Ok(()); } @@ -5628,6 +5680,9 @@ fn read_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter: T, instruction.imm = read_num(&mut bytes_iter, 1)? as u8 as u64; *length += 1; + if instruction.operands[1] == OperandSpec::RegMMM { + instruction.modrm_mmm.bank = RegisterBank::MM; + } instruction.operands[2] = OperandSpec::ImmI8; instruction.operand_count = 3; }, @@ -5776,7 +5831,7 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter } OperandCode::ModRM_0x0f0d => { let modrm = read_modrm(&mut bytes_iter, length)?; - let r = modrm & 0b111; + let r = (modrm >> 3) & 0b111; let opwidth = imm_width_from_prefixes_64(SizeCode::vq, instruction.prefixes); @@ -5842,46 +5897,94 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter return read_operands(decoder, bytes_iter, instruction, operands, length); }, OperandCode::ModRM_0x0f3a => { + let opcode = read_modrm(&mut bytes_iter, length)?; + if opcode == 0xcc { + instruction.opcode = Opcode::SHA1RNDS4; + return read_operands(decoder, bytes_iter, instruction, OperandCode::G_E_xmm_Ib, length); + } else if opcode == 0x0f { + instruction.opcode = Opcode::PALIGNR; + return read_operands(decoder, bytes_iter, instruction, OperandCode::G_E_mm_Ib, length); + } }, 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_64(SizeCode::vq, instruction.prefixes); - instruction.modrm_rrr = - RegSpec::from_parts(modrm & 7, instruction.prefixes.rex().r(), match opwidth { - 8 => RegisterBank::Q, - 4 => RegisterBank::D, - 2 => RegisterBank::W, - _ => unreachable!() - }); + let is_reg = (modrm & 0xc0) == 0xc0; + + let r = (modrm >> 3) & 0b111; + + let opcode = match r { + 0b001 => { + if is_reg { + instruction.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOperand); + } else { + if instruction.prefixes.rex().w() { + Opcode::CMPXCHG16B + } else { + Opcode::CMPXCHG8B + } } - 0b110 => { - instruction.opcode = Opcode::RDRAND; - instruction.operand_count = 1; - instruction.operands[0] = OperandSpec::RegRRR; - let opwidth = imm_width_from_prefixes_64(SizeCode::vq, instruction.prefixes); - instruction.modrm_rrr = - RegSpec::from_parts(modrm & 7, instruction.prefixes.rex().r(), match opwidth { - 8 => RegisterBank::Q, - 4 => RegisterBank::D, - 2 => RegisterBank::W, - _ => unreachable!() - }); + } + 0b011 => { + if is_reg { + instruction.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOperand); + } else { + if instruction.prefixes.rex().w() { + Opcode::XRSTORS64 + } else { + Opcode::XRSTORS + } } - _ => { + } + 0b100 => { + if is_reg { instruction.opcode = Opcode::Invalid; - return Err(DecodeError::InvalidOpcode); + return Err(DecodeError::InvalidOperand); + } else { + if instruction.prefixes.rex().w() { + Opcode::XSAVEC64 + } else { + Opcode::XSAVEC + } } } - } else { - instruction.opcode = Opcode::Invalid; - return Err(DecodeError::InvalidOpcode); - } + 0b101 => { + if is_reg { + instruction.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOperand); + } else { + if instruction.prefixes.rex().w() { + Opcode::XSAVES64 + } else { + Opcode::XSAVES + } + } + } + 0b110 => { + if is_reg { + Opcode::RDRAND + } else { + Opcode::VMPTRLD + } + } + 0b111 => { + if is_reg { + Opcode::RDSEED + } else { + Opcode::VMPTRST + } + } + _ => { + instruction.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOperand); + } + }; + + instruction.opcode = opcode; + instruction.operand_count = 1; + let opwidth = imm_width_from_prefixes_64(SizeCode::vqp, instruction.prefixes); + instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, opwidth, length)?; }, OperandCode::ModRM_0x0f71 => { instruction.operand_count = 2; @@ -6168,15 +6271,33 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter instruction.operands[1] = OperandSpec::ImmU8; }, OperandCode::ModRM_0x660fc7 => { + let opwidth = imm_width_from_prefixes_64(SizeCode::vqp, instruction.prefixes); let modrm = read_modrm(&mut bytes_iter, length)?; let r = (modrm >> 3) & 7; match r { 6 => { instruction.opcode = Opcode::VMCLEAR; - instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, 1 /* doesn't matter, something using this width is invalid */, length)?; + instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, opwidth, length)?; if instruction.operands[0] == OperandSpec::RegMMM { - return Err(DecodeError::InvalidOperand); + // this would be invalid as `vmclear`, so fall back to the parse as + // 66-prefixed rdrand. this is a register operand, so just demote it to the + // word-form operand: + instruction.modrm_mmm = RegSpec { bank: RegisterBank::W, num: instruction.modrm_mmm.num }; + instruction.opcode = Opcode::RDRAND; + } + instruction.operand_count = 1; + } + 7 => { + instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, opwidth, length)?; + if instruction.operands[0] == OperandSpec::RegMMM { + // this would be invalid as `vmclear`, so fall back to the parse as + // 66-prefixed rdrand. this is a register operand, so just demote it to the + // word-form operand: + instruction.modrm_mmm = RegSpec { bank: RegisterBank::W, num: instruction.modrm_mmm.num }; + instruction.opcode = Opcode::RDSEED; + } else { + return Err(DecodeError::InvalidOpcode); } instruction.operand_count = 1; } @@ -6185,15 +6306,108 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter } } }, + OperandCode::ModRM_0x660fae => { + let modrm = read_modrm(&mut bytes_iter, length)?; + if modrm < 0xc0 { + instruction.opcode = match (modrm >> 3) & 7 { + 6 => { + Opcode::CLWB + } + 7 => { + Opcode::CLFLUSHOPT + } + _ => { + instruction.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOpcode); + } + }; + instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, 1 /* opwidth */, length)?; + instruction.operand_count = 1; + } else { + instruction.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOpcode); + } + }, + OperandCode::ModRM_0xf30fae => { + let modrm = read_modrm(&mut bytes_iter, length)?; + + if (modrm & 0xc0) == 0xc0 { + let r = (modrm >> 3) & 7; + let m = modrm & 7; + match r { + 0 => { + instruction.opcode = Opcode::RDFSBASE; + let opwidth = if instruction.prefixes.rex().w() { + RegisterBank::Q + } else { + RegisterBank::D + }; + instruction.modrm_mmm = RegSpec::from_parts(m, instruction.prefixes.rex().x(), opwidth); + instruction.operands[0] = OperandSpec::RegMMM; + instruction.operand_count = 1; + } + 1 => { + instruction.opcode = Opcode::RDGSBASE; + let opwidth = if instruction.prefixes.rex().w() { + RegisterBank::Q + } else { + RegisterBank::D + }; + instruction.modrm_mmm = RegSpec::from_parts(m, instruction.prefixes.rex().x(), opwidth); + instruction.operands[0] = OperandSpec::RegMMM; + instruction.operand_count = 1; + + } + 2 => { + instruction.opcode = Opcode::WRFSBASE; + let opwidth = if instruction.prefixes.rex().w() { + RegisterBank::Q + } else { + RegisterBank::D + }; + instruction.modrm_mmm = RegSpec::from_parts(m, instruction.prefixes.rex().x(), opwidth); + instruction.operands[0] = OperandSpec::RegMMM; + instruction.operand_count = 1; + } + 3 => { + instruction.opcode = Opcode::WRGSBASE; + let opwidth = if instruction.prefixes.rex().w() { + RegisterBank::Q + } else { + RegisterBank::D + }; + instruction.modrm_mmm = RegSpec::from_parts(m, instruction.prefixes.rex().x(), opwidth); + instruction.operands[0] = OperandSpec::RegMMM; + instruction.operand_count = 1; + + } + _ => { + instruction.opcode = Opcode::Invalid; + return Err(DecodeError::InvalidOpcode); + } + } + } + } OperandCode::ModRM_0xf30fc7 => { + let opwidth = imm_width_from_prefixes_64(SizeCode::vqp, instruction.prefixes); let modrm = read_modrm(&mut bytes_iter, length)?; let r = (modrm >> 3) & 7; match r { 6 => { instruction.opcode = Opcode::VMXON; - instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, 1 /* doesn't matter, something using this width is invalid */, length)?; + instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, opwidth, length)?; if instruction.operands[0] == OperandSpec::RegMMM { + // this would be invalid as `vmxon`, so fall back to the parse as + // f3-prefixed rdrand + instruction.opcode = Opcode::RDRAND; + } + instruction.operand_count = 1; + } + 7 => { + instruction.opcode = Opcode::RDPID; + instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, opwidth, length)?; + if instruction.operands[0] != OperandSpec::RegMMM { return Err(DecodeError::InvalidOperand); } instruction.operand_count = 1; |