0

I am currently developing on a UEFI program using the EDK2 framework and would like to fetch HPET time or utilize the HPET interrupt as a countdown timer. I have attempted to use HpetTimerDxe from the PcAtChipsetPkg, but I have no idea how to implement it.

There is limited information and articles on this topic online, but I still hope to use it to address time-related issues.

  • Target architecture: x86_64
  • edk2 version: edk2-stable202105
EFI_STATUS
FindHpetTable(
    EFI_ACPI_HIGH_PRECISION_EVENT_TIMER_TABLE_HEADER **HpetTable)
{
   EFI_STATUS Status;
   EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp;
   EFI_ACPI_DESCRIPTION_HEADER *Xsdt;
   UINTN EntryCount;
   UINTN Index;

   Status = EfiGetSystemConfigurationTable(
       &gEfiAcpi20TableGuid,
       (VOID **)&Rsdp);
   if (EFI_ERROR(Status))
   {
      return Status;
   }

   Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)Rsdp->XsdtAddress;
   EntryCount = (Xsdt->Length - sizeof(EFI_ACPI_DESCRIPTION_HEADER)) / sizeof(UINT64);

   for (Index = 0; Index < EntryCount; Index++)
   {
      EFI_ACPI_DESCRIPTION_HEADER *CurrentTable;
      CurrentTable = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)(((UINT64 *)(Xsdt + 1))[Index]);

      if (CurrentTable->Signature == EFI_ACPI_3_0_HIGH_PRECISION_EVENT_TIMER_TABLE_SIGNATURE)
      {
         *HpetTable = (EFI_ACPI_HIGH_PRECISION_EVENT_TIMER_TABLE_HEADER *)CurrentTable;
         return EFI_SUCCESS;
      }
   }

   return EFI_NOT_FOUND;
}
VOID AccessHpetRegisters(
    EFI_ACPI_HIGH_PRECISION_EVENT_TIMER_TABLE_HEADER *HpetTable)
{
   if (HpetTable == NULL)
   {
      return;
   }

   UINT64 HpetBaseAddress = HpetTable->BaseAddressLower32Bit.Address;

   // Access HPET registers using HpetBaseAddress
   // Read the General Capabilities and ID Register
   UINT64 CapabilitiesAndId = *(volatile UINT64 *)HpetBaseAddress;
   Print(L"HPET General Capabilities and ID Register: 0x%lX\n", CapabilitiesAndId);

   // Read the Main Counter Value register
   UINT64 MainCounterValue = *(volatile UINT64 *)(HpetBaseAddress + HPET_MAIN_COUNTER_OFFSET);
   Print(L"HPET Main Counter Value: 0x%lX\n", MainCounterValue);

   UINT64 HpetPeriod = MmioRead32(HpetBaseAddress + 0x4);

   // 1 femtosecond = 1e-15 seconds
   UINT64 timeValue = DivU64x64Remainder(MultU64x64(MainCounterValue, HpetPeriod), 1000000000000ULL, NULL);
   Print(L"time in microsecond: %llu\n", timeValue);
}
// in UefiMain
EFI_ACPI_HIGH_PRECISION_EVENT_TIMER_TABLE_HEADER *HpetTable;

Status = FindHpetTable(&HpetTable);
if (EFI_ERROR(Status))
{
  Print(L"HPET table not found.\n");
  return Status;
}

AccessHpetRegisters(HpetTable);
Kaiser
  • 9
  • 1
  • What is your target architecture? You can use the rdtsc instruction ob X64 and X86 and calibrate it with the stall method from the boot service table (this may be inaccurate). – MiSimon Apr 17 '23 at 18:10
  • Thanks for the reminder, I have added it to the question. My target architecture is x86_64, and the edk2 version is edk2-stable202105, which HpetTimerDxe is located in HpetTimerDxe. I have already used Stall, RTC and TSC in my project, so currently I want to use HPET. – Kaiser Apr 18 '23 at 03:01
  • What type of information do you need? Reading the current value from the HPET is a simple task, you need to get the memory address of the [registers](https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/software-developers-hpet-spec-1-0a.pdf) from the [ACPI](https://wiki.osdev.org/HPET) table and with that you can access it directly. Setting up the interrupts and the handlers is far more complex. – MiSimon Apr 18 '23 at 06:13
  • @MiSimon Thank you for your feedback. I have tried to create two functions: `FindHpetTable` and `AccessHpetRegisters` in order to fetch time in microseconds. These functions are then called within the `UefiMain`. However, I've encountered an issue when comparing the elapsed time between TSC and HPET, as there seems to be a significant difference between the two values. Could you please help me identify any potential mistakes in my code? I have provided the relevant code above. – Kaiser Apr 21 '23 at 16:11
  • How are you calculating/calibrating the TSC frequency? it's not the same as the HPETs frequency. – MiSimon Apr 21 '23 at 17:22
  • I don't compute the TSC frequency; instead, I obtain the elapsed TSC time by `GetTimeInNanoSecond(GetPerformanceCounter())`. For instance, when the TSC elapsed time is 5 seconds, the result from my function that retrieves the HPET time is approximately 5.3 seconds. This significant discrepancy suggests that there might be an issue with my function. – Kaiser Apr 24 '23 at 06:22

0 Answers0