Manual Completo de Rust para Programación del Kernel Linux

Este manual cubre desde los fundamentos de Rust en el contexto del kernel Linux hasta conceptos avanzados para desarrollar componentes del kernel, con ejemplos prácticos y consideraciones de seguridad.

1. Introducción a Rust en el Kernel Linux

1.1 Estado actual de Rust en Linux

Rust se ha convertido en un lenguaje secundario oficial para el desarrollo del kernel Linux desde la versión 6.1. Sus ventajas incluyen:

1.2 Configuración del entorno

Preparación del entorno de desarrollo

Instalar herramientas básicas:

sudo apt update && sudo apt install build-essential libssl-dev pkg-config

Instalar Rust:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env

Configurar para desarrollo del kernel:

rustup override set $(scripts/min-tool-version.sh rustc)
rustup component add rust-src
cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen

Obtener el código fuente del kernel:

git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux

2. Fundamentos de Rust para el Kernel

2.1 Diferencias clave con Rust estándar

Característica Rust Estándar Rust para Kernel
Biblioteca estándar Disponible (std) No disponible (no_std)
Asignación de memoria Global allocator Allocators del kernel
Concurrencia std::sync kernel::sync
Manejo de errores std::error::Error kernel::error::Result

2.2 Estructura básica de un módulo

// Módulo mínimo del kernel en Rust
#![no_std]  // No usar la stdlib estándar
#![feature(allocator_api, global_asm)]

use kernel::prelude::*;

// Estructura del módulo
struct RustModule;

impl kernel::Module for RustModule {
    fn init(_name: &'static CStr, _module: &'static kernel::ThisModule) -> Result<Self> {
        pr_info!("Módulo Rust inicializado\n");
        Ok(RustModule)
    }
}

// Macro para declarar el módulo
module! {
    type: RustModule,
    name: "rust_kernel_module",
    author: "Tu Nombre",
    description: "Un módulo simple del kernel en Rust",
    license: "GPL",
}

3. Gestión de Memoria en el Kernel

3.1 Asignación de memoria

use kernel::{
    alloc::GFP_KERNEL,
    prelude::*,
    str::CStr,
};

fn allocate_buffer() -> Result<Box<[u8]>> {
    // Asignar 1KB de memoria
    let buffer = try_with(1024, GFP_KERNEL, |slice| {
        slice.fill(0);  // Inicializar a cero
        Ok(())
    })?;
    
    Ok(buffer)
}

struct DeviceData {
    buffer: Box<[u8]>,
}

impl DeviceData {
    fn new() -> Result<Self> {
        Ok(Self {
            buffer: allocate_buffer()?,
        })
    }
}

3.2 Tipos de asignadores

Tipo Descripción Uso típico
GFP_KERNEL Asignación normal Mayoría de casos
GFP_ATOMIC Asignación atómica (sin dormir) Contextos de interrupción
GFP_DMA Memoria accesible por DMA Dispositivos DMA

4. Concurrencia y Sincronización

4.1 Mecanismos de sincronización

use kernel::{
    sync::{Mutex, SpinLock},
    task::Task,
};

struct SharedData {
    // Mutex para datos que pueden dormir
    config: Mutex<DeviceConfig>,
    
    // SpinLock para datos en contexto atómico
    stats: SpinLock<DeviceStats>,
}

impl SharedData {
    fn new() -> Result<Self> {
        Ok(Self {
            config: Mutex::new(DeviceConfig::default()),
            stats: SpinLock::new(DeviceStats::new()),
        })
    }
    
    fn update_config(&self, new_config: DeviceConfig) {
        let mut config = self.config.lock();
        *config = new_config;
    }
}

4.2 Trabajo diferido

use kernel::{
    workqueue::{Work, WorkQueue},
    prelude::*,
    sync::Arc,
};

struct DeferredWork {
    work: Work,
    data: Arc<SharedData>,
}

impl DeferredWork {
    fn new(data: Arc<SharedData>) -> Result<Self> {
        let mut work = Work::new();
        work.init();
        
        Ok(Self { work, data })
    }
    
    fn schedule(&self, wq: &WorkQueue) {
        unsafe { wq.enqueue(&self.work) };
    }
}

impl Work for DeferredWork {
    fn run(&self) {
        // Este código se ejecuta en contexto de trabajo diferido
        pr_info!("Ejecutando trabajo diferido\n");
    }
}

5. Interacción con el Hardware

5.1 Mapeo de memoria

use kernel::{
    device::Device,
    io_mem::Resource,
    address_space::AddressSpace,
};

struct HwRegisters {
    regs: *mut u32,
    size: usize,
}

impl HwRegisters {
    fn new(dev: &dyn Device, res: Resource) -> Result<Self> {
        let addr_space = AddressSpace::new(dev)?;
        let virt_addr = addr_space.map(res)?;
        let size = res.size();
        
        Ok(Self {
            regs: virt_addr.as_ptr() as *mut u32,
            size,
        })
    }
    
    unsafe fn read(&self, offset: usize) -> u32 {
        assert!(offset < self.size / 4);
        *self.regs.add(offset)
    }
    
    unsafe fn write(&mut self, offset: usize, value: u32) {
        assert!(offset < self.size / 4);
        *self.regs.add(offset) = value;
    }
}

5.2 Manejo de interrupciones

use kernel::{
    interrupt,
    device::irq::Irq,
    sync::SpinLock,
};

struct InterruptData {
    count: SpinLock<u32>,
    dev: Arc<DeviceData>,
}

impl interrupt::Handler for InterruptData {
    fn handle(&self, irq_data: &dyn interrupt::IrqData) -> interrupt::Result {
        let mut count = self.count.lock();
        *count += 1;
        
        // Procesar la interrupción
        self.dev.process_interrupt();
        
        interrupt::Result::Handled
    }
}

fn register_irq(irq_num: u32, dev: Arc<DeviceData>) -> Result<InterruptData> {
    let irq = Irq::try_new(irq_num, dev.as_ref())?;
    let data = InterruptData {
        count: SpinLock::new(0),
        dev,
    };
    
    unsafe {
        interrupt::register_handler(&data)?;
    }
    
    Ok(data)
}

6. Subsistemas del Kernel

6.1 Sistema de archivos

use kernel::{
    file::File,
    file_operations::{FileOperations, FileOpener},
    prelude::*,
    str::CStr,
    user_ptr::UserSlicePtrWriter,
};

struct FsNode {
    data: Mutex<Vec<u8>>,
}

#[vtable]
impl FileOperations for FsNode {
    type Data = Box<Self>;

    fn open(_data: &Arc<Self>, _file: &File) -> Result<Self::Data> {
        Ok(Box::try_new(FsNode {
            data: Mutex::new(Vec::new()),
        })?)
    }

    fn read(
        &self,
        _file: &File,
        data: UserSlicePtrWriter,
        _offset: u64,
    ) -> Result<usize> {
        let buffer = self.data.lock();
        data.write_slice(&buffer)?;
        Ok(buffer.len())
    }

    fn write(
        &self,
        _file: &File,
        data: UserSlicePtrWriter,
        _offset: u64,
    ) -> Result<usize> {
        let mut buffer = self.data.lock();
        buffer.clear();
        data.read_all(&mut buffer)?;
        Ok(buffer.len())
    }
}

6.2 Redes

use kernel::{
    net::{self, SkBuff, Protocol},
    prelude::*,
};

struct RustProtocol;

impl Protocol for RustProtocol {
    fn recv(&self, skb: SkBuff) {
        pr_info!("Paquete recibido: len={}\n", skb.len());
    }
}

fn register_protocol() -> Result<net::Registration<RustProtocol>> {
    let proto = net::Protocol::new(
        "RustProto",
        RustProtocol,
        0x1234,  // ETH_P_RUST
    )?;
    
    Ok(proto)
}

7. Pruebas y Depuración

7.1 Pruebas unitarias

// Ejemplo de prueba unitaria en el kernel
#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_hw_registers() {
        // Configurar prueba simulada
        let mut regs = HwRegisters::new_for_test(1024);
        
        // Probar lectura/escritura
        unsafe {
            regs.write(0, 0x12345678);
            assert_eq!(regs.read(0), 0x12345678);
        }
    }
}

7.2 Depuración con kgdb

Configurar kgdb para depuración

1. Compilar el kernel con soporte para kgdb:

make menuconfig

Seleccionar: Kernel hacking → KGDB: kernel debugger

2. Arrancar el kernel con parámetros:

kgdboc=ttyS0,115200

3. Conectar desde otra máquina:

gdb vmlinux
target remote /dev/ttyS0

4. Depurar módulos Rust:

add-symbol-file rust_module.ko 0xffffffffc0000000

8. Ejemplo Completo: Módulo de Kernel

8.1 Módulo de estadísticas

// src/lib.rs
#![no_std]
#![feature(allocator_api, global_asm)]

use kernel::{
    file::File,
    file_operations::{FileOperations, FileOpener},
    module_misc_device,
    prelude::*,
    str::CStr,
    sync::Mutex,
    user_ptr::UserSlicePtrWriter,
};

struct KernelStats {
    read_count: Mutex<u64>,
    write_count: Mutex<u64>,
}

impl KernelStats {
    fn new() -> Result<Self> {
        Ok(Self {
            read_count: Mutex::new(0),
            write_count: Mutex::new(0),
        })
    }
}

#[vtable]
impl FileOperations for KernelStats {
    type Data = Box<Self>;

    fn open(_data: &Arc<Self>, _file: &File) -> Result<Self::Data> {
        KernelStats::new()
    }

    fn read(
        &self,
        _file: &File,
        data: UserSlicePtrWriter,
        _offset: u64,
    ) -> Result<usize> {
        let mut reads = self.read_count.lock();
        *reads += 1;
        
        let stats = format!(
            "Reads: {}, Writes: {}\n",
            reads,
            *self.write_count.lock()
        );
        
        data.write_slice(stats.as_bytes())?;
        Ok(stats.len())
    }

    fn write(
        &self,
        _file: &File,
        _data: UserSlicePtrWriter,
        _offset: u64,
    ) -> Result<usize> {
        let mut writes = self.write_count.lock();
        *writes += 1;
        Ok(0)
    }
}

struct StatsModule;

impl kernel::Module for StatsModule {
    fn init(name: &'static CStr, _module: &'static kernel::ThisModule) -> Result<Self> {
        pr_info!("Inicializando módulo de estadísticas\n");
        
        let _reg = module_misc_device(
            name,
            None,
            Arc::try_new(KernelStats::new()?)?,
        )?;
        
        Ok(StatsModule)
    }
}

impl Drop for StatsModule {
    fn drop(&mut self) {
        pr_info!("Descargando módulo de estadísticas\n");
    }
}

module! {
    type: StatsModule,
    name: "rust_kernel_stats",
    author: "Tu Nombre",
    description: "Módulo de estadísticas del kernel en Rust",
    license: "GPL",
}

8.2 Compilación y carga

Integración con el sistema de compilación del kernel

1. Agregar en el Makefile del kernel:

obj-$(CONFIG_RUST) += rust_kernel_stats.o

2. Configurar Kconfig:

config RUST_KERNEL_STATS
    tristate "Estadísticas del kernel en Rust"
    depends on RUST
    help
      Módulo de ejemplo que muestra estadísticas de lectura/escritura

3. Compilar:

make -C /lib/modules/$(uname -r)/build M=$(pwd) modules

4. Cargar el módulo:

sudo insmod rust_kernel_stats.ko

5. Interactuar:

echo "test" > /dev/rust_stats
cat /dev/rust_stats

9. Buenas Prácticas

9.1 Seguridad en el kernel

9.2 Performance

10. Recursos Adicionales

10.1 Documentación oficial

10.2 Herramientas

Este manual proporciona una base sólida para desarrollar componentes del kernel Linux en Rust. Para proyectos reales, consulta la documentación específica del subsistema del kernel con el que estés trabajando.