IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 如何理解UEFI的事件机制(三)——时钟中断 -> 正文阅读

[嵌入式]如何理解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]、恢复寄存器。
这部分戴正华老师的书里描述的很详细,我就不写了。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-04-14 00:28:41  更:2022-04-14 00:47:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 5:26:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码