efivarfs 实现原理

EFIVARFS 实现原理

efivarfs 是一种特殊的文件系统,用于挂载和访问 EFI 变量。EFI(Extensible Firmware Interface)是一个标准,提供了操作系统和平台固件之间的接口。efivarfs 提供了一种直接访问 EFI 变量的方法,使操作系统能够读取和写入这些变量。

基本原理

1、文件系统类型:

  • efivarfs 是一个内存中的虚拟文件系统,类似于 proc 和 sysfs。

2、EFI 变量:

  • 每个 EFI 变量在 efivarfs 中表示为一个文件,文件名是变量的名称。文件内容是变量的值。
  • EFI 变量的元数据(如 GUID、属性)通过文件的元数据进行存储和访问。

3、挂载 efivarfs:

  • 在 Linux 系统中,可以通过以下命令挂载 efivarfs:
sudo mount -t efivarfs efivarfs /sys/firmware/efi/efivars
  • 挂载后,EFI 变量可以作为常规文件进行访问。

内核支持:

  • efivarfs 的实现位于 Linux 内核中,代码在 drivers/firmware/efi/efivarfs.c 文件中。
  • 通过 CONFIG_EFIVAR_FS 内核配置选项来启用 efivarfs 支持。

工作流程

1、初始化:

  • 在内核引导过程中,如果检测到 EFI 环境,内核会初始化 efivarfs 文件系统。
  • efivarfs 挂载点通常是 /sys/firmware/efi/efivars。

2、文件操作:

  • 每个 EFI 变量在 efivarfs 中表示为一个文件,文件名是变量的名称。
  • 读取文件内容等同于读取 EFI 变量的值,写入文件内容则会更新 EFI 变量的值。
  • 文件元数据(如权限、所有者)可以表示 EFI 变量的属性。

3、用户空间交互:

  • 用户空间工具(如 efibootmgr)可以通过标准文件操作(如 open、read、write、ioctl)来管理 EFI 变量。
  • 例如,efibootmgr 工具使用 efivarfs 文件系统来读取和修改引导顺序等 EFI 设置。

实现细节

1、挂载函数:

  • efivarfs_mount 函数用于挂载 efivarfs 文件系统,它会创建并初始化挂载点的 super_block 结构。

2、文件系统操作:

  • efivarfs 文件系统定义了一组文件系统操作,如 read, write, open, release。
  • 这些操作通过调用 EFI 固件接口来完成实际的 EFI 变量读取和写入。

3、内核接口:

  • 内核通过 efi.get_variable 和 efi.set_variable 函数与 EFI 固件交互。
  • 这些函数使用 EFI 固件接口读取和写入 EFI 变量。

示例

以下是一个简化的示例代码片段,展示了如何使用 efivarfs 读取 EFI 变量:

#include <linux/efi.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>

static int __init efivarfs_demo_init(void)
{
    // struct efi_variable efi_var;
    efi_char16_t var_name[] = L"BootOrder";
    efi_guid_t guid = EFI_GLOBAL_VARIABLE_GUID;
    unsigned long data_size = 0;
    u8 *data = NULL;
    efi_status_t status;
    int i;

    /* 获取变量大小 */
    status = efi.get_variable(var_name, &guid, NULL, &data_size, NULL);
    if (status != EFI_BUFFER_TOO_SMALL) {
        pr_err("Failed to get variable size: %lx\n", status);
        return -1;
    }

    data = kzalloc(data_size, GFP_KERNEL);
    if (!data) {
        pr_err("Failed to allocate memory for variable data\n");
        return -ENOMEM;
    }

    /* 读取变量 */
    status = efi.get_variable(var_name, &guid, NULL, &data_size, data);
    if (status != EFI_SUCCESS) {
        pr_err("Failed to read variable: %lx\n", status);
        kfree(data);
        return -1;
    }

    pr_info("data:\n");
    for (i = 0; i < data_size; i++) {
        printk("%02x ", data[i]);
    }
    printk("\n");
    pr_info("Successfully read EFI variable\n");

    kfree(data);
    return 0;
}

static void __exit efivarfs_demo_exit(void)
{
    pr_info("Exiting efivarfs demo\n");
}

module_init(efivarfs_demo_init);
module_exit(efivarfs_demo_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("efivarfs demo module");

Makefile

obj-m += efi.o

all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
nvidia@tegra-ubuntu:~$ sudo insmod efi.ko 
nvidia@tegra-ubuntu:~$ sudo dmesg | tail -n 21
[ 5449.967521] data:
[ 5449.967529] 08 
[ 5449.967531] 00 
[ 5449.967532] 01 
[ 5449.967533] 00 
[ 5449.967533] 00 
[ 5449.967534] 00 
[ 5449.967534] 02 
[ 5449.967535] 00 
[ 5449.967535] 03 
[ 5449.967536] 00 
[ 5449.967536] 04 
[ 5449.967536] 00 
[ 5449.967537] 05 
[ 5449.967538] 00 
[ 5449.967538] 06 
[ 5449.967539] 00 
[ 5449.967539] 07 
[ 5449.967540] 00 

[ 5449.967541] Successfully read EFI variable

此模块初始化时会尝试读取 EFI 变量 BootOrder 的内容并打印结果。代码展示了如何通过内核接口访问 EFI 变量。实际生产环境中,通常不会直接编写这样的内核模块,而是使用现有的用户空间工具(如 efibootmgr)来管理 EFI 变量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Li-Yongjun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值