//------------------------------------------------------------------- // tsc.c // // This character driver allows application programs to access // the current value of the cpu's Time Stamp Counter register, // as if it were a file of length 8 bytes (64 bits). // // Note: Developed and tested with Linux kernel version 2.4.18. // // programmer: ALLAN CRUSE // written on: 03 APR 2003 //------------------------------------------------------------------- #define __KERNEL__ #define MODULE #include // for init_module() #include // for get_ds(), set_fs() #define SUCCESS 0 #define ACCESS 0666 static char modname[] = "tsc"; static char devname[] = "/dev/tsc"; static int my_major = 0; static loff_t filesize = 8; static unsigned char tsc_value[8]; static ssize_t my_read( struct file *file, char *buf, size_t count, loff_t *pos ) { int more; // sanity check: we cannot read beyond end-of-file if ( *pos >= filesize ) return -EINVAL; // if necessary, adjust the count of bytes to be read if ( *pos + count > filesize ) count = filesize - *pos; // assembly language is needed (to get cpu register-value) asm(" rdtsc "); asm(" movl %eax, tsc_value+0 "); asm(" movl %edx, tsc_value+4 "); // copy requested number of bytes (starting from *pos) more = copy_to_user( buf, tsc_value + *pos, count ); if ( more ) return -EFAULT; // update *pos, and return the number of bytes transferred *pos += count; return count; } static loff_t my_llseek( struct file *file, loff_t offset, int whence ) { loff_t newpos; switch( whence ) { case 0: // SEEK_SET newpos = offset; break; case 1: // SEEK_CUR newpos = file->f_pos + offset; break; case 2: // SEEK_END newpos = filesize + offset; break; default: // should never occur return -EINVAL; } if ( newpos < 0 ) return -EINVAL; file->f_pos = newpos; return newpos; } static struct file_operations my_fops = { owner: THIS_MODULE, read: my_read, llseek: my_llseek, }; int init_module( void ) { dev_t dev_id; mode_t mode; printk( "<1>\nInstalling \'%s\' module\n", modname ); my_major = register_chrdev( 0, modname, &my_fops ); if ( my_major < 0 ) return -EBUSY; set_fs( get_ds() ); sys_unlink( devname ); mode = S_IFCHR; dev_id = MKDEV( my_major, 0 ); sys_mknod( devname, mode, dev_id ); mode = ACCESS; sys_chmod( devname, mode ); return SUCCESS; } void cleanup_module( void ) { unregister_chrdev( my_major, modname ); set_fs( get_ds() ); sys_unlink( devname ); printk( "<1>Removing \'%s\' module\n", modname ); } MODULE_LICENSE("GPL");