UEFI Runtime Drivers

UEFI runtime driver 是当OS 调用ExitBootServices 之后继续给OS 提供服务的驱动,UEFI Driver Writer Guide 也提到了一个比较好的UEFI runtime driver 是UNDI driver,其实写过网卡驱动的同学知道,在UEFI 环境一下我们只要提供SNP 驱动就可以了(参考VirtNet),提供UNDI 驱动是为了支持有的OS 启动的时候也可以直接调用UNDI 提供的接口去收发网络包。(感兴趣可以参考Network Driver Design Guidelines)

UEFI runtime driver 比一般驱动稍微麻烦一点的就是注意,OS启动之后用的是虚拟地址,它的物理地址和逻辑地址存在一个转换关系,但是UEFI 环境下,比如X86_64, 在DXE 之后一般是进入64Bit Mode, 分页模式虽然已经打开但是逻辑地址和物理地址是1:1 关系,所以我们UEFI runtime driver 驱动的一些地址要进行转换比如全局变量地址、allocate 返回的地址、寄存器mmio地址等等。当然UEFI runtime driver 本身代码地址UEFI core 会帮忙转换的。

举个简单例子OS 下虚拟地址和物理地址转换关系f(x) = phyaddr + 0x1000;

// 变量A 地址是0x2000 存的值是100
.data 0x2000 : 100
.textmovl  (0x2000), %eax     ;  把A的值赋给寄存器eax

如果在UEFI 环境下,A 变量逻辑地址和物理地址都是0x2000 并且该地址存的值是100,但是如果在OS 下,由于OS 下MMU 是开启的,那么在movl (0x2000), %eax 寻址的时候会进行转变,如上关系得到phyaddr = f(x) - 0x1000 = 0x2000 - 0x1000 = 0x1000, 所以这行代码就会把物理地址0x1000的值给到eax ,当然这个不是我们想要的结果。所以我们会调用EfiConvertPointer 这个function 把A的地址进行转换,转换结果A的地址就会变成0x3000 所以执行 movl (0x3000), %eax 就会把物理地址0x2000地址的值给到eax.

下面具体讲解UEFI runtime driver 从UEFI 到OS 细节讲解

UEFI 部分:

uefi runtime core 相关代码稍微有点分散

  • DxeMain.c
  • DxeProtocolNotify.c
  • Image.c
  • Event.c
  • Runtime.c

他们之间的联系是DXE 会临时声明一个EFI_RUNTIME_ARCH_PROTOCOL, 这个Protocol 会把所有Image.c 汇报的类型是RUNTIME_DRIVER 的驱动收集到ImageHead List 上,也会把Event.c 汇报上去所有注册为EVT_RUNTIME (EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE) Event 收集放到EventHead List 上用。当真正的Runtime跑的时候通过DxeProtcolNotify.c 注册的NotifyProtocol 把临时EFI_RUNTIME_ARCH_PROTOCOL上面的内容复制到Runtime.c注册的 EFI_RUNTIME_ARCH_PROTOCOL 上。Runtime.c 会把所有runtime driver image 和 注册EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE 变量进行重定向工作。

//DxeMain.c
EFI_RUNTIME_ARCH_PROTOCOL gRuntimeTemplate = {INITIALIZE_LIST_HEAD_VARIABLE (gRuntimeTemplate.ImageHead),INITIALIZE_LIST_HEAD_VARIABLE (gRuntimeTemplate.EventHead),//// Make sure Size != sizeof (EFI_MEMORY_DESCRIPTOR). This will// prevent people from having pointer math bugs in their code.// now you have to use *DescriptorSize to make things work.//sizeof (EFI_MEMORY_DESCRIPTOR) + sizeof (UINT64) - (sizeof (EFI_MEMORY_DESCRIPTOR) % sizeof (UINT64)),EFI_MEMORY_DESCRIPTOR_VERSION,0,NULL,NULL,FALSE,FALSE
};EFI_RUNTIME_ARCH_PROTOCOL *gRuntime = &gRuntimeTemplate;
// Image.c   每当调用LoadImage 就会把runtime driver 注册到链表Imagehead中if (Image->ImageContext.ImageType == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) {//// Make a list off all the RT images so we can let the RT AP know about them.//Image->RuntimeData = AllocateRuntimePool (sizeof(EFI_RUNTIME_IMAGE_ENTRY));if (Image->RuntimeData == NULL) {goto Done;}Image->RuntimeData->ImageBase      = Image->Info.ImageBase;Image->RuntimeData->ImageSize      = (UINT64) (Image->Info.ImageSize);Image->RuntimeData->RelocationData = Image->ImageContext.FixupData;Image->RuntimeData->Handle         = Image->Handle;InsertTailList (&gRuntime->ImageHead, &Image->RuntimeData->Link);InsertImageRecord (Image->RuntimeData);}
// 我们代码每次注册 gEfiEventVirtualAddressChangeGuid 的时候就会把相关信息收集到链表Eventhead中if ((Type & EVT_RUNTIME) != 0) {//// Keep a list of all RT events so we can tell the RT AP.//IEvent->RuntimeData.Type           = Type;IEvent->RuntimeData.NotifyTpl      = NotifyTpl;IEvent->RuntimeData.NotifyFunction = NotifyFunction;IEvent->RuntimeData.NotifyContext  = (VOID *) NotifyContext;IEvent->RuntimeData.Event          = (EFI_EVENT *) IEvent;InsertTailList (&gRuntime->EventHead, &IEvent->RuntimeData.Link);}
// DxeProtocolNotify.c 当Runtime.c 跑起来并且注册EFI_RUNTIME_ARCH_PROTOCOL 就会把临时数据拷贝到新的Runtime_Arch_Protocol上// Copy all the registered Image to new gRuntime protocol//for (Link = gRuntimeTemplate.ImageHead.ForwardLink; Link != &gRuntimeTemplate.ImageHead; Link = TempLinkNode.ForwardLink) {CopyMem (&TempLinkNode, Link, sizeof(LIST_ENTRY));InsertTailList (&gRuntime->ImageHead, Link);}//// Copy all the registered Event to new gRuntime protocol//for (Link = gRuntimeTemplate.EventHead.ForwardLink; Link != &gRuntimeTemplate.EventHead; Link = TempLinkNode.ForwardLink) {CopyMem (&TempLinkNode, Link, sizeof(LIST_ENTRY));InsertTailList (&gRuntime->EventHead, Link);}

接下来是UEFI Runtime Core 入口函数首先会安装EfiRuntimeArchProtocol , 根据上面介绍mRuntime 的ImageHead, EventHead 会在DxeProtocolNotify.c 中把比RuntimeDxe Driver 更早跑的runtime 类型驱动和相关runtime event 重新copy 到Runtime.c 中的mRuntime 结构体里,以后一直会用Runtime.c 中的mRuntime. 在这个驱动中,我们可以看到两个核心函数

  1. RuntimeDriverSetVirtualAddressMap
  2. RuntimeDriverConvertPointer

在RuntimeCore 之前我们应该知道我们目前处理器处于什么状态,以X86-64 为例,我们目前已经处于long mode (PE,PAE , PG , LME 都是enable 状态),页表处于1:1 映射状态,对于页面是选择1G/2M/4K 那要看代码实现了。想检查也简单方法如下 PDPTE.PS ? 1G:(PDE.PS ? 2M : 4K) 建立1:1 页表可以参考 CreateIdentityMappingPageTables;

RuntimeCore 两个核心函数RuntimeDriverSetVirtualAddressMap, RuntimeDriverConvertPointer
RuntimeDriverSetVirtualAddressMap 是OS 调用的,调用这个function 之前,OS 会调用GetMemoryMap,我们知道MemoryMap 会得到EFI_MEMORY_DESCRIPTOR 如下所示,OS 会把所有具有Runtime 属性的EFI_MEMORY_DESCRIPTOR 中的VirtualStart 填入,然后把这些MemoryMap 描述符一起传入到RuntimeDriverSetVirtualAddressMap 这个function 中,所以RuntimeDriverConvertPointer 就知道了虚拟地址和物理地址的转换关系,RuntimeDriverSetVirtualAddressMap 要做的就是把所有Runtime Driver 进行重定位,对于注册了gEfiEventVirtualAddressChangeGuid Events 进行地址转换。关于PE 进行重定位挺简单的,可以参考

UEFI 文件类型

typedef struct {UINT32                Type;EFI_PHYSICAL_ADDRESS  PhysicalStart;EFI_VIRTUAL_ADDRESS   VirtualStart;UINT64                NumberOfPages;UINT64                Attribute;
} EFI_MEMORY_DESCRIPTOR;

这些就是整个UEFI Runtime Core 的逻辑,单看UEFI 部分还比较简单容易理解。但是从UEFI到OS 一个整体就会有一些难度比如:

  1. 我们知道RuntimeDriver 是被RuntimeCore中的RuntimeDriverSetVirtualAddressMap
    进行重定位和地址转换之后才会被OS 访问,但是RuntimeDriverSetVirtualAddressMap 是直接被OS调用的,那么当时OS 页表和UEFI 一样么?直接调用会不会有问题?

  2. OS 是多任务,那么有没有可能比如RuntimeDriver 和 其他进程同时访问设备资源,会不会有问题?

  3. OS 对于性能和并发的互斥性比较在意的,那么如果RuntimeDriver 比较耗时会不会影响操作系统性能? RuntimeDriver 是否可以多进程同时访问,访问RuntimeDriver 用了什么样的同步机制。
    等等问题需要我们去研究,OS 相关问题之后博客会更新

针对第二个问题引用EBBR 中的一段话

2.5.1. Runtime Device Mappings
Firmware shall not create runtime mappings, or perform any runtime IO that will conflict with device access by the OS. Normally this means a device may be controlled by firmware, or controlled by the OS, but not both. e.g. If firmware attempts to access an eMMC device at runtime then it will conflict with transactions being performed by the OS.
Devices that are provided to the OS (i.e., via PCIe discovery or ACPI/DT description) shall not be accessed by firmware at runtime. Similarly, devices retained by firmware (i.e., not discoverable by the OS) shall not be accessed by the OS.
Only devices that explicitly support concurrent access by both firmware and an OS may be mapped at runtime by both firmware and the OS.

UEFI Runtime Drivers相关推荐

  1. UEFI Specification 第二章 概述

    UEFI支持通过加载UEFI驱动和UEFI应用程序映象来扩展平台固件.当UEFI驱动程序和UEFI应用程序加载时,它们可以访问所有UEFI定义的运行时和引导服务. UEFI允许将OS加载程序和平台固件 ...

  2. 探秘主流BIOS架构:UEFI

    回顾计算机的发展历史,并不是一开始就形成了现在的硬件+固件+操作系统这样的分工合作.在大型机时代(20世纪60年代),比如<人月神话>中曾经介绍过的IBM System/360,其硬件.操 ...

  3. UEFI Shell命令详解,自写一个UEFI Shell命令

    首先,我们从BIOS进入Shell,输入help命令查看帮助信息 Shell:helpacpiview - Display ACPI Table information. alias - Displa ...

  4. Linux 下调用UEFI的函数

    Linux 下调用UEFI的函数 摘要 Linux 调用UEFI function 时候调用约定的转换 64bits calling convention Microsoft calling conv ...

  5. udk开发-稀里糊涂

    一.EDK2简介 1.EDK2工作流 ​ 二.EDK2 Packages 1.Packages介绍 ​ EDK2 Packages是一个容器,其中包含一组模块及模块的相关定义.每个Package是一个 ...

  6. EDK2设备驱动模型

    UEFI设备驱动模型 1. UEFI Drivers UEFI Drivers是UEFI Image的一种,UEFI Drivers与UEFI Applications的区别: Objects man ...

  7. Linux内核驱动之efi-rtc

    Linux内核驱动之efi-rtc 1. UEFI与BIOS概述 1.1. BIOS 概述 1.1.1. BIOS缺点: 1.1.2. BIOS的启动流程 1.2 UEFI 概述 1.2.1 Boot ...

  8. 【文献阅读】ShEF: Shielded Enclaves for Cloud FPGAs

    吐了,好tm难懂 Remote attestation is a method by which a host (client) authenticates it's hardware and sof ...

  9. EDK II Module Writers Guide下

    三.常见UEFI Module类型 1.UEFI APP ​ UEFI Application是EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION类型的EFI Image. UEF ...

最新文章

  1. Android Studio 配置虚拟设备的镜像文件的存放路径
  2. JDK源码解析 Integer类使用了享元模式
  3. php pdo mysql哪个好_php pdo和mysqli对比选择
  4. 【转载】#323 - A Generic Class is a Template for a Class
  5. python—gc.collect()清楚内存
  6. 【SICP练习】150 练习4.6
  7. st_aggrid pip下载失败问题
  8. 数字电路基础知识——组合逻辑电路(数据选择器MUX、多路复用器)
  9. Java 聊天室实现
  10. 史上最全的Go语言模块(Module)管理详解(基于Go1.19)
  11. VOIP技术的专业性网站
  12. Flutter 错误解决Building with plugins requires symlink support.
  13. PPPOE总结和配置
  14. 黑马点评关键业务流程梳理一
  15. linux设备驱动七(时间、延迟及延缓操作)
  16. EMD经验模态分解实例(转C代码)
  17. 期货开户手机APP有哪些?
  18. 基于华为云原生数据湖MRS HetuEgine的数据虚拟化实践
  19. 全球范围内12个典型区域提取(total runoff)
  20. vue-render

热门文章

  1. 那些年,我以为产品经理就是市场竞争的胜负手...
  2. wsl使用可视化界面_win10安装子系统ubuntu附带图形化界面
  3. 2019同济夏令营考核+试前准备
  4. 城巴站小偷假扮摩的司机,慎防
  5. 推荐系统实践--基于邻域的社会化推荐算法
  6. 【华为OD机试】分班【2023 B卷|100分】
  7. 通用配置——Nginx优化性能
  8. 开机引导项多了onekey ghost,怎么删除?
  9. 思科路由器、交换机配置Console 线线序 (亲测)
  10. java weka搭建_weka的安装和配置