Overview
There is a high precision time counter register (TSC) in IA architecture(x86) started from Pentium core. It counts every CPU clock circle.
Solution
User can use RDTSC (Read time-stamp counter into EDX:EAX) to read the register.
Below is an example function written for GCC.
void getHpTime(uint32_t *h, uint32_t *l)
{
uint32_t timeLow = 0, timeHigh = 0;
asm("rdtsc; movl %%eax, %0; movl %%edx, %1"
:"=g" (timeLow), "=g" (timeHigh)
:
: "eax", "edx");
*h = timeHigh;
*l = timeLow;
}
There is also similar function/macro (rdtscll()) in Linux kernel, so if you are writing a Linux driver, please use it instead.
On Linux platform, gettimeofday() can be a better choice. But the approach in this article can be applied in the case its user want to get system boot up time. It will include BIOS/BootLoader/LinuxKernel/SystemInitScripts times. TSC is a register that will count from the first clock circle of the CPU.
Sample code
Below is the Linux driver code and an application for reading the time.
/* start of hptime.c */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <asm/msr.h>
MODULE_LICENSE("Dual BSD/GPL");
#define HPTIME_DEVICE_NAME "hptime"
static int device_open = 0;
int major_number = -1;
static int
hptime_release(struct inode *inode, struct file *filp)
{
device_open--;
return 0;
}
static int
hptime_open(struct inode *inode, struct file *filp)
{
if (device_open)
return -EBUSY;
device_open++;
return 0;
}
static int
hptime_ioctl( struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
uint64_t tscll;
switch (cmd) {
case 0:
rdtscll(tscll);
if (copy_to_user((void *)arg, &tscll, sizeof(tscll)))
{
return -1;
}
break;
default:
break;
}
return 0;
}
static struct file_operations hptime_fops = {
.owner = THIS_MODULE,
.open = hptime_open,
.ioctl = hptime_ioctl,
.release = hptime_release,
};
static int hptime_init(void)
{
if ((major_number =
register_chrdev(0, HPTIME_DEVICE_NAME, &hptime_fops)) < 0)
{
printk("Register hptime device failed!/n");
return -1;
}
printk(KERN_DEBUG "Hptime device major %d/n", major_number);
return 0;
}
static void hptime_exit(void)
{
unregister_chrdev(major_number, HPTIME_DEVICE_NAME);
printk(KERN_DEBUG "Hptime exit./n");
}
module_init(hptime_init);
module_exit(hptime_exit);
/* end of hptime.c */
/* start of get-hptime.c */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#define HPTIME_DEVICE "/dev/hptime"
#undef DISPLAY_IN_SECOND
#ifdef DISPLAY_IN_SECOND
/* please change the CPU_FREQ based on your CPU configuration */
#define CPU_FREQ 800000000
#endif
int
main(int argc, char **argv)
{
int rev;
int fd = -1;
unsigned long long tsc = 0;
#ifdef DISPLAY_IN_SECOND
float second;
#endif
if ((fd = open(HPTIME_DEVICE, O_RDONLY)) < 0)
{
fprintf(stderr, "Open %s error./n", HPTIME_DEVICE);
return -1;
}
rev = ioctl(fd, 0, &tsc);
if (rev != 0)
{
fprintf(stderr, "Read high precision time failed./n");
close(fd);
return -1;
}
#ifndef DISPLAY_IN_SECOND
printf("High precision time 0x%llx./n", tsc);
#else
second = tsc;
second /= CPU_FREQ;
printf("Time offset %f./n", second);
#endif
close(fd);
return 0;
}
/* end of get-hptime.c */