00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include "vm_local.h"
00037
00038
00039 vm_t *currentVM = NULL;
00040 vm_t *lastVM = NULL;
00041 int vm_debugLevel;
00042
00043 #define MAX_VM 3
00044 vm_t vmTable[MAX_VM];
00045
00046
00047 void VM_VmInfo_f( void );
00048 void VM_VmProfile_f( void );
00049
00050
00051
00052
00053 void *VM_VM2C( vmptr_t p, int length ) {
00054 return (void *)p;
00055 }
00056
00057 void VM_Debug( int level ) {
00058 vm_debugLevel = level;
00059 }
00060
00061
00062
00063
00064
00065
00066 void VM_Init( void ) {
00067 Cvar_Get( "vm_cgame", "2", CVAR_ARCHIVE );
00068 Cvar_Get( "vm_game", "2", CVAR_ARCHIVE );
00069 Cvar_Get( "vm_ui", "2", CVAR_ARCHIVE );
00070
00071 Cmd_AddCommand ("vmprofile", VM_VmProfile_f );
00072 Cmd_AddCommand ("vminfo", VM_VmInfo_f );
00073
00074 Com_Memset( vmTable, 0, sizeof( vmTable ) );
00075 }
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085 const char *VM_ValueToSymbol( vm_t *vm, int value ) {
00086 vmSymbol_t *sym;
00087 static char text[MAX_TOKEN_CHARS];
00088
00089 sym = vm->symbols;
00090 if ( !sym ) {
00091 return "NO SYMBOLS";
00092 }
00093
00094
00095 while ( sym->next && sym->next->symValue <= value ) {
00096 sym = sym->next;
00097 }
00098
00099 if ( value == sym->symValue ) {
00100 return sym->symName;
00101 }
00102
00103 Com_sprintf( text, sizeof( text ), "%s+%i", sym->symName, value - sym->symValue );
00104
00105 return text;
00106 }
00107
00108
00109
00110
00111
00112
00113
00114
00115 vmSymbol_t *VM_ValueToFunctionSymbol( vm_t *vm, int value ) {
00116 vmSymbol_t *sym;
00117 static vmSymbol_t nullSym;
00118
00119 sym = vm->symbols;
00120 if ( !sym ) {
00121 return &nullSym;
00122 }
00123
00124 while ( sym->next && sym->next->symValue <= value ) {
00125 sym = sym->next;
00126 }
00127
00128 return sym;
00129 }
00130
00131
00132
00133
00134
00135
00136
00137 int VM_SymbolToValue( vm_t *vm, const char *symbol ) {
00138 vmSymbol_t *sym;
00139
00140 for ( sym = vm->symbols ; sym ; sym = sym->next ) {
00141 if ( !strcmp( symbol, sym->symName ) ) {
00142 return sym->symValue;
00143 }
00144 }
00145 return 0;
00146 }
00147
00148
00149
00150
00151
00152
00153
00154 const char *VM_SymbolForCompiledPointer( vm_t *vm, void *code ) {
00155 int i;
00156
00157 if ( code < (void *)vm->codeBase ) {
00158 return "Before code block";
00159 }
00160 if ( code >= (void *)(vm->codeBase + vm->codeLength) ) {
00161 return "After code block";
00162 }
00163
00164
00165 for ( i = 0 ; i < vm->codeLength ; i++ ) {
00166 if ( (void *)vm->instructionPointers[i] > code ) {
00167 break;
00168 }
00169 }
00170 i--;
00171
00172
00173 return VM_ValueToSymbol( vm, i );
00174 }
00175
00176
00177
00178
00179
00180
00181
00182
00183 int ParseHex( const char *text ) {
00184 int value;
00185 int c;
00186
00187 value = 0;
00188 while ( ( c = *text++ ) != 0 ) {
00189 if ( c >= '0' && c <= '9' ) {
00190 value = value * 16 + c - '0';
00191 continue;
00192 }
00193 if ( c >= 'a' && c <= 'f' ) {
00194 value = value * 16 + 10 + c - 'a';
00195 continue;
00196 }
00197 if ( c >= 'A' && c <= 'F' ) {
00198 value = value * 16 + 10 + c - 'A';
00199 continue;
00200 }
00201 }
00202
00203 return value;
00204 }
00205
00206
00207
00208
00209
00210
00211 void VM_LoadSymbols( vm_t *vm ) {
00212 int len;
00213 char *mapfile, *text_p, *token;
00214 char name[MAX_QPATH];
00215 char symbols[MAX_QPATH];
00216 vmSymbol_t **prev, *sym;
00217 int count;
00218 int value;
00219 int chars;
00220 int segment;
00221 int numInstructions;
00222
00223
00224 if ( !com_developer->integer ) {
00225 return;
00226 }
00227
00228 COM_StripExtension( vm->name, name );
00229 Com_sprintf( symbols, sizeof( symbols ), "vm/%s.map", name );
00230 len = FS_ReadFile( symbols, (void **)&mapfile );
00231 if ( !mapfile ) {
00232 Com_Printf( "Couldn't load symbol file: %s\n", symbols );
00233 return;
00234 }
00235
00236 numInstructions = vm->instructionPointersLength >> 2;
00237
00238
00239 text_p = mapfile;
00240 prev = &vm->symbols;
00241 count = 0;
00242
00243 while ( 1 ) {
00244 token = COM_Parse( &text_p );
00245 if ( !token[0] ) {
00246 break;
00247 }
00248 segment = ParseHex( token );
00249 if ( segment ) {
00250 COM_Parse( &text_p );
00251 COM_Parse( &text_p );
00252 continue;
00253 }
00254
00255 token = COM_Parse( &text_p );
00256 if ( !token[0] ) {
00257 Com_Printf( "WARNING: incomplete line at end of file\n" );
00258 break;
00259 }
00260 value = ParseHex( token );
00261
00262 token = COM_Parse( &text_p );
00263 if ( !token[0] ) {
00264 Com_Printf( "WARNING: incomplete line at end of file\n" );
00265 break;
00266 }
00267 chars = strlen( token );
00268 sym = Hunk_Alloc( sizeof( *sym ) + chars, h_high );
00269 *prev = sym;
00270 prev = &sym->next;
00271 sym->next = NULL;
00272
00273
00274 if ( value >= 0 && value < numInstructions ) {
00275 value = vm->instructionPointers[value];
00276 }
00277
00278 sym->symValue = value;
00279 Q_strncpyz( sym->symName, token, chars + 1 );
00280
00281 count++;
00282 }
00283
00284 vm->numSymbols = count;
00285 Com_Printf( "%i symbols parsed from %s\n", count, symbols );
00286 FS_FreeFile( mapfile );
00287 }
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327 int QDECL VM_DllSyscall( int arg, ... ) {
00328 #if ((defined __linux__) && (defined __powerpc__))
00329
00330 int args[16];
00331 int i;
00332 va_list ap;
00333
00334 args[0] = arg;
00335
00336 va_start(ap, arg);
00337 for (i = 1; i < sizeof (args) / sizeof (args[i]); i++)
00338 args[i] = va_arg(ap, int);
00339 va_end(ap);
00340
00341 return currentVM->systemCall( args );
00342 #else // original id code
00343 return currentVM->systemCall( &arg );
00344 #endif
00345 }
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355 vm_t *VM_Restart( vm_t *vm ) {
00356 vmHeader_t *header;
00357 int length;
00358 int dataLength;
00359 int i;
00360 char filename[MAX_QPATH];
00361
00362
00363 if ( vm->dllHandle ) {
00364 char name[MAX_QPATH];
00365 int (*systemCall)( int *parms );
00366
00367 systemCall = vm->systemCall;
00368 Q_strncpyz( name, vm->name, sizeof( name ) );
00369
00370 VM_Free( vm );
00371
00372 vm = VM_Create( name, systemCall, VMI_NATIVE );
00373 return vm;
00374 }
00375
00376
00377 Com_Printf( "VM_Restart()\n", filename );
00378 Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", vm->name );
00379 Com_Printf( "Loading vm file %s.\n", filename );
00380 length = FS_ReadFile( filename, (void **)&header );
00381 if ( !header ) {
00382 Com_Error( ERR_DROP, "VM_Restart failed.\n" );
00383 }
00384
00385
00386 for ( i = 0 ; i < sizeof( *header ) / 4 ; i++ ) {
00387 ((int *)header)[i] = LittleLong( ((int *)header)[i] );
00388 }
00389
00390
00391 if ( header->vmMagic != VM_MAGIC
00392 || header->bssLength < 0
00393 || header->dataLength < 0
00394 || header->litLength < 0
00395 || header->codeLength <= 0 ) {
00396 VM_Free( vm );
00397 Com_Error( ERR_FATAL, "%s has bad header", filename );
00398 }
00399
00400
00401
00402 dataLength = header->dataLength + header->litLength + header->bssLength;
00403 for ( i = 0 ; dataLength > ( 1 << i ) ; i++ ) {
00404 }
00405 dataLength = 1 << i;
00406
00407
00408 Com_Memset( vm->dataBase, 0, dataLength );
00409
00410
00411 Com_Memcpy( vm->dataBase, (byte *)header + header->dataOffset, header->dataLength + header->litLength );
00412
00413
00414 for ( i = 0 ; i < header->dataLength ; i += 4 ) {
00415 *(int *)(vm->dataBase + i) = LittleLong( *(int *)(vm->dataBase + i ) );
00416 }
00417
00418
00419 FS_FreeFile( header );
00420
00421 return vm;
00422 }
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433 #define STACK_SIZE 0x20000
00434
00435 vm_t *VM_Create( const char *module, int (*systemCalls)(int *),
00436 vmInterpret_t interpret ) {
00437 vm_t *vm;
00438 vmHeader_t *header;
00439 int length;
00440 int dataLength;
00441 int i, remaining;
00442 char filename[MAX_QPATH];
00443
00444 if ( !module || !module[0] || !systemCalls ) {
00445 Com_Error( ERR_FATAL, "VM_Create: bad parms" );
00446 }
00447
00448 remaining = Hunk_MemoryRemaining();
00449
00450
00451 for ( i = 0 ; i < MAX_VM ; i++ ) {
00452 if (!Q_stricmp(vmTable[i].name, module)) {
00453 vm = &vmTable[i];
00454 return vm;
00455 }
00456 }
00457
00458
00459 for ( i = 0 ; i < MAX_VM ; i++ ) {
00460 if ( !vmTable[i].name[0] ) {
00461 break;
00462 }
00463 }
00464
00465 if ( i == MAX_VM ) {
00466 Com_Error( ERR_FATAL, "VM_Create: no free vm_t" );
00467 }
00468
00469 vm = &vmTable[i];
00470
00471 Q_strncpyz( vm->name, module, sizeof( vm->name ) );
00472 vm->systemCall = systemCalls;
00473
00474
00475 if ( interpret == VMI_NATIVE ) {
00476 if ( Cvar_VariableValue( "fs_restrict" ) ) {
00477 interpret = VMI_COMPILED;
00478 }
00479 }
00480
00481 if ( interpret == VMI_NATIVE ) {
00482
00483 Com_Printf( "Loading dll file %s.\n", vm->name );
00484 vm->dllHandle = Sys_LoadDll( module, vm->fqpath , &vm->entryPoint, VM_DllSyscall );
00485 if ( vm->dllHandle ) {
00486 return vm;
00487 }
00488
00489 Com_Printf( "Failed to load dll, looking for qvm.\n" );
00490 interpret = VMI_COMPILED;
00491 }
00492
00493
00494 Com_sprintf( filename, sizeof(filename), "vm/%s.qvm", vm->name );
00495 Com_Printf( "Loading vm file %s.\n", filename );
00496 length = FS_ReadFile( filename, (void **)&header );
00497 if ( !header ) {
00498 Com_Printf( "Failed.\n" );
00499 VM_Free( vm );
00500 return NULL;
00501 }
00502
00503
00504 for ( i = 0 ; i < sizeof( *header ) / 4 ; i++ ) {
00505 ((int *)header)[i] = LittleLong( ((int *)header)[i] );
00506 }
00507
00508
00509 if ( header->vmMagic != VM_MAGIC
00510 || header->bssLength < 0
00511 || header->dataLength < 0
00512 || header->litLength < 0
00513 || header->codeLength <= 0 ) {
00514 VM_Free( vm );
00515 Com_Error( ERR_FATAL, "%s has bad header", filename );
00516 }
00517
00518
00519
00520 dataLength = header->dataLength + header->litLength + header->bssLength;
00521 for ( i = 0 ; dataLength > ( 1 << i ) ; i++ ) {
00522 }
00523 dataLength = 1 << i;
00524
00525
00526 vm->dataBase = Hunk_Alloc( dataLength, h_high );
00527 vm->dataMask = dataLength - 1;
00528
00529
00530 Com_Memcpy( vm->dataBase, (byte *)header + header->dataOffset, header->dataLength + header->litLength );
00531
00532
00533 for ( i = 0 ; i < header->dataLength ; i += 4 ) {
00534 *(int *)(vm->dataBase + i) = LittleLong( *(int *)(vm->dataBase + i ) );
00535 }
00536
00537
00538 vm->instructionPointersLength = header->instructionCount * 4;
00539 vm->instructionPointers = Hunk_Alloc( vm->instructionPointersLength, h_high );
00540
00541
00542 vm->codeLength = header->codeLength;
00543
00544 if ( interpret >= VMI_COMPILED ) {
00545 vm->compiled = qtrue;
00546 VM_Compile( vm, header );
00547 } else {
00548 vm->compiled = qfalse;
00549 VM_PrepareInterpreter( vm, header );
00550 }
00551
00552
00553 FS_FreeFile( header );
00554
00555
00556 VM_LoadSymbols( vm );
00557
00558
00559 vm->programStack = vm->dataMask + 1;
00560 vm->stackBottom = vm->programStack - STACK_SIZE;
00561
00562 Com_Printf("%s loaded in %d bytes on the hunk\n", module, remaining - Hunk_MemoryRemaining());
00563
00564 return vm;
00565 }
00566
00567
00568
00569
00570
00571
00572 void VM_Free( vm_t *vm ) {
00573
00574 if ( vm->dllHandle ) {
00575 Sys_UnloadDll( vm->dllHandle );
00576 Com_Memset( vm, 0, sizeof( *vm ) );
00577 }
00578 #if 0 // now automatically freed by hunk
00579 if ( vm->codeBase ) {
00580 Z_Free( vm->codeBase );
00581 }
00582 if ( vm->dataBase ) {
00583 Z_Free( vm->dataBase );
00584 }
00585 if ( vm->instructionPointers ) {
00586 Z_Free( vm->instructionPointers );
00587 }
00588 #endif
00589 Com_Memset( vm, 0, sizeof( *vm ) );
00590
00591 currentVM = NULL;
00592 lastVM = NULL;
00593 }
00594
00595 void VM_Clear(void) {
00596 int i;
00597 for (i=0;i<MAX_VM; i++) {
00598 if ( vmTable[i].dllHandle ) {
00599 Sys_UnloadDll( vmTable[i].dllHandle );
00600 }
00601 Com_Memset( &vmTable[i], 0, sizeof( vm_t ) );
00602 }
00603 currentVM = NULL;
00604 lastVM = NULL;
00605 }
00606
00607 void *VM_ArgPtr( int intValue ) {
00608 if ( !intValue ) {
00609 return NULL;
00610 }
00611
00612 if ( currentVM==NULL )
00613 return NULL;
00614
00615 if ( currentVM->entryPoint ) {
00616 return (void *)(currentVM->dataBase + intValue);
00617 }
00618 else {
00619 return (void *)(currentVM->dataBase + (intValue & currentVM->dataMask));
00620 }
00621 }
00622
00623 void *VM_ExplicitArgPtr( vm_t *vm, int intValue ) {
00624 if ( !intValue ) {
00625 return NULL;
00626 }
00627
00628
00629 if ( currentVM==NULL )
00630 return NULL;
00631
00632
00633 if ( vm->entryPoint ) {
00634 return (void *)(vm->dataBase + intValue);
00635 }
00636 else {
00637 return (void *)(vm->dataBase + (intValue & vm->dataMask));
00638 }
00639 }
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665 #define MAX_STACK 256
00666 #define STACK_MASK (MAX_STACK-1)
00667
00668 int QDECL VM_Call( vm_t *vm, int callnum, ... ) {
00669 vm_t *oldVM;
00670 int r;
00671 int i;
00672 int args[16];
00673 va_list ap;
00674
00675
00676 if ( !vm ) {
00677 Com_Error( ERR_FATAL, "VM_Call with NULL vm" );
00678 }
00679
00680 oldVM = currentVM;
00681 currentVM = vm;
00682 lastVM = vm;
00683
00684 if ( vm_debugLevel ) {
00685 Com_Printf( "VM_Call( %i )\n", callnum );
00686 }
00687
00688
00689 if ( vm->entryPoint ) {
00690
00691 va_start(ap, callnum);
00692 for (i = 0; i < sizeof (args) / sizeof (args[i]); i++) {
00693 args[i] = va_arg(ap, int);
00694 }
00695 va_end(ap);
00696
00697 r = vm->entryPoint( callnum, args[0], args[1], args[2], args[3],
00698 args[4], args[5], args[6], args[7],
00699 args[8], args[9], args[10], args[11],
00700 args[12], args[13], args[14], args[15]);
00701 } else if ( vm->compiled ) {
00702 r = VM_CallCompiled( vm, &callnum );
00703 } else {
00704 r = VM_CallInterpreted( vm, &callnum );
00705 }
00706
00707 if ( oldVM != NULL )
00708 currentVM = oldVM;
00709 return r;
00710 }
00711
00712
00713
00714 static int QDECL VM_ProfileSort( const void *a, const void *b ) {
00715 vmSymbol_t *sa, *sb;
00716
00717 sa = *(vmSymbol_t **)a;
00718 sb = *(vmSymbol_t **)b;
00719
00720 if ( sa->profileCount < sb->profileCount ) {
00721 return -1;
00722 }
00723 if ( sa->profileCount > sb->profileCount ) {
00724 return 1;
00725 }
00726 return 0;
00727 }
00728
00729
00730
00731
00732
00733
00734
00735 void VM_VmProfile_f( void ) {
00736 vm_t *vm;
00737 vmSymbol_t **sorted, *sym;
00738 int i;
00739 double total;
00740
00741 if ( !lastVM ) {
00742 return;
00743 }
00744
00745 vm = lastVM;
00746
00747 if ( !vm->numSymbols ) {
00748 return;
00749 }
00750
00751 sorted = Z_Malloc( vm->numSymbols * sizeof( *sorted ) );
00752 sorted[0] = vm->symbols;
00753 total = sorted[0]->profileCount;
00754 for ( i = 1 ; i < vm->numSymbols ; i++ ) {
00755 sorted[i] = sorted[i-1]->next;
00756 total += sorted[i]->profileCount;
00757 }
00758
00759 qsort( sorted, vm->numSymbols, sizeof( *sorted ), VM_ProfileSort );
00760
00761 for ( i = 0 ; i < vm->numSymbols ; i++ ) {
00762 int perc;
00763
00764 sym = sorted[i];
00765
00766 perc = 100 * (float) sym->profileCount / total;
00767 Com_Printf( "%2i%% %9i %s\n", perc, sym->profileCount, sym->symName );
00768 sym->profileCount = 0;
00769 }
00770
00771 Com_Printf(" %9.0f total\n", total );
00772
00773 Z_Free( sorted );
00774 }
00775
00776
00777
00778
00779
00780
00781
00782 void VM_VmInfo_f( void ) {
00783 vm_t *vm;
00784 int i;
00785
00786 Com_Printf( "Registered virtual machines:\n" );
00787 for ( i = 0 ; i < MAX_VM ; i++ ) {
00788 vm = &vmTable[i];
00789 if ( !vm->name[0] ) {
00790 break;
00791 }
00792 Com_Printf( "%s : ", vm->name );
00793 if ( vm->dllHandle ) {
00794 Com_Printf( "native\n" );
00795 continue;
00796 }
00797 if ( vm->compiled ) {
00798 Com_Printf( "compiled on load\n" );
00799 } else {
00800 Com_Printf( "interpreted\n" );
00801 }
00802 Com_Printf( " code length : %7i\n", vm->codeLength );
00803 Com_Printf( " table length: %7i\n", vm->instructionPointersLength );
00804 Com_Printf( " data length : %7i\n", vm->dataMask + 1 );
00805 }
00806 }
00807
00808
00809
00810
00811
00812
00813
00814
00815 void VM_LogSyscalls( int *args ) {
00816 static int callnum;
00817 static FILE *f;
00818
00819 if ( !f ) {
00820 f = fopen("syscalls.log", "w" );
00821 }
00822 callnum++;
00823 fprintf(f, "%i: %i (%i) = %i %i %i %i\n", callnum, args - (int *)currentVM->dataBase,
00824 args[0], args[1], args[2], args[3], args[4] );
00825 }
00826
00827
00828
00829 #ifdef oDLL_ONLY // bk010215 - for DLL_ONLY dedicated servers/builds w/o VM
00830 int VM_CallCompiled( vm_t *vm, int *args ) {
00831 return(0);
00832 }
00833
00834 void VM_Compile( vm_t *vm, vmHeader_t *header ) {}
00835 #endif // DLL_ONLY