#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <err.h>
#include <x86/priv.h>
#include <sys/neutrino.h>


#define MSR_TSC						0x0010
#define MSR_PERF_CTR0				0x00c1
#define MSR_PERF_CTR1				0x00c2
#define MSR_PERF_EVT_SEL0		0x0186
#define MSR_PERF_EVT_SEL1		0x0187
#define MSR_LASTBRANCHFROMIP	0x01db
#define MSR_LASTBRANCHTOIP		0x01dc
#define MSR_LASTINTFROMIP		0x01dd
#define MSR_LASTINTTOIP			0x01de


static volatile uint32_t		RdMsrNr;
static volatile uint64_t		RdMsrVal;       
static volatile uint32_t		WrMsrNr;
static volatile uint64_t		WrMsrVal;       
static struct sigevent		IntrEvent;
static intrspin_t				Spinlock;
static int							IntrId;


static const struct sigevent *IntrHnd( void *area, int size ) {
	area = area; size = size;

	InterruptLock( &Spinlock );

	if ( RdMsrNr ) {
		RdMsrVal = rdmsr( RdMsrNr );
		RdMsrNr = 0;
	}

	if ( WrMsrNr ) {
		wrmsr( WrMsrNr, WrMsrVal );
		WrMsrNr = 0;
	}

	InterruptUnlock( &Spinlock );

	return &IntrEvent;
}

uint64_t ReadMsr( uint32_t idx ) {
	uint64_t  res;

	InterruptMask( 0, IntrId );
	InterruptLock( &Spinlock );
	RdMsrNr = idx;
	InterruptUnlock( &Spinlock );
	InterruptUnmask( 0, IntrId );

	InterruptWait( 0, NULL );

	InterruptMask( 0, IntrId );
	InterruptLock( &Spinlock );
	res = RdMsrVal;
	InterruptUnlock( &Spinlock );
	InterruptUnmask( 0, IntrId );

	return res;
}

void WriteMsr( uint32_t idx, uint64_t val ) {
	InterruptMask( 0, IntrId );
	InterruptLock( &Spinlock );
	WrMsrVal = val;
	WrMsrNr  = idx;
	InterruptUnlock( &Spinlock );
	InterruptUnmask( 0, IntrId );

	InterruptWait( 0, NULL );
}

int main( int argc, char *argv[] ) {
	uint32_t  idx = MSR_TSC;

	if ( argc > 1 )
		idx = strtoul( argv[1], NULL, 0 );

	ThreadCtl( _NTO_TCTL_IO, 0 );
	SIGEV_INTR_INIT( &IntrEvent );

	if ( -1 == ( IntrId = InterruptAttach( 0, IntrHnd, NULL, 0, 0 ) ) )
		err( EXIT_FAILURE, "InterruptAttach()" );

	for (;;) {
		printf( "\rMSR 0x%x = 0x%llx                ", idx, ReadMsr( idx ) );
		fflush( stdout );
	}

	return EXIT_SUCCESS;
}

