blob: d07f196a1b17429b6d5b8a4523c12f72ce73b4ae [file] [log] [blame]
/*
* Copyright 2016 Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <assert.h>
#include <stdint.h>
#include "base/die.h"
#include "base/xalloc.h"
#include "drivers/timer/timer.h"
#include "uefi/edk/Protocol/Timestamp.h"
#include "uefi/uefi.h"
static EFI_GUID uefi_timer_timestamp_guid = EFI_TIMESTAMP_PROTOCOL_GUID;
static void uefi_timer_get_prot(EFI_HANDLE handle,
EFI_TIMESTAMP_PROTOCOL **prot)
{
EFI_SYSTEM_TABLE *system_table = uefi_system_table_ptr();
assert(system_table);
EFI_BOOT_SERVICES *boot_services = system_table->BootServices;
EFI_STATUS status = boot_services->HandleProtocol(
handle, &uefi_timer_timestamp_guid, (void **)prot);
die_if(status != EFI_SUCCESS, "Failed to get timestamp protocol.\n");
}
static void uefi_timer_get_props(EFI_TIMESTAMP_PROTOCOL *prot,
EFI_TIMESTAMP_PROPERTIES *props)
{
EFI_STATUS status = prot->GetProperties(props);
die_if(status != EFI_SUCCESS, "Failed to get timestamp properties.\n");
}
static uint64_t uefi_timer_calc_rollover(EFI_TIMESTAMP_PROTOCOL *prot)
{
EFI_TIMESTAMP_PROPERTIES properties;
uefi_timer_get_props(prot, &properties);
return properties.EndValue / properties.Frequency;
}
static EFI_TIMESTAMP_PROTOCOL *timer_uefi_timestamp_protocol(void)
{
static EFI_TIMESTAMP_PROTOCOL *timestamp_prot;
if (timestamp_prot)
return timestamp_prot;
EFI_SYSTEM_TABLE *system_table = uefi_system_table_ptr();
assert(system_table);
EFI_BOOT_SERVICES *boot_services = system_table->BootServices;
UINTN buf_size = 0;
EFI_HANDLE dummy_handle;
EFI_STATUS status = boot_services->LocateHandle(
ByProtocol, &uefi_timer_timestamp_guid, NULL,
&buf_size, &dummy_handle);
die_if(status == EFI_NOT_FOUND,
"No timestamp protocol handles found.\n");
die_if(status != EFI_BUFFER_TOO_SMALL,
"Error retrieving timestamp protocol handles.\n");
EFI_HANDLE *handles = xmalloc(buf_size);
status = boot_services->LocateHandle(
ByProtocol, &uefi_timer_timestamp_guid, NULL,
&buf_size, handles);
die_if(status != EFI_SUCCESS,
"Failed to retrieve timestamp protocol handles.\n");
int handle_count = buf_size / sizeof(dummy_handle);
EFI_TIMESTAMP_PROTOCOL *best_prot;
uefi_timer_get_prot(handles[0], &best_prot);
uint64_t best_roll_over = uefi_timer_calc_rollover(best_prot);
if (handle_count > 1) {
printf("Multiple timestamp sources found.\n"
"Picking the one that will take the "
"longest to rollover.\n");
}
for (int i = 1; i < handle_count; i++) {
EFI_TIMESTAMP_PROTOCOL *prot;
uefi_timer_get_prot(handles[i], &prot);
uint64_t roll_over = uefi_timer_calc_rollover(prot);
if (roll_over > best_roll_over) {
best_roll_over = roll_over;
best_prot = prot;
}
}
free(handles);
timestamp_prot = best_prot;
return timestamp_prot;
}
uint64_t timer_hz(void)
{
static uint64_t frequency;
if (!frequency) {
EFI_TIMESTAMP_PROPERTIES properties;
EFI_TIMESTAMP_PROTOCOL *prot = timer_uefi_timestamp_protocol();
uefi_timer_get_props(prot, &properties);
frequency = properties.Frequency;
}
return frequency;
}
uint64_t timer_raw_value(void)
{
EFI_TIMESTAMP_PROTOCOL *prot = timer_uefi_timestamp_protocol();
return prot->GetTimestamp();
}