main.rs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. /*
  2. * This is a small program that is meant for testing the AML parser on artificial
  3. * AML. We want to:
  4. * - scan a directory for ASL files
  5. * - compile them using `iasl` into AML files (these should be gitignored), but only if the ASL file has a
  6. * newer timestamp than the AML file (or just compile if there isn't a corresponding AML file)
  7. * - Run the AML parser on each AML file, printing test output like `cargo test` does in a nice table for
  8. * each AML file
  9. * - For failing tests, print out a nice summary of the errors for each file
  10. */
  11. use aml::{AmlContext, DebugVerbosity};
  12. use clap::{Arg, ArgAction};
  13. use std::{
  14. ffi::OsStr,
  15. fs::{self, File},
  16. io::{Read, Write},
  17. ops::Not,
  18. path::Path,
  19. process::Command,
  20. };
  21. fn main() -> std::io::Result<()> {
  22. log::set_logger(&Logger).unwrap();
  23. log::set_max_level(log::LevelFilter::Trace);
  24. let matches = clap::Command::new("aml_tester")
  25. .version("v0.1.0")
  26. .author("Isaac Woods")
  27. .about("Compiles and tests ASL files")
  28. .arg(Arg::new("path").short('p').long("path").required(true).action(ArgAction::Set).value_name("DIR"))
  29. .arg(Arg::new("no_compile").long("no-compile").action(ArgAction::SetTrue))
  30. .get_matches();
  31. let dir_path = Path::new(matches.get_one::<String>("path").unwrap());
  32. println!("Running tests in directory: {:?}", dir_path);
  33. if !matches.get_flag("no_compile") {
  34. let (passed, failed) = compile_asl_files(dir_path)?;
  35. println!("Compiled {} ASL files: {} passed, {} failed.", passed + failed, passed, failed);
  36. }
  37. /*
  38. * Now, we find all the AML files in the directory, and try to compile them with the `aml`
  39. * parser.
  40. */
  41. let aml_files = fs::read_dir(dir_path)?
  42. .filter(|entry| entry.is_ok() && entry.as_ref().unwrap().path().extension() == Some(OsStr::new("aml")))
  43. .map(Result::unwrap);
  44. let (passed, failed) = aml_files.fold((0, 0), |(passed, failed), file_entry| {
  45. print!("Testing AML file: {:?}... ", file_entry.path());
  46. std::io::stdout().flush().unwrap();
  47. let mut file = File::open(file_entry.path()).unwrap();
  48. let mut contents = Vec::new();
  49. file.read_to_end(&mut contents).unwrap();
  50. const AML_TABLE_HEADER_LENGTH: usize = 36;
  51. let mut context = AmlContext::new(Box::new(Handler), DebugVerbosity::None);
  52. match context.parse_table(&contents[AML_TABLE_HEADER_LENGTH..]) {
  53. Ok(()) => {
  54. println!("{}OK{}", termion::color::Fg(termion::color::Green), termion::style::Reset);
  55. println!("Namespace: {:#?}", context.namespace);
  56. (passed + 1, failed)
  57. }
  58. Err(err) => {
  59. println!("{}Failed ({:?}){}", termion::color::Fg(termion::color::Red), err, termion::style::Reset);
  60. println!("Namespace: {:#?}", context.namespace);
  61. (passed, failed + 1)
  62. }
  63. }
  64. });
  65. println!("Test results: {} passed, {} failed", passed, failed);
  66. Ok(())
  67. }
  68. fn compile_asl_files(dir_path: &Path) -> std::io::Result<(u32, u32)> {
  69. let mut asl_files = fs::read_dir(dir_path)?
  70. .filter(|entry| entry.is_ok() && entry.as_ref().unwrap().path().extension() == Some(OsStr::new("asl")))
  71. .map(Result::unwrap)
  72. .peekable();
  73. if !asl_files.peek().is_none() {
  74. // Test if `iasl` is installed, so we can give a good error if it's not
  75. match Command::new("iasl").arg("-v").status() {
  76. Ok(exit_status) => if exit_status.success().not() {
  77. panic!("`iasl` exited with unsuccessfull status: {:?}", exit_status);
  78. },
  79. Err(_) => panic!("`iasl` is not installed, but we want to compile some ASL files! Pass --no-compile, or install `iasl`"),
  80. }
  81. }
  82. let mut passed = 0;
  83. let mut failed = 0;
  84. for file in asl_files {
  85. let aml_path = file.path().with_extension(OsStr::new("aml"));
  86. /*
  87. * Check if an AML path with a matching last-modified date exists. If it
  88. * does, we don't need to compile the ASL file again.
  89. */
  90. if aml_path.is_file() {
  91. let asl_last_modified = file.metadata()?.modified()?;
  92. let aml_last_modified = aml_path.metadata()?.modified()?;
  93. if asl_last_modified <= aml_last_modified {
  94. continue;
  95. }
  96. }
  97. // Compile the ASL file using `iasl`
  98. println!("Compiling file: {}", file.path().to_str().unwrap());
  99. let output = Command::new("iasl").arg(file.path()).output()?;
  100. if output.status.success() {
  101. passed += 1;
  102. } else {
  103. failed += 1;
  104. println!(
  105. "Failed to compile ASL file: {}. Output from iasl:\n {}",
  106. file.path().to_str().unwrap(),
  107. String::from_utf8_lossy(&output.stderr)
  108. );
  109. }
  110. }
  111. Ok((passed, failed))
  112. }
  113. struct Logger;
  114. impl log::Log for Logger {
  115. fn enabled(&self, _: &log::Metadata) -> bool {
  116. true
  117. }
  118. fn log(&self, record: &log::Record) {
  119. println!("[{}] {}", record.level(), record.args());
  120. }
  121. fn flush(&self) {
  122. std::io::stdout().flush().unwrap();
  123. }
  124. }
  125. struct Handler;
  126. impl aml::Handler for Handler {
  127. fn read_u8(&self, address: usize) -> u8 {
  128. println!("read_u8 {address:#x}");
  129. 0
  130. }
  131. fn read_u16(&self, address: usize) -> u16 {
  132. println!("read_u16 {address:#x}");
  133. 0
  134. }
  135. fn read_u32(&self, address: usize) -> u32 {
  136. println!("read_u32 {address:#x}");
  137. 0
  138. }
  139. fn read_u64(&self, address: usize) -> u64 {
  140. println!("read_u64 {address:#x}");
  141. 0
  142. }
  143. fn write_u8(&mut self, address: usize, value: u8) {
  144. println!("write_u8 {address:#x}<-{value:#x}");
  145. }
  146. fn write_u16(&mut self, address: usize, value: u16) {
  147. println!("write_u16 {address:#x}<-{value:#x}");
  148. }
  149. fn write_u32(&mut self, address: usize, value: u32) {
  150. println!("write_u32 {address:#x}<-{value:#x}");
  151. }
  152. fn write_u64(&mut self, address: usize, value: u64) {
  153. println!("write_u64 {address:#x}<-{value:#x}");
  154. }
  155. fn read_io_u8(&self, port: u16) -> u8 {
  156. println!("read_io_u8 {port:#x}");
  157. 0
  158. }
  159. fn read_io_u16(&self, port: u16) -> u16 {
  160. println!("read_io_u16 {port:#x}");
  161. 0
  162. }
  163. fn read_io_u32(&self, port: u16) -> u32 {
  164. println!("read_io_u32 {port:#x}");
  165. 0
  166. }
  167. fn write_io_u8(&self, port: u16, value: u8) {
  168. println!("write_io_u8 {port:#x}<-{value:#x}");
  169. }
  170. fn write_io_u16(&self, port: u16, value: u16) {
  171. println!("write_io_u16 {port:#x}<-{value:#x}");
  172. }
  173. fn write_io_u32(&self, port: u16, value: u32) {
  174. println!("write_io_u32 {port:#x}<-{value:#x}");
  175. }
  176. fn read_pci_u8(&self, segment: u16, bus: u8, device: u8, function: u8, _offset: u16) -> u8 {
  177. println!("read_pci_u8 ({segment:#x}, {bus:#x}, {device:#x}, {function:#x})");
  178. 0
  179. }
  180. fn read_pci_u16(&self, segment: u16, bus: u8, device: u8, function: u8, _offset: u16) -> u16 {
  181. println!("read_pci_u16 ({segment:#x}, {bus:#x}, {device:#x}, {function:#x})");
  182. 0
  183. }
  184. fn read_pci_u32(&self, segment: u16, bus: u8, device: u8, function: u8, _offset: u16) -> u32 {
  185. println!("read_pci_32 ({segment:#x}, {bus:#x}, {device:#x}, {function:#x})");
  186. 0
  187. }
  188. fn write_pci_u8(&self, segment: u16, bus: u8, device: u8, function: u8, _offset: u16, value: u8) {
  189. println!("write_pci_u8 ({segment:#x}, {bus:#x}, {device:#x}, {function:#x})<-{value:#x}");
  190. }
  191. fn write_pci_u16(&self, segment: u16, bus: u8, device: u8, function: u8, _offset: u16, value: u16) {
  192. println!("write_pci_u16 ({segment:#x}, {bus:#x}, {device:#x}, {function:#x})<-{value:#x}");
  193. }
  194. fn write_pci_u32(&self, segment: u16, bus: u8, device: u8, function: u8, _offset: u16, value: u32) {
  195. println!("write_pci_u32 ({segment:#x}, {bus:#x}, {device:#x}, {function:#x})<-{value:#x}");
  196. }
  197. }