//-------------------------------------------------------------------
//	smlayout.cpp
//
//	This program tries to discover the current SMBASE register-
//	value for each processor by using services of our 'smram.c'
//	device-driver kernel-module to search this station's System 
//	Management Memory for an expected pattern we associate with
//	the CR0 register's contents and then computing the location
//	for the SMBASE field within the Intel SMRAM Save-State Map. 
//
//	This forensic technique succeeded in identifying the SMBASE
//	addresses on our classroom workstations, which was required
//	for loading our own 'System Management Interrupt' handlers.	
//
//	     compile using:  $ smlayout.cpp -o smlayout
//	     execute using:  $ ./smlayout /dev/smram
//
//	Reference: "SMRAM Save State Map for Intel 64 Architecture"
//	(from Intel 64 and IA-32 Architectures Software Developer's 
//	Manual, Volume 3B).
//
//	NOTE: This application is useful only on Intel64 platforms.
//	
//				----------
//
//	Research question: does the processor always set the TS-bit
//	in its SMRAM Save-State field for Control Register CR0 upon 
//	entering System Management Mode?  We did not find this item
//	discussed in the Intel Software Developer's Manuals.  Logic
//	would argue that, since the floating point registers do not
//	get saved, yet could be modified while in System Management
//	Mode, the TS-flag ought to be set to guard against a task's
//	using 'stale' FPU register-values when it resumes following 
//	the System Management Interrupt.  But it is unclear whether
//	Intel's documentation regards this type of 'context-switch'
//	as constituting a 'task-switch', since the task remains the
//	same.  Our software tools, created for exploring SMIs, will
//	allow us to investigate such questions through experiments.  
//
//				----------
//
//	programmer: ALLAN CRUSE
//	written on: 08 DEC 2008
//-------------------------------------------------------------------


#include <stdio.h>	// for printf(), perror() 
#include <fcntl.h>	// for open() 
#include <stdlib.h>	// for exit() 
#include <unistd.h>	// for read() 
#include <string.h>	// for strncpy()


char filename[ 64 ] = "/dev/smram";		// name of input-file
unsigned long long  patt = 0x8005003BLL;	// expected CR0 value


int main( int argc, char **argv )
{
	// allow user to override the default input-file
	if ( argc > 1 ) strncpy( filename, argv[1], 63 ); 

	// open the specified input-file
	int	fd = open( filename, O_RDONLY );
	if ( fd < 0 ) { perror( filename ); exit(1); }

	// obtain the size of this station's System Management Memory
	int	filesize = lseek( fd, 0, SEEK_END );
	printf( "\n \'%s\' (filesize = 0x%08X) \n\n", filename, filesize );

	// loop to search the station's System Management Memory
	int	where = 0;
	while ( where < filesize )
		{
		unsigned long long	pair[ 2 ] = { 0 };
		lseek( fd, where, SEEK_SET );
		if ( read( fd, pair, sizeof( pair ) ) < 0 ) break;
		if ( pair[1] == patt ) 
			{
			printf( " %016llX ", pair[1] );
			printf( "where=%08X  ", where );

			unsigned int	smbase;
			int	plant = where - 0x7FF0;
			lseek( fd, plant + 0x7EF8, SEEK_SET );
			read( fd, &smbase, sizeof( smbase ) );
			printf( " smbase=%08X ", smbase );		
			printf( " plant=%08X  ", plant );
			printf( "\n" );
			}
		where += 16;
		}
	printf( "\n" );
}