如何理解UEFI的事件机制(三)——时钟中断

一,时钟中断概述

UEFI 中的EVENT是使用时钟中断来驱动的。
在时钟中断处理函数中,它会检查系统中的定时器事件并处理到期的定时器事件,并在合适的时机调度事件的Notify函数,是事件的实现基础。时钟中断在DXE的主函数DxeMain中初始化(准确的说是在初始化事件时)并开始使用,具体的流程请看下图——当时作为UEFI小白的我画了一天!其实后来看,有一些是架构协议与触发函数注册的内容,待会浅提一下。
在这里插入图片描述

二,时钟中断执行流程

执行时钟中断的单元函数是CoreTimerTick,我剧透一下,因为它被注册为mTimerNotifyFunction,所以在其他架构下看到的mTimerNotifyFunction函数其实就对应CoreTimerTick。
当然你也可以叛逆期到了自己写一个……

CoreTimerTick (IN UINT64  Duration)
{IEVENT  *Event;CoreAcquireLock (&mEfiSystemTimeLock);mEfiSystemTime += Duration;if (!IsListEmpty (&mEfiTimerList)) {Event = CR (mEfiTimerList.ForwardLink, IEVENT, Timer.Link, EVENT_SIGNATURE);if (Event->Timer.TriggerTime <= mEfiSystemTime) {CoreSignalEvent (mEfiCheckTimerEvent);}}CoreReleaseLock (&mEfiSystemTimeLock);
}

其实源代码里注释写的很清楚了
首先升高优先级,更新系统时间,这里的优先级是31——UEFI中定义的最高优先级,不会被抢断
mEfiTimerList是所有定时事件,按触发时间从近到远排列,如果第一个没有被触发,说明没有定时器事件到期,反之,开始触发定时器事件。
不过需要注意一点,我之前写过事件在升高降低优先级时进行调度,这里也进行了这一行为,但实际上,在进这个函数时优先级已经很高了,所以调度并不是在这里,而是在另一个函数里,这就涉及到时钟中断的注册流程。

三,时钟中断的注册流程

时钟中断的注册分为好几部分,我们先来看最简单的部分好了

句柄注册与触发函数

在这里插入图片描述

(好大的图片!)
这段就是我之前说的,不应该放在这里的部分,稍微提一提就好了。
我们都知道BDS阶段的入口是BdsEntry函数,然而它是怎么跳过去的呢?这就涉及架构协议注册与触发函数机制(我起的名)
在DXE中,定义了以下协议为架构协议,这些协议大多承担了比较重要的架构功能

EFI_CORE_PROTOCOL_NOTIFY_ENTRY  mArchProtocols[] = {{ &gEfiSecurityArchProtocolGuid,         (VOID **)&gSecurity,      NULL, NULL, FALSE },{ &gEfiCpuArchProtocolGuid,              (VOID **)&gCpu,           NULL, NULL, FALSE },{ &gEfiMetronomeArchProtocolGuid,        (VOID **)&gMetronome,     NULL, NULL, FALSE },{ &gEfiTimerArchProtocolGuid,            (VOID **)&gTimer,         NULL, NULL, FALSE },{ &gEfiBdsArchProtocolGuid,              (VOID **)&gBds,           NULL, NULL, FALSE },{ &gEfiWatchdogTimerArchProtocolGuid,    (VOID **)&gWatchdogTimer, NULL, NULL, FALSE },{ &gEfiRuntimeArchProtocolGuid,          (VOID **)&gRuntime,       NULL, NULL, FALSE },{ &gEfiVariableArchProtocolGuid,         (VOID **)NULL,            NULL, NULL, FALSE },{ &gEfiVariableWriteArchProtocolGuid,    (VOID **)NULL,            NULL, NULL, FALSE },{ &gEfiCapsuleArchProtocolGuid,          (VOID **)NULL,            NULL, NULL, FALSE },{ &gEfiMonotonicCounterArchProtocolGuid, (VOID **)NULL,            NULL, NULL, FALSE },{ &gEfiResetArchProtocolGuid,            (VOID **)NULL,            NULL, NULL, FALSE },{ &gEfiRealTimeClockArchProtocolGuid,    (VOID **)NULL,            NULL, NULL, FALSE },{ NULL,                                  (VOID **)NULL,            NULL, NULL, FALSE }
};

对于这些架构协议,DXE设计了一套机制,在主函数DxeMain中调用CoreNotifyOnProtocolInstallation给这些函数都注册了一个事件,当上述协议被模块Install时,事件会触发回调函数GenericProtocolNotify
在回调函数中,UEFI注册了传入的协议对应的Entry函数(比如对于Bds,这个函数就是BdsEntry),并且,当判断到传入的协议是gTimer时,会做如下处理

if (CompareGuid (Entry->ProtocolGuid, &gEfiTimerArchProtocolGuid)) {//// Register the Core timer tick handler with the Timer AP//gTimer->RegisterHandler (gTimer, CoreTimerTick);}

相当于,在gTimer被注册后,会调用gTimer->RegisterHandler (gTimer, CoreTimerTick);这个函数
这个函数其实就一行有用的

mTimerNotifyFunction = NotifyFunction;

其实就是把CoreTimerTick注册为时钟中断的原子函数

CPU视角下的时钟中断

在内核中,唯一不变的只有CPU一次次的打点,就像我灰暗的人生,唯一不变的只有、永远一秒一秒流逝的时间。
时钟中断的计时依赖于CPU,那么在CPU视角下,是如何注册时钟中断的呢?
这部分不同架构实现的也不太一样,而且涉及到汇编,有些复杂,这里的例子用的源码的OVM架构的,因为其他架构的我没看懂
在Ovm架构下,Timer模块初始化时运行了如下内容

Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&mCpu);
Status = gBS->LocateProtocol (&gEfiLegacy8259ProtocolGuid, NULL, (VOID **)&mLegacy8259);
TimerVector = 0;
Status      = mLegacy8259->GetVector (mLegacy8259, Efi8259Irq0, (UINT8 *)&TimerVector);
Status = mCpu->RegisterInterruptHandler (mCpu, TimerVector, TimerInterruptHandler);

前两行,分别找到打开的CPU架构和8259协议,8259是芯片,它的IRQ0记录了时钟中断的中断号,在第四行被提取了出来
然后调用RegisterInterruptHandler将传入的中断号为默认中断(TimerVector),并且保存了函数指针到ExternalVectorTable[InterruptType]
在CPU驱动中,调用CommonInterruptEntry进入中断,这个函数只干三件事,保存寄存器、调用ExternalVectorTable[InterruptType]、恢复寄存器。
这部分戴正华老师的书里描述的很详细,我就不写了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/354187.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

UEFI原理与编程(七):包及.dsc、.dec、.fdf文件

包及.dsc、.dec、.fdf文件 前言 前面的文章中比较详细介绍了UEFI工程文件即.inf。UEFI的包中一般都会包含一个.dsc文件和一个dec文件。在包生成固件Image、Option Rom Image&#xff0c;这个包还要包含.fdf文件。.fdf用于生成固件Image、Option Rom Image或可以启动Image。 b…

UEFI学习——在qemu上读取设备PCI信息

1.编写读取设备PCI信息的Application 代码参考罗斌大佬&#xff0c;博客地址&#xff1a;UEFI开发探索13 – 访问PCI/PCI-E设备1 感谢罗斌大佬的贡献&#xff0c;让我在学习UEFI的道路上站在了巨人的肩膀上。 代码&#xff1a; #include <Uefi.h> #include <L…

UEFI开发与调试---ImageHandle和ControllerHandle

##1.ImageHandle 每个uefi module都是一个image&#xff0c;而每个image对应都有一个ImageHandle&#xff0c;其实ImageHandle的类型就是EFI_HANDLE typedef VOID *EFI_HANDLE;因此实际上每个ImageHandle是一个void*指针&#xff0c;那么也就是说任何结…

UEFI的诞生与优势

UEFI的诞生 随着CPU及其他硬件设备的技术革新&#xff0c;CPU和操作系统已支持到64位&#xff0c;而BIOS却还停留在16位&#xff1b;主板上更加丰富多样的扩展设备&#xff0c;也让BIOS愈加无能为力&#xff0c;使得硬件无法完成初始化。现实表明&#xff0c;BIOS已经无法满足…

UEFI shell - 标准应用程序的编译和加载过程

一、标准应用工程编译 首先了解下,应用程序是怎么被编译成.efi文件: UefiMain.c首先被编译成目标文件UefiMain.obj连接器将目标文件UefiMain.obj和其他库连接成UefiMain.dllGenFw工具将UefiMain.dll转换成UefiMain.efi 说明:连接器在生成UefiMain.dll时使用了/dll/entry:_Mo…

UEFI开发,记录第一场胜利——调用一个自己编写的protocol

本文参考BIOS/UEFI基础——Protocol介绍 大四第一个签三方的工作&#xff0c;BIOS的开发&#xff0c;第一次接触这个领域&#xff0c;在实习之前很好奇&#xff0c;也很有兴趣&#xff0c;但是学习BIOS在一开始注定要碰多次碰壁&#xff0c;实习第三周第二天&#xff0c;终于写…

linux系统nohob安装,Linux启动详解1

一、固件运行 本部分主要参考 戴正华 著《UEFI原理与编程》 CPU在加电后会进入16位实模式状态运行&#xff0c;同时CPU的逻辑电路设计为加电瞬间将CS的值设置为 0xF000、IP的值置为0xFFF0&#xff0c;这样CS&#xff1a;IP就指向0xFFFF0这个地址位置。然后开始执行固件 固件的执…

《UEFI原理与编程》读书笔记

《UEFI原理与编程》读书笔记 读书笔记仅摘取对个人有用的部分&#xff0c;详细内容请阅读原著 目前更新至第七章&#xff0c;因个人仅需要引导程序&#xff0c;故其余部分未介绍&#xff0c;详细内容请阅读原著 书及其资料均已放置在本人资源中&#xff0c;如需下载请移步资源模…

UEFI简介

前言 大多数人接触UEFI都是在PC的应用场景上&#xff0c;有在PC上安装过多操作系统的经历的同学&#xff0c;通常会进入UEFI界面设置操作系统引导顺序、CPU虚拟化等设置。UEFI诞生之初也确实是作为BIOS的替代者&#xff0c;主要应用在PC电脑上。随着手机/平板等移动设备的发展&…

10月书讯(上) | 小长假我读这些新书

华章科技提前祝大家国庆快乐 7天小长假&#xff0c;正是读书好时节 又到上新季&#xff0c;读书与休假更配哦 10月书讯&#xff08;上&#xff09;请查收 快来看看哪本书最属你心意 参与文末赠书活动&#xff0c;好书就要抢先读 — 新书速览 — 1、《计算机系统解密&#xff1a…

UEFI启动流程浅析

BIOS启动流程 SEC&#xff08;Security Phase&#xff0c;安全阶段&#xff09;阶段 SEC阶段是平台初始话的第一个阶段&#xff0c;计算机系统加电后首先进入这个阶段。 CPU上电之后&#xff0c;首先会进行硬件初始化&#xff08;hard reset&#xff09; 其次会进行可选的自检…

UEFI规范实现EDKII项目学习笔记绪论[0]

UEFI规范实现EDKII项目学习笔记绪论[0] 2015-07-10 北京海淀区 张俊浩 这段时间在学习UEFI( Unified Extensible Firmware Interface,统一的可扩展固件接口)&#xff0c;熟悉EDKII&#xff08;EFI Developer KitII&#xff0c;EFI开发工具包&#xff09;项目&#xff0c;…

3.UEFI-edk2 增加中文显示

UEFI-edk2源码中默认只有英文和法文的字库&#xff0c;在UI界面上或者shell终端打印中文字符串&#xff0c;则无法显示。例如&#xff0c;上一篇博客中的TestoneApp.cpp中&#xff0c;增加一行带中文字符串的打印&#xff1a; Print(L"Hello, world!\r\n");Print(L&…

UEFI原理与编程(一)

第一章 UEFI概述(Unified Extensible Firmware Interface 统一的可扩展固件接口) 常见缩写及描述&#xff1a; 缩略词全名描述UEFIUnified Extensible Firmware Interface统一的可扩展固件接口BSBoot Services启动服务RTRuntime Service运行时服务BIOSBasic Input Output Sys…

UEFI学习——事件函数WaitForEvent和CreateEvent/CreateEventEx

本文参考戴正华《UEFI原理与编程》 1.等待事件的服务WaitForEvent 启动服务中的WaitForEvent服务的函数原型&#xff1a; /**等待Event数组内任一事件被触发retval EFI_SUCCESS 下表为*index的事件被触发retval EFI_UNSUPPORTED 当前的TPL不是TPL_AP…

开宗明义—UEFI介绍 (二)

UEFI介绍 声明 上一篇介绍了UEFI的发展历史&#xff0c;以及对UEFI在ARM嵌入式领域的生态状况做了简单的调研。本篇旨在对UEFI规范和PI规范的内容以及二者之间的关系做一个简单的梳理。 本篇参考内容主要来源于以下3方面&#xff1a; (1) 微信公众号“ Wolf UEFI社区 ”系列文章…

UEFI学习——使用gRT->GetVariable读取Setup选项值

先列出代码&#xff0c;程序的解释在后面。 代码&#xff1a; #include <Uefi.h> #include <Library/UefiBootServicesTableLib.h> #include <Library/UefiRuntimeServicesTableLib.h> #include <Library/DebugLib.h> #include <Universal\DriverS…

2.1 对称量量化和非对称量化

前言 int8的数据范围可以表示为-128到127之间的整数 uint8的数据范围可以表示为0到255之间的整数 注释&#xff1a;int8就是用8个比特位来保存整数&#xff0c;第一位用来表示符号。uint8表示无符号整数&#xff0c;没有符号位&#xff0c;8个比特位全部用来表示整数。 1.问题…

Zinx框架学习 - 连接管理

Zinx - V0.9 连接管理 每个服务器的能够处理的最大IO数量是有限的&#xff0c;根据当前服务器能开辟的IO数量决定&#xff0c;最终决定权是内存大小现在我们要为Zinx框架增加链接个数的限定&#xff0c;如果超过⼀定量的客户端个数&#xff0c;Zinx为了保证后端的及时响应&…

澳网:公茂鑫/张择创历史 中国男网夺大满贯首胜

资料图&#xff1a;张择在比赛中。 中新网1月16日电 16日&#xff0c;2019年澳大利亚网球公开赛男双第一轮展开争夺&#xff0c;中国组合张择/公茂鑫苦战3盘战胜马特沃斯基/克里赞&#xff0c;收获中国男网在大满贯正赛的首场胜利&#xff0c;创造历史。 首盘比赛&#xff0c…