Linux中将EFI从物理模式切换到虚拟模式efi_enter_virtual_mode函数的实现

将EFI从物理模式切换到虚拟模式efi_enter_virtual_mode

c 复制代码
void __init efi_enter_virtual_mode(void)
{
        efi_memory_desc_t *md;
        efi_status_t status;
        int i;

        efi.systab = NULL;

        for (i = 0; i < memmap.nr_map; i++) {
                md = &memmap.map[i];

                if (md->attribute & EFI_MEMORY_RUNTIME) {
                        md->virt_addr =
                                (unsigned long)ioremap(md->phys_addr,
                                        md->num_pages << EFI_PAGE_SHIFT);
                        if (!(unsigned long)md->virt_addr) {
                                printk(KERN_ERR PFX "ioremap of 0x%lX failed\n",
                                        (unsigned long)md->phys_addr);
                        }

                        if (((unsigned long)md->phys_addr <=
                                        (unsigned long)efi_phys.systab) &&
                                ((unsigned long)efi_phys.systab <
                                        md->phys_addr +
                                        ((unsigned long)md->num_pages <<
                                                EFI_PAGE_SHIFT))) {
                                unsigned long addr;

                                addr = md->virt_addr - md->phys_addr +
                                                (unsigned long)efi_phys.systab;
                                efi.systab = (efi_system_table_t *)addr;
                        }
                }
        }

        if (!efi.systab)
                BUG();

        status = phys_efi_set_virtual_address_map(
                        sizeof(efi_memory_desc_t) * memmap.nr_map,
                        sizeof(efi_memory_desc_t),
                        memmap.desc_version,
                        memmap.phys_map);

        if (status != EFI_SUCCESS) {
                printk (KERN_ALERT "You are screwed! "
                        "Unable to switch EFI into virtual mode "
                        "(status=%lx)\n", status);
                panic("EFI call to SetVirtualAddressMap() failed!");
        }

        /*
         * Now that EFI is in virtual mode, update the function
         * pointers in the runtime service table to the new virtual addresses.
         */

        efi.get_time = (efi_get_time_t *) efi.systab->runtime->get_time;
        efi.set_time = (efi_set_time_t *) efi.systab->runtime->set_time;
        efi.get_wakeup_time = (efi_get_wakeup_time_t *)
                                        efi.systab->runtime->get_wakeup_time;
        efi.set_wakeup_time = (efi_set_wakeup_time_t *)
                                        efi.systab->runtime->set_wakeup_time;
        efi.get_variable = (efi_get_variable_t *)
                                        efi.systab->runtime->get_variable;
        efi.get_next_variable = (efi_get_next_variable_t *)
                                        efi.systab->runtime->get_next_variable;
        efi.set_variable = (efi_set_variable_t *)
                                        efi.systab->runtime->set_variable;
        efi.get_next_high_mono_count = (efi_get_next_high_mono_count_t *)
                                        efi.systab->runtime->get_next_high_mono_count;
        efi.reset_system = (efi_reset_system_t *)
                                        efi.systab->runtime->reset_system;
}

函数功能概述

这个函数将EFI(可扩展固件接口)从物理模式切换到虚拟模式,重新映射EFI运行时服务到内核虚拟地址空间,并更新所有运行时服务函数指针

代码逐行详细解释

第一部分:函数声明和变量初始化

c 复制代码
void __init efi_enter_virtual_mode(void)
{
        efi_memory_desc_t *md;
        efi_status_t status;
        int i;

        efi.systab = NULL;
  • void __init efi_enter_virtual_mode(void)

    • __init:初始化函数,完成后内存可释放
    • 函数名明确表示进入EFI虚拟模式
    • 无参数
  • 变量声明

    • efi_memory_desc_t *md:EFI内存描述符指针
    • efi_status_t status:EFI操作状态返回值
    • int i:循环计数器
  • efi.systab = NULL:清空EFI系统表指针,准备重新查找

第二部分:内存映射遍历和重新映射

c 复制代码
        for (i = 0; i < memmap.nr_map; i++) {
                md = &memmap.map[i];

                if (md->attribute & EFI_MEMORY_RUNTIME) {
  • 循环条件i < memmap.nr_map 遍历所有内存映射条目
  • md = &memmap.map[i]:获取第i个内存描述符
  • if (md->attribute & EFI_MEMORY_RUNTIME)
    • 检查内存区域是否有EFI_MEMORY_RUNTIME属性
    • 这个标志表示该内存区域需要在运行时被EFI使用

第三部分:物理地址到虚拟地址的映射

c 复制代码
                        md->virt_addr =
                                (unsigned long)ioremap(md->phys_addr,
                                        md->num_pages << EFI_PAGE_SHIFT);
                        if (!(unsigned long)md->virt_addr) {
                                printk(KERN_ERR PFX "ioremap of 0x%lX failed\n",
                                        (unsigned long)md->phys_addr);
                        }
  • ioremap(md->phys_addr, md->num_pages << EFI_PAGE_SHIFT)

    • 将物理地址映射到内核虚拟地址空间
    • md->num_pages << EFI_PAGE_SHIFT:计算总字节数
    • EFI_PAGE_SHIFT 通常是12(4KB页面)
  • 错误检查 :如果ioremap返回NULL,打印错误信息但继续执行

第四部分:定位EFI系统表

c 复制代码
                        if (((unsigned long)md->phys_addr <=
                                        (unsigned long)efi_phys.systab) &&
                                ((unsigned long)efi_phys.systab <
                                        md->phys_addr +
                                        ((unsigned long)md->num_pages <<
                                                EFI_PAGE_SHIFT))) {
                                unsigned long addr;

                                addr = md->virt_addr - md->phys_addr +
                                                (unsigned long)efi_phys.systab;
                                efi.systab = (efi_system_table_t *)addr;
                        }
                }
        }
  • 复杂条件判断:检查EFI系统表是否位于当前内存区域内

    • 条件1:md->phys_addr <= efi_phys.systab(起始地址检查)
    • 条件2:efi_phys.systab < md->phys_addr + 区域大小(结束地址检查)
  • 虚拟地址计算

    • addr = md->virt_addr - md->phys_addr + (unsigned long)efi_phys.systab

    复制代码
      addr = md->virt_addr - md->phys_addr + efi_phys.systab
           = (md->virt_addr) + (efi_phys.systab - md->phys_addr)
           = 虚拟基地址 + 物理偏移量
    text 复制代码
    物理内存布局:
    0x0000000000001000 +----------------+ ← md->phys_addr
                       |  EFI运行时代码  |
    0x0000000000001500 +----------------+ ← efi_phys.systab
                       |  EFI系统表     |
    0x0000000000001600 +----------------+
                       |  其他EFI数据   |
    0x0000000000002000 +----------------+
    
    虚拟内存布局:
    0xffff000000001000 +----------------+ ← md->virt_addr
                       |  重新映射的     |
    0xffff000000001500 +----------------+ ← efi.systab (计算结果)
                       |  EFI区域       |
    0xffff000000001600 +----------------+
                       |                |
    0xffff000000002000 +----------------+

第五部分:系统表存在性验证

c 复制代码
        if (!efi.systab)
                BUG();
  • 关键检查:如果没找到EFI系统表,触发内核BUG
  • 这表明EFI系统表是必需的,找不到则系统无法继续

第六部分:切换到虚拟模式

c 复制代码
        status = phys_efi_set_virtual_address_map(
                        sizeof(efi_memory_desc_t) * memmap.nr_map,
                        sizeof(efi_memory_desc_t),
                        memmap.desc_version,
                        memmap.phys_map);

        if (status != EFI_SUCCESS) {
                printk (KERN_ALERT "You are screwed! "
                        "Unable to switch EFI into virtual mode "
                        "(status=%lx)\n", status);
                panic("EFI call to SetVirtualAddressMap() failed!");
        }
  • phys_efi_set_virtual_address_map:调用EFI运行时服务

    • 参数1:内存映射表总大小
    • 参数2:单个描述符大小
    • 参数3:描述符版本
    • 参数4:物理内存映射表地址
  • 错误处理:如果调用失败,打印严重错误并panic

第七部分:更新运行时服务函数指针

c 复制代码
        /*
         * Now that EFI is in virtual mode, update the function
         * pointers in the runtime service table to the new virtual addresses.
         */

        efi.get_time = (efi_get_time_t *) efi.systab->runtime->get_time;
        efi.set_time = (efi_set_time_t *) efi.systab->runtime->set_time;
        efi.get_wakeup_time = (efi_get_wakeup_time_t *)
                                        efi.systab->runtime->get_wakeup_time;
        efi.set_wakeup_time = (efi_set_wakeup_time_t *)
                                        efi.systab->runtime->set_wakeup_time;
        efi.get_variable = (efi_get_variable_t *)
                                        efi.systab->runtime->get_variable;
        efi.get_next_variable = (efi_get_next_variable_t *)
                                        efi.systab->runtime->get_next_variable;
        efi.set_variable = (efi_set_variable_t *)
                                        efi.systab->runtime->set_variable;
        efi.get_next_high_mono_count = (efi_get_next_high_mono_count_t *)
                                        efi.systab->runtime->get_next_high_mono_count;
        efi.reset_system = (efi_reset_system_t *)
                                        efi.systab->runtime->reset_system;
}
  • 现在EFI处于虚拟模式,需要更新函数指针
  • 函数指针更新 :将EFI运行时服务表中的函数指针复制到内核的efi结构中
  • 包含的服务
    • 时间管理:get_time, set_time, get_wakeup_time, set_wakeup_time
    • 变量服务:get_variable, get_next_variable, set_variable
    • 系统服务:get_next_high_mono_count, reset_system

关键技术原理

EFI内存映射的重要性

复制代码
物理模式:EFI服务使用物理地址
虚拟模式:EFI服务使用虚拟地址 ← 切换目标

ioremap的作用

  • 将物理内存映射到内核虚拟地址空间
  • 使得内核可以访问EFI运行时服务所需的内存区域

函数指针更新的必要性

  • EFI系统表中的函数指针在虚拟模式切换后变为虚拟地址
  • 内核需要保存这些指针以便后续调用EFI服务

总结

这个函数完成了EFI从物理模式到虚拟模式的关键切换:

  1. 内存重映射:将EFI运行时内存映射到虚拟地址空间
  2. 系统表定位:找到并更新EFI系统表的虚拟地址
  3. 模式切换:调用EFI服务正式进入虚拟模式
  4. 服务更新:更新所有运行时服务函数指针

phys_efi_set_virtual_address_map

c 复制代码
static efi_status_t
phys_efi_set_virtual_address_map(unsigned long memory_map_size,
                                 unsigned long descriptor_size,
                                 u32 descriptor_version,
                                 efi_memory_desc_t *virtual_map)
{
        efi_status_t status;

        efi_call_phys_prelog();
        status = efi_call_phys(efi_phys.set_virtual_address_map,
                                     memory_map_size, descriptor_size,
                                     descriptor_version, virtual_map);
        efi_call_phys_epilog();
        return status;
}

函数功能概述

这个函数是EFI(可扩展固件接口)运行时服务调用的封装函数,用于在物理模式下调用EFI的SetVirtualAddressMap服务,将EFI从物理地址模式切换到虚拟地址模式。

代码逐行详细解释

第一部分:函数声明和参数

c 复制代码
static efi_status_t
phys_efi_set_virtual_address_map(unsigned long memory_map_size,
                                 unsigned long descriptor_size,
                                 u32 descriptor_version,
                                 efi_memory_desc_t *virtual_map)
{
  • static efi_status_t

    • static:函数只在当前文件内可见
    • efi_status_t:返回值类型,EFI标准定义的状态码
  • 函数名称phys_efi_set_virtual_address_map

    • phys_:表示在物理模式下调用
    • efi_set_virtual_address_map:对应的EFI运行时服务名称
  • 参数列表

    • unsigned long memory_map_size:内存映射表的总大小(字节)
    • unsigned long descriptor_size:单个内存描述符的大小(字节)
    • u32 descriptor_version:描述符版本号
    • efi_memory_desc_t *virtual_map:虚拟地址映射表指针

第二部分:局部变量声明

c 复制代码
        efi_status_t status;
  • efi_status_t status
    • 声明一个变量来存储EFI调用的返回状态
    • efi_status_t 是EFI标准定义的状态类型,通常是64位整数
    • 可能的值包括:EFI_SUCCESSEFI_INVALID_PARAMETER

第三部分:物理模式调用前准备

c 复制代码
        efi_call_phys_prelog();
  • efi_call_phys_prelog()物理模式调用前奏
    • 这是一个架构特定的宏或函数
    • 主要功能包括:
      • 保存当前CPU状态
      • 切换到EFI调用所需的执行环境
      • 可能禁用中断
      • 设置特殊的寄存器状态
      • 确保CPU处于正确的模式(实模式或保护模式)

第四部分:EFI服务调用

c 复制代码
        status = efi_call_phys(efi_phys.set_virtual_address_map,
                                     memory_map_size, descriptor_size,
                                     descriptor_version, virtual_map);
  • efi_call_phys物理模式EFI调用宏

    • 这是执行实际EFI调用的关键宏
    • 处理调用约定和参数传递
  • 参数详解

    • efi_phys.set_virtual_address_map:EFI运行时服务函数指针
    • memory_map_size:传递给EFI服务的内存映射大小
    • descriptor_size:描述符大小
    • descriptor_version:版本信息
    • virtual_map:虚拟地址映射表
  • SetVirtualAddressMap服务的作用

    • 通知EFI固件内核的虚拟内存布局
    • 让EFI更新其内部指针为虚拟地址
    • 这是EFI从物理模式切换到虚拟模式的关键步骤

第五部分:调用后清理

c 复制代码
        efi_call_phys_epilog();
  • efi_call_phys_epilog()物理模式调用收尾
    • efi_call_phys_prelog()对应
    • 主要功能包括:
      • 恢复之前保存的CPU状态
      • 重新启用中断
      • 恢复正常的执行环境
      • 清理临时设置

第六部分:返回状态

c 复制代码
        return status;
}
  • return status:返回EFI调用的状态码
  • 调用者根据这个状态判断操作是否成功

efi_call_phys_prelog

c 复制代码
static void efi_call_phys_prelog(void)
{
        unsigned long cr4;
        unsigned long temp;

        spin_lock(&efi_rt_lock);
        local_irq_save(efi_rt_eflags);

        /*
         * If I don't have PSE, I should just duplicate two entries in page
         * directory. If I have PSE, I just need to duplicate one entry in
         * page directory.
         */
        __asm__ __volatile__("movl %%cr4, %0":"=r"(cr4));

        if (cr4 & X86_CR4_PSE) {
                efi_bak_pg_dir_pointer[0].pgd =
                    swapper_pg_dir[pgd_index(0)].pgd;
                swapper_pg_dir[0].pgd =
                    swapper_pg_dir[pgd_index(PAGE_OFFSET)].pgd;
        } else {
                efi_bak_pg_dir_pointer[0].pgd =
                    swapper_pg_dir[pgd_index(0)].pgd;
                efi_bak_pg_dir_pointer[1].pgd =
                    swapper_pg_dir[pgd_index(0x400000)].pgd;
                swapper_pg_dir[pgd_index(0)].pgd =
                    swapper_pg_dir[pgd_index(PAGE_OFFSET)].pgd;
                temp = PAGE_OFFSET + 0x400000;
                swapper_pg_dir[pgd_index(0x400000)].pgd =
                    swapper_pg_dir[pgd_index(temp)].pgd;
        }

        /*
         * After the lock is released, the original page table is restored.
         */
        local_flush_tlb();

        cpu_gdt_descr[0].address = __pa(cpu_gdt_descr[0].address);
        __asm__ __volatile__("lgdt %0":"=m"
                            (*(struct Xgt_desc_struct *) __pa(&cpu_gdt_descr[0])));
}

函数功能概述

这个函数为EFI物理模式调用准备执行环境,包括保存状态、修改页表映射、刷新TLB和加载物理地址GDT,确保在物理地址模式下正确调用EFI运行时服务

代码逐行详细解释

第一部分:函数声明和变量定义

c 复制代码
static void efi_call_phys_prelog(void)
{
        unsigned long cr4;
        unsigned long temp;
  • static void efi_call_phys_prelog(void)

    • static:函数只在当前文件内可见
    • void:没有返回值
    • 函数名表示EFI物理调用的前导操作
  • 变量声明

    • unsigned long cr4:存储CR4控制寄存器值
    • unsigned long temp:临时计算变量

第二部分:锁和中断保护

c 复制代码
        spin_lock(&efi_rt_lock);
        local_irq_save(efi_rt_eflags);
  • spin_lock(&efi_rt_lock)

    • 获取EFI运行时服务的自旋锁
    • 防止多CPU同时进入EFI调用环境
  • local_irq_save(efi_rt_eflags)

    • 保存当前中断标志并禁用中断
    • 确保EFI调用期间不被中断打断

第三部分:检查PSE支持

c 复制代码
        __asm__ __volatile__("movl %%cr4, %0":"=r"(cr4));

        if (cr4 & X86_CR4_PSE) {
  • 内联汇编"movl %%cr4, %0":"=r"(cr4)

    • 将CR4控制寄存器的值读取到cr4变量
    • %%cr4:CR4寄存器
    • %0:输出操作数,对应cr4变量
    • "=r":约束,表示输出到寄存器
  • PSE检查if (cr4 & X86_CR4_PSE)

    • X86_CR4_PSE:Page Size Extension标志位
    • PSE允许使用4MB大页面,而不仅仅是4KB页面

第四部分:PSE启用时的页表处理

c 复制代码
                efi_bak_pg_dir_pointer[0].pgd =
                    swapper_pg_dir[pgd_index(0)].pgd;
                swapper_pg_dir[0].pgd =
                    swapper_pg_dir[pgd_index(PAGE_OFFSET)].pgd;
  • 备份原始映射

    • 将虚拟地址0对应的页目录项备份到efi_bak_pg_dir_pointer[0]
    • pgd_index(0):计算虚拟地址0在页目录中的索引
  • 建立新映射

    • PAGE_OFFSET处的内核映射复制到虚拟地址0的位置
    • 这样虚拟地址0就指向了内核空间,EFI可以使用物理地址访问

第五部分:PSE禁用时的页表处理

c 复制代码
        } else {
                efi_bak_pg_dir_pointer[0].pgd =
                    swapper_pg_dir[pgd_index(0)].pgd;
                efi_bak_pg_dir_pointer[1].pgd =
                    swapper_pg_dir[pgd_index(0x400000)].pgd;
                swapper_pg_dir[pgd_index(0)].pgd =
                    swapper_pg_dir[pgd_index(PAGE_OFFSET)].pgd;
                temp = PAGE_OFFSET + 0x400000;
                swapper_pg_dir[pgd_index(0x400000)].pgd =
                    swapper_pg_dir[pgd_index(temp)].pgd;
        }
  • 备份两个页目录项

    • 虚拟地址0和0x400000(4MB)处的映射
    • 因为没有PSE,需要处理两个4KB页面目录项
  • 建立新映射

    • 将内核空间映射复制到低地址区域
    • temp = PAGE_OFFSET + 0x400000:计算对应的内核虚拟地址

第六部分:TLB刷新和GDT处理

c 复制代码
        local_flush_tlb();

        cpu_gdt_descr[0].address = __pa(cpu_gdt_descr[0].address);
        __asm__ __volatile__("lgdt %0":"=m"
                            (*(struct Xgt_desc_struct *) __pa(&cpu_gdt_descr[0])));
  • local_flush_tlb()

    • 刷新当前CPU的TLB(转换后备缓冲区)
    • 确保页表修改立即生效
  • GDT地址转换

    • __pa(cpu_gdt_descr[0].address):将GDT描述符地址转换为物理地址
    • EFI调用需要在物理地址模式下运行
  • 加载GDT

    • lgdt %0:加载全局描述符表
    • __pa(&cpu_gdt_descr[0]):GDT描述符的物理地址
    • 使用物理地址确保在EFI调用期间GDT可访问

技术原理深度解析

为什么要修改页表映射?

问题:EFI运行时服务期望使用物理地址,但内核运行在虚拟地址空间。

解决方案:建立物理地址到虚拟地址的1:1映射

复制代码
虚拟地址空间修改前:
0x00000000 +----------------+ ← 用户空间/未映射
           |                |
PAGE_OFFSET +----------------+ ← 内核空间开始
           |    内核映射     |
           +----------------+

修改后:
0x00000000 +----------------+ ← 现在映射到内核空间
           |    内核映射     |    (物理地址0对应虚拟地址0)
PAGE_OFFSET +----------------+ ← 内核空间开始
           |    内核映射     |
           +----------------+

PSE的影响

有PSE(4MB页面)

  • 只需要修改一个页目录项
  • 虚拟地址0-4MB映射到内核空间

无PSE(4KB页面)

  • 需要修改两个页目录项
  • 虚拟地址0-4MB和4MB-8MB都映射到内核空间

GDT处理的重要性

在物理模式调用期间:

  • CPU使用物理地址寻址
  • GDT描述符必须包含物理地址
  • 否则CPU无法正确解析段描述符

内存布局示例

典型的x86内存布局

复制代码
物理地址空间:
0x00000000 +----------------+ ← 物理内存开始
           |                |
0x00100000 +----------------+ ← 可能的内核加载位置
           |                |

虚拟地址空间(修改前):
0x00000000 +----------------+ ← 用户空间
           |                |
0xC0000000 +----------------+ ← PAGE_OFFSET,内核空间开始
           |  内核映射      |
           +----------------+

虚拟地址空间(修改后):
0x00000000 +----------------+ ← 现在映射到物理内存
           |  物理内存映射   |    EFI可以使用物理地址0访问实际内存
0xC0000000 +----------------+ ← 内核空间
           |  内核映射      |
           +----------------+

efi_call_phys_epilog

c 复制代码
static void efi_call_phys_epilog(void)
{
        unsigned long cr4;

        cpu_gdt_descr[0].address =
                (unsigned long) __va(cpu_gdt_descr[0].address);
        __asm__ __volatile__("lgdt %0":"=m"(cpu_gdt_descr));
        __asm__ __volatile__("movl %%cr4, %0":"=r"(cr4));

        if (cr4 & X86_CR4_PSE) {
                swapper_pg_dir[pgd_index(0)].pgd =
                    efi_bak_pg_dir_pointer[0].pgd;
        } else {
                swapper_pg_dir[pgd_index(0)].pgd =
                    efi_bak_pg_dir_pointer[0].pgd;
                swapper_pg_dir[pgd_index(0x400000)].pgd =
                    efi_bak_pg_dir_pointer[1].pgd;
        }

        /*
         * After the lock is released, the original page table is restored.
         */
        local_flush_tlb();

        local_irq_restore(efi_rt_eflags);
        spin_unlock(&efi_rt_lock);
}

函数功能概述

这个函数是EFI物理模式调用的后处理函数,负责恢复在efi_call_phys_prelog中修改的系统状态,包括恢复GDT、页表映射,刷新TLB,并释放锁和恢复中断状态

代码逐行详细解释

第一部分:函数声明和变量定义

c 复制代码
static void efi_call_phys_epilog(void)
{
        unsigned long cr4;
  • static void efi_call_phys_epilog(void)

    • static:函数只在当前文件内可见
    • void:没有返回值
    • 函数名表示EFI物理调用的收尾操作
    • epilogprelog对应,形成完整的调用包装
  • unsigned long cr4:存储CR4控制寄存器值,用于检查PSE功能

第二部分:恢复GDT描述符

c 复制代码
        cpu_gdt_descr[0].address =
                (unsigned long) __va(cpu_gdt_descr[0].address);
        __asm__ __volatile__("lgdt %0":"=m"(cpu_gdt_descr));
  • GDT地址恢复

    • __va(cpu_gdt_descr[0].address):将物理地址转换回虚拟地址
    • 在prelog中,GDT地址被转换为物理地址供EFI调用使用
    • 现在需要恢复为虚拟地址供内核正常使用
  • 重新加载GDT

    • lgdt %0:加载全局描述符表
    • "=m"(cpu_gdt_descr):操作数约束,表示内存操作数
    • 使用恢复后的虚拟地址GDT描述符

第三部分:检查PSE支持

c 复制代码
        __asm__ __volatile__("movl %%cr4, %0":"=r"(cr4));

        if (cr4 & X86_CR4_PSE) {
  • 读取CR4寄存器"movl %%cr4, %0":"=r"(cr4)

    • 内联汇编读取CR4控制寄存器到cr4变量
    • 需要检查PSE状态以确定如何恢复页表
  • PSE检查if (cr4 & X86_CR4_PSE)

    • 检查CR4寄存器的PSE位是否启用
    • 决定恢复页表映射的策略

第四部分:PSE启用时的页表恢复

c 复制代码
                swapper_pg_dir[pgd_index(0)].pgd =
                    efi_bak_pg_dir_pointer[0].pgd;
  • 恢复单个页目录项
    • swapper_pg_dir[pgd_index(0)].pgd:虚拟地址0对应的页目录项
    • efi_bak_pg_dir_pointer[0].pgd:之前备份的原始页目录项值
    • 效果:恢复虚拟地址0的原始映射

第五部分:PSE禁用时的页表恢复

c 复制代码
        } else {
                swapper_pg_dir[pgd_index(0)].pgd =
                    efi_bak_pg_dir_pointer[0].pgd;
                swapper_pg_dir[pgd_index(0x400000)].pgd =
                    efi_bak_pg_dir_pointer[1].pgd;
        }
  • 恢复两个页目录项
    • 第一项:恢复虚拟地址0x00000000的原始映射
    • 第二项:恢复虚拟地址0x00400000(4MB)的原始映射
    • 对应prelog中备份的两个页目录项

第六部分:TLB刷新和状态恢复

c 复制代码
        local_flush_tlb();

        local_irq_restore(efi_rt_eflags);
        spin_unlock(&efi_rt_lock);
  • 刷新TLBlocal_flush_tlb()

    • 使页表修改立即生效
    • 清除陈旧的地址转换缓存
  • 恢复中断状态local_irq_restore(efi_rt_eflags)

    • 恢复之前保存的中断标志
    • 重新启用中断(如果之前是启用的)
  • 释放锁spin_unlock(&efi_rt_lock)

    • 释放EFI运行时服务锁
    • 允许其他CPU进行EFI调用
相关推荐
噜啦噜啦嘞好3 小时前
Linux:库制作与原理
linux·运维·服务器
刘某的Cloud4 小时前
磁盘-IO
linux·运维·系统·磁盘io
我狸才不是赔钱货4 小时前
容器:软件世界的标准集装箱
linux·运维·c++·docker·容器
云知谷4 小时前
【嵌入式基本功】单片机嵌入式学习路线
linux·c语言·c++·单片机·嵌入式硬件
挺6的还5 小时前
Boost搜索引擎
linux
天赐学c语言6 小时前
Linux进程信号(上)
linux·可重入函数·进程信号
ajassi20006 小时前
开源 Linux 服务器与中间件(四)服务器--Tomcat
linux·服务器·开源
云半S一7 小时前
春招准备之Linux系统篇
linux·经验分享·笔记
帅锅锅0077 小时前
SeLinux 全面详解
android·linux