1#![no_std]
2#![cfg(any(target_arch = "x86_64", target_arch = "x86"))]
3use core::arch::x86_64::{__cpuid, _rdtsc};
4
5bitflags::bitflags! {
6 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
7 pub struct Detection: u32 {
8 const RDTSC = 0b00001;
10 const HYPERVISOR_BIT = 0b00010;
12 const HYPERVISOR_CPU_VENDOR = 0b00100;
14 const UNEXPECTED_CPU_VENDOR = 0b01000;
16 }
17}
18
19const KNOWN_HYPERVISORS: [u32; 7] = [
20 fnv1(b"bhyve bhyve "),
21 fnv1(b"KVMKVMKVM\0\0\0"),
22 fnv1(b"TCGTCGTCGTCG"),
23 fnv1(b"Microsoft Hv"),
24 fnv1(b" lrpepyh vr"),
25 fnv1(b"VMwareVMware"),
26 fnv1(b"XenVMMXenVMM"),
27];
28
29const EXPECTED_VENDORS: [u32; 2] = [fnv1(b"GenuineIntel"), fnv1(b"AuthenticAMD")];
30
31pub fn vm_detect() -> Detection {
35 let mut detection = Detection::empty();
36
37 if rdtsc_detection() {
38 detection |= Detection::RDTSC;
39 }
40
41 if hypervisor_vendor().is_some() {
42 detection |= Detection::HYPERVISOR_BIT;
43 }
44
45 let vendor_hash = fnv1(&cpu_vendor());
46
47 if KNOWN_HYPERVISORS.iter().any(|&hash| hash == vendor_hash) {
48 detection |= Detection::HYPERVISOR_CPU_VENDOR;
49 } else if !EXPECTED_VENDORS.iter().any(|&hash| hash == vendor_hash) {
50 detection |= Detection::UNEXPECTED_CPU_VENDOR;
51 }
52
53 detection
54}
55
56#[inline(always)]
57const fn fnv1(data: &[u8; 12]) -> u32 {
58 let mut res: u32 = 0x811c9dc5;
59
60 let mut i = 0;
61 while i < data.len() {
62 res = res.wrapping_mul(0x01000193) ^ (data[i] as u32);
63
64 i += 1;
65 }
66
67 res
68}
69
70#[allow(dead_code)]
71fn brand_name() -> Option<[u8; 16 * 3]> {
72 if unsafe { __cpuid(0x80000000) }.eax < 0x80000004 {
73 return None;
74 }
75 let cpuids: [_; 3] = unsafe {
76 [
77 __cpuid(0x80000002),
78 __cpuid(0x80000003),
79 __cpuid(0x80000004),
80 ]
81 };
82
83 let mut result = [0; 16 * 3];
84
85 for (i, cpuid) in cpuids.into_iter().enumerate() {
86 for (j, data) in [cpuid.eax, cpuid.ebx, cpuid.ecx, cpuid.edx]
87 .into_iter()
88 .enumerate()
89 {
90 let index = i * 16 + j * 4;
91 result[index..index + 4].copy_from_slice(&data.to_le_bytes());
92 }
93 }
94
95 Some(result)
96}
97
98fn rdtsc_detection() -> bool {
99 const ITERATIONS: u64 = 1000;
100
101 let sum: u64 = (0..ITERATIONS)
102 .map(|_| unsafe {
103 let start = _rdtsc();
104 let _ = __cpuid(0);
105 let end = _rdtsc();
106 end - start
107 })
108 .sum();
109
110 let avg = sum / ITERATIONS;
111 avg > 500
112}
113
114fn cpu_vendor() -> [u8; 12] {
115 let cpuid = unsafe { __cpuid(0) };
116
117 let mut result = [0; 12];
118
119 for (i, data) in [cpuid.ebx, cpuid.edx, cpuid.ecx].into_iter().enumerate() {
120 let index = i * 4;
121 result[index..index + 4].copy_from_slice(&data.to_le_bytes());
122 }
123
124 result
125}
126
127fn hypervisor_vendor() -> Option<[u8; 12]> {
128 let processor_info = unsafe { __cpuid(1) };
129 if processor_info.ecx >> 31 & 1 == 0 {
131 return None;
132 }
133
134 let cpuid = unsafe { __cpuid(0x40000000) };
135
136 let mut result = [0; 12];
137
138 for (i, data) in [cpuid.ebx, cpuid.ecx, cpuid.edx].into_iter().enumerate() {
139 let index = i * 4;
140 result[index..index + 4].copy_from_slice(&data.to_le_bytes());
141 }
142
143 Some(result)
144}