Rust UEFI开发库uefi-raw的使用:原生支持UEFI固件开发的底层接口和功能

Rust UEFI开发库uefi-raw的使用:原生支持UEFI固件开发的底层接口和功能

uefi-raw是一个Rust crate,它包含了与UEFI规范中定义紧密匹配的原始UEFI类型。

安装

在项目目录中运行以下Cargo命令:

cargo add uefi-raw

或者在Cargo.toml中添加以下行:

uefi-raw = "0.11.0"

使用示例

以下是一个使用uefi-raw进行UEFI开发的完整示例demo:

// 引入uefi-raw库
use uefi_raw::table::boot::{BootServices, SystemTable};
use uefi_raw::proto::console::gop::GraphicsOutput;
use uefi_raw::status::Status;

// UEFI入口点函数
#[no_mangle]
pub extern "efiapi" fn efi_main(
    image_handle: uefi_raw::Handle,
    system_table: *mut SystemTable,
) -> Status {
    // 获取引导服务
    let boot_services = unsafe { &mut *((*system_table).boot_services) };
    
    // 获取图形输出协议
    let mut gop: *mut GraphicsOutput = core::ptr::null_mut();
    let status = unsafe {
        boot_services.locate_protocol(
            &GraphicsOutput::GUID,
            core::ptr::null_mut(),
            &mut gop as *mut _ as *mut *mut core::ffi::c_void,
        )
    };
    
    if status != Status::SUCCESS {
        return status;
    }
    
    // 设置屏幕背景色为蓝色
    let gop = unsafe { &mut *gop };
    let mode = unsafe { &mut *gop.mode };
    let mut pixel = [0, 0, 255, 0]; // 蓝色
    
    for y in 0..mode.info().resolution().1 {
        for x in 0..mode.info().resolution().0 {
            unsafe {
                let buffer = mode.frame_buffer().as_mut_ptr();
                let pixel_size = mode.info().pixel_format().bytes_per_pixel();
                let offset = (y * mode.info().stride() + x) * pixel_size;
                core::ptr::copy_nonoverlapping(
                    pixel.as_ptr(),
                    buffer.add(offset as usize),
                    pixel_size as usize,
                );
            }
        }
    }
    
    Status::SUCCESS
}

功能特点

  1. 提供与UEFI规范紧密匹配的原始类型定义
  2. 支持UEFI引导服务、运行时服务和协议
  3. 可用于开发UEFI固件和UEFI应用程序

文档

更多详细文档可以在docs.rs上查看uefi-raw文档

许可证

uefi-raw采用MIT或Apache-2.0双重许可证。


1 回复

Rust UEFI开发库uefi-raw的使用指南

概述

uefi-raw是一个Rust库,提供了对UEFI(统一可扩展固件接口)的底层原生支持,使开发者能够在Rust中直接与UEFI固件交互。这个库特别适合需要直接访问UEFI服务和协议的系统级开发。

主要特性

  • 提供对UEFI规范定义的类型、常量和函数的原始绑定
  • 支持UEFI启动服务、运行时服务和协议
  • 允许直接内存操作和硬件访问
  • 提供UEFI环境下的基本I/O功能

使用方法

添加依赖

首先在Cargo.toml中添加依赖:

[dependencies]
uefi-raw = "0.1"

基本示例

use uefi_raw::table::{SystemTable, Boot, Runtime};
use uefi_raw::status::Status;
use uefi_raw::proto::console::gop::GraphicsOutput;

fn main() -> Status {
    // 获取UEFI系统表
    let system_table = SystemTable::<Boot>::current();
    
    // 使用控制台输出服务
    let stdout = system_table.stdout();
    stdout.output_string("Hello, UEFI World!\r\n").unwrap();
    
    // 获取图形输出协议
    let gop = system_table.boot_services()
        .locate_protocol::<GraphicsOutput>()
        .unwrap();
    
    // 设置图形模式
    gop.set_mode(0).unwrap();
    
    Status::SUCCESS
}

完整示例代码

以下是一个完整的UEFI应用程序示例,展示了如何使用uefi-raw库的基本功能:

//! 一个完整的UEFI应用程序示例

use uefi_raw::{
    table::{SystemTable, Boot},
    status::Status,
    proto::console::{
        gop::GraphicsOutput,
        text::Output,
    },
    event::{Event, Timer, TPL, TimerTrigger},
    table::boot::MemoryType,
    memory::{PhysicalAddress, VirtAddr},
};

fn main() -> Status {
    // 1. 获取系统表
    let system_table = SystemTable::<Boot>::current();
    let boot_services = system_table.boot_services();
    
    // 2. 使用文本输出
    let stdout = system_table.stdout();
    stdout.output_string("UEFI应用程序启动...\r\n").unwrap();
    
    // 3. 获取图形输出协议
    let gop = boot_services.locate_protocol::<GraphicsOutput>()
        .expect("无法获取图形输出协议");
    
    // 设置图形模式
    gop.set_mode(0).expect("设置图形模式失败");
    
    // 4. 内存管理示例
    let buffer = boot_services.allocate_pool(MemoryType::LoaderData, 1024)
        .expect("内存分配失败");
    
    // 使用分配的内存...
    
    // 释放内存
    boot_services.free_pool(buffer).expect("内存释放失败");
    
    // 5. 事件处理示例
    let event = Event::new(TPL::APPLICATION)
        .expect("创建事件失败");
    let timer = Timer::new(&event)
        .expect("创建定时器失败");
    
    timer.set_timer(TimerTrigger::Periodic, 1_000_000)
        .expect("设置定时器失败");
    
    // 6. 直接硬件访问示例
    let phys_addr = PhysicalAddress::new(0xB8000);
    let virt_addr = boot_services.map_memory(phys_addr, 4096)
        .expect("内存映射失败");
    
    unsafe {
        let vga_buffer = virt_addr.as_mut_ptr::<u16>();
        // 在屏幕左上角显示白色'A'
        *vga_buffer.offset(0) = 0x0F41;
    }
    
    // 7. 自定义协议示例
    #[repr(C)]
    #[derive(uefi_raw::proto::Protocol)]
    #[guid = "12345678-9abc-def0-1234-56789abcdef0"]
    struct MyProtocol {
        version: u32,
        do_work: extern "efiapi" fn(this: &MyProtocol, param: u64) -> Status,
    }
    
    extern "efiapi" fn do_work_impl(_this: &MyProtocol, param: u64) -> Status {
        stdout.output_string(&format!("执行工作,参数: {}\r\n", param)).unwrap();
        Status::SUCCESS
    }
    
    let my_proto = MyProtocol {
        version: 1,
        do_work: do_work_impl,
    };
    
    // 使用自定义协议
    (my_proto.do_work)(&my_proto, 42).expect("自定义协议执行失败");
    
    stdout.output_string("UEFI应用程序执行完成\r\n").unwrap();
    
    Status::SUCCESS
}

编译设置

.cargo/config.toml中添加:

[build]
target = "x86_64-unknown-uefi"

[target.x86_64-unknown-uefi]
rustflags = [
    "-C", "link-arg=-nostdlib",
    "-C", "link-arg=-Wl,-dll",
    "-C", "link-arg=-shared",
    "-C", "link-arg=-Wl,--subsystem,efi_application",
]

注意事项

  1. UEFI环境有特殊的内存管理要求,避免使用标准库的内存分配
  2. 大多数操作需要检查返回的Status
  3. 运行时服务在ExitBootServices()后可能不可用
  4. 代码通常需要编译为efi目标
  5. 需要特殊的链接器脚本和编译参数

uefi-raw为Rust开发者提供了直接访问UEFI服务的底层接口,适合开发固件、引导加载程序和其他系统级软件。使用时需特别注意UEFI环境的特殊性和安全性要求。

回到顶部