4
0

prototyper.rs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. use std::{
  2. env, fs,
  3. path::PathBuf,
  4. process::{Command, ExitStatus},
  5. };
  6. use crate::utils::{CmdOptional, cargo};
  7. use clap::Args;
  8. #[derive(Debug, Args, Clone)]
  9. pub struct PrototyperArg {
  10. #[clap(long, short = 'f')]
  11. pub features: Vec<String>,
  12. #[clap(long, env = "PROTOTYPER_FDT_PATH")]
  13. pub fdt: Option<String>,
  14. #[clap(long, env = "PROTOTYPER_PAYLOAD_PATH")]
  15. pub payload: Option<String>,
  16. #[clap(long)]
  17. pub jump: bool,
  18. #[clap(long, short = 'c')]
  19. pub config_file: Option<PathBuf>,
  20. }
  21. const ARCH: &str = "riscv64imac-unknown-none-elf";
  22. const PACKAGE_NAME: &str = "rustsbi-prototyper";
  23. #[must_use]
  24. pub fn run(arg: &PrototyperArg) -> Option<ExitStatus> {
  25. let dirs = prepare_directories()?;
  26. setup_config_file(&dirs.target_config_toml, arg)?;
  27. let exit_status = build_prototyper(arg)?;
  28. if !exit_status.success() {
  29. error!(
  30. "Failed to execute rust-objcopy. Please ensure that cargo-binutils is installed and available in your system's PATH."
  31. );
  32. return Some(exit_status);
  33. }
  34. copy_output_files(&dirs.target_dir, arg)?;
  35. Some(exit_status)
  36. }
  37. struct Directories {
  38. target_dir: PathBuf,
  39. target_config_toml: PathBuf,
  40. }
  41. fn prepare_directories() -> Option<Directories> {
  42. let current_dir = env::current_dir().ok()?;
  43. let raw_target_dir = current_dir.join("target");
  44. let target_dir = raw_target_dir.join(ARCH).join("release");
  45. let target_config_toml = raw_target_dir.join("config.toml");
  46. Some(Directories {
  47. target_dir,
  48. target_config_toml,
  49. })
  50. }
  51. fn setup_config_file(target_config_toml: &PathBuf, arg: &PrototyperArg) -> Option<()> {
  52. // Delete old config if exists
  53. if fs::exists(target_config_toml).ok()? {
  54. info!("Delete old config");
  55. fs::remove_file(target_config_toml).ok()?;
  56. }
  57. // Determine config file path
  58. let current_dir = env::current_dir().ok()?;
  59. let default_config_file = current_dir
  60. .join("prototyper")
  61. .join("prototyper")
  62. .join("config")
  63. .join("default.toml");
  64. let config_file = arg.config_file.clone().unwrap_or(default_config_file);
  65. // Copy config
  66. info!("Copy config from: {}", config_file.display());
  67. fs::copy(&config_file, target_config_toml).ok()?;
  68. Some(())
  69. }
  70. fn build_prototyper(arg: &PrototyperArg) -> Option<ExitStatus> {
  71. info!("Building Prototyper");
  72. // Build the prototyper
  73. let status = cargo::Cargo::new("build")
  74. .package(PACKAGE_NAME)
  75. .target(ARCH)
  76. .unstable("build-std", ["core", "alloc"])
  77. .env("RUSTFLAGS", "-C relocation-model=pie -C link-arg=-pie")
  78. .features(&arg.features)
  79. .optional(arg.fdt.is_some(), |cargo| {
  80. cargo.env("PROTOTYPER_FDT_PATH", arg.fdt.as_ref().unwrap());
  81. cargo.features(["fdt".to_string()])
  82. })
  83. .optional(arg.payload.is_some(), |cargo| {
  84. cargo.env("PROTOTYPER_PAYLOAD_PATH", arg.payload.as_ref().unwrap());
  85. cargo.features(["payload".to_string()])
  86. })
  87. .optional(arg.jump, |cargo| cargo.features(["jump".to_string()]))
  88. .release()
  89. .status()
  90. .ok()?;
  91. if !status.success() {
  92. error!(
  93. "Failed to build prototyper. Please check the cargo output above for detailed error information."
  94. );
  95. return Some(status);
  96. }
  97. // Get target directory once instead of recreating it
  98. let target_dir = prepare_directories()?.target_dir;
  99. let elf_path = target_dir.join(PACKAGE_NAME);
  100. let bin_path = target_dir.join(format!("{}.bin", PACKAGE_NAME));
  101. // Create binary from ELF
  102. info!("Converting ELF to binary with rust-objcopy");
  103. let result = Command::new("rust-objcopy")
  104. .args([
  105. "-O",
  106. "binary",
  107. "--binary-architecture=riscv64",
  108. &elf_path.to_string_lossy(),
  109. &bin_path.to_string_lossy(),
  110. ])
  111. .status()
  112. .ok();
  113. if result.is_none() {
  114. error!(
  115. "Failed to execute rust-objcopy. Command not found or failed to start.\n\
  116. Source: {}\n\
  117. Destination: {}\n\
  118. Please install cargo-binutils with cmd: cargo install cargo-binutils",
  119. elf_path.display(),
  120. bin_path.display()
  121. );
  122. }
  123. result
  124. }
  125. fn copy_output_files(target_dir: &PathBuf, arg: &PrototyperArg) -> Option<()> {
  126. let mode_suffix = if arg.payload.is_some() {
  127. info!("Copy for payload mode");
  128. "payload"
  129. } else if arg.jump {
  130. info!("Copy for jump mode");
  131. "jump"
  132. } else {
  133. info!("Copy for dynamic mode");
  134. "dynamic"
  135. };
  136. // Copy ELF file
  137. let elf_source = target_dir.join(PACKAGE_NAME);
  138. let elf_dest = target_dir.join(format!("{}-{}.elf", PACKAGE_NAME, mode_suffix));
  139. info!(
  140. "Copying ELF file: {} -> {}",
  141. elf_source.display(),
  142. elf_dest.display()
  143. );
  144. fs::copy(&elf_source, &elf_dest).ok()?;
  145. // Copy binary file
  146. let bin_source = target_dir.join(format!("{}.bin", PACKAGE_NAME));
  147. let bin_dest = target_dir.join(format!("{}-{}.bin", PACKAGE_NAME, mode_suffix));
  148. info!(
  149. "Copying binary file: {} -> {}",
  150. bin_source.display(),
  151. bin_dest.display()
  152. );
  153. fs::copy(&bin_source, &bin_dest).ok()?;
  154. Some(())
  155. }