Logo Search packages:      
Sourcecode: u++ version File versions

uMachContext.cc

//                              -*- Mode: C++ -*- 
// 
// uC++ Version 5.0.1, Copyright (C) Peter A. Buhr 1994
// 
// uMachContext.cc -- 
// 
// Author           : Peter Buhr
// Created On       : Fri Feb 25 15:46:42 1994
// Last Modified By : Peter A. Buhr
// Last Modified On : Tue Aug 31 11:23:04 2004
// Update Count     : 462
//
// This  library is free  software; you  can redistribute  it and/or  modify it
// under the terms of the GNU Lesser General Public License as published by the
// Free Software  Foundation; either  version 2.1 of  the License, or  (at your
// option) any later version.
// 
// This library 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 Lesser General Public License
// for more details.
// 
// You should  have received a  copy of the  GNU Lesser General  Public License
// along  with this library.
// 


#define __U_KERNEL__
#include <uC++.h>
#include <uAlign.h>
#include <uProfiler.h>

//#include <uDebug.h>

#include <cstdio>                         // fprintf
#include <cerrno>

#if defined( __sparc__ )
extern "C" void uInvokeStub( uMachContext * );
#if defined(  __solaris__ )
#include <sys/stack.h>
#else
#include <machine/trap.h>
#include <machine/asm_linkage.h>
#include <machine/psl.h>
#endif // __solaris__
#endif // __sparc__


extern "C" void _pthread_deletespecific( void * );    // see pthread simulation


void uMachContext::uInvokeCoroutine( uBaseCoroutine &This ) { // magically invoke the "main" of the most derived class
    // Called from the kernel when starting a coroutine or task so must switch
    // back to user mode.

    This.uSetState( uBaseCoroutine::uActive );        // set state of next coroutine to active
    THREAD_GETMEM( uSelf )->uEnableInterrupts();

    if ( uThisTask().uProfileActive && uProfiler::uProfiler_PostallocateMetricMemory ) {
      (*uProfiler::uProfiler_PostallocateMetricMemory)( uProfiler::uProfilerInstance, uThisTask() );
    } // if

    // At this point, execution is on the stack of the new coroutine or task
    // that has just been switched to by the kernel.  Therefore, interrupts can
    // legitimately occur now.

    try {
      This.uCoStarter();                        // moved from uCoroutineMain to allow recursion on "main"
      This.main();                              // start coroutine's "main" routine
    } catch ( ... ) {                           // unhandled exception ?
      uRaise uBaseCoroutine::uFailure( This, "unhandled exception propagated to resumer" ) uAt *(This.uLast);
      This.uNotHalted = false;                  // terminate coroutine
      This.uCoSuspend();                        // restart last resumer, which should immediately propagate the nonlocal exception
    } // try
    This.uCoFinish();
} // uMachContext::uInvokeCoroutine


void uMachContext::uInvokeTask( uBaseTask &This ) {   // magically invoke the "main" of the most derived class
    // Called from the kernel when starting a coroutine or task so must switch
    // back to user mode.

#if defined (__U_MULTI__)
    THREAD_SETMEM( uActiveTask, ((uBaseTask *)&This) );
#endif

    errno = 0;                                  // reset errno for each task
    This.uCurrCoroutine->uSetState( uBaseCoroutine::uActive ); // set state of next coroutine to active
    This.uSetState( uBaseTask::uRunning );
    THREAD_GETMEM( uSelf )->uEnableInterrupts();

    if ( uThisTask().uProfileActive && uProfiler::uProfiler_PostallocateMetricMemory ) {
      (*uProfiler::uProfiler_PostallocateMetricMemory)( uProfiler::uProfilerInstance, uThisTask() );
    } // if

    // At this point, execution is on the stack of the new coroutine or task
    // that has just been switched to by the kernel.  Therefore, interrupts can
    // legitimately occur now.

    try {
      This.main();                              // start task's "main" routine
      uDisable {                          // task has terminated so ignore concurrent exceptions
          This.uNotHalted = false;
          This.uSetState( uBaseTask::uTerminate );

          // Clean up storage associated with the task for pthread
          // thread-specific data, e.g., exception handling associates
          // thread-specific data with any task.

          if ( This.pthreadData != NULL ) {
            _pthread_deletespecific( This.pthreadData );
          } // if
          This.serial->uLeave2();
          uAbort( "(uMachContext::uInvokeTask() : internal error, CONTROL NEVER REACHES HERE!" );
      } // uDisable
    } catch( uEHM::uDualClass &evt ) {
      evt.defaultTerminate();
    } catch( uEHM::uThrowClass &evt ) {
      evt.defaultTerminate();
    } catch( ... ) {
    } // try

    try {
      std::terminate();                   // call task terminate routine
    } catch( ... ) {                            // control should not return
    } // try

    if ( This.pthreadData != NULL ) {                 // see above for explanation
      _pthread_deletespecific( This.pthreadData );
    } // if

    uEHM::uTerminate();                         // call abort terminate routine
} // uMachContext::uInvokeTask


void *uMachContext::uRtnAdr( void (*rtn)() ) {
#if defined( __linux__ ) && defined( __ia64__ )
    if ( ! rtn ) return NULL;

    struct TOC {
      void *rtn;
      void *toc;
    };

    return ((TOC *)rtn)->rtn;
#else
    return (void *)rtn;
#endif
} // uMachContext::uRtnAdr


unsigned int uMachContext::uMagic() {
    return 0x12345678;
} // uMachContext::uMagic


void uMachContext::uStartHere( void (*uInvoke)( uMachContext & ) ) {

/*******************************************************
  magic number (16 bytes)
  ,-----------------. \ <--- uBase (stack grows down)
  |                 | |
  |    task stack   | } uSize
  |                 | |
  `-----------------' / <--- uLimit
  magic number (16 bytes)
  ,-----------------.
  |                 |
  | __U_CONTEXT_T__ |
  |                 |
0 `-----------------' <--- uStorage
*******************************************************/

#if defined( __i386__ )

    // set base and limits of stack

    uLimit = (char *)uStorage + uCeiling( sizeof(__U_CONTEXT_T__), 16 ) + 16;
    uBase = (char *)uLimit + uSize;

#if ! defined( __U_SWAPCONTEXT__ )
    struct uFakeStack {
      void *uFixedRegisters[3];                 // fixed registers ebx, edi, esi
      void *uReturn;
      void *uDummyReturn;
      void *uArgument;
    };

    ((uContext_t *)uStorage)->uSP = (char *)uBase - sizeof( uFakeStack );
    ((uContext_t *)uStorage)->uFP = NULL;       // terminate stack with NULL fp

    ((uFakeStack *)(((uContext_t *)uStorage)->uSP))->uArgument = this; // argument to uInvoke
    ((uFakeStack *)(((uContext_t *)uStorage)->uSP))->uReturn = uRtnAdr( (void (*)())uInvoke );
#else
    // set base and limits of stack

    uLimit = (char *)uStorage + sizeof(ucontext_t) + 16;
    uBase = (char *)uLimit + uSize;

    if ( ::getcontext( (ucontext *)uStorage ) == -1 ) {     // initialize ucontext area
      uAbort( "internal error, getcontext failed" );
    } // if
    ((ucontext *)uStorage)->uc_stack.ss_sp = (char *)uLimit;
    ((ucontext *)uStorage)->uc_stack.ss_size = uSize; 
    ((ucontext *)uStorage)->uc_stack.ss_flags = 0;
    ::makecontext( (ucontext *)uStorage, (void (*)())uInvoke, 2, this );
    // TEMPORARY: stack is incorrectly initialized to allow stack walking
    ((ucontext *)uStorage)->uc_mcontext.gregs[ REG_EBP ] = 0; // terminate stack with NULL fp
#endif

#elif defined( __ia64__ )

#if ! defined( __U_SWAPCONTEXT__ )
    #error uC++ internal error : swapcontext must be used on the ia64 architecture
#else
    // set base and limits of stack

    uLimit = (char *)uStorage + sizeof(ucontext_t) + 16;
    uBase = (char *)uLimit + uSize;

    if ( ::getcontext( (ucontext *)uStorage ) == -1 ) {     // initialize ucontext area
      uAbort( "internal error, getcontext failed" );
    } // if
    ((ucontext *)uStorage)->uc_stack.ss_sp = (char *)uLimit;
    ((ucontext *)uStorage)->uc_stack.ss_size = uSize;
    ((ucontext *)uStorage)->uc_stack.ss_flags = 0;
    ::makecontext( (ucontext *)uStorage, (void (*)())uInvoke, 2, this );

    // TEMPORARY: getcontext doesn't get the signal mask properly
    sigset_t new_mask;
    sigemptyset( &new_mask );
    if ( sigprocmask( SIG_BLOCK, &new_mask, (sigset_t*)&(((ucontext *)uStorage)->uc_sigmask) ) == -1 ) {
        uAbort( "internal error, sigprocmask" );
    } // if
#endif

#elif defined( __sparc__ )

    // set base and limits of stack

    uLimit = (char *)uStorage + uCeiling( sizeof(__U_CONTEXT_T__), 16 ) + 16;
    uBase = (char *)uLimit + uSize;

#if ! defined( __U_SWAPCONTEXT__ )
    struct uFakeStack {
      void *uLocalRegs[8];
      void *uInRegs[8];
      void *uStructRetAddress;
      void *uCalleeRegArgs[6];
    };

    ((uContext_t *)uStorage)->uFP = (char *)(SA( (unsigned long)uBase - STACK_ALIGN + 1 ) - SA( MINFRAME ));
    ((uContext_t *)uStorage)->uSP = (char *)((uContext_t *)uStorage)->uFP - SA( MINFRAME );
    ((uContext_t *)uStorage)->uPC = (char *)uRtnAdr( (void (*)())uInvokeStub ) - 8;

    ((uFakeStack *)((uContext_t *)uStorage)->uFP)->uInRegs[0] = this; // argument to uInvoke
    ((uFakeStack *)((uContext_t *)uStorage)->uFP)->uInRegs[1] = (void *)uInvoke; // uInvoke routine
    ((uFakeStack *)((uContext_t *)uStorage)->uFP)->uInRegs[6] = NULL; // terminate stack with NULL fp
#else
    if ( ::getcontext( (ucontext *)uStorage ) == -1 ) {     // initialize ucontext area
      uAbort( "internal error, getcontext failed" );
    } // if
    ((ucontext *)uStorage)->uc_stack.ss_sp = (char *)uBase - 8;   // TEMPORARY: -8 for bug in Solaris (fixed in Solaris 4.10)
    ((ucontext *)uStorage)->uc_stack.ss_size = uSize; 
    ((ucontext *)uStorage)->uc_stack.ss_flags = 0;
    ::makecontext( (ucontext *)uStorage, (void (*)(...))uInvoke, 2, this );
    // TEMPORARY: stack is incorrectly initialized to allow stack walking
    *((int *)uBase - 8) = 0;                    // terminate stack with NULL fp
#endif

#elif defined( __mips__ )

#if ! defined( __U_SWAPCONTEXT__ )
    struct uFakeStack {
      void *uFixedRegisters[10];                // fixed registers r16-r23,r30,r31
      void *uDummyAlign[2];                     // make multiple of 16
    };

    uLimit = uStorage;
    uBase = (char *)uStorage + uSize - uAlign();

    uSP = (char *)uBase - sizeof( uFakeStack );

    uProcVal  = (void (*)())uInvoke;

    // argument to uInvoke appears by magic, see uSwitch
    ((uFakeStack *)uSP)->uFixedRegisters[9] = uRtnAdr( (void (*)())uInvoke );
#else
    // set base and limits of stack

    uLimit = (char *)uStorage + sizeof(ucontext_t) + 16;
    uBase = (char *)uLimit + uSize;

    if ( ::getcontext( (ucontext *)uStorage ) == -1 ) {     // initialize ucontext area
      uAbort( "internal error, getcontext failed" );
    } // if
    ((ucontext *)uStorage)->uc_stack.ss_sp = (char *)uBase - 8;   // TEMPORARY: -8 for bug in Solaris (fixed in Solaris 4.10)
    ((ucontext *)uStorage)->uc_stack.ss_size = uSize; 
    ((ucontext *)uStorage)->uc_stack.ss_flags = 0;
    ::makecontext( (ucontext *)uStorage, (void (*)())uInvoke, 2, this );
#endif

#else
    #error uC++ internal error : unsupported architecture
#endif

    // set magic words on stack

    *(unsigned int *)((char *)uLimit - 16) = uMagic();
    *(unsigned int *)uBase = uMagic();
} // uMachContext::uStartHere


void uMachContext::uCreateContext( unsigned int stacksize ) { // used by all constructors
    uSize = uCeiling( stacksize, 16 );
    uLimit = uBase = NULL;
    uErrno = 0;
    uExtras.uAllExtras = 0;
} // uMachContext::uCreateContext


uMachContext::uMachContext( unsigned int stacksize ) {
    uCreateContext( stacksize );
    // use malloc because "new" handles the overflow
    uStorage = malloc( uCeiling( sizeof(__U_CONTEXT_T__), 16 ) + uSize + 32 );
    if ( uStorage == NULL ) {                   // allocate stack from heap
      uAbort( "attempt to allocate %d bytes of storage for coroutine or task execution-state but insufficient memory available.", stacksize );
    } // if
} // uMachContext::uMachContext


uMachContext::~uMachContext() {
    free( uStorage );
} // uMachContext::~uMachContext


void *uMachContext::uStackPointer() const {
    if ( &uThisCoroutine() == this ) {                // accessing myself ?
      void *sp;                           // use my current stack value
#if defined( __i386__ )
      asm("movl %%esp,%0" : "=m" (sp) : );
#elif defined( __ia64__ )
      asm("mov %0 = r12" : "=r" (sp) : );
#elif defined( __sparc__ )
      asm("st %%sp,%0" : "=m" (sp) : );
#elif defined( __mips__ )
#ifdef _ABIN32
      asm("sw $sp,%0" : "=m" (sp) : );
#elif _ABI64
      asm("sd $sp,%0" : "=m" (sp) : );
#else
      #error uC++ internal error : unsupported architecture
#endif
#else
      #error uC++ internal error : unsupported architecture
#endif
      return sp;
    } else {                                    // accessing another coroutine
#if defined( __U_SWAPCONTEXT__ )
      return (void *)(((ucontext_t *)uStorage)->uc_mcontext.
#if defined( __linux__ ) && defined( __i386__ )
          gregs[REG_ESP]);
#elif defined( __linux__ ) && defined( __ia64__ )
          sc_gr[12]);
#elif defined( __solaris__ ) && defined( __sparc__ )
          gregs[REG_SP]);
#elif defined( __irix__ ) &&  defined( __mips__ )
          gregs[CTX_SP]);
#else
          #error uC++ internal error : unsupported architecture
#endif
#else
      return ((uContext_t *)uStorage)->uSP;
#endif
    } // if
} // uMachContext::uStackPointer


#if defined( __ia64__ )
void *uMachContext::uRegisterStackPointer() const {
#if defined( __U_SWAPCONTEXT__ )
    if ( &uThisCoroutine() == this ) {                // accessing myself ?
      void *bsp;                          // use my current stack value
      asm("mov %0 = ar.bsp" : "=r" (bsp) : );
      return bsp;
    } else {                                    // accessing another coroutine
      return (void *)(((ucontext_t *)uStorage)->uc_mcontext.sc_ar_bsp);
    } // if
#else
    #error uC++ internal error : swapcontext must be used on the ia64 architecture
#endif
} // uMachContext::uRegisterStackPointer
#endif // __ia64__


unsigned int uMachContext::uStackSize() const {
    return uSize;
} // uMachContext::uStackSize


ptrdiff_t uMachContext::uStackFree() const {
    return (char *)uStackPointer() - (char *)uLimit;
} // uMachContext::uStackFree


ptrdiff_t uMachContext::uStackUsed() const {
    ptrdiff_t amt = (char *)uBase - (char *)uStackPointer();
    return amt < 0 ? -amt : amt;                // abs, for stacks growing up
} // uMachContext::uStackUsed


void uMachContext::uVerify() {
    // Ignore the boot task as it uses the UNIX stack.
    if ( uStorage == ((uBaseTask *)uKernelModule::uTaskBoot)->uStorage ) return;

    if ( uStackPointer() < uLimit ) {
      uAbort( ": stack overflow detected: stack pointer 0x%p below limit 0x%p.\n"
            "Possible cause is allocation of large stack frame(s) and/or deep call stack.",
            uStackPointer(), uLimit );
    } else if ( (char *)uStackPointer() + 4 * 1024 < uLimit ) {
      fprintf( stderr, ": WARNING within 4K of stack limit: stack pointer 0x%p and limit 0x%p.\n"
             "Possible cause is allocation of large stack frame(s) and/or deep call stack.\n",
             uStackPointer(), uLimit );
    } else if ( uStackPointer() > uBase ) {
      uAbort( ": stack corruption detected.\n"
            "Possible cause is corrupted stack frame via overwriting memory." );
    } else if ( ( *(unsigned int *)uBase != uMagic() ) || ( *(unsigned int *)((char *)uLimit - 16) != uMagic() ) ) {
      uAbort( ": stack corruption detected.\n"
            "Possible cause is corrupted stack frame via overwriting memory." );
#if defined( __ia64__ )
    } else if ( uRegisterStackPointer() >= uStackPointer() ) {
        // on ia64 the stack grows from both ends; when the two stack pointers cross, we have overflow
      uAbort( ": stack overflow detected: stack pointer 0x%p at or below register stack pointer 0x%p.\n"
            "Possible cause is allocation of large stack frame(s) and/or deep call stack.",
            uStackPointer(), uRegisterStackPointer() );
#endif // __ia64__
    } // if
} // uMachContext::uVerify


void uMachContext::uExtraSave() {
    // Don't need to test extras bit, it is sufficent to check context list

    uContext *context;
    for ( uSeqGen<uContext> gen(uAdditionalContexts); gen >> context; ) {
      context->uSave();
    } // for
} // uMachContext::uExtraSave


void uMachContext::uExtraRestore() {
    // Don't need to test extras bit, it is sufficent to check context list

    uContext *context;
    for ( uSeqGen<uContext> gen(uAdditionalContexts); gen >> context; ) {
      context->uRestore();
    } // for
} // uMachContext::uExtraRestore


// Local Variables: //
// compile-command: "gmake install" //
// End: //

Generated by  Doxygen 1.6.0   Back to index