1use anyhow::{anyhow, ensure, Result};
2use debug_info::{Module, Process, SymbolInfo};
3use ffi2::ffi;
4use intervaltree::IntervalTree;
5use kernel::{find_kernel_with_idt, KernelInfo};
6use raw_cstr::AsRawCstr;
7use simics::{
8 debug, get_interface, get_object, get_processor_number, info, sys::cpu_cb_handle_t, warn,
9 ConfObject, CpuInstrumentationSubscribeInterface, IntRegisterInterface,
10 ProcessorInfoV2Interface,
11};
12use std::{
13 collections::{hash_map::Entry, HashMap, HashSet},
14 ffi::c_void,
15 path::{Path, PathBuf},
16};
17use structs::WindowsKpcr;
18use util::read_virtual;
19
20use vergilius::bindings::*;
21
22use crate::{source_cov::SourceCache, Tsffs};
23
24use super::DebugInfoConfig;
25
26pub mod debug_info;
27pub mod idt;
28pub mod kernel;
29pub mod paging;
30pub mod pdb;
31pub mod structs;
32pub mod util;
33
34const KUSER_SHARED_DATA_ADDRESS_X86_64: u64 = 0xFFFFF78000000000;
35
36#[derive(Debug)]
37pub struct CpuInstrumentationCbHandle(usize);
38
39impl From<*mut cpu_cb_handle_t> for CpuInstrumentationCbHandle {
40 fn from(value: *mut cpu_cb_handle_t) -> Self {
41 Self(value as usize)
42 }
43}
44
45impl From<CpuInstrumentationCbHandle> for *mut cpu_cb_handle_t {
46 fn from(value: CpuInstrumentationCbHandle) -> Self {
47 value.0 as *mut _
48 }
49}
50
51#[derive(Debug, Default)]
52pub struct WindowsOsInfo {
54 pub kernel_info: Option<KernelInfo>,
56 pub processes: HashMap<i32, Process>,
58 pub modules: HashMap<i32, Vec<Module>>,
60 pub symbol_lookup_trees: HashMap<i32, IntervalTree<u64, SymbolInfo>>,
62 pub not_found_full_name_cache: HashSet<String>,
65 pub instruction_callbacks: HashMap<i32, CpuInstrumentationCbHandle>,
67}
68
69impl WindowsOsInfo {
70 pub fn collect<P>(
73 &mut self,
74 processor: *mut ConfObject,
75 download_directory: P,
76 user_debug_info: &mut DebugInfoConfig,
77 source_cache: &SourceCache,
78 ) -> Result<()>
79 where
80 P: AsRef<Path>,
81 {
82 info!(get_object("tsffs")?, "Collecting Windows OS information");
83 let processor_nr = get_processor_number(processor)?;
84 let mut processor_info_v2: ProcessorInfoV2Interface = get_interface(processor)?;
85
86 if self.kernel_info.is_none() {
87 info!(get_object("tsffs")?, "Collecting kernel information");
88 ensure!(
90 processor_info_v2.get_logical_address_width()? == 64,
91 "Only 64-bit Windows is supported"
92 );
93
94 let kuser_shared_data = read_virtual::<windows_10_0_22631_2428_x64::_KUSER_SHARED_DATA>(
95 processor,
96 KUSER_SHARED_DATA_ADDRESS_X86_64,
97 )?;
98
99 let (maj, min, build) = (
100 kuser_shared_data.NtMajorVersion,
101 kuser_shared_data.NtMinorVersion,
102 kuser_shared_data.NtBuildNumber,
103 );
104
105 ensure!(maj == 10, "Only Windows 10/11 is supported");
106
107 let _ = WindowsKpcr::new(processor, maj, min, build)?;
109 let kernel_base = find_kernel_with_idt(processor, build)?;
110
111 info!(get_object("tsffs")?, "Found kernel base {kernel_base:#x}");
112
113 self.kernel_info = Some(KernelInfo::new(
114 processor,
115 "ntoskrnl.exe",
116 kernel_base,
117 download_directory.as_ref(),
118 &mut self.not_found_full_name_cache,
119 user_debug_info,
120 )?);
121 }
122
123 info!(get_object("tsffs")?, "Collecting process list");
124
125 self.processes.insert(
126 processor_nr,
127 self.kernel_info
128 .as_mut()
129 .expect("Kernel Info must be set at this point")
130 .current_process(
131 processor,
132 download_directory.as_ref(),
133 &mut self.not_found_full_name_cache,
134 user_debug_info,
135 )?,
136 );
137
138 info!(get_object("tsffs")?, "Collecting module list");
139
140 self.modules.insert(
141 processor_nr,
142 self.kernel_info
143 .as_mut()
144 .expect("Kernel Info must be set at this point")
145 .loaded_module_list(
146 processor,
147 download_directory.as_ref(),
148 &mut self.not_found_full_name_cache,
149 user_debug_info,
150 )?,
151 );
152
153 let elements = self
154 .modules
155 .get_mut(&processor_nr)
156 .ok_or_else(|| anyhow!("No modules for processor {processor_nr}"))?
157 .iter_mut()
158 .filter_map(|m| {
159 m.intervals(source_cache).ok().or_else(|| {
160 get_object("tsffs")
161 .and_then(|obj| {
162 debug!(
163 obj,
164 "Failed (or skipped) getting intervals for module {}", &m.full_name
165 );
166 Err(
167 anyhow!("Failed to get intervals for module {}", &m.full_name)
168 .into(),
169 )
170 })
171 .ok()
172 })
173 })
174 .collect::<Vec<_>>()
175 .into_iter()
176 .chain(
177 self.kernel_info
178 .as_mut()
179 .expect("Kernel Info must be set at this point")
180 .current_process(
181 processor,
182 download_directory.as_ref(),
183 &mut self.not_found_full_name_cache,
184 user_debug_info,
185 )?
186 .modules
187 .iter_mut()
188 .filter_map(|m| {
189 m.intervals(source_cache).ok().or_else(|| {
190 get_object("tsffs")
191 .and_then(|obj| {
192 debug!(
193 obj,
194 "Failed (or skipped) getting intervals for module {}",
195 &m.full_name
196 );
197 Err(anyhow!(
198 "Failed to get intervals for module {}",
199 &m.full_name
200 )
201 .into())
202 })
203 .ok()
204 })
205 })
206 .collect::<Vec<_>>(),
207 )
208 .flatten()
209 .collect::<Vec<_>>();
210
211 let mut filtered_elements = HashSet::new();
212
213 let elements = elements
215 .into_iter()
216 .filter(|e| filtered_elements.insert(e.range.clone()))
217 .collect::<Vec<_>>();
218
219 elements.iter().map(|e| &e.value).for_each(|si| {
221 if let Some(first) = si.lines.first() {
222 let record = user_debug_info.coverage.get_or_insert_mut(&first.file_path);
223 record.add_function_if_not_exists(
224 first.start_line as usize,
225 si.lines.last().map(|l| l.end_line as usize),
226 &si.name,
227 );
228 si.lines.iter().for_each(|l| {
229 (l.start_line..=l.end_line).for_each(|line| {
230 record.add_line_if_not_exists(line as usize);
231 });
232 });
233 }
234 });
235
236 self.symbol_lookup_trees.insert(
237 processor_nr,
238 elements.iter().cloned().collect::<IntervalTree<_, _>>(),
239 );
240
241 Ok(())
242 }
243}
244
245impl Tsffs {
246 pub fn on_control_register_write_windows_symcov(
248 &mut self,
249 trigger_obj: *mut ConfObject,
250 register_nr: i64,
251 value: i64,
252 ) -> Result<()> {
253 let mut int_register: IntRegisterInterface = get_interface(trigger_obj)?;
254 let processor_nr = get_processor_number(trigger_obj)?;
255
256 if self.processors.contains_key(&processor_nr)
257 && self.coverage_enabled
258 && self.windows
259 && self.symbolic_coverage
260 && register_nr == int_register.get_number("cr3".as_raw_cstr()?)? as i64
261 && self
262 .cr3_cache
263 .get(&processor_nr)
264 .is_some_and(|v| *v != value)
265 {
266 info!(
267 get_object("tsffs")?,
268 "Got write {value:#x} to CR3 for processor {processor_nr}, refreshing kernel & process mappings"
269 );
270
271 self.windows_os_info.collect(
272 trigger_obj,
273 &self.debuginfo_download_directory,
274 &mut DebugInfoConfig {
275 system: self.symbolic_coverage_system,
276 user_debug_info: &self.debug_info,
277 coverage: &mut self.coverage,
278 },
279 &self.source_file_cache,
280 )?;
281
282 self.cr3_cache.insert(processor_nr, value);
283 }
284
285 Ok(())
286 }
287}