use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::path::Path; /// Tegra X1 (T210) hardware specifications #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TegraX1Config { /// SoC variant pub soc_variant: String, /// Process node pub process: String, /// Big cores (Cortex-A57) pub big_cores: CoreConfig, /// Little cores (Cortex-A53) pub little_cores: CoreConfig, /// GPU config pub gpu: GpuConfig, /// Memory config pub memory: MemoryConfig, /// Bootloader config pub bootloader: BootloaderConfig, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CoreConfig { pub architecture: String, pub count: u32, pub frequency_mhz: u32, /// ARM extensions supported pub extensions: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GpuConfig { pub model: String, pub arch: String, pub cores: u32, pub frequency_mhz: u32, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MemoryConfig { pub type_name: String, pub size_gb: u32, pub bus_width: u32, pub bandwidth_gbps: f32, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BootloaderConfig { /// Hekate version pub hekate_version: String, /// Boot method (payload, hekate, direct) pub boot_method: String, /// Use BDK (Boot Development Kit) for payload pub use_bdk: bool, /// Custom payload path (if any) pub custom_payload: Option, } impl TegraX1Config { /// ODNX02-A2 variant configuration (Nintendo Switch HAC-001) pub fn odnx02_a2() -> Self { Self { soc_variant: "ODNX02-A2".to_string(), process: "TSMC 20nm".to_string(), big_cores: CoreConfig { architecture: "armv8-a".to_string(), count: 4, frequency_mhz: 2200, extensions: vec![ "lse".to_string(), // Large System Extensions "fp16".to_string(), // Half-precision FP "crc32".to_string(), // CRC32 instruction ], }, little_cores: CoreConfig { architecture: "armv8-a".to_string(), count: 4, frequency_mhz: 1300, extensions: vec![ // A53 has fewer extensions; target base for compatibility ], }, gpu: GpuConfig { model: "GM20B".to_string(), arch: "Maxwell".to_string(), cores: 256, frequency_mhz: 1000, }, memory: MemoryConfig { type_name: "LPDDR4".to_string(), size_gb: 8, bus_width: 64, bandwidth_gbps: 25.6, }, bootloader: BootloaderConfig { hekate_version: "6.5.4".to_string(), boot_method: "payload".to_string(), use_bdk: true, custom_payload: None, }, } } } /// Build configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BuildConfig { pub tegra: TegraX1Config, /// Image / partition layout settings #[serde(default)] pub image: ImageConfig, pub cross_compiler: String, pub kernel_version: String, pub target_arch: String, /// Compiler flags optimized for Cortex-A57 (big cores) pub cflags: String, /// Rust target triple pub rust_target: String, /// Output directory for hekate payload pub payload_output: String, } /// Partition layout for the bootable SD-card image. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ImageConfig { /// Filesystem label used by the L4T initramfs to locate the rootfs. /// Must match `id=` in the hekate L4T entry. pub label: String, /// Size of the FAT32 boot partition (holds `bootloader/` + `switchroot/`). pub boot_size_mib: u32, /// Default total image size in GiB. pub size_gb: u32, } impl Default for ImageConfig { fn default() -> Self { Self { label: "ALARM".to_string(), boot_size_mib: 512, size_gb: 8, } } } impl BuildConfig { pub fn default_for_odnx02_a2() -> Self { let tegra = TegraX1Config::odnx02_a2(); // Cortex-A57 compiler flags: // -march=armv8-a+lse+fp16+crc: Use extensions available on A57 // -mtune=cortex-a57: Optimize scheduling for A57 pipeline // -mcpu=cortex-a57: Full CPU feature set let cflags = [ "-march=armv8-a+lse+fp16+crc", "-mtune=cortex-a57", "-mcpu=cortex-a57", "-O2", "-pipe", "-fstack-protector-strong", "-fno-plt", "-fPIC", ].join(" "); Self { tegra, image: ImageConfig::default(), cross_compiler: "aarch64-linux-gnu".to_string(), kernel_version: "7.2".to_string(), target_arch: "arm64".to_string(), cflags, rust_target: "aarch64-unknown-linux-gnu".to_string(), payload_output: "./output".to_string(), } } } /// Initialize build environment pub fn init_build_env(build_dir: &Path) -> Result<()> { let config_dir = build_dir.join("config"); std::fs::create_dir_all(&config_dir) .context("Failed to create config directory")?; // Write default config let config = BuildConfig::default_for_odnx02_a2(); let config_path = config_dir.join("build.toml"); let config_str = toml::to_string_pretty(&config) .context("Failed to serialize config")?; std::fs::write(&config_path, config_str) .context("Failed to write config")?; // Create directory structure let dirs = [ "kernel", "uboot", "rootfs", "packages", "device-tree", "payload", "scripts", "tmp", ]; for dir in &dirs { std::fs::create_dir_all(build_dir.join(dir)) .context(format!("Failed to create {} directory", dir))?; } println!("Build environment initialized at: {}", build_dir.display()); println!("Config: {}", config_path.display()); Ok(()) } /// Load configuration from build directory pub fn load_config(build_dir: &Path) -> Result { let config_path = build_dir.join("config/build.toml"); if !config_path.exists() { anyhow::bail!( "Config not found at {}. Run 'init' first.", config_path.display() ); } let config_str = std::fs::read_to_string(&config_path) .context("Failed to read config")?; let config: BuildConfig = toml::from_str(&config_str) .context("Failed to parse config")?; Ok(config) } /// Display current configuration pub fn show_config() -> Result<()> { let config = BuildConfig::default_for_odnx02_a2(); println!("\n{}", "=== Tegra X1 (ODNX02-A2) Build Configuration ===".cyan().bold()); println!("\n{}", "Hardware:".yellow().bold()); println!(" SoC Variant: {}", config.tegra.soc_variant); println!(" Process: {}", config.tegra.process); println!(" Big Cores: {}x {} @ {} MHz", config.tegra.big_cores.count, config.tegra.big_cores.architecture, config.tegra.big_cores.frequency_mhz); println!(" Little Cores: {}x {} @ {} MHz", config.tegra.little_cores.count, config.tegra.little_cores.architecture, config.tegra.little_cores.frequency_mhz); println!(" GPU: {} ({}) {} cores @ {} MHz", config.tegra.gpu.model, config.tegra.gpu.arch, config.tegra.gpu.cores, config.tegra.gpu.frequency_mhz); println!(" Memory: {} GB {} ({}-bit, {} GB/s)", config.tegra.memory.size_gb, config.tegra.memory.type_name, config.tegra.memory.bus_width, config.tegra.memory.bandwidth_gbps); println!("\n{}", "Bootloader:".yellow().bold()); println!(" Hekate: v{}", config.tegra.bootloader.hekate_version); println!(" Boot Method: {}", config.tegra.bootloader.boot_method); println!(" Use BDK: {}", config.tegra.bootloader.use_bdk); println!("\n{}", "Build Settings:".yellow().bold()); println!(" Cross Compiler: {}", config.cross_compiler); println!(" Target Arch: {}", config.target_arch); println!(" Rust Target: {}", config.rust_target); println!(" Kernel Version: {}", config.kernel_version); println!(" Payload Output: {}", config.payload_output); println!("\n{}", "Image Layout:".yellow().bold()); println!(" Root FS label: {}", config.image.label); println!(" Boot partition: {} MiB (FAT32)", config.image.boot_size_mib); println!(" Default size: {} GiB", config.image.size_gb); println!("\n{}", "Compiler Flags (for Cortex-A57):".yellow().bold()); println!(" {}", config.cflags); println!("\n{}", "ARM Extensions (A57):".yellow().bold()); for ext in &config.tegra.big_cores.extensions { println!(" +{}", ext); } println!("\n{}", "Note:".yellow().bold()); println!(" Default boot uses the switchroot L4T boot chain (known-good display)."); println!(" Use '--kernel built' to package the locally built kernel."); println!(" Cortex-A53 (LITTLE) supports base ARMv8-A only."); Ok(()) } use colored::Colorize;