From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: mx.groups.io; dkim=missing; spf=softfail (domain: citrix.com, ip: , mailfrom: anthony.perard@citrix.com) Received: from esa3.hc3370-68.iphmx.com (esa3.hc3370-68.iphmx.com []) by groups.io with SMTP; Thu, 04 Jul 2019 07:57:58 -0700 Authentication-Results: esa3.hc3370-68.iphmx.com; dkim=none (message not signed) header.i=none; spf=None smtp.pra=anthony.perard@citrix.com; spf=Pass smtp.mailfrom=anthony.perard@citrix.com; spf=None smtp.helo=postmaster@mail.citrix.com Received-SPF: None (esa3.hc3370-68.iphmx.com: no sender authenticity information available from domain of anthony.perard@citrix.com) identity=pra; client-ip=162.221.158.21; receiver=esa3.hc3370-68.iphmx.com; envelope-from="anthony.perard@citrix.com"; x-sender="anthony.perard@citrix.com"; x-conformance=sidf_compatible Received-SPF: Pass (esa3.hc3370-68.iphmx.com: domain of anthony.perard@citrix.com designates 162.221.158.21 as permitted sender) identity=mailfrom; client-ip=162.221.158.21; receiver=esa3.hc3370-68.iphmx.com; envelope-from="anthony.perard@citrix.com"; x-sender="anthony.perard@citrix.com"; x-conformance=sidf_compatible; x-record-type="v=spf1"; x-record-text="v=spf1 ip4:209.167.231.154 ip4:178.63.86.133 ip4:195.66.111.40/30 ip4:85.115.9.32/28 ip4:199.102.83.4 ip4:192.28.146.160 ip4:192.28.146.107 ip4:216.52.6.88 ip4:216.52.6.188 ip4:162.221.158.21 ip4:162.221.156.83 ~all" Received-SPF: None (esa3.hc3370-68.iphmx.com: no sender authenticity information available from domain of postmaster@mail.citrix.com) identity=helo; client-ip=162.221.158.21; receiver=esa3.hc3370-68.iphmx.com; envelope-from="anthony.perard@citrix.com"; x-sender="postmaster@mail.citrix.com"; x-conformance=sidf_compatible IronPort-SDR: XwY+2LcYU7eJILgFTrrMso06eXHcC4hwloCVWyC/QyHdmrnu/N6xzMfF4cBM8ucApkIc46OXGG nKdlR+Lvt7mJbVTEl9DZJZcX5ZJrABbJBb7iy3OotolrKIMJRHJFJVHVpEthiXKHuJZeXXSkpq 5eLnfvR/5wVU8OYhMWQqNX5l7x2KHROr42ldNwMoKz8Q0fflBq0grGiHSocO/0rfD0gYieJp2C 2CeXc8vhk+knlo9waYbLPPY2JTj750tdSIUE9+y2IJbk8LjJFPqMoauYK4Qws8wmZ0XlyvBz2T 6/E= X-SBRS: 2.7 X-MesageID: 2602730 X-Ironport-Server: esa3.hc3370-68.iphmx.com X-Remote-IP: 162.221.158.21 X-Policy: $RELAYED X-IronPort-AV: E=Sophos;i="5.63,451,1557201600"; d="scan'208";a="2602730" From: "Anthony PERARD" To: CC: , Ard Biesheuvel , Jordan Justen , Laszlo Ersek , Julien Grall , Anthony PERARD Subject: [PATCH v3 31/35] OvmfPkg/OvmfXen: Introduce XenTimerDxe Date: Thu, 4 Jul 2019 15:42:29 +0100 Message-ID: <20190704144233.27968-32-anthony.perard@citrix.com> X-Mailer: git-send-email 2.22.0 In-Reply-To: <20190704144233.27968-1-anthony.perard@citrix.com> References: <20190704144233.27968-1-anthony.perard@citrix.com> MIME-Version: 1.0 Return-Path: anthony.perard@citrix.com Content-Transfer-Encoding: quoted-printable Content-Type: text/plain "PcAtChipsetPkg/8254TimerDxe" is replaced with a Xen-specific EFI_TIMER_ARCH_PROTOCOL implementation. Also remove 8259InterruptControllerDxe as it is not used anymore. This Timer uses the local APIC timer as time source as it can work on both a Xen PVH guest and an HVM one. Based on the "PcAtChipsetPkg/8254TimerDxe" implementation. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=3D1689 Signed-off-by: Anthony PERARD --- Notes: v3: - rebased, SPDX, copyright =20=20=20=20 v2: - Use InitializeApicTimer instead of WriteLocalApicReg - rework comments (remove many that don't apply) - remove unused includes, and libs - have a macro to the timervector. - cleanup, copyright - rework calculation of TimerCount, value to be use by the APIC timer - check for overflow of TimerPeriod, with the apic timer, the period can be up to about 42s on Xen (or even higher by changing the DivideValue= ). OvmfPkg/OvmfXen.dsc | 3 +- OvmfPkg/OvmfXen.fdf | 3 +- OvmfPkg/XenTimerDxe/XenTimerDxe.inf | 42 ++++ OvmfPkg/XenTimerDxe/XenTimerDxe.h | 177 ++++++++++++++ OvmfPkg/XenTimerDxe/XenTimerDxe.c | 355 ++++++++++++++++++++++++++++ 5 files changed, 576 insertions(+), 4 deletions(-) create mode 100644 OvmfPkg/XenTimerDxe/XenTimerDxe.inf create mode 100644 OvmfPkg/XenTimerDxe/XenTimerDxe.h create mode 100644 OvmfPkg/XenTimerDxe/XenTimerDxe.c diff --git a/OvmfPkg/OvmfXen.dsc b/OvmfPkg/OvmfXen.dsc index bc6b6602c6..1ecae3fb45 100644 --- a/OvmfPkg/OvmfXen.dsc +++ b/OvmfPkg/OvmfXen.dsc @@ -547,10 +547,9 @@ [Components] MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf=0D =0D MdeModulePkg/Universal/EbcDxe/EbcDxe.inf=0D - OvmfPkg/8259InterruptControllerDxe/8259.inf=0D + OvmfPkg/XenTimerDxe/XenTimerDxe.inf=0D UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf=0D UefiCpuPkg/CpuDxe/CpuDxe.inf=0D - OvmfPkg/8254TimerDxe/8254Timer.inf=0D OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.inf= =0D OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.inf=0D MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf {=0D diff --git a/OvmfPkg/OvmfXen.fdf b/OvmfPkg/OvmfXen.fdf index 49997fee9b..fa0830a324 100644 --- a/OvmfPkg/OvmfXen.fdf +++ b/OvmfPkg/OvmfXen.fdf @@ -298,10 +298,9 @@ [FV.DXEFV] INF MdeModulePkg/Core/RuntimeDxe/RuntimeDxe.inf=0D INF MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf=0D INF MdeModulePkg/Universal/EbcDxe/EbcDxe.inf=0D -INF OvmfPkg/8259InterruptControllerDxe/8259.inf=0D +INF OvmfPkg/XenTimerDxe/XenTimerDxe.inf=0D INF UefiCpuPkg/CpuIo2Dxe/CpuIo2Dxe.inf=0D INF UefiCpuPkg/CpuDxe/CpuDxe.inf=0D -INF OvmfPkg/8254TimerDxe/8254Timer.inf=0D INF OvmfPkg/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.= inf=0D INF OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.inf=0D INF MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf=0D diff --git a/OvmfPkg/XenTimerDxe/XenTimerDxe.inf b/OvmfPkg/XenTimerDxe/XenT= imerDxe.inf new file mode 100644 index 0000000000..add1d01bbf --- /dev/null +++ b/OvmfPkg/XenTimerDxe/XenTimerDxe.inf @@ -0,0 +1,42 @@ +## @file=0D +# Local APIC timer driver that provides Timer Arch protocol.=0D +#=0D +# Copyright (c) 2005 - 2019, Intel Corporation. All rights reserved.
=0D +# Copyright (c) 2019, Citrix Systems, Inc.=0D +#=0D +# SPDX-License-Identifier: BSD-2-Clause-Patent=0D +#=0D +##=0D +=0D +[Defines]=0D + INF_VERSION =3D 0x00010005=0D + BASE_NAME =3D XenTimerDxe=0D + FILE_GUID =3D 52fe8196-f9de-4d07-b22f-51f77a0e7c41= =0D + MODULE_TYPE =3D DXE_DRIVER=0D + VERSION_STRING =3D 1.0=0D +=0D + ENTRY_POINT =3D TimerDriverInitialize=0D +=0D +[Packages]=0D + MdePkg/MdePkg.dec=0D + UefiCpuPkg/UefiCpuPkg.dec=0D + OvmfPkg/OvmfPkg.dec=0D +=0D +[LibraryClasses]=0D + UefiBootServicesTableLib=0D + BaseLib=0D + DebugLib=0D + UefiDriverEntryPoint=0D + LocalApicLib=0D +=0D +[Sources]=0D + XenTimerDxe.h=0D + XenTimerDxe.c=0D +=0D +[Protocols]=0D + gEfiCpuArchProtocolGuid ## CONSUMES=0D + gEfiTimerArchProtocolGuid ## PRODUCES=0D +[Pcd]=0D + gEfiMdePkgTokenSpaceGuid.PcdFSBClock ## CONSUMES=0D +[Depex]=0D + gEfiCpuArchProtocolGuid=0D diff --git a/OvmfPkg/XenTimerDxe/XenTimerDxe.h b/OvmfPkg/XenTimerDxe/XenTim= erDxe.h new file mode 100644 index 0000000000..e0a3d95fd0 --- /dev/null +++ b/OvmfPkg/XenTimerDxe/XenTimerDxe.h @@ -0,0 +1,177 @@ +/** @file=0D + Private data structures=0D +=0D +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.
=0D +Copyright (c) 2019, Citrix Systems, Inc.=0D +=0D +SPDX-License-Identifier: BSD-2-Clause-Patent=0D +**/=0D +=0D +#ifndef _TIMER_H_=0D +#define _TIMER_H_=0D +=0D +#include =0D +=0D +#include =0D +#include =0D +=0D +#include =0D +=0D +#include =0D +#include =0D +#include =0D +#include =0D +#include =0D +=0D +// The default timer tick duration is set to 10 ms =3D 100000 100 ns units= =0D +//=0D +#define DEFAULT_TIMER_TICK_DURATION 100000=0D +=0D +//=0D +// The Timer Vector use for interrupt=0D +//=0D +#define LOCAL_APIC_TIMER_VECTOR 32=0D +=0D +//=0D +// Function Prototypes=0D +//=0D +/**=0D + Initialize the Timer Architectural Protocol driver=0D +=0D + @param ImageHandle ImageHandle of the loaded driver=0D + @param SystemTable Pointer to the System Table=0D +=0D + @retval EFI_SUCCESS Timer Architectural Protocol created=0D + @retval EFI_OUT_OF_RESOURCES Not enough resources available to initial= ize driver.=0D + @retval EFI_DEVICE_ERROR A device error occurred attempting to ini= tialize the driver.=0D +=0D +**/=0D +EFI_STATUS=0D +EFIAPI=0D +TimerDriverInitialize (=0D + IN EFI_HANDLE ImageHandle,=0D + IN EFI_SYSTEM_TABLE *SystemTable=0D + )=0D +;=0D +=0D +/**=0D +=0D + This function adjusts the period of timer interrupts to the value specif= ied=0D + by TimerPeriod. If the timer period is updated, then the selected timer= =0D + period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. = If=0D + the timer hardware is not programmable, then EFI_UNSUPPORTED is returned= .=0D + If an error occurs while attempting to update the timer period, then the= =0D + timer hardware will be put back in its state prior to this call, and=0D + EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer inter= rupt=0D + is disabled. This is not the same as disabling the CPU's interrupts.=0D + Instead, it must either turn off the timer hardware, or it must adjust t= he=0D + interrupt controller so that a CPU interrupt is not generated when the t= imer=0D + interrupt fires.=0D +=0D +=0D + @param This The EFI_TIMER_ARCH_PROTOCOL instance.=0D + @param NotifyFunction The rate to program the timer interrupt in 100 nS= units. If=0D + the timer hardware is not programmable, then EFI_= UNSUPPORTED is=0D + returned. If the timer is programmable, then the= timer period=0D + will be rounded up to the nearest timer period th= at is supported=0D + by the timer hardware. If TimerPeriod is set to = 0, then the=0D + timer interrupts will be disabled.=0D +=0D + @retval EFI_SUCCESS The timer period was changed.=0D + @retval EFI_UNSUPPORTED The platform cannot change the period o= f the timer interrupt.=0D + @retval EFI_DEVICE_ERROR The timer period could not be changed d= ue to a device error.=0D +=0D +**/=0D +EFI_STATUS=0D +EFIAPI=0D +TimerDriverRegisterHandler (=0D + IN EFI_TIMER_ARCH_PROTOCOL *This,=0D + IN EFI_TIMER_NOTIFY NotifyFunction=0D + )=0D +;=0D +=0D +/**=0D +=0D + This function adjusts the period of timer interrupts to the value specif= ied=0D + by TimerPeriod. If the timer period is updated, then the selected timer= =0D + period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. = If=0D + the timer hardware is not programmable, then EFI_UNSUPPORTED is returned= .=0D + If an error occurs while attempting to update the timer period, then the= =0D + timer hardware will be put back in its state prior to this call, and=0D + EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer inter= rupt=0D + is disabled. This is not the same as disabling the CPU's interrupts.=0D + Instead, it must either turn off the timer hardware, or it must adjust t= he=0D + interrupt controller so that a CPU interrupt is not generated when the t= imer=0D + interrupt fires.=0D +=0D +=0D + @param This The EFI_TIMER_ARCH_PROTOCOL instance.=0D + @param TimerPeriod The rate to program the timer interrupt in 100 nS= units. If=0D + the timer hardware is not programmable, then EFI_= UNSUPPORTED is=0D + returned. If the timer is programmable, then the= timer period=0D + will be rounded up to the nearest timer period th= at is supported=0D + by the timer hardware. If TimerPeriod is set to = 0, then the=0D + timer interrupts will be disabled.=0D +=0D + @retval EFI_SUCCESS The timer period was changed.=0D + @retval EFI_UNSUPPORTED The platform cannot change the period o= f the timer interrupt.=0D + @retval EFI_DEVICE_ERROR The timer period could not be changed d= ue to a device error.=0D +=0D +**/=0D +EFI_STATUS=0D +EFIAPI=0D +TimerDriverSetTimerPeriod (=0D + IN EFI_TIMER_ARCH_PROTOCOL *This,=0D + IN UINT64 TimerPeriod=0D + )=0D +;=0D +=0D +/**=0D +=0D + This function retrieves the period of timer interrupts in 100 ns units,= =0D + returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPer= iod=0D + is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 = is=0D + returned, then the timer is currently disabled.=0D +=0D +=0D + @param This The EFI_TIMER_ARCH_PROTOCOL instance.=0D + @param TimerPeriod A pointer to the timer period to retrieve in 100 = ns units. If=0D + 0 is returned, then the timer is currently disabl= ed.=0D +=0D + @retval EFI_SUCCESS The timer period was returned in TimerPer= iod.=0D + @retval EFI_INVALID_PARAMETER TimerPeriod is NULL.=0D +=0D +**/=0D +EFI_STATUS=0D +EFIAPI=0D +TimerDriverGetTimerPeriod (=0D + IN EFI_TIMER_ARCH_PROTOCOL *This,=0D + OUT UINT64 *TimerPeriod=0D + )=0D +;=0D +=0D +/**=0D +=0D + This function generates a soft timer interrupt. If the platform does not= support soft=0D + timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCE= SS is returned.=0D + If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.Reg= isterHandler()=0D + service, then a soft timer interrupt will be generated. If the timer int= errupt is=0D + enabled when this service is called, then the registered handler will be= invoked. The=0D + registered handler should not be able to distinguish a hardware-generate= d timer=0D + interrupt from a software-generated timer interrupt.=0D +=0D +=0D + @param This The EFI_TIMER_ARCH_PROTOCOL instance.=0D +=0D + @retval EFI_SUCCESS The soft timer interrupt was generated.=0D + @retval EFI_UNSUPPORTED The platform does not support the generation o= f soft timer interrupts.=0D +=0D +**/=0D +EFI_STATUS=0D +EFIAPI=0D +TimerDriverGenerateSoftInterrupt (=0D + IN EFI_TIMER_ARCH_PROTOCOL *This=0D + )=0D +;=0D +=0D +#endif=0D diff --git a/OvmfPkg/XenTimerDxe/XenTimerDxe.c b/OvmfPkg/XenTimerDxe/XenTim= erDxe.c new file mode 100644 index 0000000000..9f9e04766c --- /dev/null +++ b/OvmfPkg/XenTimerDxe/XenTimerDxe.c @@ -0,0 +1,355 @@ +/** @file=0D + Timer Architectural Protocol as defined in the DXE CIS=0D +=0D +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.
=0D +Copyright (c) 2019, Citrix Systems, Inc.=0D +=0D +SPDX-License-Identifier: BSD-2-Clause-Patent=0D +=0D +**/=0D +=0D +#include "XenTimerDxe.h"=0D +=0D +//=0D +// The handle onto which the Timer Architectural Protocol will be installe= d=0D +//=0D +EFI_HANDLE mTimerHandle =3D NULL;=0D +=0D +//=0D +// The Timer Architectural Protocol that this driver produces=0D +//=0D +EFI_TIMER_ARCH_PROTOCOL mTimer =3D {=0D + TimerDriverRegisterHandler,=0D + TimerDriverSetTimerPeriod,=0D + TimerDriverGetTimerPeriod,=0D + TimerDriverGenerateSoftInterrupt=0D +};=0D +=0D +//=0D +// Pointer to the CPU Architectural Protocol instance=0D +//=0D +EFI_CPU_ARCH_PROTOCOL *mCpu;=0D +=0D +//=0D +// The notification function to call on every timer interrupt.=0D +// A bug in the compiler prevents us from initializing this here.=0D +//=0D +EFI_TIMER_NOTIFY mTimerNotifyFunction;=0D +=0D +//=0D +// The current period of the timer interrupt=0D +//=0D +volatile UINT64 mTimerPeriod =3D 0;=0D +=0D +//=0D +// Worker Functions=0D +//=0D +/**=0D + Interrupt Handler.=0D +=0D + @param InterruptType The type of interrupt that occurred=0D + @param SystemContext A pointer to the system context when the interru= pt occurred=0D +**/=0D +VOID=0D +EFIAPI=0D +TimerInterruptHandler (=0D + IN EFI_EXCEPTION_TYPE InterruptType,=0D + IN EFI_SYSTEM_CONTEXT SystemContext=0D + )=0D +{=0D + EFI_TPL OriginalTPL;=0D +=0D + OriginalTPL =3D gBS->RaiseTPL (TPL_HIGH_LEVEL);=0D +=0D + SendApicEoi();=0D +=0D + if (mTimerNotifyFunction !=3D NULL) {=0D + //=0D + // @bug : This does not handle missed timer interrupts=0D + //=0D + mTimerNotifyFunction (mTimerPeriod);=0D + }=0D +=0D + gBS->RestoreTPL (OriginalTPL);=0D +}=0D +=0D +/**=0D +=0D + This function registers the handler NotifyFunction so it is called every= time=0D + the timer interrupt fires. It also passes the amount of time since the = last=0D + handler call to the NotifyFunction. If NotifyFunction is NULL, then the= =0D + handler is unregistered. If the handler is registered, then EFI_SUCCESS= is=0D + returned. If the CPU does not support registering a timer interrupt han= dler,=0D + then EFI_UNSUPPORTED is returned. If an attempt is made to register a h= andler=0D + when a handler is already registered, then EFI_ALREADY_STARTED is return= ed.=0D + If an attempt is made to unregister a handler when a handler is not regi= stered,=0D + then EFI_INVALID_PARAMETER is returned. If an error occurs attempting t= o=0D + register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ER= ROR=0D + is returned.=0D +=0D +=0D + @param This The EFI_TIMER_ARCH_PROTOCOL instance.=0D + @param NotifyFunction The function to call when a timer interrupt fire= s. This=0D + function executes at TPL_HIGH_LEVEL. The DXE Co= re will=0D + register a handler for the timer interrupt, so i= t can know=0D + how much time has passed. This information is u= sed to=0D + signal timer based events. NULL will unregister= the handler.=0D +=0D + @retval EFI_SUCCESS The timer handler was registered.= =0D + @retval EFI_UNSUPPORTED The platform does not support time= r interrupts.=0D + @retval EFI_ALREADY_STARTED NotifyFunction is not NULL, and a = handler is already=0D + registered.=0D + @retval EFI_INVALID_PARAMETER NotifyFunction is NULL, and a hand= ler was not=0D + previously registered.=0D + @retval EFI_DEVICE_ERROR The timer handler could not be reg= istered.=0D +=0D +**/=0D +EFI_STATUS=0D +EFIAPI=0D +TimerDriverRegisterHandler (=0D + IN EFI_TIMER_ARCH_PROTOCOL *This,=0D + IN EFI_TIMER_NOTIFY NotifyFunction=0D + )=0D +{=0D + //=0D + // Check for invalid parameters=0D + //=0D + if (NotifyFunction =3D=3D NULL && mTimerNotifyFunction =3D=3D NULL) {=0D + return EFI_INVALID_PARAMETER;=0D + }=0D +=0D + if (NotifyFunction !=3D NULL && mTimerNotifyFunction !=3D NULL) {=0D + return EFI_ALREADY_STARTED;=0D + }=0D +=0D + mTimerNotifyFunction =3D NotifyFunction;=0D +=0D + return EFI_SUCCESS;=0D +}=0D +=0D +/**=0D +=0D + This function adjusts the period of timer interrupts to the value specif= ied=0D + by TimerPeriod. If the timer period is updated, then the selected timer= =0D + period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. = If=0D + the timer hardware is not programmable, then EFI_UNSUPPORTED is returned= .=0D + If an error occurs while attempting to update the timer period, then the= =0D + timer hardware will be put back in its state prior to this call, and=0D + EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer inter= rupt=0D + is disabled. This is not the same as disabling the CPU's interrupts.=0D + Instead, it must either turn off the timer hardware, or it must adjust t= he=0D + interrupt controller so that a CPU interrupt is not generated when the t= imer=0D + interrupt fires.=0D +=0D +=0D + @param This The EFI_TIMER_ARCH_PROTOCOL instance.=0D + @param TimerPeriod The rate to program the timer interrupt in 100 nS= units. If=0D + the timer hardware is not programmable, then EFI_= UNSUPPORTED is=0D + returned. If the timer is programmable, then the= timer period=0D + will be rounded up to the nearest timer period th= at is supported=0D + by the timer hardware. If TimerPeriod is set to = 0, then the=0D + timer interrupts will be disabled.=0D +=0D + @retval EFI_SUCCESS The timer period was changed.=0D + @retval EFI_UNSUPPORTED The platform cannot change the period o= f the timer interrupt.=0D + @retval EFI_DEVICE_ERROR The timer period could not be changed d= ue to a device error.=0D +=0D +**/=0D +EFI_STATUS=0D +EFIAPI=0D +TimerDriverSetTimerPeriod (=0D + IN EFI_TIMER_ARCH_PROTOCOL *This,=0D + IN UINT64 TimerPeriod=0D + )=0D +{=0D + UINT64 TimerCount;=0D + UINT32 TimerFrequency;=0D + UINTN DivideValue =3D 1;=0D +=0D + if (TimerPeriod =3D=3D 0) {=0D + //=0D + // Disable timer interrupt for a TimerPeriod of 0=0D + //=0D + DisableApicTimerInterrupt();=0D + } else {=0D + TimerFrequency =3D PcdGet32(PcdFSBClock) / DivideValue;=0D +=0D + //=0D + // Convert TimerPeriod into local APIC counts=0D + //=0D + // TimerPeriod is in 100ns=0D + // TimerPeriod/10000000 will be in seconds.=0D + TimerCount =3D DivU64x32 (MultU64x32 (TimerPeriod, TimerFrequency),=0D + 10000000);=0D +=0D + // Check for overflow=0D + if (TimerCount > MAX_UINT32) {=0D + TimerCount =3D MAX_UINT32;=0D + /* TimerPeriod =3D (MAX_UINT32 / TimerFrequency) * 10000000; */=0D + TimerPeriod =3D 429496730;=0D + }=0D +=0D + //=0D + // Program the timer with the new count value=0D + //=0D + InitializeApicTimer(DivideValue, TimerCount, TRUE, LOCAL_APIC_TIMER_VE= CTOR);=0D +=0D + //=0D + // Enable timer interrupt=0D + //=0D + EnableApicTimerInterrupt();=0D + }=0D + //=0D + // Save the new timer period=0D + //=0D + mTimerPeriod =3D TimerPeriod;=0D +=0D + return EFI_SUCCESS;=0D +}=0D +=0D +/**=0D +=0D + This function retrieves the period of timer interrupts in 100 ns units,= =0D + returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPer= iod=0D + is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 = is=0D + returned, then the timer is currently disabled.=0D +=0D +=0D + @param This The EFI_TIMER_ARCH_PROTOCOL instance.=0D + @param TimerPeriod A pointer to the timer period to retrieve in 100 = ns units. If=0D + 0 is returned, then the timer is currently disabl= ed.=0D +=0D + @retval EFI_SUCCESS The timer period was returned in TimerPer= iod.=0D + @retval EFI_INVALID_PARAMETER TimerPeriod is NULL.=0D +=0D +**/=0D +EFI_STATUS=0D +EFIAPI=0D +TimerDriverGetTimerPeriod (=0D + IN EFI_TIMER_ARCH_PROTOCOL *This,=0D + OUT UINT64 *TimerPeriod=0D + )=0D +{=0D + if (TimerPeriod =3D=3D NULL) {=0D + return EFI_INVALID_PARAMETER;=0D + }=0D +=0D + *TimerPeriod =3D mTimerPeriod;=0D +=0D + return EFI_SUCCESS;=0D +}=0D +=0D +/**=0D +=0D + This function generates a soft timer interrupt. If the platform does not= support soft=0D + timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCE= SS is returned.=0D + If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.Reg= isterHandler()=0D + service, then a soft timer interrupt will be generated. If the timer int= errupt is=0D + enabled when this service is called, then the registered handler will be= invoked. The=0D + registered handler should not be able to distinguish a hardware-generate= d timer=0D + interrupt from a software-generated timer interrupt.=0D +=0D +=0D + @param This The EFI_TIMER_ARCH_PROTOCOL instance.=0D +=0D + @retval EFI_SUCCESS The soft timer interrupt was generated.=0D + @retval EFI_UNSUPPORTED The platform does not support the generation o= f soft timer interrupts.=0D +=0D +**/=0D +EFI_STATUS=0D +EFIAPI=0D +TimerDriverGenerateSoftInterrupt (=0D + IN EFI_TIMER_ARCH_PROTOCOL *This=0D + )=0D +{=0D + EFI_TPL OriginalTPL;=0D +=0D + if (GetApicTimerInterruptState()) {=0D + //=0D + // Invoke the registered handler=0D + //=0D + OriginalTPL =3D gBS->RaiseTPL (TPL_HIGH_LEVEL);=0D +=0D + if (mTimerNotifyFunction !=3D NULL) {=0D + //=0D + // @bug : This does not handle missed timer interrupts=0D + //=0D + mTimerNotifyFunction (mTimerPeriod);=0D + }=0D +=0D + gBS->RestoreTPL (OriginalTPL);=0D + } else {=0D + return EFI_UNSUPPORTED;=0D + }=0D +=0D + return EFI_SUCCESS;=0D +}=0D +=0D +/**=0D + Initialize the Timer Architectural Protocol driver=0D +=0D + @param ImageHandle ImageHandle of the loaded driver=0D + @param SystemTable Pointer to the System Table=0D +=0D + @retval EFI_SUCCESS Timer Architectural Protocol created=0D + @retval EFI_OUT_OF_RESOURCES Not enough resources available to initial= ize driver.=0D + @retval EFI_DEVICE_ERROR A device error occurred attempting to ini= tialize the driver.=0D +=0D +**/=0D +EFI_STATUS=0D +EFIAPI=0D +TimerDriverInitialize (=0D + IN EFI_HANDLE ImageHandle,=0D + IN EFI_SYSTEM_TABLE *SystemTable=0D + )=0D +{=0D + EFI_STATUS Status;=0D +=0D + //=0D + // Initialize the pointer to our notify function.=0D + //=0D + mTimerNotifyFunction =3D NULL;=0D +=0D + //=0D + // Make sure the Timer Architectural Protocol is not already installed i= n the system=0D + //=0D + ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiTimerArchProtocolGuid);=0D +=0D + //=0D + // Find the CPU architectural protocol.=0D + //=0D + Status =3D gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **= ) &mCpu);=0D + ASSERT_EFI_ERROR (Status);=0D +=0D + //=0D + // Force the timer to be disabled=0D + //=0D + Status =3D TimerDriverSetTimerPeriod (&mTimer, 0);=0D + ASSERT_EFI_ERROR (Status);=0D +=0D + //=0D + // Install interrupt handler for Local APIC Timer=0D + //=0D + Status =3D mCpu->RegisterInterruptHandler (mCpu, LOCAL_APIC_TIMER_VECTOR= ,=0D + TimerInterruptHandler);=0D + ASSERT_EFI_ERROR (Status);=0D +=0D + //=0D + // Force the timer to be enabled at its default period=0D + //=0D + Status =3D TimerDriverSetTimerPeriod (&mTimer, DEFAULT_TIMER_TICK_DURATI= ON);=0D + ASSERT_EFI_ERROR (Status);=0D +=0D + //=0D + // Install the Timer Architectural Protocol onto a new handle=0D + //=0D + Status =3D gBS->InstallMultipleProtocolInterfaces (=0D + &mTimerHandle,=0D + &gEfiTimerArchProtocolGuid, &mTimer,=0D + NULL=0D + );=0D + ASSERT_EFI_ERROR (Status);=0D +=0D + return Status;=0D +}=0D +=0D --=20 Anthony PERARD