os/bootloader/tests/bios_tests.rs

405 lines
15 KiB
Rust

//! BIOS Bootloader Tests
//!
//! This module contains unit and integration tests for BIOS bootloader functionality.
//! These tests validate the core BIOS bootloader features including memory detection,
//! VESA graphics, boot menu, and assembly entry points.
#[cfg(test)]
mod tests {
use nos_bootloader::memory::bios::{BiosMemoryScanner, BiosMemoryManager};
use nos_bootloader::graphics::vbe::{VbeController, VbeControllerInfo};
use nos_bootloader::boot_menu::{BootMenuConfig, BootMenuEntry};
use nos_bootloader::arch::x86_64::bios::{X86_64CpuInfo, get_cpu_info, is_virtual_machine};
use nos_bootloader::protocol::multiboot2::{Multiboot2Protocol, create_e820_entry};
// Test CPU detection functionality
#[test]
fn test_cpu_detection() {
println!("Testing CPU detection functionality...");
let mut cpu_info = X86_64CpuInfo::new();
// Test CPU detection (this will only work in actual BIOS environment)
let result = cpu_info.detect();
if result.is_ok() {
println!("CPU Vendor: {}", cpu_info.vendor_str());
println!("CPU Brand: {}", cpu_info.brand_str());
println!("CPU Family: {}", cpu_info.family);
println!("CPU Model: {}", cpu_info.model);
println!("CPU Stepping: {}", cpu_info.stepping);
println!("Long Mode Support: {}", cpu_info.supports_long_mode());
println!("SSE2 Support: {}", cpu_info.supports_sse2());
println!("AVX Support: {}", cpu_info.supports_avx());
// Verify that we detected a valid CPU
assert!(!cpu_info.vendor.is_empty(), "CPU vendor should not be empty");
assert_eq!(cpu_info.vendor_str(), cpu_info.vendor_str()); // Consistency check
} else {
println!("CPU detection failed (expected in non-BIOS environment)");
}
}
#[test]
fn test_cpu_info_functions() {
// Test CPU utility functions
let cpu_info = get_cpu_info();
println!("Current CPU Info: {:?}", cpu_info);
let vm_detected = is_virtual_machine();
println!("Running in virtual machine: {}", vm_detected);
// These should always work
assert!(!cpu_info.vendor.is_empty());
}
// Test memory detection functionality
#[test]
fn test_bios_memory_scanner() {
println!("Testing BIOS memory scanner...");
let mut scanner = BiosMemoryScanner::new();
// Test initial state
assert!(!scanner.is_initialized(), "Scanner should start uninitialized");
assert_eq!(scanner.get_total_memory(), 0, "Total memory should be 0 initially");
// In a real BIOS environment, this would detect memory
let init_result = scanner.initialize();
if init_result.is_ok() {
println!("Memory scanner initialized successfully");
println!("Base memory: {} KB", scanner.get_base_memory_kb());
println!("Extended memory: {} KB", scanner.get_extended_memory_kb());
println!("Total memory: {} KB", scanner.get_total_memory() / 1024);
assert!(scanner.is_initialized(), "Scanner should be initialized");
assert!(scanner.get_total_memory() > 0, "Should detect some memory");
// Test memory map creation
let memory_map_result = scanner.build_memory_map();
assert!(memory_map_result.is_ok(), "Should be able to build memory map");
let memory_map = memory_map_result.unwrap();
println!("Memory map entries: {}", memory_map.entries.len());
assert!(!memory_map.entries.is_empty(), "Memory map should have entries");
} else {
println!("Memory scanner initialization failed (expected in non-BIOS environment)");
}
}
#[test]
fn test_bios_memory_manager() {
println!("Testing BIOS memory manager...");
let mut manager = BiosMemoryManager::new();
// Test initial state
assert!(!manager.is_a20_enabled(), "A20 should not be enabled initially");
let init_result = manager.initialize();
if init_result.is_ok() {
println!("Memory manager initialized successfully");
println!("A20 enabled: {}", manager.is_a20_enabled());
// Test memory map access
let memory_map_result = manager.get_memory_map();
assert!(memory_map_result.is_ok(), "Should be able to get memory map");
let memory_map = memory_map_result.unwrap();
println!("Available memory: {} KB", memory_map.available_memory / 1024);
// Test memory allocation (simplified)
let region_result = manager.find_bootloader_region(4096, 4096);
if let Some(address) = region_result {
println!("Found suitable region at address: {:#X}", address);
}
} else {
println!("Memory manager initialization failed (expected in non-BIOS environment)");
}
}
// Test VESA graphics functionality
#[test]
fn test_vbe_controller() {
println!("Testing VBE controller...");
let mut controller = VbeController::new();
// Test initial state
assert!(!controller.is_initialized(), "VBE controller should start uninitialized");
let init_result = controller.initialize();
if init_result.is_ok() {
println!("VBE controller initialized successfully");
assert!(controller.is_initialized(), "VBE controller should be initialized");
// Test getting controller info
if let Some(info) = controller.get_controller_info() {
println!("VBE Version: {}.{}", (info.version >> 8) & 0xFF, info.version & 0xFF);
println!("Total Memory: {} KB", info.total_memory as u32 * 64);
// Test mode enumeration
let modes = controller.get_supported_modes();
println!("Supported VBE modes: {}", modes.len());
if !modes.is_empty() {
println!("First few VBE modes: {:?}", &modes[..modes.len().min(5)]);
}
// Test finding modes
let mode = controller.find_best_mode(1024, 768, 32);
if let Some(mode_num) = mode {
println!("Found suitable 1024x768x32 mode: 0x{:04X}", mode_num);
} else {
println!("No suitable 1024x768x32 mode found");
}
}
} else {
println!("VBE controller initialization failed (expected in non-BIOS environment)");
}
}
// Test Multiboot2 functionality
#[test]
fn test_multiboot2_protocol() {
println!("Testing Multiboot2 protocol...");
let mut protocol = Multiboot2Protocol::new();
// Test initial state
assert!(!protocol.is_initialized(), "Multiboot2 protocol should start uninitialized");
// Create a test buffer
let mut buffer = [0u8; 8192];
let init_result = protocol.initialize(&mut buffer[..], buffer.len());
if init_result.is_ok() {
println!("Multiboot2 protocol initialized successfully");
assert!(protocol.is_initialized(), "Multiboot2 protocol should be initialized");
// Test building Multiboot2 info structure
let e820_entries = vec![
create_e820_entry(0x00000000, 0x0009FC00, 1), // Available memory
create_e820_entry(0x0009FC00, 0x00000400, 2), // Reserved
create_e820_entry(0x00100000, 0x00F00000, 1), // Available memory
];
let info_size_result = protocol.build_info(
Some("quiet splash"),
640, // mem_lower in KB
32768, // mem_upper in KB
&e820_entries,
None,
&[],
);
if let Ok(info_size) = info_size_result {
println!("Multiboot2 info structure built successfully");
println!("Info structure size: {} bytes", info_size);
// Test header validation
let validation_result = protocol.validate_header(0x100000);
println!("Header validation result: {:?}", validation_result);
} else {
println!("Failed to build Multiboot2 info structure");
}
} else {
println!("Multiboot2 protocol initialization failed");
}
}
// Test boot menu functionality
#[test]
fn test_boot_menu_configuration() {
println!("Testing boot menu configuration...");
let mut config = BootMenuConfig::new();
// Test default configuration
assert_eq!(config.entries.len(), 0, "Should start with no entries");
assert_eq!(config.global_timeout, 5, "Default timeout should be 5");
assert!(config.show_menu, "Show menu should be true by default");
// Test validation with no entries
assert!(config.validate().is_err(), "Should fail validation with no entries");
// Add test entries
config.add_entry(BootMenuEntry::default_entry(
"NOS OS - Normal".to_string(),
"kernel.bin".to_string(),
"root=/dev/sda1".to_string(),
).with_timeout(5));
config.add_entry(BootMenuEntry::new(
"NOS OS - Recovery".to_string(),
"kernel.bin".to_string(),
"root=/dev/sda1 single".to_string(),
));
// Test validation with entries
assert!(config.validate().is_ok(), "Should pass validation with entries");
assert_eq!(config.entries.len(), 2, "Should have 2 entries");
assert_eq!(config.default_entry, 0, "First entry should be default");
assert!(config.entries[0].is_default, "First entry should be marked as default");
assert!(!config.entries[1].is_default, "Second entry should not be default");
// Test getting default entry
let default_entry = config.get_default_entry();
assert!(default_entry.is_some(), "Should have a default entry");
assert_eq!(default_entry.unwrap().name, "NOS OS - Normal", "Default entry name should match");
}
#[test]
fn test_boot_menu_entry_creation() {
println!("Testing boot menu entry creation...");
// Test basic entry creation
let entry = BootMenuEntry::new(
"Test OS".to_string(),
"test.bin".to_string(),
"test=1".to_string(),
);
assert_eq!(entry.name, "Test OS");
assert_eq!(entry.kernel_path, "test.bin");
assert_eq!(entry.cmdline, "test=1");
assert_eq!(entry.timeout, 0);
assert!(!entry.is_default);
// Test default entry creation
let default_entry = BootMenuEntry::default_entry(
"Default OS".to_string(),
"default.bin".to_string(),
"".to_string(),
);
assert_eq!(default_entry.name, "Default OS");
assert!(default_entry.is_default);
// Test entry with timeout
let timeout_entry = BootMenuEntry::new(
"Timeout OS".to_string(),
"timeout.bin".to_string(),
"".to_string(),
).with_timeout(10);
assert_eq!(timeout_entry.timeout, 10);
}
// Test E820 entry creation
#[test]
fn test_e820_entry_creation() {
println!("Testing E820 entry creation...");
let entry = create_e820_entry(0x100000, 0x100000, 1);
assert_eq!(entry.base_addr, 0x100000);
assert_eq!(entry.length, 0x100000);
assert_eq!(entry.type_, 1); // Available memory
assert_eq!(entry.zero, 0);
// Test different memory types
let reserved_entry = create_e820_entry(0x0, 0x1000, 2);
assert_eq!(reserved_entry.type_, 2); // Reserved
let acpi_entry = create_e820_entry(0x200000, 0x10000, 3);
assert_eq!(acpi_entry.type_, 3); // ACPI reclaimable
}
// Test compilation time constants
#[test]
fn test_build_time_constants() {
// These constants are defined in the build script
// In a real build, these would be available
#[cfg(target_arch = "x86_64")]
{
use nos_bootloader::arch::x86_64::consts;
// Test that constants are defined
assert_eq!(consts::PAGE_SIZE, 4096);
assert_eq!(consts::PAGE_SHIFT, 12);
assert_eq!(consts::PAGE_MASK, 4095);
assert_eq!(consts::VGA_BUFFER, 0xB8000);
assert_eq!(consts::VGA_WIDTH, 80);
assert_eq!(consts::VGA_HEIGHT, 25);
}
}
// Test architecture utilities
#[cfg(target_arch = "x86_64")]
#[test]
fn test_x86_64_utilities() {
use nos_bootloader::arch::x86_64::X86_64Utils;
// Test utility functions that are safe to call
let rflags = X86_64Utils::get_rflags();
println!("Current RFLAGS: {:#018X}", rflags);
let cr0 = X86_64Utils::read_cr0();
println!("Current CR0: {:#018X}", cr0);
let cr4 = X86_64Utils::read_cr4();
println!("Current CR4: {:#018X}", cr4);
// These should work regardless of environment
assert!(rflags != 0, "RFLAGS should not be zero");
}
// Integration test that combines multiple components
#[test]
fn test_bios_integration() {
println!("Testing BIOS bootloader integration...");
// Test that all components can be created together
let mut memory_scanner = BiosMemoryScanner::new();
let mut vbe_controller = VbeController::new();
let mut multiboot2_protocol = Multiboot2Protocol::new();
let mut boot_menu_config = BootMenuConfig::new();
// All should start uninitialized
assert!(!memory_scanner.is_initialized());
assert!(!vbe_controller.is_initialized());
assert!(!multiboot2_protocol.is_initialized());
assert_eq!(boot_menu_config.entries.len(), 0);
// Add a boot menu entry
boot_menu_config.add_entry(BootMenuEntry::default_entry(
"NOS OS".to_string(),
"kernel.bin".to_string(),
"root=/dev/sda1".to_string(),
));
assert_eq!(boot_menu_config.entries.len(), 1);
assert!(boot_menu_config.validate().is_ok());
println!("Integration test completed successfully");
}
}
// Test helper functions
#[cfg(test)]
mod test_helpers {
use super::*;
/// Helper function to test if we're running in a suitable environment
pub fn is_bios_environment() -> bool {
// This is a simplified check - in reality, this would need to
// detect if we're running in a BIOS environment
cfg!(target_arch = "x86_64") && !cfg!(target_os = "uefi")
}
/// Helper function to create test E820 entries
pub fn create_test_memory_map() -> Vec<nos_bootloader::protocol::multiboot2::Multiboot2MmapEntry> {
vec![
nos_bootloader::protocol::multiboot2::create_e820_entry(0x00000000, 0x0009FC00, 1),
nos_bootloader::protocol::multiboot2::create_e820_entry(0x0009FC00, 0x00000400, 2),
nos_bootloader::protocol::multiboot2::create_e820_entry(0x00100000, 0x00F00000, 1),
]
}
}