1use sha2::{Digest, Sha256};
34use std::collections::{HashMap, HashSet};
35use std::env;
36use std::fs::File;
37use std::io::prelude::*;
38use std::path::{Path, PathBuf};
39use std::process::Command;
40use std::sync::Arc;
41use toml::Value;
42
43use crate::bitvector::BV;
44use crate::ir::{Loc, Name, Reset, Symtab, Val};
45use crate::lexer::Lexer;
46use crate::value_parser::{LocParser, ValParser};
47use crate::zencode;
48
49fn find_tool_path<P>(program: P) -> Result<PathBuf, String>
52where
53 P: AsRef<Path>,
54{
55 env::var_os("PATH")
56 .and_then(|paths| {
57 env::split_paths(&paths)
58 .filter_map(|dir| {
59 let full_path = dir.join(&program);
60 if full_path.is_file() {
61 Some(full_path)
62 } else {
63 None
64 }
65 })
66 .next()
67 })
68 .ok_or_else(|| format!("Tool {} not found in $PATH", program.as_ref().display()))
69}
70
71#[derive(Debug)]
72pub struct Tool {
73 pub executable: PathBuf,
74 pub options: Vec<String>,
75}
76
77impl Tool {
78 pub fn command(&self) -> Command {
79 let mut cmd = Command::new(&self.executable);
80 cmd.args(&self.options);
81 cmd
82 }
83}
84
85fn get_tool_path(config: &Value, tool: &str) -> Result<Tool, String> {
86 match config.get(tool) {
87 Some(Value::String(tool)) => {
88 let mut words = tool.split_whitespace();
89 let program = words.next().ok_or_else(|| format!("Configuration option {} cannot be empty", tool))?;
90 Ok(Tool { executable: find_tool_path(program)?, options: words.map(|w| w.to_string()).collect() })
91 }
92 _ => Err(format!("Configuration option {} must be specified", tool)),
93 }
94}
95
96fn get_program_counter(config: &Value, symtab: &Symtab) -> Result<Name, String> {
99 match config.get("pc") {
100 Some(Value::String(register)) => match symtab.get(&zencode::encode(®ister)) {
101 Some(symbol) => Ok(symbol),
102 None => Err(format!("Register {} does not exist in supplied architecture", register)),
103 },
104 _ => Err("Configuration file must specify the program counter via `pc = \"REGISTER_NAME\"`".to_string()),
105 }
106}
107
108fn get_ifetch_read_kind(config: &Value, symtab: &Symtab) -> Result<Name, String> {
111 match config.get("ifetch") {
112 Some(Value::String(rk)) => match symtab.get(&zencode::encode(&rk)) {
113 Some(symbol) => Ok(symbol),
114 None => Err(format!("Read kind {} does not exist in supplied architecture", rk)),
115 },
116 _ => Err("Configuration file must specify a read_kind for instruction-fetch events".to_string()),
117 }
118}
119
120fn get_exclusives(config: &Value, exclusives_type: &str, symtab: &Symtab) -> Result<Vec<Name>, String> {
121 match config.get(exclusives_type) {
122 Some(Value::Array(exclusives)) => exclusives
123 .iter()
124 .map(|v| {
125 let kind = v.as_str().ok_or_else(|| "Each exclusive must be a string value")?;
126 match symtab.get(&zencode::encode(kind)) {
127 Some(symbol) => Ok(symbol),
128 None => Err(format!("Exclusive kind {} does not exist in supplied architecture", kind)),
129 }
130 })
131 .collect::<Result<_, _>>(),
132 _ => Err("Configuration file must specify some exclusives".to_string()),
133 }
134}
135
136#[derive(Debug)]
137pub enum Kind<A> {
138 Read(A),
139 Write(A),
140 CacheOp(A),
141}
142
143macro_rules! event_kinds_in_table {
144 ($events: ident, $kind: path, $event_str: expr, $result: ident, $symtab: ident) => {
145 for (k, sets) in $events {
146 let k = $symtab
147 .get(&zencode::encode(k))
148 .ok_or_else(|| format!(concat!("Could not find ", $event_str, "_kind {} in architecture"), k))?;
149 let sets = match sets.as_str() {
150 Some(set) => vec![set],
151 None => sets
152 .as_array()
153 .and_then(|sets| sets.iter().map(|set| set.as_str()).collect::<Option<Vec<_>>>())
154 .ok_or_else(|| {
155 format!(concat!(
156 "Each ",
157 $event_str,
158 "_kind in [",
159 $event_str,
160 "s] must specify at least one cat set"
161 ))
162 })?,
163 };
164 for set in sets.into_iter() {
165 match $result.get_mut(set) {
166 None => {
167 $result.insert(set.to_string(), vec![$kind(k)]);
168 }
169 Some(kinds) => kinds.push($kind(k)),
170 }
171 }
172 }
173 };
174}
175
176fn get_event_sets(config: &Value, symtab: &Symtab) -> Result<HashMap<String, Vec<Kind<Name>>>, String> {
177 let reads =
178 config.get("reads").and_then(Value::as_table).ok_or_else(|| "Config file has no [reads] table".to_string())?;
179 let writes = config
180 .get("writes")
181 .and_then(Value::as_table)
182 .ok_or_else(|| "Config file must has no [writes] table".to_string())?;
183 let cache_ops = config
184 .get("cache_ops")
185 .and_then(Value::as_table)
186 .ok_or_else(|| "Config file must has no [cache_ops] table".to_string())?;
187
188 let mut result: HashMap<String, Vec<Kind<Name>>> = HashMap::new();
189
190 event_kinds_in_table!(reads, Kind::Read, "read", result, symtab);
191 event_kinds_in_table!(writes, Kind::Write, "write", result, symtab);
192 event_kinds_in_table!(cache_ops, Kind::CacheOp, "cache_op", result, symtab);
193
194 Ok(result)
195}
196
197fn get_table_value(config: &Value, table: &str, key: &str) -> Result<u64, String> {
198 config
199 .get(table)
200 .and_then(|threads| threads.get(key).and_then(|value| value.as_str()))
201 .ok_or_else(|| format!("No {}.{} found in config", table, key))
202 .and_then(|value| {
203 if value.len() >= 2 && &value[0..2] == "0x" {
204 u64::from_str_radix(&value[2..], 16)
205 } else {
206 u64::from_str_radix(value, 10)
207 }
208 .map_err(|e| format!("Could not parse {} as a 64-bit unsigned integer in {}.{}: {}", value, table, key, e))
209 })
210}
211
212fn from_toml_value<B: BV>(value: &Value) -> Result<Val<B>, String> {
213 match value {
214 Value::Boolean(b) => Ok(Val::Bool(*b)),
215 Value::Integer(i) => Ok(Val::I128(*i as i128)),
216 Value::String(s) => match ValParser::new().parse(Lexer::new(&s)) {
217 Ok(value) => Ok(value),
218 Err(e) => Err(format!("Parse error when reading register value from configuration: {}", e)),
219 },
220 _ => Err(format!("Could not parse TOML value {} as register value", value)),
221 }
222}
223
224fn get_default_registers<B: BV>(config: &Value, symtab: &Symtab) -> Result<HashMap<Name, Val<B>>, String> {
225 let defaults = config
226 .get("registers")
227 .and_then(|registers| registers.as_table())
228 .and_then(|registers| registers.get("defaults"));
229
230 if let Some(defaults) = defaults {
231 if let Some(defaults) = defaults.as_table() {
232 defaults
233 .into_iter()
234 .map(|(register, value)| {
235 if let Some(register) = symtab.get(&zencode::encode(register)) {
236 match from_toml_value(value) {
237 Ok(value) => Ok((register, value)),
238 Err(e) => Err(e),
239 }
240 } else {
241 Err(format!(
242 "Could not find register {} when parsing registers.defaults in configuration",
243 register
244 ))
245 }
246 })
247 .collect()
248 } else {
249 Err("registers.defaults should be a table of <register> = <value> pairs".to_string())
250 }
251 } else {
252 Ok(HashMap::new())
253 }
254}
255
256pub fn reset_to_toml_value<B: BV>(value: &Value) -> Result<Reset<B>, String> {
257 if let Err(e) = from_toml_value::<B>(value) {
258 return Err(e);
259 };
260
261 let value = value.clone();
262 Ok(Arc::new(move |_, _| Ok(from_toml_value(&value).unwrap())))
263}
264
265pub fn toml_reset_registers<B: BV>(toml: &Value, symtab: &Symtab) -> Result<HashMap<Loc<Name>, Reset<B>>, String> {
266 if let Some(defaults) = toml.as_table() {
267 defaults
268 .into_iter()
269 .map(|(register, value)| {
270 let lexer = Lexer::new(®ister);
271 if let Ok(loc) = LocParser::new().parse::<B, _, _>(lexer) {
272 if let Some(loc) = symtab.get_loc(&loc) {
273 Ok((loc, reset_to_toml_value(value)?))
274 } else {
275 Err(format!("Could not find register {} when parsing register reset information", register))
276 }
277 } else {
278 Err(format!("Could not parse register {} when parsing register reset information", register))
279 }
280 })
281 .collect()
282 } else {
283 Err("registers.reset should be a table of <register> = <value> pairs".to_string())
284 }
285}
286
287fn get_reset_registers<B: BV>(config: &Value, symtab: &Symtab) -> Result<HashMap<Loc<Name>, Reset<B>>, String> {
288 let defaults =
289 config.get("registers").and_then(|registers| registers.as_table()).and_then(|registers| registers.get("reset"));
290
291 if let Some(defaults) = defaults {
292 toml_reset_registers(defaults, symtab)
293 } else {
294 Ok(HashMap::new())
295 }
296}
297
298fn get_register_renames(config: &Value, symtab: &Symtab) -> Result<HashMap<String, Name>, String> {
299 let defaults = config
300 .get("registers")
301 .and_then(|registers| registers.as_table())
302 .and_then(|registers| registers.get("renames"));
303
304 if let Some(defaults) = defaults {
305 if let Some(defaults) = defaults.as_table() {
306 defaults
307 .into_iter()
308 .map(|(name, register)| {
309 if let Some(register) = register.as_str().and_then(|r| symtab.get(&zencode::encode(r))) {
310 Ok((name.to_string(), register))
311 } else {
312 Err(format!(
313 "Could not find register {} when parsing registers.renames in configuration",
314 register
315 ))
316 }
317 })
318 .collect()
319 } else {
320 Err("registers.names should be a table or <name> = <register> pairs".to_string())
321 }
322 } else {
323 Ok(HashMap::new())
324 }
325}
326
327fn get_ignored_registers(config: &Value, symtab: &Symtab) -> Result<HashSet<Name>, String> {
328 let ignored = config
329 .get("registers")
330 .and_then(|registers| registers.as_table())
331 .and_then(|registers| registers.get("ignore"));
332
333 if let Some(ignored) = ignored {
334 if let Some(ignored) = ignored.as_array() {
335 ignored
336 .iter()
337 .map(|register| {
338 if let Some(register) = register.as_str().and_then(|r| symtab.get(&zencode::encode(r))) {
339 Ok(register)
340 } else {
341 Err(format!(
342 "Could not find register {} when parsing registers.ignore in configuration",
343 register
344 ))
345 }
346 })
347 .collect()
348 } else {
349 Err("registers.ignore should be a list of register names".to_string())
350 }
351 } else {
352 Ok(HashSet::new())
353 }
354}
355
356fn get_barriers(config: &Value, symtab: &Symtab) -> Result<HashMap<Name, String>, String> {
357 if let Some(value) = config.get("barriers") {
358 if let Some(table) = value.as_table() {
359 let mut barriers = HashMap::new();
360 for (bk, name) in table.iter() {
361 let bk = match symtab.get(&zencode::encode(bk)) {
362 Some(bk) => bk,
363 None => return Err(format!("barrier_kind {} could not be found in the architecture", bk)),
364 };
365 let name = match name.as_str() {
366 Some(name) => name,
367 None => return Err(format!("{} must be a string", name)),
368 };
369 barriers.insert(bk, name.to_string());
370 }
371 Ok(barriers)
372 } else {
373 Err("[barriers] Must define a table of barrier_kind = name pairs".to_string())
374 }
375 } else {
376 Ok(HashMap::new())
377 }
378}
379
380pub struct ISAConfig<B> {
381 pub pc: Name,
383 pub ifetch_read_kind: Name,
385 pub read_exclusives: Vec<Name>,
387 pub write_exclusives: Vec<Name>,
389 pub event_sets: HashMap<String, Vec<Kind<Name>>>,
391 pub assembler: Tool,
393 pub objdump: Tool,
395 pub linker: Tool,
397 pub barriers: HashMap<Name, String>,
400 pub page_table_base: u64,
402 pub page_size: u64,
404 pub s2_page_table_base: u64,
406 pub s2_page_size: u64,
408 pub thread_base: u64,
410 pub thread_top: u64,
412 pub thread_stride: u64,
414 pub symbolic_addr_base: u64,
416 pub symbolic_addr_stride: u64,
418 pub default_registers: HashMap<Name, Val<B>>,
420 pub reset_registers: HashMap<Loc<Name>, Reset<B>>,
422 pub register_renames: HashMap<String, Name>,
424 pub ignored_registers: HashSet<Name>,
426 pub probes: HashSet<Name>,
428}
429
430impl<B: BV> ISAConfig<B> {
431 pub fn parse(contents: &str, symtab: &Symtab) -> Result<Self, String> {
432 let config = match contents.parse::<Value>() {
433 Ok(config) => config,
434 Err(e) => return Err(format!("Error when parsing configuration: {}", e)),
435 };
436
437 Ok(ISAConfig {
438 pc: get_program_counter(&config, symtab)?,
439 ifetch_read_kind: get_ifetch_read_kind(&config, symtab)?,
440 read_exclusives: get_exclusives(&config, "read_exclusives", symtab)?,
441 write_exclusives: get_exclusives(&config, "write_exclusives", symtab)?,
442 event_sets: get_event_sets(&config, symtab)?,
443 assembler: get_tool_path(&config, "assembler")?,
444 objdump: get_tool_path(&config, "objdump")?,
445 linker: get_tool_path(&config, "linker")?,
446 barriers: get_barriers(&config, symtab)?,
447 page_table_base: get_table_value(&config, "mmu", "page_table_base")?,
448 page_size: get_table_value(&config, "mmu", "page_size")?,
449 s2_page_table_base: get_table_value(&config, "mmu", "s2_page_table_base")?,
450 s2_page_size: get_table_value(&config, "mmu", "s2_page_size")?,
451 thread_base: get_table_value(&config, "threads", "base")?,
452 thread_top: get_table_value(&config, "threads", "top")?,
453 thread_stride: get_table_value(&config, "threads", "stride")?,
454 symbolic_addr_base: get_table_value(&config, "symbolic_addrs", "base")?,
455 symbolic_addr_stride: get_table_value(&config, "symbolic_addrs", "stride")?,
456 default_registers: get_default_registers(&config, symtab)?,
457 reset_registers: get_reset_registers(&config, symtab)?,
458 register_renames: get_register_renames(&config, symtab)?,
459 ignored_registers: get_ignored_registers(&config, symtab)?,
460 probes: HashSet::new(),
461 })
462 }
463
464 pub fn new(symtab: &Symtab) -> Result<Self, String> {
466 Self::parse(include_str!("../default_config.toml"), symtab)
467 }
468
469 pub fn from_file<P>(hasher: &mut Sha256, path: P, symtab: &Symtab) -> Result<Self, String>
471 where
472 P: AsRef<Path>,
473 {
474 let mut contents = String::new();
475 match File::open(&path) {
476 Ok(mut handle) => match handle.read_to_string(&mut contents) {
477 Ok(_) => (),
478 Err(e) => return Err(format!("Unexpected failure while reading config: {}", e)),
479 },
480 Err(e) => return Err(format!("Error when loading config '{}': {}", path.as_ref().display(), e)),
481 };
482 hasher.input(&contents);
483 Self::parse(&contents, symtab)
484 }
485}