alarm-tegra-x1 / src/config.rs

raw file 9552 bytes
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<String>,
}

#[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<String>,
}

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<BuildConfig> {
    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;