Skip to content

Feature_Addition

Zikithezikit edited this page Mar 2, 2026 · 2 revisions

Feature Addition Guide

This guide provides a comprehensive walkthrough for adding new features to the rust-kernel project. It covers the entire development process from planning to testing.

Table of Contents

  1. Planning
  2. Implementation
  3. Testing
  4. Documentation
  5. Code Review
  6. Common Feature Types

Planning

1. Understand the Scope

Before starting, consider:

  • What problem does this feature solve?
  • Is it aligned with project goals?
  • What are the performance implications?
  • Does it introduce security risks?
  • How complex is the implementation?

2. Check Existing Code

Look for similar implementations:

# Search for related code
grep -r "similar_feature" src/

# Check if similar functionality exists
rg "pattern" src/

3. Design the API

Plan your public interfaces:

  • Function signatures - What parameters and return types?
  • Error handling - What can go wrong?
  • Thread safety - Does it need synchronization?
  • Resource management - How are resources cleaned up?

4. Choose the Right Location

Decide where to place your code:

  • New module - If it's a standalone feature
  • Existing module - If it's an extension
  • Architecture-specific - If it's CPU-dependent
  • Driver - If it's hardware-related

Implementation

1. Create the Module Structure

New Module

# Create module directory
mkdir -p src/implementation/kernel/new_feature

# Create main module file
touch src/implementation/kernel/new_feature/mod.rs

# Create implementation files
touch src/implementation/kernel/new_feature/lib.rs

Existing Module

Add to existing module structure:

// src/implementation/kernel/new_feature/mod.rs
pub mod lib;
pub mod utils;

2. Implement Core Functionality

Basic Structure

// src/implementation/kernel/new_feature/lib.rs

//! New feature implementation

use crate::error::{KernelError, KernelResult};

/// Main feature struct
pub struct NewFeature {
    // Internal state
}

impl NewFeature {
    /// Creates a new instance
    pub fn new() -> Self {
        Self {
            // Initialize state
        }
    }
    
    /// Main feature operation
    pub fn perform_operation(&self, input: Input) -> KernelResult<Output> {
        // Implementation
        Ok(output)
    }
}

/// Error type for this feature
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NewFeatureError {
    InvalidInput,
    OperationFailed,
    ResourceUnavailable,
}

impl From<NewFeatureError> for KernelError {
    fn from(err: NewFeatureError) -> Self {
        match err {
            NewFeatureError::InvalidInput => KernelError::InvalidArgument,
            NewFeatureError::OperationFailed => KernelError::Unknown,
            NewFeatureError::ResourceUnavailable => KernelError::OutOfMemory,
        }
    }
}

3. Add Error Handling

Unified Error Handling

use crate::error::KernelError;

impl NewFeature {
    pub fn perform_operation(&self, input: Input) -> KernelResult<Output> {
        if input.is_invalid() {
            return Err(KernelError::InvalidArgument);
        }
        
        // Operation that might fail
        let result = some_operation()?;
        
        Ok(result)
    }
}

Custom Error Types

If needed, create custom error types that map to KernelError:

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NewFeatureError {
    InvalidInput,
    OperationFailed,
}

impl From<NewFeatureError> for KernelError {
    fn from(err: NewFeatureError) -> Self {
        match err {
            NewFeatureError::InvalidInput => KernelError::InvalidArgument,
            NewFeatureError::OperationFailed => KernelError::Unknown,
        }
    }
}

4. Implement Thread Safety

Using Spin Locks

use spin::Mutex;

static FEATURE_INSTANCE: Mutex<Option<NewFeature>> = Mutex::new(None);

pub fn get_instance() -> &'static NewFeature {
    let mut instance = FEATURE_INSTANCE.lock();
    if instance.is_none() {
        *instance = Some(NewFeature::new());
    }
    instance.as_ref().unwrap()
}

Using Once

For one-time initialization:

use spin::Once;

static INIT: Once<KernelResult<NewFeature>> = Once::new();

pub fn get_instance() -> KernelResult<&'static NewFeature> {
    INIT.call_once(|| {
        Ok(NewFeature::new())
    })
}

5. Add Initialization

Module Initialization

// src/implementation/kernel/new_feature/init.rs

use crate::error::KernelResult;

pub fn init() -> KernelResult<()> {
    // Initialize feature
    let feature = NewFeature::new();
    
    // Store in global state if needed
    let mut kernel = crate::kernel::kernel();
    kernel.set_new_feature(feature);
    
    Ok(())
}

Integration with Main Kernel

Add to main initialization:

// src/implementation/kernel/main.rs
mod new_feature;

// In init_submodules()
halt_on_err(new_feature::init(), "New feature init failed");

Testing

1. Create Test Module

// src/implementation/kernel/new_feature/tests.rs

use crate::error::KernelResult;

use super::NewFeature;

#[test_case]
fn test_new_feature_creation() -> KernelResult<()> {
    let feature = NewFeature::new();
    assert!(feature.is_valid());
    Ok(())
}

#[test_case]
fn test_feature_operation() -> KernelResult<()> {
    let feature = NewFeature::new();
    let input = Input::valid();
    let result = feature.perform_operation(input)?;
    assert_eq!(result, expected_output);
    Ok(())
}

#[test_case]
fn test_error_handling() -> KernelResult<()> {
    let feature = NewFeature::new();
    let input = Input::invalid();
    
    match feature.perform_operation(input) {
        Err(KernelError::InvalidArgument) -> Ok(()),
        _ -> Err(KernelError::Unknown),
    }
}

2. Add to Test Runner

// src/implementation/kernel/tests/mod.rs
mod new_feature;

pub fn run_tests() -> KernelResult<()> {
    new_feature::run_tests()?;
    Ok(())
}

3. Test Edge Cases

Test boundary conditions:

#[test_case]
fn test_edge_cases() -> KernelResult<()> {
    let feature = NewFeature::new();
    
    // Test with minimum valid input
    let min_input = Input::min();
    feature.perform_operation(min_input)?;
    
    // Test with maximum valid input
    let max_input = Input::max();
    feature.perform_operation(max_input)?;
    
    // Test with empty input
    let empty_input = Input::empty();
    feature.perform_operation(empty_input)?;
    
    Ok(())
}

Documentation

1. Module Documentation

//! New Feature Module
//!
//! This module provides functionality for [feature description].
//! It is used for [use cases] and integrates with [related systems].
//!
//! ## Features
//! - [Feature 1 description]
//! - [Feature 2 description]
//! - [Feature 3 description]

2. Public API Documentation

/// Main feature struct
///
/// This struct provides the core functionality for the new feature.
/// It manages [what it manages] and provides [what it provides].
///
/// # Examples
///
/// ```
/// let feature = NewFeature::new();
/// let result = feature.perform_operation(input);
/// ```
pub struct NewFeature {
    // Fields
}

impl NewFeature {
    /// Creates a new instance
    ///
    /// # Arguments
    ///
    /// * `param` - [parameter description]
    ///
    /// # Returns
    ///
    /// A new `NewFeature` instance
    ///
    /// # Errors
    ///
    /// Returns `KernelError::InvalidState` if [condition]
    pub fn new() -> Self {
        Self {
            // Implementation
        }
    }
    
    /// Performs the main operation
    ///
    /// This is the primary function that users will call.
    /// It [what it does] and returns [what it returns].
    ///
    /// # Arguments
    ///
    /// * `input` - [input description]
    ///
    /// # Returns
    ///
    /// The result of the operation
    ///
    /// # Errors
    ///
    /// Returns `KernelError::InvalidArgument` if [condition]
    /// Returns `KernelError::OperationFailed` if [condition]
    pub fn perform_operation(&self, input: Input) -> KernelResult<Output> {
        // Implementation
        Ok(output)
    }
}

3. Inline Documentation

// Good: Clear explanation of complex logic
// SAFETY: We can dereference this pointer because it was allocated by our allocator
// and we have exclusive access to it during this operation.
let ptr = unsafe { &mut *allocated_ptr };

Code Review

Before Submitting

  1. Build passes - make build should succeed
  2. Tests pass - make run-tests should pass
  3. No warnings - Fix all compiler warnings
  4. Documentation complete - All public APIs documented
  5. Error handling complete - All error cases handled
  6. Thread safety verified - All shared state protected

Common Review Issues

Memory Safety

  • Unsafe blocks - Are they necessary and documented?
  • Pointer usage - Are raw pointers properly managed?
  • Lifetime issues - Are references valid for their entire use?

Error Handling

  • Missing error cases - Are all failure modes handled?
  • Error propagation - Are errors properly propagated with ??
  • Error context - Do errors provide useful debugging information?

Common Feature Types

1. New System Call

Steps

  1. Add syscall number to syscall/numbers.rs
  2. Implement handler in syscall/handler.rs
  3. Add to syscall dispatch table
  4. Write tests

Example

// syscall/numbers.rs
#[repr(u32)]
pub enum SyscallNumber {
    // ... existing calls
    MySyscall = 60,
}

// syscall/handler.rs
pub fn handle_syscall(num: u32, args: &[usize; 6]) -> SyscallResult {
    match num {
        // ... existing handlers
        SyscallNumber::MySyscall as u32 => sys_my_syscall(args),
        _ -> Err(KernelError::InvalidSyscall),
    }
}

2. New Filesystem

Steps

  1. Create module in vfs/ directory
  2. Implement Inode trait
  3. Add to VFS mount system
  4. Write tests

Example

// vfs/myfs.rs
pub struct MyFsInode {
    // Internal state
}

impl Inode for MyFsInode {
    fn read(&self, offset: u64, buffer: &mut [u8]) -> KernelResult<usize> {
        // Implementation
        Ok(bytes_read)
    }
    
    // Other trait methods
}

3. Hardware Driver

Steps

  1. Create driver in drivers/ directory
  2. Implement initialization in drivers/init.rs
  3. Add to driver initialization sequence
  4. Write tests

Example

// drivers/my_device.rs
pub struct MyDevice {
    base_addr: usize,
}

impl MyDevice {
    pub fn new(base_addr: usize) -> Self {
        Self { base_addr }
    }
    
    pub fn read(&self, reg: u8) -> u8 {
        // Memory-mapped I/O
        unsafe { &*(self.base_addr as *const u8).add(reg as usize) }
    }
}

4. New Scheduler Algorithm

Steps

  1. Create new scheduler in task/scheduler/
  2. Implement scheduling algorithm
  3. Add to scheduler selection
  4. Write tests

Example

// task/scheduler/priority.rs
pub struct PriorityScheduler {
    // Priority queue implementation
}

impl Scheduler for PriorityScheduler {
    fn schedule_next(&mut self) -> Option<TaskId> {
        // Priority-based scheduling
        self.queue.pop()
    }
    
    // Other scheduler methods
}