aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2021-01-15 14:52:11 -0800
committeriximeow <me@iximeow.net>2021-01-15 14:52:11 -0800
commitf057c712f91b215034fe84fa0f22694aaa8dffb3 (patch)
tree6c28340154b900ba984ccd188fd7149aa4de6d75
parent81e9b93aab9217cf7cb508f64b19fc1c0df024b5 (diff)
fix several missing or invalid decodings among 0f01 opcodes
* `mwaitx`, `monitorx`, `rdpru`, and `clzero` are now supported * swapgs is no longer decoded in protected mode * rdpkru and wrpkru are no longer decoded if mod bits != 11
-rw-r--r--CHANGELOG6
-rw-r--r--src/long_mode/display.rs8
-rw-r--r--src/long_mode/mod.rs36
-rw-r--r--src/protected_mode/display.rs8
-rw-r--r--src/protected_mode/mod.rs42
-rw-r--r--test/long_mode/mod.rs42
-rw-r--r--test/protected_mode/mod.rs39
7 files changed, 175 insertions, 6 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 8de4801..1325597 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,9 @@
+## 0.1.5
+* fix several issues around 0f01* opcode decoding;
+ - AMD-only `monitorx`, `mwaitx`, `clzero`, and `rdpru` are now supported
+ - `swapgs` is invalid in non-64-bit modes
+ - `rdpkru` and `wrpkru` were incorrectly decoded when modrm bits were not `11`
+
## 0.1.4
* [long mode only]: fix decoding of rex-prefixed modrm+sib operands selecting index 0b100 and base 0b101
- for memory operands with a base, index, and displacement either
diff --git a/src/long_mode/display.rs b/src/long_mode/display.rs
index e653399..c3f4413 100644
--- a/src/long_mode/display.rs
+++ b/src/long_mode/display.rs
@@ -513,6 +513,8 @@ const MNEMONICS: &[&'static str] = &[
"vmxoff",
"monitor",
"mwait",
+ "monitorx",
+ "mwaitx",
"clac",
"stac",
"encls",
@@ -527,6 +529,8 @@ const MNEMONICS: &[&'static str] = &[
"enclu",
"rdpkru",
"wrpkru",
+ "rdpru",
+ "clzero",
"rdseed",
"rdrand",
"addps",
@@ -2152,6 +2156,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::VMXOFF |
Opcode::MONITOR |
Opcode::MWAIT |
+ Opcode::MONITORX |
+ Opcode::MWAITX |
Opcode::SKINIT |
Opcode::CLGI |
Opcode::STGI |
@@ -2169,6 +2175,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::ENCLU |
Opcode::RDPKRU |
Opcode::WRPKRU |
+ Opcode::RDPRU |
+ Opcode::CLZERO |
Opcode::LAR => { write!(out, "{}", colors.platform_op(self)) }
Opcode::CRC32 |
diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs
index d165e38..f15e2a1 100644
--- a/src/long_mode/mod.rs
+++ b/src/long_mode/mod.rs
@@ -1131,6 +1131,8 @@ pub enum Opcode {
VMXOFF,
MONITOR,
MWAIT,
+ MONITORX,
+ MWAITX,
CLAC,
STAC,
ENCLS,
@@ -1146,6 +1148,9 @@ pub enum Opcode {
RDPKRU,
WRPKRU,
+ RDPRU,
+ CLZERO,
+
RDSEED,
RDRAND,
@@ -3278,6 +3283,12 @@ impl InstDecoder {
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) {
@@ -8049,6 +8060,14 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter
instruction.operand_count = 1;
instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, 2, length)?;
} else if r == 5 {
+ let mod_bits = modrm >> 6;
+ if mod_bits != 0b11 {
+ instruction.opcode = Opcode::Invalid;
+ instruction.operands[0] = OperandSpec::Nothing;
+ instruction.operand_count = 0;
+ return Err(DecodeError::InvalidOpcode);
+ }
+
let m = modrm & 7;
match m {
0b110 => {
@@ -8084,6 +8103,23 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter
instruction.opcode = Opcode::RDTSCP;
instruction.operands[0] = OperandSpec::Nothing;
instruction.operand_count = 0;
+ } else if m == 2 {
+ instruction.opcode = Opcode::MONITORX;
+ instruction.operands[0] = OperandSpec::Nothing;
+ instruction.operand_count = 0;
+ } else if m == 3 {
+ instruction.opcode = Opcode::MWAITX;
+ instruction.operands[0] = OperandSpec::Nothing;
+ instruction.operand_count = 0;
+ } else if m == 4 {
+ instruction.opcode = Opcode::CLZERO;
+ instruction.operands[0] = OperandSpec::Nothing;
+ instruction.operand_count = 0;
+ } else if m == 5 {
+ instruction.opcode = Opcode::RDPRU;
+ instruction.operands[0] = OperandSpec::RegRRR;
+ instruction.modrm_rrr = RegSpec::ecx();
+ instruction.operand_count = 1;
} else {
instruction.opcode = Opcode::Invalid;
return Err(DecodeError::InvalidOpcode);
diff --git a/src/protected_mode/display.rs b/src/protected_mode/display.rs
index c92737b..a0161c2 100644
--- a/src/protected_mode/display.rs
+++ b/src/protected_mode/display.rs
@@ -504,6 +504,8 @@ const MNEMONICS: &[&'static str] = &[
"vmxoff",
"monitor",
"mwait",
+ "monitorx",
+ "mwaitx",
"clac",
"stac",
"encls",
@@ -518,6 +520,8 @@ const MNEMONICS: &[&'static str] = &[
"enclu",
"rdpkru",
"wrpkru",
+ "rdpru",
+ "clzero",
"rdseed",
"rdrand",
"addps",
@@ -2162,6 +2166,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::VMXOFF |
Opcode::MONITOR |
Opcode::MWAIT |
+ Opcode::MONITORX |
+ Opcode::MWAITX |
Opcode::SKINIT |
Opcode::CLGI |
Opcode::STGI |
@@ -2179,6 +2185,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::ENCLU |
Opcode::RDPKRU |
Opcode::WRPKRU |
+ Opcode::RDPRU |
+ Opcode::CLZERO |
Opcode::ARPL |
Opcode::LAR => { write!(out, "{}", colors.platform_op(self)) }
diff --git a/src/protected_mode/mod.rs b/src/protected_mode/mod.rs
index b60ff34..9327f64 100644
--- a/src/protected_mode/mod.rs
+++ b/src/protected_mode/mod.rs
@@ -1036,6 +1036,8 @@ pub enum Opcode {
VMXOFF,
MONITOR,
MWAIT,
+ MONITORX,
+ MWAITX,
CLAC,
STAC,
ENCLS,
@@ -1051,6 +1053,9 @@ pub enum Opcode {
RDPKRU,
WRPKRU,
+ RDPRU,
+ CLZERO,
+
RDSEED,
RDRAND,
@@ -3191,6 +3196,12 @@ impl InstDecoder {
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) {
@@ -7893,6 +7904,14 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter
instruction.operand_count = 1;
instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, 2, length)?;
} else if r == 5 {
+ let mod_bits = modrm >> 6;
+ if mod_bits != 0b11 {
+ instruction.opcode = Opcode::Invalid;
+ instruction.operands[0] = OperandSpec::Nothing;
+ instruction.operand_count = 0;
+ return Err(DecodeError::InvalidOpcode);
+ }
+
let m = modrm & 7;
match m {
0b110 => {
@@ -7921,13 +7940,30 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter
let m = modrm & 7;
if mod_bits == 0b11 {
if m == 0 {
- instruction.opcode = Opcode::SWAPGS;
- instruction.operands[0] = OperandSpec::Nothing;
- instruction.operand_count = 0;
+ // swapgs is not valid in modes other than 64-bit
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
} else if m == 1 {
instruction.opcode = Opcode::RDTSCP;
instruction.operands[0] = OperandSpec::Nothing;
instruction.operand_count = 0;
+ } else if m == 2 {
+ instruction.opcode = Opcode::MONITORX;
+ instruction.operands[0] = OperandSpec::Nothing;
+ instruction.operand_count = 0;
+ } else if m == 3 {
+ instruction.opcode = Opcode::MWAITX;
+ instruction.operands[0] = OperandSpec::Nothing;
+ instruction.operand_count = 0;
+ } else if m == 4 {
+ instruction.opcode = Opcode::CLZERO;
+ instruction.operands[0] = OperandSpec::Nothing;
+ instruction.operand_count = 0;
+ } else if m == 5 {
+ instruction.opcode = Opcode::RDPRU;
+ instruction.operands[0] = OperandSpec::RegRRR;
+ instruction.modrm_rrr = RegSpec::ecx();
+ instruction.operand_count = 1;
} else {
instruction.opcode = Opcode::Invalid;
return Err(DecodeError::InvalidOpcode);
diff --git a/test/long_mode/mod.rs b/test/long_mode/mod.rs
index 8489822..0830c2d 100644
--- a/test/long_mode/mod.rs
+++ b/test/long_mode/mod.rs
@@ -831,6 +831,9 @@ fn test_ssse3() {
fn test_0f01() {
// drawn heavily from "Table A-6. Opcode Extensions for One- and Two-byte Opcodes by Group
// Number"
+ for x in &[0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f] {
+ test_invalid(&[0x0f, 0x01, *x]);
+ }
test_display(&[0x0f, 0x01, 0x38], "invlpg [rax]");
test_display(&[0x0f, 0x01, 0x3f], "invlpg [rdi]");
test_display(&[0x0f, 0x01, 0x40, 0xff], "sgdt [rax - 0x1]");
@@ -839,18 +842,24 @@ fn test_0f01() {
test_display(&[0x0f, 0x01, 0x51, 0xff], "lgdt [rcx - 0x1]");
test_display(&[0x0f, 0x01, 0x59, 0xff], "lidt [rcx - 0x1]");
test_display(&[0x0f, 0x01, 0x61, 0xff], "smsw [rcx - 0x1]");
+ test_invalid(&[0x0f, 0x01, 0x69, 0xff]);
+ test_display(&[0x0f, 0x01, 0x71, 0xff], "lmsw [rcx - 0x1]");
+ test_display(&[0x0f, 0x01, 0x79, 0xff], "invlpg [rcx - 0x1]");
test_display(&[0x0f, 0x01, 0xc0], "enclv");
test_display(&[0x0f, 0x01, 0xc1], "vmcall");
test_display(&[0x0f, 0x01, 0xc2], "vmlaunch");
test_display(&[0x0f, 0x01, 0xc3], "vmresume");
test_display(&[0x0f, 0x01, 0xc4], "vmxoff");
- test_invalid(&[0x0f, 0x01, 0xc5]);
+ test_invalid(&[0x0f, 0x01, 0xc5]); // TODO: TME would make this `pconfig`
test_invalid(&[0x0f, 0x01, 0xc6]);
test_invalid(&[0x0f, 0x01, 0xc7]);
test_display(&[0x0f, 0x01, 0xc8], "monitor");
test_display(&[0x0f, 0x01, 0xc9], "mwait");
test_display(&[0x0f, 0x01, 0xca], "clac");
test_display(&[0x0f, 0x01, 0xcb], "stac");
+ test_invalid(&[0x0f, 0x01, 0xcc]);
+ test_invalid(&[0x0f, 0x01, 0xcd]);
+ test_invalid(&[0x0f, 0x01, 0xce]);
test_display(&[0x0f, 0x01, 0xcf], "encls");
test_display(&[0x0f, 0x01, 0xd0], "xgetbv");
test_display(&[0x0f, 0x01, 0xd1], "xsetbv");
@@ -868,10 +877,41 @@ fn test_0f01() {
test_display(&[0x0f, 0x01, 0xdd], "clgi");
test_display(&[0x0f, 0x01, 0xde], "skinit eax");
test_display(&[0x0f, 0x01, 0xdf], "invlpga rax, ecx");
+ test_display(&[0x4f, 0x0f, 0x01, 0xe0], "smsw r8w");
+ test_display(&[0x0f, 0x01, 0xe0], "smsw ax");
+ test_display(&[0x0f, 0x01, 0xe1], "smsw cx");
+ test_display(&[0x0f, 0x01, 0xe2], "smsw dx");
+ test_display(&[0x0f, 0x01, 0xe3], "smsw bx");
+ test_display(&[0x0f, 0x01, 0xe4], "smsw sp");
+ test_display(&[0x0f, 0x01, 0xe5], "smsw bp");
+ test_display(&[0x0f, 0x01, 0xe6], "smsw si");
+ test_display(&[0x0f, 0x01, 0xe7], "smsw di");
+ test_invalid(&[0x0f, 0x01, 0xe8]);
+ test_invalid(&[0x0f, 0x01, 0xe8]);
+ test_invalid(&[0x0f, 0x01, 0xe9]);
+ test_invalid(&[0x0f, 0x01, 0xea]);
+ test_invalid(&[0x0f, 0x01, 0xeb]);
+ test_invalid(&[0x0f, 0x01, 0xec]);
+ test_invalid(&[0x0f, 0x01, 0xed]);
test_display(&[0x0f, 0x01, 0xee], "rdpkru");
test_display(&[0x0f, 0x01, 0xef], "wrpkru");
+ test_display(&[0x4f, 0x0f, 0x01, 0xf0], "lmsw r8w");
+ test_display(&[0x0f, 0x01, 0xf0], "lmsw ax");
+ test_display(&[0x0f, 0x01, 0xf1], "lmsw cx");
+ test_display(&[0x0f, 0x01, 0xf2], "lmsw dx");
+ test_display(&[0x0f, 0x01, 0xf3], "lmsw bx");
+ test_display(&[0x0f, 0x01, 0xf4], "lmsw sp");
+ test_display(&[0x0f, 0x01, 0xf5], "lmsw bp");
+ test_display(&[0x0f, 0x01, 0xf6], "lmsw si");
+ test_display(&[0x0f, 0x01, 0xf7], "lmsw di");
test_display(&[0x0f, 0x01, 0xf8], "swapgs");
test_display(&[0x0f, 0x01, 0xf9], "rdtscp");
+ test_display(&[0x0f, 0x01, 0xfa], "monitorx");
+ test_display(&[0x0f, 0x01, 0xfb], "mwaitx");
+ test_display(&[0x0f, 0x01, 0xfc], "clzero");
+ test_display(&[0x0f, 0x01, 0xfd], "rdpru ecx");
+ test_invalid(&[0x0f, 0x01, 0xfe]);
+ test_invalid(&[0x0f, 0x01, 0xff]);
}
#[test]
diff --git a/test/protected_mode/mod.rs b/test/protected_mode/mod.rs
index dd0a51f..e3c7975 100644
--- a/test/protected_mode/mod.rs
+++ b/test/protected_mode/mod.rs
@@ -766,18 +766,24 @@ fn test_0f01() {
test_display(&[0x0f, 0x01, 0x51, 0xff], "lgdt [ecx - 0x1]");
test_display(&[0x0f, 0x01, 0x59, 0xff], "lidt [ecx - 0x1]");
test_display(&[0x0f, 0x01, 0x61, 0xff], "smsw [ecx - 0x1]");
+ test_invalid(&[0x0f, 0x01, 0x69, 0xff]);
+ test_display(&[0x0f, 0x01, 0x71, 0xff], "lmsw [ecx - 0x1]");
+ test_display(&[0x0f, 0x01, 0x79, 0xff], "invlpg [ecx - 0x1]");
test_display(&[0x0f, 0x01, 0xc0], "enclv");
test_display(&[0x0f, 0x01, 0xc1], "vmcall");
test_display(&[0x0f, 0x01, 0xc2], "vmlaunch");
test_display(&[0x0f, 0x01, 0xc3], "vmresume");
test_display(&[0x0f, 0x01, 0xc4], "vmxoff");
- test_invalid(&[0x0f, 0x01, 0xc5]);
+ test_invalid(&[0x0f, 0x01, 0xc5]); // TODO: TME would make this `pconfig`
test_invalid(&[0x0f, 0x01, 0xc6]);
test_invalid(&[0x0f, 0x01, 0xc7]);
test_display(&[0x0f, 0x01, 0xc8], "monitor");
test_display(&[0x0f, 0x01, 0xc9], "mwait");
test_display(&[0x0f, 0x01, 0xca], "clac");
test_display(&[0x0f, 0x01, 0xcb], "stac");
+ test_invalid(&[0x0f, 0x01, 0xcc]);
+ test_invalid(&[0x0f, 0x01, 0xcd]);
+ test_invalid(&[0x0f, 0x01, 0xce]);
test_display(&[0x0f, 0x01, 0xcf], "encls");
test_display(&[0x0f, 0x01, 0xd0], "xgetbv");
test_display(&[0x0f, 0x01, 0xd1], "xsetbv");
@@ -795,10 +801,39 @@ fn test_0f01() {
test_display(&[0x0f, 0x01, 0xdd], "clgi");
test_display(&[0x0f, 0x01, 0xde], "skinit eax");
test_display(&[0x0f, 0x01, 0xdf], "invlpga eax, ecx");
+ test_display(&[0x0f, 0x01, 0xe0], "smsw ax");
+ test_display(&[0x0f, 0x01, 0xe1], "smsw cx");
+ test_display(&[0x0f, 0x01, 0xe2], "smsw dx");
+ test_display(&[0x0f, 0x01, 0xe3], "smsw bx");
+ test_display(&[0x0f, 0x01, 0xe4], "smsw sp");
+ test_display(&[0x0f, 0x01, 0xe5], "smsw bp");
+ test_display(&[0x0f, 0x01, 0xe6], "smsw si");
+ test_display(&[0x0f, 0x01, 0xe7], "smsw di");
+ test_invalid(&[0x0f, 0x01, 0xe8]);
+ test_invalid(&[0x0f, 0x01, 0xe8]);
+ test_invalid(&[0x0f, 0x01, 0xe9]);
+ test_invalid(&[0x0f, 0x01, 0xea]);
+ test_invalid(&[0x0f, 0x01, 0xeb]);
+ test_invalid(&[0x0f, 0x01, 0xec]);
+ test_invalid(&[0x0f, 0x01, 0xed]);
test_display(&[0x0f, 0x01, 0xee], "rdpkru");
test_display(&[0x0f, 0x01, 0xef], "wrpkru");
- test_display(&[0x0f, 0x01, 0xf8], "swapgs");
+ test_display(&[0x0f, 0x01, 0xf0], "lmsw ax");
+ test_display(&[0x0f, 0x01, 0xf1], "lmsw cx");
+ test_display(&[0x0f, 0x01, 0xf2], "lmsw dx");
+ test_display(&[0x0f, 0x01, 0xf3], "lmsw bx");
+ test_display(&[0x0f, 0x01, 0xf4], "lmsw sp");
+ test_display(&[0x0f, 0x01, 0xf5], "lmsw bp");
+ test_display(&[0x0f, 0x01, 0xf6], "lmsw si");
+ test_display(&[0x0f, 0x01, 0xf7], "lmsw di");
+ test_invalid(&[0x0f, 0x01, 0xf8]);
test_display(&[0x0f, 0x01, 0xf9], "rdtscp");
+ test_display(&[0x0f, 0x01, 0xfa], "monitorx");
+ test_display(&[0x0f, 0x01, 0xfb], "mwaitx");
+ test_display(&[0x0f, 0x01, 0xfc], "clzero");
+ test_display(&[0x0f, 0x01, 0xfd], "rdpru ecx");
+ test_invalid(&[0x0f, 0x01, 0xfe]);
+ test_invalid(&[0x0f, 0x01, 0xff]);
}
#[test]