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

本文深入解析UEFI中的时钟中断,包括时钟中断如何驱动EVENT,执行流程以及注册机制。时钟中断处理函数CoreTimerTick负责更新系统时间并处理到期定时器事件。注册流程中,CoreTimerTick被注册为时钟中断的原子函数。此外,还探讨了在CPU视角下如何注册时钟中断,涉及CPU架构和中断处理。
摘要由CSDN通过智能技术生成

一,时钟中断概述

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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值