dhewm3/neo/sys/linux/main.cpp
dhewg f81d32918f Implement Sys_GetProcessorId for x86 and x86_64
Detect CPU features at runtime via cpuid - code borrowed from
libavutil.
Availability of cpuid is not checked since pentium3 supports it
and that was the minimum requirement anyway.
Only features enabled at compile time will be available.

Forced MMX/SSE/SSE2/SSE3 and it passed all tests via:
./doom3.x86_64 +disconnect +set s_noSound 1 +testSIMD
2011-12-13 19:26:16 +01:00

664 lines
15 KiB
C++

/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "../../idlib/precompiled.h"
#include "../posix/posix_public.h"
#include "../sys_local.h"
#include "local.h"
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#ifdef ID_MCHECK
#include <mcheck.h>
#endif
static idStr basepath;
static idStr savepath;
/*
===========
Sys_InitScanTable
===========
*/
void Sys_InitScanTable( void ) {
common->DPrintf( "TODO: Sys_InitScanTable\n" );
}
/*
=================
Sys_AsyncThread
=================
*/
THREAD_RETURN_TYPE Sys_AsyncThread( void * ) {
int now;
int next;
int want_sleep;
// multi tick compensate for poor schedulers (Linux 2.4)
int ticked, to_ticked;
now = Sys_Milliseconds();
ticked = now >> 4;
while (1) {
// sleep
now = Sys_Milliseconds();
next = ( now & 0xFFFFFFF0 ) + 0x10;
want_sleep = ( next-now-1 ) * 1000;
if ( want_sleep > 0 ) {
usleep( want_sleep ); // sleep 1ms less than true target
}
// compensate if we slept too long
now = Sys_Milliseconds();
to_ticked = now >> 4;
// show ticking statistics - every 100 ticks, print a summary
#if 0
#define STAT_BUF 100
static int stats[STAT_BUF];
static int counter = 0;
// how many ticks to play
stats[counter] = to_ticked - ticked;
counter++;
if (counter == STAT_BUF) {
Sys_DebugPrintf("\n");
for( int i = 0; i < STAT_BUF; i++) {
if ( ! (i & 0xf) ) {
Sys_DebugPrintf("\n");
}
Sys_DebugPrintf( "%d ", stats[i] );
}
Sys_DebugPrintf("\n");
counter = 0;
}
#endif
while ( ticked < to_ticked ) {
common->Async();
ticked++;
Sys_TriggerEvent( TRIGGER_EVENT_ONE );
}
// thread exit
pthread_testcancel();
}
return (THREAD_RETURN_TYPE) 0;
}
/*
==============
Sys_DefaultSavePath
==============
*/
const char *Sys_DefaultSavePath(void) {
#if defined( ID_DEMO_BUILD )
sprintf( savepath, "%s/.doom3-demo", getenv( "HOME" ) );
#else
sprintf( savepath, "%s/.doom3", getenv( "HOME" ) );
#endif
return savepath.c_str();
}
/*
==============
Sys_EXEPath
==============
*/
const char *Sys_EXEPath( void ) {
static char buf[ 1024 ];
idStr linkpath;
int len;
buf[ 0 ] = '\0';
sprintf( linkpath, "/proc/%d/exe", getpid() );
len = readlink( linkpath.c_str(), buf, sizeof( buf ) );
if ( len == -1 ) {
Sys_Printf("couldn't stat exe path link %s\n", linkpath.c_str());
buf[ 0 ] = '\0';
}
return buf;
}
/*
================
Sys_DefaultBasePath
Get the default base path
- binary image path
- current directory
- hardcoded
Try to be intelligent: if there is no BASE_GAMEDIR, try the next path
================
*/
const char *Sys_DefaultBasePath(void) {
struct stat st;
idStr testbase;
basepath = Sys_EXEPath();
if ( basepath.Length() ) {
basepath.StripFilename();
testbase = basepath; testbase += "/"; testbase += BASE_GAMEDIR;
if ( stat( testbase.c_str(), &st ) != -1 && S_ISDIR( st.st_mode ) ) {
return basepath.c_str();
} else {
common->Printf( "no '%s' directory in exe path %s, skipping\n", BASE_GAMEDIR, basepath.c_str() );
}
}
if ( basepath != Posix_Cwd() ) {
basepath = Posix_Cwd();
testbase = basepath; testbase += "/"; testbase += BASE_GAMEDIR;
if ( stat( testbase.c_str(), &st ) != -1 && S_ISDIR( st.st_mode ) ) {
return basepath.c_str();
} else {
common->Printf("no '%s' directory in cwd path %s, skipping\n", BASE_GAMEDIR, basepath.c_str());
}
}
common->Printf( "WARNING: using hardcoded default base path\n" );
return LINUX_DEFAULT_PATH;
}
/*
===============
Sys_GetConsoleKey
===============
*/
unsigned char Sys_GetConsoleKey( bool shifted ) {
return shifted ? '~' : '`';
}
/*
===============
Sys_Shutdown
===============
*/
void Sys_Shutdown( void ) {
basepath.Clear();
savepath.Clear();
Posix_Shutdown();
}
/*
===============
Sys_GetProcessorId
===============
*/
static char cpustring[13] = "generic\0";
#if defined(__x86_64__) || defined(__i386__)
#if __x86_64__
# define REG_b "rbx"
# define REG_S "rsi"
#elif __i386__
# define REG_b "ebx"
# define REG_S "esi"
#endif
#define cpuid(index,eax,ebx,ecx,edx) \
__asm__ volatile \
( "mov %%" REG_b ", %%" REG_S "\n\t" \
"cpuid\n\t" \
"xchg %%" REG_b ", %%" REG_S \
: "=a" (eax), "=S" (ebx), \
"=c" (ecx), "=d" (edx) \
: "0" (index));
int Sys_GetProcessorId( void ) {
int eax, ebx, ecx, edx;
int max_std_level, max_ext_level, std_caps=0, ext_caps=0;
union { int i[3]; char c[12]; } vendor;
int i = CPUID_GENERIC;
cpuid(0, max_std_level, ebx, ecx, edx);
vendor.i[0] = ebx;
vendor.i[1] = edx;
vendor.i[2] = ecx;
strncpy(cpustring, vendor.c, 12);
cpustring[12] = 0;
Sys_Printf("Detected '%s' CPU with", cpustring);
if (max_std_level >= 1) {
cpuid(1, eax, ebx, ecx, std_caps);
#ifdef __MMX__
if (std_caps & (1<<23)) {
Sys_Printf(" MMX");
i |= CPUID_MMX;
}
#endif
#ifdef __SSE__
if (std_caps & (1<<25)) {
Sys_Printf(" SSE");
i |= CPUID_SSE;
}
#endif
#ifdef __SSE2__
if (std_caps & (1<<26)) {
Sys_Printf(" SSE2");
i |= CPUID_SSE2;
}
#endif
#ifdef __SSE3__
if (ecx & 1) {
Sys_Printf(" SSE3");
i |= CPUID_SSE3;
}
#endif
}
cpuid(0x80000000, max_ext_level, ebx, ecx, edx);
if (max_ext_level >= 0x80000001) {
cpuid(0x80000001, eax, ebx, ecx, ext_caps);
#ifdef __3dNOW__
if (ext_caps & (1U<<31)) {
Sys_Printf(" 3DNOW");
i |= CPUID_3DNOW;
}
#endif
#ifdef __MMX__
if (ext_caps & (1<<23)) {
if (!(i & CPUID_MMX))
Sys_Printf(" MMX");
i |= CPUID_MMX;
}
#endif
}
Sys_Printf("\n");
return i;
}
#else
int Sys_GetProcessorId( void ) {
return CPUID_GENERIC;
}
#endif
/*
===============
Sys_GetProcessorString
===============
*/
const char *Sys_GetProcessorString( void ) {
return cpustring;
}
/*
===============
Sys_FPU_EnableExceptions
===============
*/
void Sys_FPU_EnableExceptions( int exceptions ) {
}
/*
===============
Sys_FPE_handler
===============
*/
void Sys_FPE_handler( int signum, siginfo_t *info, void *context ) {
assert( signum == SIGFPE );
Sys_Printf( "FPE\n" );
}
/*
===============
Sys_GetClockticks
===============
*/
double Sys_GetClockTicks( void ) {
#if defined( __i386__ )
unsigned long lo, hi;
__asm__ __volatile__ (
"push %%ebx\n" \
"xor %%eax,%%eax\n" \
"cpuid\n" \
"rdtsc\n" \
"mov %%eax,%0\n" \
"mov %%edx,%1\n" \
"pop %%ebx\n"
: "=r" (lo), "=r" (hi) );
return (double) lo + (double) 0xFFFFFFFF * hi;
#else
return 0.0;
#endif
}
/*
===============
MeasureClockTicks
===============
*/
double MeasureClockTicks( void ) {
double t0, t1;
t0 = Sys_GetClockTicks( );
Sys_Sleep( 1000 );
t1 = Sys_GetClockTicks( );
return t1 - t0;
}
/*
===============
Sys_ClockTicksPerSecond
===============
*/
double Sys_ClockTicksPerSecond(void) {
static bool init = false;
static double ret;
int fd, len, pos, end;
char buf[ 4096 ];
if ( init ) {
return ret;
}
fd = open( "/proc/cpuinfo", O_RDONLY );
if ( fd == -1 ) {
common->Printf( "couldn't read /proc/cpuinfo\n" );
ret = MeasureClockTicks();
init = true;
common->Printf( "measured CPU frequency: %g MHz\n", ret / 1000000.0 );
return ret;
}
len = read( fd, buf, 4096 );
close( fd );
pos = 0;
while ( pos < len ) {
if ( !idStr::Cmpn( buf + pos, "cpu MHz", 7 ) ) {
pos = strchr( buf + pos, ':' ) - buf + 2;
end = strchr( buf + pos, '\n' ) - buf;
if ( pos < len && end < len ) {
buf[end] = '\0';
ret = atof( buf + pos );
} else {
common->Printf( "failed parsing /proc/cpuinfo\n" );
ret = MeasureClockTicks();
init = true;
common->Printf( "measured CPU frequency: %g MHz\n", ret / 1000000.0 );
return ret;
}
common->Printf( "/proc/cpuinfo CPU frequency: %g MHz\n", ret );
ret *= 1000000;
init = true;
return ret;
}
pos = strchr( buf + pos, '\n' ) - buf + 1;
}
common->Printf( "failed parsing /proc/cpuinfo\n" );
ret = MeasureClockTicks();
init = true;
common->Printf( "measured CPU frequency: %g MHz\n", ret / 1000000.0 );
return ret;
}
/*
================
Sys_GetSystemRam
returns in megabytes
================
*/
int Sys_GetSystemRam( void ) {
long count, page_size;
int mb;
count = sysconf( _SC_PHYS_PAGES );
if ( count == -1 ) {
common->Printf( "GetSystemRam: sysconf _SC_PHYS_PAGES failed\n" );
return 512;
}
page_size = sysconf( _SC_PAGE_SIZE );
if ( page_size == -1 ) {
common->Printf( "GetSystemRam: sysconf _SC_PAGE_SIZE failed\n" );
return 512;
}
mb= (int)( (double)count * (double)page_size / ( 1024 * 1024 ) );
// round to the nearest 16Mb
mb = ( mb + 8 ) & ~15;
return mb;
}
/*
==================
Sys_DoStartProcess
if we don't fork, this function never returns
the no-fork lets you keep the terminal when you're about to spawn an installer
if the command contains spaces, system() is used. Otherwise the more straightforward execl ( system() blows though )
==================
*/
void Sys_DoStartProcess( const char *exeName, bool dofork ) {
bool use_system = false;
if ( strchr( exeName, ' ' ) ) {
use_system = true;
} else {
// set exec rights when it's about a single file to execute
struct stat buf;
if ( stat( exeName, &buf ) == -1 ) {
printf( "stat %s failed: %s\n", exeName, strerror( errno ) );
} else {
if ( chmod( exeName, buf.st_mode | S_IXUSR ) == -1 ) {
printf( "cmod +x %s failed: %s\n", exeName, strerror( errno ) );
}
}
}
if ( dofork ) {
switch ( fork() ) {
case -1:
printf( "fork failed: %s\n", strerror( errno ) );
break;
case 0:
if ( use_system ) {
printf( "system %s\n", exeName );
if (system( exeName ) == -1)
printf( "system failed: %s\n", strerror( errno ) );
_exit( 0 );
} else {
printf( "execl %s\n", exeName );
execl( exeName, exeName, NULL );
printf( "execl failed: %s\n", strerror( errno ) );
_exit( -1 );
}
break;
default:
break;
}
} else {
if ( use_system ) {
printf( "system %s\n", exeName );
if (system( exeName ) == -1)
printf( "system failed: %s\n", strerror( errno ) );
else
sleep( 1 ); // on some systems I've seen that starting the new process and exiting this one should not be too close
} else {
printf( "execl %s\n", exeName );
execl( exeName, exeName, NULL );
printf( "execl failed: %s\n", strerror( errno ) );
}
// terminate
_exit( 0 );
}
}
/*
=================
Sys_OpenURL
=================
*/
void idSysLocal::OpenURL( const char *url, bool quit ) {
const char *script_path;
idFile *script_file;
char cmdline[ 1024 ];
static bool quit_spamguard = false;
if ( quit_spamguard ) {
common->DPrintf( "Sys_OpenURL: already in a doexit sequence, ignoring %s\n", url );
return;
}
common->Printf( "Open URL: %s\n", url );
// opening an URL on *nix can mean a lot of things ..
// just spawn a script instead of deciding for the user :-)
// look in the savepath first, then in the basepath
script_path = fileSystem->BuildOSPath( cvarSystem->GetCVarString( "fs_savepath" ), "", "openurl.sh" );
script_file = fileSystem->OpenExplicitFileRead( script_path );
if ( !script_file ) {
script_path = fileSystem->BuildOSPath( cvarSystem->GetCVarString( "fs_basepath" ), "", "openurl.sh" );
script_file = fileSystem->OpenExplicitFileRead( script_path );
}
if ( !script_file ) {
common->Printf( "Can't find URL script 'openurl.sh' in either savepath or basepath\n" );
common->Printf( "OpenURL '%s' failed\n", url );
return;
}
fileSystem->CloseFile( script_file );
// if we are going to quit, only accept a single URL before quitting and spawning the script
if ( quit ) {
quit_spamguard = true;
}
common->Printf( "URL script: %s\n", script_path );
// StartProcess is going to execute a system() call with that - hence the &
idStr::snPrintf( cmdline, 1024, "%s '%s' &", script_path, url );
sys->StartProcess( cmdline, quit );
}
/*
==================
Sys_DoPreferences
==================
*/
void Sys_DoPreferences( void ) { }
/*
================
Sys_FPU_SetDAZ
================
*/
void Sys_FPU_SetDAZ( bool enable ) {
/*
DWORD dwData;
_asm {
movzx ecx, byte ptr enable
and ecx, 1
shl ecx, 6
STMXCSR dword ptr dwData
mov eax, dwData
and eax, ~(1<<6) // clear DAX bit
or eax, ecx // set the DAZ bit
mov dwData, eax
LDMXCSR dword ptr dwData
}
*/
}
/*
================
Sys_FPU_SetFTZ
================
*/
void Sys_FPU_SetFTZ( bool enable ) {
/*
DWORD dwData;
_asm {
movzx ecx, byte ptr enable
and ecx, 1
shl ecx, 15
STMXCSR dword ptr dwData
mov eax, dwData
and eax, ~(1<<15) // clear FTZ bit
or eax, ecx // set the FTZ bit
mov dwData, eax
LDMXCSR dword ptr dwData
}
*/
}
/*
===============
mem consistency stuff
===============
*/
#ifdef ID_MCHECK
const char *mcheckstrings[] = {
"MCHECK_DISABLED",
"MCHECK_OK",
"MCHECK_FREE", // block freed twice
"MCHECK_HEAD", // memory before the block was clobbered
"MCHECK_TAIL" // memory after the block was clobbered
};
void abrt_func( mcheck_status status ) {
Sys_Printf( "memory consistency failure: %s\n", mcheckstrings[ status + 1 ] );
Posix_SetExit( EXIT_FAILURE );
common->Quit();
}
#endif
/*
===============
main
===============
*/
int main(int argc, const char **argv) {
#ifdef ID_MCHECK
// must have -lmcheck linkage
mcheck( abrt_func );
Sys_Printf( "memory consistency checking enabled\n" );
#endif
Posix_EarlyInit( );
if ( argc > 1 ) {
common->Init( argc-1, &argv[1], NULL );
} else {
common->Init( 0, NULL, NULL );
}
Posix_LateInit( );
while (1) {
common->Frame();
}
}