477 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			477 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* 
 | 
						|
------- Strong random data generation on a Macintosh (pre - OS X) ------
 | 
						|
		
 | 
						|
--	GENERAL: We aim to generate unpredictable bits without explicit
 | 
						|
	user interaction. A general review of the problem may be found
 | 
						|
	in RFC 1750, "Randomness Recommendations for Security", and some
 | 
						|
	more discussion, of general and Mac-specific issues has appeared
 | 
						|
	in "Using and Creating Cryptographic- Quality Random Numbers" by
 | 
						|
	Jon Callas (www.merrymeet.com/jon/usingrandom.html).
 | 
						|
 | 
						|
	The data and entropy estimates provided below are based on my
 | 
						|
	limited experimentation and estimates, rather than by any
 | 
						|
	rigorous study, and the entropy estimates tend to be optimistic.
 | 
						|
	They should not be considered absolute.
 | 
						|
 | 
						|
	Some of the information being collected may be correlated in
 | 
						|
	subtle ways. That includes mouse positions, timings, and disk
 | 
						|
	size measurements. Some obvious correlations will be eliminated
 | 
						|
	by the programmer, but other, weaker ones may remain. The
 | 
						|
	reliability of the code depends on such correlations being
 | 
						|
	poorly understood, both by us and by potential interceptors.
 | 
						|
 | 
						|
	This package has been planned to be used with OpenSSL, v. 0.9.5.
 | 
						|
	It requires the OpenSSL function RAND_add. 
 | 
						|
 | 
						|
--	OTHER WORK: Some source code and other details have been
 | 
						|
	published elsewhere, but I haven't found any to be satisfactory
 | 
						|
	for the Mac per se:
 | 
						|
 | 
						|
	* The Linux random number generator (by Theodore Ts'o, in
 | 
						|
	  drivers/char/random.c), is a carefully designed open-source
 | 
						|
	  crypto random number package. It collects data from a variety
 | 
						|
	  of sources, including mouse, keyboard and other interrupts.
 | 
						|
	  One nice feature is that it explicitly estimates the entropy
 | 
						|
	  of the data it collects. Some of its features (e.g. interrupt
 | 
						|
	  timing) cannot be reliably exported to the Mac without using
 | 
						|
	  undocumented APIs.
 | 
						|
 | 
						|
	* Truerand by Don P. Mitchell and Matt Blaze uses variations
 | 
						|
	  between different timing mechanisms on the same system. This
 | 
						|
	  has not been tested on the Mac, but requires preemptive
 | 
						|
	  multitasking, and is hardware-dependent, and can't be relied
 | 
						|
	  on to work well if only one oscillator is present.
 | 
						|
 | 
						|
	* Cryptlib's RNG for the Mac (RNDMAC.C by Peter Gutmann),
 | 
						|
	  gathers a lot of information about the machine and system
 | 
						|
	  environment. Unfortunately, much of it is constant from one
 | 
						|
	  startup to the next. In other words, the random seed could be
 | 
						|
	  the same from one day to the next. Some of the APIs are
 | 
						|
	  hardware-dependent, and not all are compatible with Carbon (OS
 | 
						|
	  X). Incidentally, the EGD library is based on the UNIX entropy
 | 
						|
	  gathering methods in cryptlib, and isn't suitable for MacOS
 | 
						|
	  either.
 | 
						|
 | 
						|
	* Mozilla (and perhaps earlier versions of Netscape) uses the
 | 
						|
	  time of day (in seconds) and an uninitialized local variable
 | 
						|
	  to seed the random number generator. The time of day is known
 | 
						|
	  to an outside interceptor (to within the accuracy of the
 | 
						|
	  system clock). The uninitialized variable could easily be
 | 
						|
	  identical between subsequent launches of an application, if it
 | 
						|
	  is reached through the same path.
 | 
						|
 | 
						|
	* OpenSSL provides the function RAND_screen(), by G. van
 | 
						|
	  Oosten, which hashes the contents of the screen to generate a
 | 
						|
	  seed. This is not useful for an extension or for an
 | 
						|
	  application which launches at startup time, since the screen
 | 
						|
	  is likely to look identical from one launch to the next. This
 | 
						|
	  method is also rather slow.
 | 
						|
 | 
						|
	* Using variations in disk drive seek times has been proposed
 | 
						|
	  (Davis, Ihaka and Fenstermacher, world.std.com/~dtd/;
 | 
						|
	  Jakobsson, Shriver, Hillyer and Juels,
 | 
						|
	  www.bell-labs.com/user/shriver/random.html). These variations
 | 
						|
	  appear to be due to air turbulence inside the disk drive
 | 
						|
	  mechanism, and are very strongly unpredictable. Unfortunately
 | 
						|
	  this technique is slow, and some implementations of it may be
 | 
						|
	  patented (see Shriver's page above.) It of course cannot be
 | 
						|
	  used with a RAM disk.
 | 
						|
 | 
						|
--	TIMING: On the 601 PowerPC the time base register is guaranteed
 | 
						|
	to change at least once every 10 addi instructions, i.e. 10
 | 
						|
	cycles. On a 60 MHz machine (slowest PowerPC) this translates to
 | 
						|
	a resolution of 1/6 usec. Newer machines seem to be using a 10
 | 
						|
	cycle resolution as well.
 | 
						|
	
 | 
						|
	For 68K Macs, the Microseconds() call may be used. See Develop
 | 
						|
	issue 29 on the Apple developer site
 | 
						|
	(developer.apple.com/dev/techsupport/develop/issue29/minow.html)
 | 
						|
	for information on its accuracy and resolution. The code below
 | 
						|
	has been tested only on PowerPC based machines.
 | 
						|
 | 
						|
	The time from machine startup to the launch of an application in
 | 
						|
	the startup folder has a variance of about 1.6 msec on a new G4
 | 
						|
	machine with a defragmented and optimized disk, most extensions
 | 
						|
	off and no icons on the desktop. This can be reasonably taken as
 | 
						|
	a lower bound on the variance. Most of this variation is likely
 | 
						|
	due to disk seek time variability. The distribution of startup
 | 
						|
	times is probably not entirely even or uncorrelated. This needs
 | 
						|
	to be investigated, but I am guessing that it not a majpor
 | 
						|
	problem. Entropy = log2 (1600/0.166) ~= 13 bits on a 60 MHz
 | 
						|
	machine, ~16 bits for a 450 MHz machine.
 | 
						|
 | 
						|
	User-launched application startup times will have a variance of
 | 
						|
	a second or more relative to machine startup time. Entropy >~22
 | 
						|
	bits.
 | 
						|
 | 
						|
	Machine startup time is available with a 1-second resolution. It
 | 
						|
	is predictable to no better a minute or two, in the case of
 | 
						|
	people who show up punctually to work at the same time and
 | 
						|
	immediately start their computer. Using the scheduled startup
 | 
						|
	feature (when available) will cause the machine to start up at
 | 
						|
	the same time every day, making the value predictable. Entropy
 | 
						|
	>~7 bits, or 0 bits with scheduled startup.
 | 
						|
 | 
						|
	The time of day is of course known to an outsider and thus has 0
 | 
						|
	entropy if the system clock is regularly calibrated.
 | 
						|
 | 
						|
--	KEY TIMING: A  very fast typist (120 wpm) will have a typical
 | 
						|
	inter-key timing interval of 100 msec. We can assume a variance
 | 
						|
	of no less than 2 msec -- maybe. Do good typists have a constant
 | 
						|
	rhythm, like drummers? Since what we measure is not the
 | 
						|
	key-generated interrupt but the time at which the key event was
 | 
						|
	taken off the event queue, our resolution is roughly the time
 | 
						|
	between process switches, at best 1 tick (17 msec). I  therefore
 | 
						|
	consider this technique questionable and not very useful for
 | 
						|
	obtaining high entropy data on the Mac.
 | 
						|
 | 
						|
--	MOUSE POSITION AND TIMING: The high bits of the mouse position
 | 
						|
	are far from arbitrary, since the mouse tends to stay in a few
 | 
						|
	limited areas of the screen. I am guessing that the position of
 | 
						|
	the mouse is arbitrary within a 6 pixel square. Since the mouse
 | 
						|
	stays still for long periods of time, it should be sampled only
 | 
						|
	after it was moved, to avoid correlated data. This gives an
 | 
						|
	entropy of log2(6*6) ~= 5 bits per measurement.
 | 
						|
 | 
						|
	The time during which the mouse stays still can vary from zero
 | 
						|
	to, say, 5 seconds (occasionally longer). If the still time is
 | 
						|
	measured by sampling the mouse during null events, and null
 | 
						|
	events are received once per tick, its resolution is 1/60th of a
 | 
						|
	second, giving an entropy of log2 (60*5) ~= 8 bits per
 | 
						|
	measurement. Since the distribution of still times is uneven,
 | 
						|
	this estimate is on the high side.
 | 
						|
 | 
						|
	For simplicity and compatibility across system versions, the
 | 
						|
	mouse is to be sampled explicitly (e.g. in the event loop),
 | 
						|
	rather than in a time manager task.
 | 
						|
 | 
						|
--	STARTUP DISK TOTAL FILE SIZE: Varies typically by at least 20k
 | 
						|
	from one startup to the next, with 'minimal' computer use. Won't
 | 
						|
	vary at all if machine is started again immediately after
 | 
						|
	startup (unless virtual memory is on), but any application which
 | 
						|
	uses the web and caches information to disk is likely to cause
 | 
						|
	this much variation or more. The variation is probably not
 | 
						|
	random, but I don't know in what way. File sizes tend to be
 | 
						|
	divisible by 4 bytes since file format fields are often
 | 
						|
	long-aligned. Entropy > log2 (20000/4) ~= 12 bits.
 | 
						|
	
 | 
						|
--	STARTUP DISK FIRST AVAILABLE ALLOCATION BLOCK: As the volume
 | 
						|
	gets fragmented this could be anywhere in principle. In a
 | 
						|
	perfectly unfragmented volume this will be strongly correlated
 | 
						|
	with the total file size on the disk. With more fragmentation
 | 
						|
	comes less certainty. I took the variation in this value to be
 | 
						|
	1/8 of the total file size on the volume.
 | 
						|
 | 
						|
--	SYSTEM REQUIREMENTS: The code here requires System 7.0 and above
 | 
						|
	(for Gestalt and Microseconds calls). All the calls used are
 | 
						|
	Carbon-compatible.
 | 
						|
*/
 | 
						|
 | 
						|
/*------------------------------ Includes ----------------------------*/
 | 
						|
 | 
						|
#include "Randomizer.h"
 | 
						|
 | 
						|
// Mac OS API
 | 
						|
#include <Files.h>
 | 
						|
#include <Folders.h>
 | 
						|
#include <Events.h>
 | 
						|
#include <Processes.h>
 | 
						|
#include <Gestalt.h>
 | 
						|
#include <Resources.h>
 | 
						|
#include <LowMem.h>
 | 
						|
 | 
						|
// Standard C library
 | 
						|
#include <stdlib.h>
 | 
						|
#include <math.h>
 | 
						|
 | 
						|
/*---------------------- Function declarations -----------------------*/
 | 
						|
 | 
						|
// declared in OpenSSL/crypto/rand/rand.h
 | 
						|
extern "C" void RAND_add (const void *buf, int num, double entropy);
 | 
						|
 | 
						|
unsigned long GetPPCTimer (bool is601);	// Make it global if needed
 | 
						|
					// elsewhere
 | 
						|
 | 
						|
/*---------------------------- Constants -----------------------------*/
 | 
						|
 | 
						|
#define kMouseResolution 6		// Mouse position has to differ
 | 
						|
					// from the last one by this
 | 
						|
					// much to be entered
 | 
						|
#define kMousePositionEntropy 5.16	// log2 (kMouseResolution**2)
 | 
						|
#define kTypicalMouseIdleTicks 300.0	// I am guessing that a typical
 | 
						|
					// amount of time between mouse
 | 
						|
					// moves is 5 seconds
 | 
						|
#define kVolumeBytesEntropy 12.0	// about log2 (20000/4),
 | 
						|
					// assuming a variation of 20K
 | 
						|
					// in total file size and
 | 
						|
					// long-aligned file formats.
 | 
						|
#define kApplicationUpTimeEntropy 6.0	// Variance > 1 second, uptime
 | 
						|
					// in ticks  
 | 
						|
#define kSysStartupEntropy 7.0		// Entropy for machine startup
 | 
						|
					// time
 | 
						|
 | 
						|
 | 
						|
/*------------------------ Function definitions ----------------------*/
 | 
						|
 | 
						|
CRandomizer::CRandomizer (void)
 | 
						|
{
 | 
						|
	long	result;
 | 
						|
	
 | 
						|
	mSupportsLargeVolumes =
 | 
						|
		(Gestalt(gestaltFSAttr, &result) == noErr) &&
 | 
						|
		((result & (1L << gestaltFSSupports2TBVols)) != 0);
 | 
						|
	
 | 
						|
	if (Gestalt (gestaltNativeCPUtype, &result) != noErr)
 | 
						|
	{
 | 
						|
		mIsPowerPC = false;
 | 
						|
		mIs601 = false;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		mIs601 = (result == gestaltCPU601);
 | 
						|
		mIsPowerPC = (result >= gestaltCPU601);
 | 
						|
	}
 | 
						|
	mLastMouse.h = mLastMouse.v = -10;	// First mouse will
 | 
						|
						// always be recorded
 | 
						|
	mLastPeriodicTicks = TickCount();
 | 
						|
	GetTimeBaseResolution ();
 | 
						|
	
 | 
						|
	// Add initial entropy
 | 
						|
	AddTimeSinceMachineStartup ();
 | 
						|
	AddAbsoluteSystemStartupTime ();
 | 
						|
	AddStartupVolumeInfo ();
 | 
						|
	AddFiller ();
 | 
						|
}
 | 
						|
 | 
						|
void CRandomizer::PeriodicAction (void)
 | 
						|
{
 | 
						|
	AddCurrentMouse ();
 | 
						|
	AddNow (0.0);	// Should have a better entropy estimate here
 | 
						|
	mLastPeriodicTicks = TickCount();
 | 
						|
}
 | 
						|
 | 
						|
/*------------------------- Private Methods --------------------------*/
 | 
						|
 | 
						|
void CRandomizer::AddCurrentMouse (void)
 | 
						|
{
 | 
						|
	Point mouseLoc;
 | 
						|
	unsigned long lastCheck;	// Ticks since mouse was last
 | 
						|
					// sampled
 | 
						|
 | 
						|
#if TARGET_API_MAC_CARBON
 | 
						|
	GetGlobalMouse (&mouseLoc);
 | 
						|
#else
 | 
						|
	mouseLoc = LMGetMouseLocation();
 | 
						|
#endif
 | 
						|
	
 | 
						|
	if (labs (mLastMouse.h - mouseLoc.h) > kMouseResolution/2 &&
 | 
						|
	    labs (mLastMouse.v - mouseLoc.v) > kMouseResolution/2)
 | 
						|
		AddBytes (&mouseLoc, sizeof (mouseLoc),
 | 
						|
				kMousePositionEntropy);
 | 
						|
	
 | 
						|
	if (mLastMouse.h == mouseLoc.h && mLastMouse.v == mouseLoc.v)
 | 
						|
		mMouseStill ++;
 | 
						|
	else
 | 
						|
	{
 | 
						|
		double entropy;
 | 
						|
		
 | 
						|
		// Mouse has moved. Add the number of measurements for
 | 
						|
		// which it's been still. If the resolution is too
 | 
						|
		// coarse, assume the entropy is 0.
 | 
						|
 | 
						|
		lastCheck = TickCount() - mLastPeriodicTicks;
 | 
						|
		if (lastCheck <= 0)
 | 
						|
			lastCheck = 1;
 | 
						|
		entropy = log2l
 | 
						|
			(kTypicalMouseIdleTicks/(double)lastCheck);
 | 
						|
		if (entropy < 0.0)
 | 
						|
			entropy = 0.0;
 | 
						|
		AddBytes (&mMouseStill, sizeof (mMouseStill), entropy);
 | 
						|
		mMouseStill = 0;
 | 
						|
	}
 | 
						|
	mLastMouse = mouseLoc;
 | 
						|
}
 | 
						|
 | 
						|
void CRandomizer::AddAbsoluteSystemStartupTime (void)
 | 
						|
{
 | 
						|
	unsigned long	now;		// Time in seconds since
 | 
						|
					// 1/1/1904
 | 
						|
	GetDateTime (&now);
 | 
						|
	now -= TickCount() / 60;	// Time in ticks since machine
 | 
						|
					// startup
 | 
						|
	AddBytes (&now, sizeof (now), kSysStartupEntropy);
 | 
						|
}
 | 
						|
 | 
						|
void CRandomizer::AddTimeSinceMachineStartup (void)
 | 
						|
{
 | 
						|
	AddNow (1.5);			// Uncertainty in app startup
 | 
						|
					// time is > 1.5 msec (for
 | 
						|
					// automated app startup).
 | 
						|
}
 | 
						|
 | 
						|
void CRandomizer::AddAppRunningTime (void)
 | 
						|
{
 | 
						|
	ProcessSerialNumber PSN;
 | 
						|
	ProcessInfoRec		ProcessInfo;
 | 
						|
	
 | 
						|
	ProcessInfo.processInfoLength = sizeof (ProcessInfoRec);
 | 
						|
	ProcessInfo.processName = nil;
 | 
						|
	ProcessInfo.processAppSpec = nil;
 | 
						|
	
 | 
						|
	GetCurrentProcess (&PSN);
 | 
						|
	GetProcessInformation (&PSN, &ProcessInfo);
 | 
						|
 | 
						|
	// Now add the amount of time in ticks that the current process
 | 
						|
	// has been active
 | 
						|
 | 
						|
	AddBytes (&ProcessInfo, sizeof (ProcessInfoRec),
 | 
						|
			kApplicationUpTimeEntropy);
 | 
						|
}
 | 
						|
 | 
						|
void CRandomizer::AddStartupVolumeInfo (void)
 | 
						|
{
 | 
						|
	short			vRefNum;
 | 
						|
	long			dirID;
 | 
						|
	XVolumeParam	pb;
 | 
						|
	OSErr			err;
 | 
						|
	
 | 
						|
	if (!mSupportsLargeVolumes)
 | 
						|
		return;
 | 
						|
		
 | 
						|
	FindFolder (kOnSystemDisk, kSystemFolderType, kDontCreateFolder,
 | 
						|
			&vRefNum, &dirID);
 | 
						|
	pb.ioVRefNum = vRefNum;
 | 
						|
	pb.ioCompletion = 0;
 | 
						|
	pb.ioNamePtr = 0;
 | 
						|
	pb.ioVolIndex = 0;
 | 
						|
	err = PBXGetVolInfoSync (&pb);
 | 
						|
	if (err != noErr)
 | 
						|
		return;
 | 
						|
		
 | 
						|
	// Base the entropy on the amount of space used on the disk and
 | 
						|
	// on the next available allocation block. A lot else might be
 | 
						|
	// unpredictable, so might as well toss the whole block in. See
 | 
						|
	// comments for entropy estimate justifications.
 | 
						|
 | 
						|
	AddBytes (&pb, sizeof (pb),
 | 
						|
		kVolumeBytesEntropy +
 | 
						|
		log2l (((pb.ioVTotalBytes.hi - pb.ioVFreeBytes.hi)
 | 
						|
				* 4294967296.0D +
 | 
						|
			(pb.ioVTotalBytes.lo - pb.ioVFreeBytes.lo))
 | 
						|
				/ pb.ioVAlBlkSiz - 3.0));
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
	On a typical startup CRandomizer will come up with about 60
 | 
						|
	bits of good, unpredictable data. Assuming no more input will
 | 
						|
	be available, we'll need some more lower-quality data to give
 | 
						|
	OpenSSL the 128 bits of entropy it desires. AddFiller adds some
 | 
						|
	relatively predictable data into the soup.
 | 
						|
*/
 | 
						|
 | 
						|
void CRandomizer::AddFiller (void)
 | 
						|
{
 | 
						|
	struct
 | 
						|
	{
 | 
						|
		ProcessSerialNumber psn;	// Front process serial
 | 
						|
						// number
 | 
						|
		RGBColor	hiliteRGBValue;	// User-selected
 | 
						|
						// highlight color
 | 
						|
		long		processCount;	// Number of active
 | 
						|
						// processes
 | 
						|
		long		cpuSpeed;	// Processor speed
 | 
						|
		long		totalMemory;	// Total logical memory
 | 
						|
						// (incl. virtual one)
 | 
						|
		long		systemVersion;	// OS version
 | 
						|
		short		resFile;	// Current resource file
 | 
						|
	} data;
 | 
						|
	
 | 
						|
	GetNextProcess ((ProcessSerialNumber*) kNoProcess);
 | 
						|
	while (GetNextProcess (&data.psn) == noErr)
 | 
						|
		data.processCount++;
 | 
						|
	GetFrontProcess (&data.psn);
 | 
						|
	LMGetHiliteRGB (&data.hiliteRGBValue);
 | 
						|
	Gestalt (gestaltProcClkSpeed, &data.cpuSpeed);
 | 
						|
	Gestalt (gestaltLogicalRAMSize, &data.totalMemory);
 | 
						|
	Gestalt (gestaltSystemVersion, &data.systemVersion);
 | 
						|
	data.resFile = CurResFile ();
 | 
						|
	
 | 
						|
	// Here we pretend to feed the PRNG completely random data. This
 | 
						|
	// is of course false, as much of the above data is predictable
 | 
						|
	// by an outsider. At this point we don't have any more
 | 
						|
	// randomness to add, but with OpenSSL we must have a 128 bit
 | 
						|
	// seed before we can start. We just add what we can, without a
 | 
						|
	// real entropy estimate, and hope for the best.
 | 
						|
 | 
						|
	AddBytes (&data, sizeof(data), 8.0 * sizeof(data));
 | 
						|
	AddCurrentMouse ();
 | 
						|
	AddNow (1.0);
 | 
						|
}
 | 
						|
 | 
						|
//-------------------  LOW LEVEL ---------------------
 | 
						|
 | 
						|
void CRandomizer::AddBytes (void *data, long size, double entropy)
 | 
						|
{
 | 
						|
	RAND_add (data, size, entropy * 0.125);	// Convert entropy bits
 | 
						|
						// to bytes
 | 
						|
}
 | 
						|
 | 
						|
void CRandomizer::AddNow (double millisecondUncertainty)
 | 
						|
{
 | 
						|
	long time = SysTimer();
 | 
						|
	AddBytes (&time, sizeof (time), log2l (millisecondUncertainty *
 | 
						|
			mTimebaseTicksPerMillisec));
 | 
						|
}
 | 
						|
 | 
						|
//----------------- TIMING SUPPORT ------------------
 | 
						|
 | 
						|
void CRandomizer::GetTimeBaseResolution (void)
 | 
						|
{	
 | 
						|
#ifdef __powerc
 | 
						|
	long speed;
 | 
						|
	
 | 
						|
	// gestaltProcClkSpeed available on System 7.5.2 and above
 | 
						|
	if (Gestalt (gestaltProcClkSpeed, &speed) != noErr)
 | 
						|
		// Only PowerPCs running pre-7.5.2 are 60-80 MHz
 | 
						|
		// machines.
 | 
						|
		mTimebaseTicksPerMillisec =  6000.0D;
 | 
						|
	// Assume 10 cycles per clock update, as in 601 spec. Seems true
 | 
						|
	// for later chips as well.
 | 
						|
	mTimebaseTicksPerMillisec = speed / 1.0e4D;
 | 
						|
#else
 | 
						|
	// 68K VIA-based machines (see Develop Magazine no. 29)
 | 
						|
	mTimebaseTicksPerMillisec = 783.360D;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
unsigned long CRandomizer::SysTimer (void)	// returns the lower 32
 | 
						|
						// bit of the chip timer
 | 
						|
{
 | 
						|
#ifdef __powerc
 | 
						|
	return GetPPCTimer (mIs601);
 | 
						|
#else
 | 
						|
	UnsignedWide usec;
 | 
						|
	Microseconds (&usec);
 | 
						|
	return usec.lo;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
#ifdef __powerc
 | 
						|
// The timebase is available through mfspr on 601, mftb on later chips.
 | 
						|
// Motorola recommends that an 601 implementation map mftb to mfspr
 | 
						|
// through an exception, but I haven't tested to see if MacOS actually
 | 
						|
// does this. We only sample the lower 32 bits of the timer (i.e. a
 | 
						|
// few minutes of resolution)
 | 
						|
 | 
						|
asm unsigned long GetPPCTimer (register bool is601)
 | 
						|
{
 | 
						|
	cmplwi	is601, 0	// Check if 601
 | 
						|
	bne	_601		// if non-zero goto _601
 | 
						|
	mftb  	r3		// Available on 603 and later.
 | 
						|
	blr			// return with result in r3
 | 
						|
_601:
 | 
						|
	mfspr r3, spr5  	// Available on 601 only.
 | 
						|
				// blr inserted automatically
 | 
						|
}
 | 
						|
#endif
 |