diff options
-rw-r--r-- | cpukit/ChangeLog | 22 | ||||
-rw-r--r-- | cpukit/aclocal/check-smp.m4 | 19 | ||||
-rw-r--r-- | cpukit/aclocal/enable-smp.m4 | 18 | ||||
-rw-r--r-- | cpukit/configure.ac | 7 | ||||
-rw-r--r-- | cpukit/sapi/include/confdefs.h | 70 | ||||
-rw-r--r-- | cpukit/sapi/src/exinit.c | 18 | ||||
-rw-r--r-- | cpukit/score/Makefile.am | 7 | ||||
-rw-r--r-- | cpukit/score/cpu/i386/rtems/score/cpu.h | 18 | ||||
-rw-r--r-- | cpukit/score/cpu/sparc/cpu_asm.S | 545 | ||||
-rw-r--r-- | cpukit/score/cpu/sparc/rtems/score/cpu.h | 31 | ||||
-rw-r--r-- | cpukit/score/include/rtems/bspsmp.h | 233 | ||||
-rw-r--r-- | cpukit/score/include/rtems/score/basedefs.h | 13 | ||||
-rw-r--r-- | cpukit/score/include/rtems/score/context.h | 17 | ||||
-rw-r--r-- | cpukit/score/include/rtems/score/percpu.h | 131 | ||||
-rw-r--r-- | cpukit/score/include/rtems/score/smplock.h | 95 | ||||
-rw-r--r-- | cpukit/score/preinstall.am | 8 | ||||
-rw-r--r-- | cpukit/score/src/percpu.c | 49 | ||||
-rw-r--r-- | cpukit/score/src/smp.c | 153 | ||||
-rw-r--r-- | cpukit/score/src/smplock.c | 49 | ||||
-rw-r--r-- | cpukit/score/src/thread.c | 46 | ||||
-rw-r--r-- | cpukit/score/src/threadcreateidle.c | 44 |
21 files changed, 995 insertions, 598 deletions
diff --git a/cpukit/ChangeLog b/cpukit/ChangeLog index df9b122955..eee80b12c9 100644 --- a/cpukit/ChangeLog +++ b/cpukit/ChangeLog @@ -1,5 +1,27 @@ 2011-03-16 Jennifer Averett <jennifer.averett@OARcorp.com> + PR 1729/cpukit + * configure.ac, sapi/include/confdefs.h, sapi/src/exinit.c, + score/Makefile.am, score/preinstall.am, + score/cpu/i386/rtems/score/cpu.h, score/cpu/sparc/cpu_asm.S, + score/cpu/sparc/rtems/score/cpu.h, + score/include/rtems/score/basedefs.h, + score/include/rtems/score/context.h, + score/include/rtems/score/percpu.h, score/src/percpu.c, + score/src/thread.c, score/src/threadcreateidle.c: Add next step in + SMP support. This adds an allocated array of the Per_CPU structures + to support multiple cpus vs a single instance of the structure which + is still used if SMP support is disabled. Configuration support is + also added to explicitly enable or disable SMP. But SMP can only be + enabled for the CPUs which will support it initially -- SPARC and + i386. With the stub BSP support, a BSP can be run as a single core + SMP system from an RTEMS data structure standpoint. + * aclocal/check-smp.m4, aclocal/enable-smp.m4, + score/include/rtems/bspsmp.h, score/include/rtems/score/smplock.h, + score/src/smp.c, score/src/smplock.c: New files. + +2011-03-16 Jennifer Averett <jennifer.averett@OARcorp.com> + PR 1743/cpu * sapi/include/confdefs.h, score/Makefile.am, score/preinstall.am: Add Simple Priority Scheduler as complement to existing Deterministic diff --git a/cpukit/aclocal/check-smp.m4 b/cpukit/aclocal/check-smp.m4 new file mode 100644 index 0000000000..294f5c9d2c --- /dev/null +++ b/cpukit/aclocal/check-smp.m4 @@ -0,0 +1,19 @@ +dnl $Id$ +dnl +AC_DEFUN([RTEMS_CHECK_SMP], +[dnl +AC_REQUIRE([RTEMS_ENABLE_SMP])dnl + +AC_CACHE_CHECK([whether CPU supports libposix], + rtems_cv_HAS_SMP, + [dnl + case "$RTEMS_CPU" in + *) + if test "${RTEMS_HAS_SMP}" = "yes"; then + rtems_cv_HAS_SMP="yes"; + else + rtems_cv_HAS_SMP="disabled"; + fi + ;; + esac]) +]) diff --git a/cpukit/aclocal/enable-smp.m4 b/cpukit/aclocal/enable-smp.m4 new file mode 100644 index 0000000000..84561f8bbd --- /dev/null +++ b/cpukit/aclocal/enable-smp.m4 @@ -0,0 +1,18 @@ +dnl $Id$ + +AC_DEFUN([RTEMS_ENABLE_SMP], +[ +## AC_BEFORE([$0], [RTEMS_CHECK_SMP])dnl + +AC_ARG_ENABLE(smp, +[AS_HELP_STRING([--enable-smp],[enable smp interface])], +[case "${enableval}" in + yes) case "${RTEMS_CPU}" in + sparc|i386) RTEMS_HAS_SMP=yes ;; + *) RTEMS_HAS_SMP=no ;; + esac + ;; + no) RTEMS_HAS_SMP=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for enable-smp option) ;; +esac],[RTEMS_HAS_SMP=no]) +]) diff --git a/cpukit/configure.ac b/cpukit/configure.ac index a1cc748264..4a771add3d 100644 --- a/cpukit/configure.ac +++ b/cpukit/configure.ac @@ -138,6 +138,7 @@ AC_CHECK_HEADER([signal.h],[ RTEMS_CHECK_MULTIPROCESSING RTEMS_CHECK_POSIX_API RTEMS_CHECK_NETWORKING +RTEMS_CHECK_SMP rtems_major=`echo _RTEMS_VERSION | sed "s/\..*//"` rtems_minor=`echo _RTEMS_VERSION | sed "s/[[0-9]][[0-9]]*\.//;s/\..*//"` @@ -169,6 +170,11 @@ RTEMS_CPUOPT([RTEMS_POSIX_API], [1], [if posix api is supported]) +RTEMS_CPUOPT([RTEMS_SMP], + [test x"$RTEMS_HAS_SMP" = xyes], + [1], + [if SMP is enabled]) + RTEMS_CPUOPT([RTEMS_NETWORKING], [test x"$rtems_cv_HAS_NETWORKING" = xyes], [1], @@ -290,6 +296,7 @@ AM_CONDITIONAL(LIBRPC,[test x"$rtems_cv_HAS_NETWORKING" = x"yes"]) AM_CONDITIONAL(NEWLIB,test x"$RTEMS_USE_NEWLIB" = x"yes") AM_CONDITIONAL(HAS_MP,test x"$enable_multiprocessing" = x"yes" ) +AM_CONDITIONAL(HAS_SMP,[test "$RTEMS_HAS_SMP" = "yes"]) AM_CONDITIONAL(HAS_PTHREADS,test x"$rtems_cv_HAS_POSIX_API" = x"yes") AM_CONDITIONAL(LIBNETWORKING,test x"$rtems_cv_HAS_NETWORKING" = x"yes") diff --git a/cpukit/sapi/include/confdefs.h b/cpukit/sapi/include/confdefs.h index d194fed1bc..ad81403430 100644 --- a/cpukit/sapi/include/confdefs.h +++ b/cpukit/sapi/include/confdefs.h @@ -174,6 +174,21 @@ rtems_fs_init_functions_t rtems_fs_init_helper = extern int rtems_telnetd_maximum_ptys; #endif +#if defined(RTEMS_SMP) + /* + * If configured for SMP, then we need to know the maximum CPU cores. + */ + #if !defined(CONFIGURE_SMP_APPLICATION) + #if !defined(CONFIGURE_SMP_MAXIMUM_PROCESSORS) + #define CONFIGURE_SMP_MAXIMUM_PROCESSORS 1 + #endif + #else + #if !defined(CONFIGURE_SMP_MAXIMUM_PROCESSORS) + #error "CONFIGURE_SMP_MAXIMUM_PROCESSORS not specified for SMP Application" + #endif + #endif +#endif + /* * Filesystems and Mount Table Configuration. * @@ -1805,12 +1820,25 @@ rtems_fs_init_functions_t rtems_fs_init_helper = _Configure_Object_RAM(1, sizeof(API_Mutex_Control)) /** - * This defines the amount of memory reserved for the IDLE task - * control structures and stack. + * This defines the formula used to compute the amount of memory + * reserved for IDLE task control structures and stacks. + */ +#define CONFIGURE_IDLE_TASKS(_count) \ + (CONFIGURE_MEMORY_FOR_TASKS(_count, 0) + \ + _count * _Configure_From_workspace( \ + (CONFIGURE_IDLE_TASK_STACK_SIZE - CONFIGURE_MINIMUM_TASK_STACK_SIZE))) + +/** + * This calculates the amount of memory reserved for the IDLE tasks. + * In an SMP system, each CPU core has its own idle task. */ -#define CONFIGURE_MEMORY_FOR_IDLE_TASK \ - (CONFIGURE_MEMORY_FOR_TASKS(1, 0) + \ - (CONFIGURE_IDLE_TASK_STACK_SIZE - CONFIGURE_MINIMUM_TASK_STACK_SIZE)) +#if defined(RTEMS_SMP) + #define CONFIGURE_MEMORY_FOR_IDLE_TASK \ + CONFIGURE_IDLE_TASKS(CONFIGURE_SMP_MAXIMUM_PROCESSORS) +#else + #define CONFIGURE_MEMORY_FOR_IDLE_TASK \ + CONFIGURE_IDLE_TASKS(1) +#endif /** * This macro accounts for general RTEMS system overhead. @@ -1898,6 +1926,15 @@ rtems_fs_init_functions_t rtems_fs_init_helper = CONFIGURE_MEMORY_FOR_USER_EXTENSIONS(CONFIGURE_MAXIMUM_USER_EXTENSIONS) \ ) +#if defined(RTEMS_SMP) + #define CONFIGURE_MEMORY_FOR_SMP \ + (CONFIGURE_SMP_MAXIMUM_PROCESSORS * \ + _Configure_From_workspace( CONFIGURE_INTERRUPT_STACK_SIZE ) \ + ) +#else + #define CONFIGURE_MEMORY_FOR_SMP 0 +#endif + #if defined(CONFIGURE_CONFDEFS_DEBUG) && defined(CONFIGURE_INIT) /** * This is a debug mechanism, so if you need to, the executable will @@ -2012,10 +2049,11 @@ rtems_fs_init_functions_t rtems_fs_init_helper = CONFIGURE_MEMORY_FOR_CLASSIC + \ CONFIGURE_MEMORY_FOR_POSIX + \ (CONFIGURE_MAXIMUM_POSIX_THREADS * CONFIGURE_MINIMUM_TASK_STACK_SIZE ) + \ - (CONFIGURE_MAXIMUM_GOROUTINES * CONFIGURE_MINIMUM_TASK_STACK_SIZE ) + \ + (CONFIGURE_MAXIMUM_GOROUTINES * CONFIGURE_MINIMUM_TASK_STACK_SIZE) + \ CONFIGURE_INITIALIZATION_THREADS_STACKS + \ CONFIGURE_MEMORY_FOR_STATIC_EXTENSIONS + \ CONFIGURE_MEMORY_FOR_MP + \ + CONFIGURE_MEMORY_FOR_SMP + \ CONFIGURE_MESSAGE_BUFFER_MEMORY + \ (CONFIGURE_MEMORY_OVERHEAD * 1024) + \ (CONFIGURE_EXTRA_TASK_STACKS) + (CONFIGURE_ADA_TASKS_STACK) \ @@ -2117,6 +2155,26 @@ rtems_fs_init_functions_t rtems_fs_init_helper = #endif /* CONFIGURE_HAS_OWN_CONFIGURATION_TABLE */ +#if defined(RTEMS_SMP) + /** + * Instantiate the variable which specifies the number of CPUs + * in an SMP configuration. + */ + #if defined(CONFIGURE_INIT) + uint32_t rtems_smp_maximum_processors = CONFIGURE_SMP_MAXIMUM_PROCESSORS; + #else + extern uint32_t rtems_smp_maximum_processors; + #endif + /* + * Instantiate the Per CPU information based upon the user configuration. + */ + #if defined(CONFIGURE_INIT) + Per_CPU_Control _Per_CPU_Information[CONFIGURE_SMP_MAXIMUM_PROCESSORS]; + Per_CPU_Control *_Per_CPU_Information_p[CONFIGURE_SMP_MAXIMUM_PROCESSORS]; + #endif + +#endif + /* * If the user has configured a set of Classic API Initialization Tasks, * then we need to install the code that runs that loop. diff --git a/cpukit/sapi/src/exinit.c b/cpukit/sapi/src/exinit.c index 2cbddfa1da..a431669641 100644 --- a/cpukit/sapi/src/exinit.c +++ b/cpukit/sapi/src/exinit.c @@ -1,7 +1,7 @@ /* * Initialization Manager * - * COPYRIGHT (c) 1989-2008. + * COPYRIGHT (c) 1989-2011. * On-Line Applications Research Corporation (OAR). * * The license and distribution terms for this file may be @@ -57,6 +57,11 @@ #include <rtems/posix/posixapi.h> #endif +#if defined(RTEMS_SMP) + #include <rtems/bspsmp.h> + #include <rtems/score/percpu.h> +#endif + Objects_Information *_Internal_Objects[ OBJECTS_INTERNAL_CLASSES_LAST + 1 ]; void rtems_initialize_data_structures(void) @@ -114,6 +119,10 @@ void rtems_initialize_data_structures(void) */ _Workspace_Handler_initialization(); + #if defined(RTEMS_SMP) + _SMP_Handler_initialize(); + #endif + _User_extensions_Handler_initialization(); _ISR_Handler_initialization(); @@ -150,6 +159,13 @@ void rtems_initialize_data_structures(void) _POSIX_API_Initialize(); #endif + /* + * Discover and initialize the secondary cores in an SMP system. + */ + #if defined(RTEMS_SMP) + _SMP_Processor_count = bsp_smp_initialize( rtems_smp_maximum_processors ); + #endif + _System_state_Set( SYSTEM_STATE_BEFORE_MULTITASKING ); /* diff --git a/cpukit/score/Makefile.am b/cpukit/score/Makefile.am index bdd8e510d9..9d3d97f593 100644 --- a/cpukit/score/Makefile.am +++ b/cpukit/score/Makefile.am @@ -12,7 +12,7 @@ SUBDIRS = cpu include_rtemsdir = $(includedir)/rtems include_rtems_HEADERS = include/rtems/debug.h include/rtems/system.h \ - include/rtems/seterr.h + include/rtems/seterr.h include/rtems/bspsmp.h include_rtems_scoredir = $(includedir)/rtems/score @@ -35,7 +35,8 @@ include_rtems_score_HEADERS = include/rtems/score/address.h \ include/rtems/score/timestamp64.h include/rtems/score/tod.h \ include/rtems/score/tqdata.h include/rtems/score/userext.h \ include/rtems/score/watchdog.h include/rtems/score/wkspace.h \ - include/rtems/score/cpuopts.h include/rtems/score/basedefs.h + include/rtems/score/cpuopts.h include/rtems/score/basedefs.h \ + include/rtems/score/smplock.h if HAS_PTHREADS include_rtems_score_HEADERS += include/rtems/score/corespinlock.h \ @@ -88,6 +89,8 @@ if HAS_MP libscore_a_SOURCES += src/mpci.c src/objectmp.c src/threadmp.c endif +libscore_a_SOURCES += src/smp.c src/smplock.c + ## CORE_APIMUTEX_C_FILES libscore_a_SOURCES += src/apimutex.c src/apimutexallocate.c \ src/apimutexlock.c src/apimutexunlock.c diff --git a/cpukit/score/cpu/i386/rtems/score/cpu.h b/cpukit/score/cpu/i386/rtems/score/cpu.h index e793b3372c..0e47c285bb 100644 --- a/cpukit/score/cpu/i386/rtems/score/cpu.h +++ b/cpukit/score/cpu/i386/rtems/score/cpu.h @@ -6,7 +6,7 @@ * This include file contains information pertaining to the Intel * i386 processor. * - * COPYRIGHT (c) 1989-2008. + * COPYRIGHT (c) 1989-2011. * On-Line Applications Research Corporation (OAR). * * The license and distribution terms for this file may be @@ -270,8 +270,6 @@ typedef enum { /* variables */ SCORE_EXTERN Context_Control_fp _CPU_Null_fp_context; -SCORE_EXTERN void *_CPU_Interrupt_stack_low; -SCORE_EXTERN void *_CPU_Interrupt_stack_high; #endif /* ASM */ @@ -437,6 +435,20 @@ uint32_t _CPU_ISR_Get_level( void ); #define _CPU_Context_Restart_self( _the_context ) \ _CPU_Context_restore( (_the_context) ); +#if defined(RTEMS_SMP) + #define _CPU_Context_switch_to_first_task_smp( _the_context ) \ + _CPU_Context_restore( (_the_context) ); + + /* address space 1 is uncacheable */ + #define SMP_CPU_SWAP( _address, _value, _previous ) \ + do { \ + asm volatile("lock; xchgl %0, %1" : \ + "+m" (*_address), "=a" (_previous) : \ + "1" (_value) : \ + "cc"); \ + } while (0) +#endif + #define _CPU_Context_Fp_start( _base, _offset ) \ ( (void *) _Addresses_Add_offset( (_base), (_offset) ) ) diff --git a/cpukit/score/cpu/sparc/cpu_asm.S b/cpukit/score/cpu/sparc/cpu_asm.S index 363ce80ad9..fb9835d16a 100644 --- a/cpukit/score/cpu/sparc/cpu_asm.S +++ b/cpukit/score/cpu/sparc/cpu_asm.S @@ -4,7 +4,7 @@ * in an specific CPU port of RTEMS. These algorithms must be implemented * in assembly language. * - * COPYRIGHT (c) 1989-2010. + * COPYRIGHT (c) 1989-2011. * On-Line Applications Research Corporation (OAR). * * The license and distribution terms for this file may be @@ -319,7 +319,6 @@ done_flushing: * * NOTE: It is unnecessary to reload some registers. */ - .align 4 PUBLIC(_CPU_Context_restore) SYM(_CPU_Context_restore): @@ -327,532 +326,32 @@ SYM(_CPU_Context_restore): rd %psr, %o2 ba SYM(_CPU_Context_restore_heir) mov %i0, %o1 ! in the delay slot + .align 4 +#if defined(RTEMS_SMP) /* - * void _ISR_Handler() - * - * This routine provides the RTEMS interrupt management. - * - * We enter this handler from the 4 instructions in the trap table with - * the following registers assumed to be set as shown: - * - * l0 = PSR - * l1 = PC - * l2 = nPC - * l3 = trap type + * void _CPU_Context_switch_to_first_task_smp( + * Context_Control *new_context + * ) * - * NOTE: By an executive defined convention, trap type is between 0 and 255 if - * it is an asynchonous trap and 256 and 511 if it is synchronous. + * This routine is only used to switch to the first task on a + * secondary core in an SMP configuration. We do not need to + * flush all the windows and, in fact, this can be dangerous + * as they may or may not be initialized properly. So we just + * reinitialize the PSR and WIM. */ + PUBLIC(_CPU_Context_switch_to_first_task_smp) +SYM(_CPU_Context_switch_to_first_task_smp): + save %sp, -CPU_MINIMUM_STACK_FRAME_SIZE, %sp - .align 4 - PUBLIC(_ISR_Handler) -SYM(_ISR_Handler): - /* - * Fix the return address for synchronous traps. - */ - - andcc %l3, SPARC_SYNCHRONOUS_TRAP_BIT_MASK, %g0 - ! Is this a synchronous trap? - be,a win_ovflow ! No, then skip the adjustment - nop ! DELAY - mov %l1, %l6 ! save trapped pc for debug info - mov %l2, %l1 ! do not return to the instruction - add %l2, 4, %l2 ! indicated - -win_ovflow: - /* - * Save the globals this block uses. - * - * These registers are not restored from the locals. Their contents - * are saved directly from the locals into the ISF below. - */ - - mov %g4, %l4 ! save the globals this block uses - mov %g5, %l5 - - /* - * When at a "window overflow" trap, (wim == (1 << cwp)). - * If we get here like that, then process a window overflow. - */ - - rd %wim, %g4 - srl %g4, %l0, %g5 ! g5 = win >> cwp ; shift count and CWP - ! are LS 5 bits ; how convenient :) - cmp %g5, 1 ! Is this an invalid window? - bne dont_do_the_window ! No, then skip all this stuff - ! we are using the delay slot - - /* - * The following is same as a 1 position right rotate of WIM - */ - - srl %g4, 1, %g5 ! g5 = WIM >> 1 - sll %g4, SPARC_NUMBER_OF_REGISTER_WINDOWS-1 , %g4 - ! g4 = WIM << (Number Windows - 1) - or %g4, %g5, %g4 ! g4 = (WIM >> 1) | - ! (WIM << (Number Windows - 1)) - - /* - * At this point: - * - * g4 = the new WIM - * g5 is free - */ - - /* - * Since we are tinkering with the register windows, we need to - * make sure that all the required information is in global registers. - */ - - save ! Save into the window - wr %g4, 0, %wim ! WIM = new WIM - nop ! delay slots - nop - nop - - /* - * Now save the window just as if we overflowed to it. - */ - - std %l0, [%sp + CPU_STACK_FRAME_L0_OFFSET] - std %l2, [%sp + CPU_STACK_FRAME_L2_OFFSET] - std %l4, [%sp + CPU_STACK_FRAME_L4_OFFSET] - std %l6, [%sp + CPU_STACK_FRAME_L6_OFFSET] - - std %i0, [%sp + CPU_STACK_FRAME_I0_OFFSET] - std %i2, [%sp + CPU_STACK_FRAME_I2_OFFSET] - std %i4, [%sp + CPU_STACK_FRAME_I4_OFFSET] - std %i6, [%sp + CPU_STACK_FRAME_I6_FP_OFFSET] - - restore - nop - -dont_do_the_window: - /* - * Global registers %g4 and %g5 are saved directly from %l4 and - * %l5 directly into the ISF below. - */ - -save_isf: - - /* - * Save the state of the interrupted task -- especially the global - * registers -- in the Interrupt Stack Frame. Note that the ISF - * includes a regular minimum stack frame which will be used if - * needed by register window overflow and underflow handlers. - * - * REGISTERS SAME AS AT _ISR_Handler - */ - - sub %fp, CONTEXT_CONTROL_INTERRUPT_FRAME_SIZE, %sp - ! make space for ISF - - std %l0, [%sp + ISF_PSR_OFFSET] ! save psr, PC - st %l2, [%sp + ISF_NPC_OFFSET] ! save nPC - st %g1, [%sp + ISF_G1_OFFSET] ! save g1 - std %g2, [%sp + ISF_G2_OFFSET] ! save g2, g3 - std %l4, [%sp + ISF_G4_OFFSET] ! save g4, g5 -- see above - std %g6, [%sp + ISF_G6_OFFSET] ! save g6, g7 - - std %i0, [%sp + ISF_I0_OFFSET] ! save i0, i1 - std %i2, [%sp + ISF_I2_OFFSET] ! save i2, i3 - std %i4, [%sp + ISF_I4_OFFSET] ! save i4, i5 - std %i6, [%sp + ISF_I6_FP_OFFSET] ! save i6/fp, i7 - - rd %y, %g1 - st %g1, [%sp + ISF_Y_OFFSET] ! save y - st %l6, [%sp + ISF_TPC_OFFSET] ! save real trapped pc - - mov %sp, %o1 ! 2nd arg to ISR Handler - - /* - * Increment ISR nest level and Thread dispatch disable level. - * - * Register usage for this section: - * - * l4 = _Thread_Dispatch_disable_level pointer - * l5 = per cpu info pointer - * l6 = _Thread_Dispatch_disable_level value - * l7 = _ISR_Nest_level value - * - * NOTE: It is assumed that l4 - l7 will be preserved until the ISR - * nest and thread dispatch disable levels are unnested. - */ - - sethi %hi(SYM(_Thread_Dispatch_disable_level)), %l4 - ld [%l4 + %lo(SYM(_Thread_Dispatch_disable_level))], %l6 - - sethi %hi(_Per_CPU_Information), %l5 - add %l5, %lo(_Per_CPU_Information), %l5 - - ld [%l5 + PER_CPU_ISR_NEST_LEVEL], %l7 - - add %l6, 1, %l6 - st %l6, [%l4 + %lo(SYM(_Thread_Dispatch_disable_level))] - - add %l7, 1, %l7 - st %l7, [%l5 + PER_CPU_ISR_NEST_LEVEL] - - /* - * If ISR nest level was zero (now 1), then switch stack. - */ - - mov %sp, %fp - subcc %l7, 1, %l7 ! outermost interrupt handler? - bnz dont_switch_stacks ! No, then do not switch stacks - - nop - ld [%l5 + PER_CPU_INTERRUPT_STACK_HIGH], %sp - -dont_switch_stacks: - /* - * Make sure we have a place on the stack for the window overflow - * trap handler to write into. At this point it is safe to - * enable traps again. - */ - - sub %sp, CPU_MINIMUM_STACK_FRAME_SIZE, %sp - - /* - * Check if we have an external interrupt (trap 0x11 - 0x1f). If so, - * set the PIL in the %psr to mask off interrupts with lower priority. - * The original %psr in %l0 is not modified since it will be restored - * when the interrupt handler returns. - */ - - mov %l0, %g5 - and %l3, 0x0ff, %g4 - -/* This is a fix for ERC32 with FPU rev.B or rev.C */ - -#if defined(FPU_REVB) - - - subcc %g4, 0x08, %g0 - be fpu_revb - subcc %g4, 0x11, %g0 - bl dont_fix_pil - subcc %g4, 0x1f, %g0 - bg dont_fix_pil - sll %g4, 8, %g4 - and %g4, SPARC_PSR_PIL_MASK, %g4 - andn %l0, SPARC_PSR_PIL_MASK, %g5 - or %g4, %g5, %g5 - srl %l0, 12, %g4 - andcc %g4, 1, %g0 - be dont_fix_pil - nop - ba,a enable_irq - - -fpu_revb: - srl %l0, 12, %g4 ! check if EF is set in %psr - andcc %g4, 1, %g0 - be dont_fix_pil ! if FPU disabled than continue as normal - and %l3, 0xff, %g4 - subcc %g4, 0x08, %g0 - bne enable_irq ! if not a FPU exception then do two fmovs - set __sparc_fq, %g4 - st %fsr, [%g4] ! if FQ is not empty and FQ[1] = fmovs - ld [%g4], %g4 ! than this is bug 3.14 - srl %g4, 13, %g4 - andcc %g4, 1, %g0 - be dont_fix_pil - set __sparc_fq, %g4 - std %fq, [%g4] - ld [%g4+4], %g4 - set 0x81a00020, %g5 - subcc %g4, %g5, %g0 - bne,a dont_fix_pil2 - wr %l0, SPARC_PSR_ET_MASK, %psr ! **** ENABLE TRAPS **** - ba,a simple_return - -enable_irq: - or %g5, SPARC_PSR_PIL_MASK, %g4 - wr %g4, SPARC_PSR_ET_MASK, %psr ! **** ENABLE TRAPS **** - nop; nop; nop - fmovs %f0, %f0 - ba dont_fix_pil - fmovs %f0, %f0 - - .data - .global __sparc_fq - .align 8 -__sparc_fq: - .word 0,0 - - .text -/* end of ERC32 FPU rev.B/C fix */ - -#else - - subcc %g4, 0x11, %g0 - bl dont_fix_pil - subcc %g4, 0x1f, %g0 - bg dont_fix_pil - sll %g4, 8, %g4 - and %g4, SPARC_PSR_PIL_MASK, %g4 - andn %l0, SPARC_PSR_PIL_MASK, %g5 - ba pil_fixed - or %g4, %g5, %g5 + mov %psr, %g1 ! Initialize WIM + add %g1, 1, %g2 + and %g2, 0x7, %g2 + set 1, %g3 + sll %g3, %g2, %g3 + mov %g3, %wim + ba done_flushing + mov %i0, %o1 ! in the delay slot #endif -dont_fix_pil: - or %g5, SPARC_PSR_PIL_MASK, %g5 -pil_fixed: - wr %g5, SPARC_PSR_ET_MASK, %psr ! **** ENABLE TRAPS **** -dont_fix_pil2: - - /* - * Vector to user's handler. - * - * NOTE: TBR may no longer have vector number in it since - * we just enabled traps. It is definitely in l3. - */ - - sethi %hi(SYM(_ISR_Vector_table)), %g4 - ld [%g4+%lo(SYM(_ISR_Vector_table))], %g4 - and %l3, 0xFF, %g5 ! remove synchronous trap indicator - sll %g5, 2, %g5 ! g5 = offset into table - ld [%g4 + %g5], %g4 ! g4 = _ISR_Vector_table[ vector ] - - - ! o1 = 2nd arg = address of the ISF - ! WAS LOADED WHEN ISF WAS SAVED!!! - mov %l3, %o0 ! o0 = 1st arg = vector number - call %g4, 0 - nop ! delay slot - - /* - * Redisable traps so we can finish up the interrupt processing. - * This is a VERY conservative place to do this. - * - * NOTE: %l0 has the PSR which was in place when we took the trap. - */ - - mov %l0, %psr ! **** DISABLE TRAPS **** - nop; nop; nop - - /* - * Decrement ISR nest level and Thread dispatch disable level. - * - * Register usage for this section: - * - * l4 = _Thread_Dispatch_disable_level pointer - * l5 = _ISR_Nest_level pointer - * l6 = _Thread_Dispatch_disable_level value - * l7 = _ISR_Nest_level value - */ - - sub %l6, 1, %l6 - st %l6, [%l4 + %lo(SYM(_Thread_Dispatch_disable_level))] - - st %l7, [%l5 + PER_CPU_ISR_NEST_LEVEL] - - /* - * If dispatching is disabled (includes nested interrupt case), - * then do a "simple" exit. - */ - - orcc %l6, %g0, %g0 ! Is dispatching disabled? - bnz simple_return ! Yes, then do a "simple" exit - ! NOTE: Use the delay slot - sethi %hi(SYM(_CPU_ISR_Dispatch_disable)), %l6 - - ! Are we dispatching from a previous ISR in the interrupted thread? - ld [%l6 + %lo(SYM(_CPU_ISR_Dispatch_disable))], %l7 - orcc %l7, %g0, %g0 ! Is this thread already doing an ISR? - bnz simple_return ! Yes, then do a "simple" exit - nop - - - /* - * If a context switch is necessary, then do fudge stack to - * return to the interrupt dispatcher. - */ - - ldub [%l5 + PER_CPU_DISPATCH_NEEDED], %l5 - - orcc %l5, %g0, %g0 ! Is thread switch necessary? - bz simple_return ! no, then do a simple return - nop - - /* - * Invoke interrupt dispatcher. - */ - - PUBLIC(_ISR_Dispatch) -SYM(_ISR_Dispatch): - ! Set ISR dispatch nesting prevention flag - mov 1,%l6 - sethi %hi(SYM(_CPU_ISR_Dispatch_disable)), %l5 - st %l6,[%l5 + %lo(SYM(_CPU_ISR_Dispatch_disable))] - - /* - * The following subtract should get us back on the interrupted - * tasks stack and add enough room to invoke the dispatcher. - * When we enable traps, we are mostly back in the context - * of the task and subsequent interrupts can operate normally. - */ - - sub %fp, CPU_MINIMUM_STACK_FRAME_SIZE, %sp - - or %l0, SPARC_PSR_ET_MASK, %l7 ! l7 = PSR with ET=1 - mov %l7, %psr ! **** ENABLE TRAPS **** - nop - nop - nop -isr_dispatch: - call SYM(_Thread_Dispatch), 0 - nop - - /* - * We invoked _Thread_Dispatch in a state similar to the interrupted - * task. In order to safely be able to tinker with the register - * windows and get the task back to its pre-interrupt state, - * we need to disable interrupts disabled so we can safely tinker - * with the register windowing. In particular, the CWP in the PSR - * is fragile during this period. (See PR578.) - */ - mov 2,%g1 ! syscall (disable interrupts) - ta 0 ! syscall (disable interrupts) - - /* - * While we had ISR dispatching disabled in this thread, - * did we miss anything. If so, then we need to do another - * _Thread_Dispatch before leaving this ISR Dispatch context. - */ - - sethi %hi(_Per_CPU_Information), %l5 - add %l5, %lo(_Per_CPU_Information), %l5 - - ldub [%l5 + PER_CPU_DISPATCH_NEEDED], %l7 - - orcc %l7, %g0, %g0 ! Is thread switch necesary? - bz allow_nest_again ! No, then clear out and return - nop - - ! Yes, then invoke the dispatcher -dispatchAgain: - mov 3,%g1 ! syscall (enable interrupts) - ta 0 ! syscall (enable interrupts) - ba isr_dispatch - nop - -allow_nest_again: - - ! Zero out ISR stack nesting prevention flag - sethi %hi(SYM(_CPU_ISR_Dispatch_disable)), %l5 - st %g0,[%l5 + %lo(SYM(_CPU_ISR_Dispatch_disable))] - - /* - * The CWP in place at this point may be different from - * that which was in effect at the beginning of the ISR if we - * have been context switched between the beginning of this invocation - * of _ISR_Handler and this point. Thus the CWP and WIM should - * not be changed back to their values at ISR entry time. Any - * changes to the PSR must preserve the CWP. - */ - -simple_return: - ld [%fp + ISF_Y_OFFSET], %l5 ! restore y - wr %l5, 0, %y - - ldd [%fp + ISF_PSR_OFFSET], %l0 ! restore psr, PC - ld [%fp + ISF_NPC_OFFSET], %l2 ! restore nPC - rd %psr, %l3 - and %l3, SPARC_PSR_CWP_MASK, %l3 ! want "current" CWP - andn %l0, SPARC_PSR_CWP_MASK, %l0 ! want rest from task - or %l3, %l0, %l0 ! install it later... - andn %l0, SPARC_PSR_ET_MASK, %l0 - - /* - * Restore tasks global and out registers - */ - - mov %fp, %g1 - - ! g1 is restored later - ldd [%fp + ISF_G2_OFFSET], %g2 ! restore g2, g3 - ldd [%fp + ISF_G4_OFFSET], %g4 ! restore g4, g5 - ldd [%fp + ISF_G6_OFFSET], %g6 ! restore g6, g7 - - ldd [%fp + ISF_I0_OFFSET], %i0 ! restore i0, i1 - ldd [%fp + ISF_I2_OFFSET], %i2 ! restore i2, i3 - ldd [%fp + ISF_I4_OFFSET], %i4 ! restore i4, i5 - ldd [%fp + ISF_I6_FP_OFFSET], %i6 ! restore i6/fp, i7 - - /* - * Registers: - * - * ALL global registers EXCEPT G1 and the input registers have - * already been restored and thuse off limits. - * - * The following is the contents of the local registers: - * - * l0 = original psr - * l1 = return address (i.e. PC) - * l2 = nPC - * l3 = CWP - */ - - /* - * if (CWP + 1) is an invalid window then we need to reload it. - * - * WARNING: Traps should now be disabled - */ - - mov %l0, %psr ! **** DISABLE TRAPS **** - nop - nop - nop - rd %wim, %l4 - add %l0, 1, %l6 ! l6 = cwp + 1 - and %l6, SPARC_PSR_CWP_MASK, %l6 ! do the modulo on it - srl %l4, %l6, %l5 ! l5 = win >> cwp + 1 ; shift count - ! and CWP are conveniently LS 5 bits - cmp %l5, 1 ! Is tasks window invalid? - bne good_task_window - - /* - * The following code is the same as a 1 position left rotate of WIM. - */ - - sll %l4, 1, %l5 ! l5 = WIM << 1 - srl %l4, SPARC_NUMBER_OF_REGISTER_WINDOWS-1 , %l4 - ! l4 = WIM >> (Number Windows - 1) - or %l4, %l5, %l4 ! l4 = (WIM << 1) | - ! (WIM >> (Number Windows - 1)) - - /* - * Now restore the window just as if we underflowed to it. - */ - - wr %l4, 0, %wim ! WIM = new WIM - nop ! must delay after writing WIM - nop - nop - restore ! now into the tasks window - - ldd [%g1 + CPU_STACK_FRAME_L0_OFFSET], %l0 - ldd [%g1 + CPU_STACK_FRAME_L2_OFFSET], %l2 - ldd [%g1 + CPU_STACK_FRAME_L4_OFFSET], %l4 - ldd [%g1 + CPU_STACK_FRAME_L6_OFFSET], %l6 - ldd [%g1 + CPU_STACK_FRAME_I0_OFFSET], %i0 - ldd [%g1 + CPU_STACK_FRAME_I2_OFFSET], %i2 - ldd [%g1 + CPU_STACK_FRAME_I4_OFFSET], %i4 - ldd [%g1 + CPU_STACK_FRAME_I6_FP_OFFSET], %i6 - ! reload of sp clobbers ISF - save ! Back to ISR dispatch window - -good_task_window: - - mov %l0, %psr ! **** DISABLE TRAPS **** - nop; nop; nop - ! and restore condition codes. - ld [%g1 + ISF_G1_OFFSET], %g1 ! restore g1 - jmp %l1 ! transfer control and - rett %l2 ! go back to tasks window - /* end of file */ diff --git a/cpukit/score/cpu/sparc/rtems/score/cpu.h b/cpukit/score/cpu/sparc/rtems/score/cpu.h index ddc5badfe5..330f56c6a3 100644 --- a/cpukit/score/cpu/sparc/rtems/score/cpu.h +++ b/cpukit/score/cpu/sparc/rtems/score/cpu.h @@ -6,7 +6,7 @@ * This include file contains information pertaining to the port of * the executive to the SPARC processor. * - * COPYRIGHT (c) 1989-2006. + * COPYRIGHT (c) 1989-2011. * On-Line Applications Research Corporation (OAR). * * The license and distribution terms for this file may be @@ -927,6 +927,35 @@ void _CPU_Context_restore( Context_Control *new_context ) RTEMS_COMPILER_NO_RETURN_ATTRIBUTE; +#if defined(RTEMS_SMP) + /* + * _CPU_Context_switch_to_first_task_smp + * + * This routine is only used to switch to the first task on a + * secondary core in an SMP configuration. We do not need to + * flush all the windows and, in fact, this can be dangerous + * as they may or may not be initialized properly. + */ + void _CPU_Context_switch_to_first_task_smp( + Context_Control *new_context + ); + + /* address space 1 is uncacheable */ + #define SMP_CPU_SWAP( _address, _value, _previous ) \ + do { \ + register unsigned int _val = _value; \ + asm volatile( \ + "swapa [%2] %3, %0" : \ + "=r" (_val) : \ + "0" (_val), \ + "r" (_address), \ + "i" (1) \ + ); \ + _previous = _val; \ + } while (0) + +#endif + /* * _CPU_Context_save_fp * diff --git a/cpukit/score/include/rtems/bspsmp.h b/cpukit/score/include/rtems/bspsmp.h new file mode 100644 index 0000000000..26da0a7614 --- /dev/null +++ b/cpukit/score/include/rtems/bspsmp.h @@ -0,0 +1,233 @@ +/** + * @file rtems/bspsmp.h + * + * This include file defines the interface between RTEMS and an + * SMP aware BSP. These methods will only be used when RTEMS + * is configured with SMP support enabled. + */ + +/* + * COPYRIGHT (c) 1989-2011. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef _RTEMS_BSPSMP_H +#define _RTEMS_BSPSMP_H + +#if defined (RTEMS_SMP) +#include <rtems/score/percpu.h> + +/** + * @defgroup RTEMS BSP SMP Interface + * + * This defines the interface between RTEMS and the BSP for + * SMP support. The interface uses the term primary + * to refer to the "boot" processor and secondary to refer + * to the "application" processors. Different architectures + * use different terminology. + * + * It is assumed that when the processor is reset and thus + * when RTEMS is initialized, that the primary processor is + * the only one executing. The others are assumed to be in + * a quiescent or reset state awaiting a command to come online. + */ + +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * This defines the bit which indicates the interprocessor interrupt + * has been requested so that RTEMS will reschedule on this CPU + * because the currently executing thread needs to be switched out. + */ +#define RTEMS_BSP_SMP_CONTEXT_SWITCH_NECESSARY 0x01 + +/** + * This defines the bit which indicates the interprocessor interrupt + * has been requested so that RTEMS will reschedule on this CPU + * because the currently executing thread has been sent a signal. + */ +#define RTEMS_BSP_SMP_SIGNAL_TO_SELF 0x02 + +/** + * This defines the bit which indicates the interprocessor interrupt + * has been requested so that this CPU will be shutdown. This is done + * as part of rtems_executive_shutdown(). + */ +#define RTEMS_BSP_SMP_SHUTDOWN 0x04 + +/** + * This defines the bit which indicates the interprocessor interrupt + * has been requested that the receiving CPU needs to perform a context + * switch to the first task. + */ +#define RTEMS_BSP_SMP_FIRST_TASK 0x08 + +#ifndef ASM +/** + * @brief Number of CPUs in SMP System + * + * This variable is set during the SMP initialization sequence to + * indicate the number of CPUs in this system. + */ +SCORE_EXTERN uint32_t _SMP_Processor_count; + +/** + * @brief Maximum Number of CPUs in SMP System + * + * This variable is set during the SMP initialization sequence to + * indicate the Maximum number of CPUs in this system. + */ +extern uint32_t rtems_smp_maximum_processors; + +/** + * @brief Initialize Secondary CPUs + * + * This method is invoked by RTEMS during initialization to bring the + * secondary CPUs out of reset. + * + * @param [in] maximum is the maximum number of CPU cores that RTEMS + * can handle + * + * @return This method returns the number of cores available in the + * system. + */ +int bsp_smp_initialize( + int maximum +); + +/** + * @brief Obtain Current CPU Index + * + * This method is invoked by RTEMS when it needs to know the index + * of the CPU it is executing on. + * + * @return This method returns the current CPU index. + */ +int bsp_smp_processor_id(void) RTEMS_COMPILER_PURE_ATTRIBUTE; + +/** + * @brief Make Request of Another CPU + * + * This method is invoked by RTEMS when it needs to make a request + * of another CPU. It should be implemented using some type of + * interprocessor interrupt. + * + * @param [in] cpu is the target CPU for this request. + * @param [in] message is message to send + */ +void bsp_smp_send_message( + int cpu, + uint32_t message +); + +/** + * @brief Make Request of Others CPUs + * + * This method is invoked by RTEMS when it needs to make a request + * of the other CPUs. It should be implemented using some type of + * interprocessor interrupt. CPUs not including the originating + * CPU should receive the message. + * + * @param [in] message is message to send + */ +void bsp_smp_broadcast_message( + uint32_t message +); + +/** + * @brief Generate a Interprocessor Broadcast Interrupt + * + * This method is invoked when RTEMS wants to let all of the other + * CPUs know that it has sent them message. CPUs not including + * the originating CPU should receive the interrupt. + + * + * @note On CPUs without the capability to generate a broadcast + * to all other CPUs interrupt, this can be implemented by + * a loop of sending interrupts to specific CPUs. + */ +void bsp_smp_broadcast_interrupt(void); + +/** + * @brief Generate a Interprocessor Interrupt + * + * This method is invoked by RTEMS to let @a cpu know that it + * has sent it a message. + * + * @param [in] cpu is the recipient CPU + */ +void bsp_smp_interrupt_cpu( + int cpu +); + +/** + * @brief Obtain CPU Core Number + * + * This method is invoked by RTEMS when it needs to know which core + * number it is executing on. This is used when it needs to perform + * some action or bookkeeping and needs to distinguish itself from + * the other cores. For example, it may need to realize it needs to + * preempt a thread on another node. + * + * @return This method returns the Id of the current CPU core. + */ +int bsp_smp_processor_id( void ); + +/** + * This method is invoked by @ref rtems_smp_secondary_cpu_initialize + * to allow the BSP to perform some intialization. The @a cpu + * parameter indicates the secondary CPU that the code is executing on + * and is currently being initialized. + * + * @note This is called by @ref rtems_smp_secondary_cpu_initialize. + */ +void bsp_smp_secondary_cpu_initialize(int cpu); + +/** + * This method is the C entry point which secondary CPUs should + * arrange to call. It performs OS initialization for the secondary + * CPU and coordinates bring it to a useful state. + * + * @note This is provided by RTEMS. + */ +void rtems_smp_secondary_cpu_initialize(void); + +/** + * This method is invoked by the BSP to initialize the per CPU structure + * for the specified @a cpu while it is bringing the secondary CPUs + * out of their reset state and into a useful state. + * + * @param [in] cpu indicates the CPU whose per cpu structure is to + * be initialized. + */ +void rtems_smp_initialize_per_cpu(int cpu); + +/** + * This is the method called by the BSP's interrupt handler + * to process the incoming interprocessor request. + */ +void rtems_smp_process_interrupt(void); + +#endif + +#ifdef __cplusplus +} +#endif + +#else + #define bsp_smp_processor_id() 0 +#endif + +#endif + +/* end of include file */ diff --git a/cpukit/score/include/rtems/score/basedefs.h b/cpukit/score/include/rtems/score/basedefs.h index 6bb03459e6..b956806313 100644 --- a/cpukit/score/include/rtems/score/basedefs.h +++ b/cpukit/score/include/rtems/score/basedefs.h @@ -136,6 +136,19 @@ #endif /** + * The following defines a compiler specific attribute which informs + * the compiler that the method has no effect except the return value + * and that the return value depends only on parameters and/or global + * variables. + */ +#ifdef __GNUC__ + #define RTEMS_COMPILER_PURE_ATTRIBUTE \ + __attribute__ ((pure)) +#else + #define RTEMS_COMPILER_PURE_ATTRIBUTE +#endif + +/** * Instructs the compiler to issue a warning whenever a variable or function * with this attribute will be used. */ diff --git a/cpukit/score/include/rtems/score/context.h b/cpukit/score/include/rtems/score/context.h index cdef1d3fa5..34baf32669 100644 --- a/cpukit/score/include/rtems/score/context.h +++ b/cpukit/score/include/rtems/score/context.h @@ -101,6 +101,23 @@ extern "C" { #define _Context_Restart_self( _the_context ) \ _CPU_Context_Restart_self( _the_context ) +#if defined(RTEMS_SMP) +/* + * @brief Switch to First Task on Secondary Core + * + * This routine is only used to switch to the first task on a + * secondary core in an SMP configuration. Since the switch + * to the first task is done from an interrupt handler, this + * may be different from simply restarting the currently running + * task. + * + * @param[in] _the_context is the context of the first thread to + * run on this core + */ +#define _Context_Switch_to_first_task_smp( _the_context ) \ + _CPU_Context_switch_to_first_task_smp( _the_context ) +#endif + /** * @brief Return Starting Address of Floating Point Context * diff --git a/cpukit/score/include/rtems/score/percpu.h b/cpukit/score/include/rtems/score/percpu.h index 2778573481..0c48b50570 100644 --- a/cpukit/score/include/rtems/score/percpu.h +++ b/cpukit/score/include/rtems/score/percpu.h @@ -6,14 +6,14 @@ */ /* - * COPYRIGHT (c) 1989-2010. + * COPYRIGHT (c) 1989-2011. * On-Line Applications Research Corporation (OAR). * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.com/license/LICENSE. * - * $Id$ + * $Id$ */ #ifndef _RTEMS_PERCPU_H @@ -23,6 +23,11 @@ #ifdef ASM #include <rtems/asm.h> +#else + #if defined(RTEMS_SMP) + #include <rtems/score/smplock.h> + #endif + #include <rtems/bspsmp.h> #endif /** @@ -41,11 +46,35 @@ extern "C" { #endif #ifndef ASM - -/** - * This forward defines the Thread Control Block structure. - */ +#ifndef __THREAD_CONTROL_DEFINED__ +#define __THREAD_CONTROL_DEFINED__ typedef struct Thread_Control_struct Thread_Control; +#endif + +#if (CPU_ALLOCATE_INTERRUPT_STACK == FALSE) && defined(RTEMS_SMP) + #error "RTEMS must allocate per CPU interrupt stack for SMP" +#endif + +typedef enum { + + /** + * This defines the constant used to indicate that the cpu code is in + * its initial powered up start. + */ + RTEMS_BSP_SMP_CPU_INITIAL_STATE = 1, + + /** + * This defines the constant used to indicate that the cpu code has + * completed basic initialization and awaits further commands. + */ + RTEMS_BSP_SMP_CPU_INITIALIZED = 2, + + /** + * This defines the constant used to indicate that the cpu code has + * shut itself down. + */ + RTEMS_BSP_SMP_CPU_SHUTDOWN = 3 +} bsp_smp_cpu_state; /** * @brief Per CPU Core Structure @@ -53,6 +82,22 @@ typedef struct Thread_Control_struct Thread_Control; * This structure is used to hold per core state information. */ typedef struct { + #if defined(RTEMS_SMP) + /** This element is used to lock this structure */ + SMP_lock_Control lock; + + /** This indicates that the CPU is online. */ + uint32_t state; + + /** + * This is the request for the interrupt. + * + * @note This may become a chain protected by atomic instructions. + */ + uint32_t message; + + #endif + #if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE) || \ (CPU_HAS_SOFTWARE_INTERRUPT_STACK == TRUE) /** @@ -69,7 +114,6 @@ typedef struct { #endif /** - * * This contains the current interrupt nesting level on this * CPU. */ @@ -91,6 +135,14 @@ typedef struct { #endif #ifdef ASM +#if defined(RTEMS_SMP) + #define PER_CPU_LOCK 0 + #define PER_CPU_STATE (1 * __RTEMS_SIZEOF_VOID_P__) + #define PER_CPU_MESSAGE (2 * __RTEMS_SIZEOF_VOID_P__) + #define PER_CPU_END_SMP (3 * __RTEMS_SIZEOF_VOID_P__) +#else + #define PER_CPU_END_SMP 0 +#endif #if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE) || \ (CPU_HAS_SOFTWARE_INTERRUPT_STACK == TRUE) @@ -98,14 +150,13 @@ typedef struct { * If this CPU target lets RTEMS allocates the interrupt stack, then * we need to have places in the per cpu table to hold them. */ - #define PER_CPU_INTERRUPT_STACK_LOW 0 - #define PER_CPU_INTERRUPT_STACK_HIGH (1 * __RTEMS_SIZEOF_VOID_P__) - #define PER_CPU_END_STACK (2 * __RTEMS_SIZEOF_VOID_P__) + #define PER_CPU_INTERRUPT_STACK_LOW PER_CPU_END_SMP + #define PER_CPU_INTERRUPT_STACK_HIGH \ + PER_CPU_INTERRUPT_STACK_LOW + (1 * __RTEMS_SIZEOF_VOID_P__) + #define PER_CPU_END_STACK \ + PER_CPU_INTERRUPT_STACK_HIGH + (1 * __RTEMS_SIZEOF_VOID_P__) #else - /* - * Otherwise, there are no interrupt stack addresses in the per CPU table. - */ - #define PER_CPU_END_STACK 0 + #define PER_CPU_END_STACK PER_CPU_END_SMP #endif /* @@ -147,20 +198,52 @@ typedef struct { * * This is an array of per CPU core information. */ -extern Per_CPU_Control _Per_CPU_Information; +extern Per_CPU_Control _Per_CPU_Information[]; + +#if defined(RTEMS_SMP) +/** + * @brief Set of Pointers to Per CPU Core Information + * + * This is an array of pointers to each CPU's per CPU data structure. + * It should be simpler to retrieve this pointer in assembly language + * that to calculate the array offset. + */ +extern Per_CPU_Control *_Per_CPU_Information_p[]; + +/** + * @brief Initialize SMP Handler + * + * This method initialize the SMP Handler. + */ +void _SMP_Handler_initialize(void); + +/** + * @brief Allocate and Initialize Per CPU Structures + * + * This method allocates and initialize the per CPU structure. + */ +void _Per_CPU_Initialize(void); + +#endif /* - * On an SMP system, these macros dereference the CPU core number. - * But on a non-SMP system, these macros are simple references. + * On a non SMP system, the bsp_smp_processor_id is defined to 0. * Thus when built for non-SMP, there should be no performance penalty. */ -#define _Thread_Heir _Per_CPU_Information.heir -#define _Thread_Executing _Per_CPU_Information.executing -#define _Thread_Idle _Per_CPU_Information.idle -#define _ISR_Nest_level _Per_CPU_Information.isr_nest_level -#define _CPU_Interrupt_stack_low _Per_CPU_Information.interrupt_stack_low -#define _CPU_Interrupt_stack_high _Per_CPU_Information.interrupt_stack_high -#define _Thread_Dispatch_necessary _Per_CPU_Information.dispatch_necessary +#define _Thread_Heir \ + _Per_CPU_Information[bsp_smp_processor_id()].heir +#define _Thread_Executing \ + _Per_CPU_Information[bsp_smp_processor_id()].executing +#define _Thread_Idle \ + _Per_CPU_Information[bsp_smp_processor_id()].idle +#define _ISR_Nest_level \ + _Per_CPU_Information[bsp_smp_processor_id()].isr_nest_level +#define _CPU_Interrupt_stack_low \ + _Per_CPU_Information[bsp_smp_processor_id()].interrupt_stack_low +#define _CPU_Interrupt_stack_high \ + _Per_CPU_Information[bsp_smp_processor_id()].interrupt_stack_high +#define _Thread_Dispatch_necessary \ + _Per_CPU_Information[bsp_smp_processor_id()].dispatch_necessary #endif /* ASM */ diff --git a/cpukit/score/include/rtems/score/smplock.h b/cpukit/score/include/rtems/score/smplock.h new file mode 100644 index 0000000000..a20d9260a4 --- /dev/null +++ b/cpukit/score/include/rtems/score/smplock.h @@ -0,0 +1,95 @@ +/** + * @file rtems/score/smplock.h + * + * This include file defines the interface for atomic locks + * which can be used in multiprocessor configurations. + */ + +/* + * COPYRIGHT (c) 1989-2011. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#ifndef _RTEMS_LOCK_H +#define _RTEMS_LOCK_H + +#include <rtems/score/isr.h> + +/** + * @defgroup RTEMS Lock Interface + * + */ + +/**@{*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * This type is used to lock elements for atomic access. + * + * @note This type may move to RTEMS. + */ +typedef volatile uint32_t SMP_lock_Control; + +/** + * @brief Initialize a Lock + * + * This method is used to initialize the lock at @a lock. + * + * @param [in] lock is the address of the lock to obtain. + * + * @note This lock may be "too low" here. It may need to move + * out of the BSP area. + */ +void _SMP_lock_Spinlock_Initialize( + SMP_lock_Control *lock +); + +/** + * @brief Obtain a Lock + * + * This method is used to obtain the lock at @a lock. + * + * @param [in] lock is the address of the lock to obtain. + * + * @return This method returns with processor interrupts disabled. + * The previous level is returned. + * + * @note This lock may be "too low" here. It may need to move + * out of the BSP area. + */ +ISR_Level _SMP_lock_Spinlock_Obtain( + SMP_lock_Control *lock +); + +/** + * @brief Release a Lock + * + * This method is used to release the lock at @a lock. + * + * @param [in] lock is the address of the lock to obtain. + * + * @note This lock may be "too low" here. It may need to move + * out of the BSP area. + */ +void _SMP_lock_Spinlock_Release( + SMP_lock_Control *lock, + ISR_Level level +); + +#ifdef __cplusplus +} +#endif + +/**@}*/ + +#endif +/* end of include file */ diff --git a/cpukit/score/preinstall.am b/cpukit/score/preinstall.am index e188eb7fe1..6979313a19 100644 --- a/cpukit/score/preinstall.am +++ b/cpukit/score/preinstall.am @@ -30,6 +30,10 @@ $(PROJECT_INCLUDE)/rtems/seterr.h: include/rtems/seterr.h $(PROJECT_INCLUDE)/rte $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/seterr.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/seterr.h +$(PROJECT_INCLUDE)/rtems/bspsmp.h: include/rtems/bspsmp.h $(PROJECT_INCLUDE)/rtems/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/bspsmp.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/bspsmp.h + $(PROJECT_INCLUDE)/rtems/score/$(dirstamp): @$(MKDIR_P) $(PROJECT_INCLUDE)/rtems/score @: > $(PROJECT_INCLUDE)/rtems/score/$(dirstamp) @@ -187,6 +191,10 @@ $(PROJECT_INCLUDE)/rtems/score/basedefs.h: include/rtems/score/basedefs.h $(PROJ $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/score/basedefs.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/score/basedefs.h +$(PROJECT_INCLUDE)/rtems/score/smplock.h: include/rtems/score/smplock.h $(PROJECT_INCLUDE)/rtems/score/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/score/smplock.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/score/smplock.h + if HAS_PTHREADS $(PROJECT_INCLUDE)/rtems/score/corespinlock.h: include/rtems/score/corespinlock.h $(PROJECT_INCLUDE)/rtems/score/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/score/corespinlock.h diff --git a/cpukit/score/src/percpu.c b/cpukit/score/src/percpu.c index 4c719af6db..2d429b4db9 100644 --- a/cpukit/score/src/percpu.c +++ b/cpukit/score/src/percpu.c @@ -1,5 +1,5 @@ /* - * COPYRIGHT (c) 1989-2010. + * COPYRIGHT (c) 1989-2011. * On-Line Applications Research Corporation (OAR). * * The license and distribution terms for this file may be @@ -19,11 +19,46 @@ #include <rtems/score/wkspace.h> #include <rtems/score/wkspace.h> #include <rtems/config.h> +#include <rtems/bspsmp.h> #include <string.h> -/* - * On single core systems, we can efficiently directly access a single - * statically allocated per cpu structure. And the fields are initialized - * as individual elements just like it has always been done. - */ -Per_CPU_Control _Per_CPU_Information; +#if defined(RTEMS_SMP) + void _SMP_Handler_initialize(void) + { + int cpu; + size_t size; + uintptr_t ptr; + + /* + * Initialize per CPU structures. + */ + size = (_SMP_Processor_count) * sizeof(Per_CPU_Control); + memset( _Per_CPU_Information, '\0', size ); + + /* + * Initialize per cpu pointer table + */ + size = Configuration.interrupt_stack_size; + _Per_CPU_Information_p[0] = &_Per_CPU_Information[0]; + for (cpu=1 ; cpu < _SMP_Processor_count ; cpu++ ) { + Per_CPU_Control *p = &_Per_CPU_Information[cpu]; + + _Per_CPU_Information_p[cpu] = p; + + p->interrupt_stack_low = _Workspace_Allocate_or_fatal_error( size ); + + ptr = (uintptr_t) _Addresses_Add_offset( p->interrupt_stack_low, size ); + ptr &= ~CPU_STACK_ALIGNMENT; + p->interrupt_stack_high = (void *)ptr; + p->state = RTEMS_BSP_SMP_CPU_INITIAL_STATE; + RTEMS_COMPILER_MEMORY_BARRIER(); + } + } +#else + /* + * On single core systems, we can efficiently directly access a single + * statically allocated per cpu structure. And the fields are initialized + * as individual elements just like it has always been done. + */ + Per_CPU_Control _Per_CPU_Information[1]; +#endif diff --git a/cpukit/score/src/smp.c b/cpukit/score/src/smp.c new file mode 100644 index 0000000000..4a0c13947f --- /dev/null +++ b/cpukit/score/src/smp.c @@ -0,0 +1,153 @@ +/* + * COPYRIGHT (c) 1989-2011. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems/system.h> +#include <rtems/bspsmp.h> +#include <rtems/score/thread.h> + +#if defined(RTEMS_SMP) +#define SMP_DEBUG + +#if defined(SMP_DEBUG) + #include <rtems/bspIo.h> +#endif + +void rtems_smp_run_first_task(int cpu) +{ + Thread_Control *heir; + + /* + * This CPU has an heir thread so we need to dispatch it. + */ + heir = _Thread_Heir; + + /* + * This is definitely a hack until we have SMP scheduling. Since there + * is only one executing and heir right now, we have to fake this out. + */ + _Thread_Dispatch_disable_level = 1; + _Thread_Executing = heir; + _CPU_Context_switch_to_first_task_smp( &heir->Registers ); +} + +void rtems_smp_secondary_cpu_initialize(void) +{ + int cpu; + + cpu = bsp_smp_processor_id(); + + bsp_smp_secondary_cpu_initialize(cpu); + + #if defined(SMP_DEBUG) + printk( "Made it to %d -- ", cpu ); + #endif + + /* + * Inform the primary CPU that this secondary CPU is initialized + * and ready to dispatch to the first thread it is supposed to + * execute when the primary CPU is ready. + */ + _Per_CPU_Information[cpu].state = RTEMS_BSP_SMP_CPU_INITIALIZED; + + /* + * HACK: Should not have to enable interrupts in real system here. + * It should happen as part of switching to the first task. + */ + + _Per_CPU_Information[cpu].isr_nest_level = 1; + _ISR_Set_level( 0 ); + while(1) ; +} + +void rtems_smp_process_interrupt(void) +{ + int cpu; + uint32_t message; + ISR_Level level; + + cpu = bsp_smp_processor_id(); + + level = _SMP_lock_Spinlock_Obtain( &_Per_CPU_Information[cpu].lock ); + message = _Per_CPU_Information[cpu].message; + _Per_CPU_Information[cpu].message &= ~message; + _SMP_lock_Spinlock_Release( &_Per_CPU_Information[cpu].lock, level ); + + #if defined(SMP_DEBUG) + { + void *sp = __builtin_frame_address(0); + if ( !(message & RTEMS_BSP_SMP_SHUTDOWN) ) + printk( "ISR on CPU %d -- (0x%02x) (0x%p)\n", cpu, message, sp ); + printk( "Dispatch level %d\n", _Thread_Dispatch_disable_level ); + } + #endif + + if ( message & RTEMS_BSP_SMP_FIRST_TASK ) { + _Per_CPU_Information[cpu].isr_nest_level = 0; + _Per_CPU_Information[cpu].message = 0; + _Per_CPU_Information[cpu].state = RTEMS_BSP_SMP_CPU_INITIALIZED; + rtems_smp_run_first_task(cpu); + /* does not return */ + } + + if ( message & RTEMS_BSP_SMP_SHUTDOWN ) { + ISR_Level level; + _Thread_Dispatch_disable_level = 0; + _Per_CPU_Information[cpu].isr_nest_level = 0; + _Per_CPU_Information[cpu].state = RTEMS_BSP_SMP_CPU_SHUTDOWN; + _ISR_Disable( level ); + while(1) + ; + /* does not continue past here */ + } + + if ( message & RTEMS_BSP_SMP_CONTEXT_SWITCH_NECESSARY ) { + printk( "switch needed\n" ); + _Per_CPU_Information[cpu].dispatch_necessary = true; + } +} + +void rtems_smp_send_message( + int cpu, + uint32_t message +) +{ + ISR_Level level; + + level = _SMP_lock_Spinlock_Obtain( &_Per_CPU_Information[cpu].lock ); + _Per_CPU_Information[cpu].message |= message; + _SMP_lock_Spinlock_Release( &_Per_CPU_Information[cpu].lock, level ); + bsp_smp_interrupt_cpu( cpu ); +} + +void rtems_smp_broadcast_message( + uint32_t message +) +{ + int dest_cpu; + int cpu; + ISR_Level level; + + cpu = bsp_smp_processor_id(); + + for ( dest_cpu=0 ; dest_cpu < _SMP_Processor_count; dest_cpu++ ) { + if ( cpu == dest_cpu ) + continue; + level = _SMP_lock_Spinlock_Obtain( &_Per_CPU_Information[cpu].lock ); + _Per_CPU_Information[dest_cpu].message |= message; + _SMP_lock_Spinlock_Release( &_Per_CPU_Information[cpu].lock, level ); + } + bsp_smp_broadcast_interrupt(); +} +#endif diff --git a/cpukit/score/src/smplock.c b/cpukit/score/src/smplock.c new file mode 100644 index 0000000000..1dd691835b --- /dev/null +++ b/cpukit/score/src/smplock.c @@ -0,0 +1,49 @@ +/* + * COPYRIGHT (c) 1989-2011. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + * + * $Id$ + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems/system.h> +#include <rtems/score/smplock.h> + +void _SMP_lock_Spinlock_Initialize( + SMP_lock_Control *lock +) +{ + *lock = 0; +} + +ISR_Level _SMP_lock_Spinlock_Obtain( + SMP_lock_Control *lock +) +{ + ISR_Level level; + uint32_t value = 1; + uint32_t previous; + + /* Note: Disable provides an implicit memory barrier. */ + _ISR_Disable( level ); + do { + SMP_CPU_SWAP( lock, value, previous ); + } while (previous == 1); + return level; +} + +void _SMP_lock_Spinlock_Release( + SMP_lock_Control *lock, + ISR_Level level +) +{ + *lock = 0; + _ISR_Enable( level ); +} diff --git a/cpukit/score/src/thread.c b/cpukit/score/src/thread.c index 6284e4b3e3..7a0eb38502 100644 --- a/cpukit/score/src/thread.c +++ b/cpukit/score/src/thread.c @@ -2,7 +2,7 @@ * Thread Handler * * - * COPYRIGHT (c) 1989-2008. + * COPYRIGHT (c) 1989-2011. * On-Line Applications Research Corporation (OAR). * * The license and distribution terms for this file may be @@ -33,8 +33,11 @@ #include <rtems/score/wkspace.h> #include <rtems/config.h> -/*PAGE - * +#if defined(RTEMS_SMP) + #include <rtems/bspsmp.h> +#endif + +/* * _Thread_Handler_initialization * * This routine initializes all thread manager related data structures. @@ -48,6 +51,7 @@ void _Thread_Handler_initialization(void) { uint32_t ticks_per_timeslice; uint32_t maximum_extensions; + uint32_t maximum_internal_threads; #if defined(RTEMS_MULTIPROCESSING) uint32_t maximum_proxies; #endif @@ -80,32 +84,40 @@ void _Thread_Handler_initialization(void) _Thread_Ticks_per_timeslice = ticks_per_timeslice; -#if defined(RTEMS_MULTIPROCESSING) - _Thread_MP_Handler_initialization( maximum_proxies ); -#endif + #if defined(RTEMS_MULTIPROCESSING) + _Thread_MP_Handler_initialization( maximum_proxies ); + #endif /* - * Initialize this class of objects. + * Initialize the internal class of threads. We need an IDLE thread + * per CPU in an SMP system. In addition, if this is a loosely + * coupled multiprocessing system, account for the MPCI Server Thread. */ + #if defined(RTEMS_SMP) + maximum_internal_threads = rtems_smp_maximum_processors; + #else + maximum_internal_threads = 1; + #endif + + #if defined(RTEMS_MULTIPROCESSING) + if ( _System_state_Is_multiprocessing ) + maximum_internal_threads += 1; + #endif _Objects_Initialize_information( &_Thread_Internal_information, OBJECTS_INTERNAL_API, OBJECTS_INTERNAL_THREADS, -#if defined(RTEMS_MULTIPROCESSING) - ( _System_state_Is_multiprocessing ) ? 2 : 1, -#else - 1, -#endif + maximum_internal_threads, sizeof( Thread_Control ), /* size of this object's control block */ false, /* true if names for this object are strings */ 8 /* maximum length of each object's name */ -#if defined(RTEMS_MULTIPROCESSING) - , - false, /* true if this is a global object class */ - NULL /* Proxy extraction support callout */ -#endif + #if defined(RTEMS_MULTIPROCESSING) + , + false, /* true if this is a global object class */ + NULL /* Proxy extraction support callout */ + #endif ); } diff --git a/cpukit/score/src/threadcreateidle.c b/cpukit/score/src/threadcreateidle.c index bea6a23b89..6a4bf65e9c 100644 --- a/cpukit/score/src/threadcreateidle.c +++ b/cpukit/score/src/threadcreateidle.c @@ -2,7 +2,7 @@ * Thread Handler * * - * COPYRIGHT (c) 1989-2008. + * COPYRIGHT (c) 1989-2011. * On-Line Applications Research Corporation (OAR). * * The license and distribution terms for this file may be @@ -30,24 +30,24 @@ #include <rtems/score/userext.h> #include <rtems/score/wkspace.h> #include <rtems/config.h> +#include <rtems/bspsmp.h> -/*PAGE - * - * _Thread_Create_idle - */ - -void _Thread_Create_idle( void ) +static inline void _Thread_Create_idle_helper( + uint32_t name_u32, + int cpu +) { - Objects_Name name; + Objects_Name name; + Thread_Control *idle; - name.name_u32 = _Objects_Build_name( 'I', 'D', 'L', 'E' ); + name.name_u32 = name_u32; /* * The entire workspace is zeroed during its initialization. Thus, all * fields not explicitly assigned were explicitly zeroed by * _Workspace_Initialization. */ - _Thread_Idle = _Thread_Internal_allocate(); + idle = _Thread_Internal_allocate(); /* * This is only called during initialization and we better be sure @@ -58,7 +58,7 @@ void _Thread_Create_idle( void ) _Thread_Initialize( &_Thread_Internal_information, - _Thread_Idle, + idle, NULL, /* allocate the stack */ _Stack_Ensure_minimum( Configuration.idle_task_stack_size ), CPU_IDLE_TASK_IS_FP, @@ -76,15 +76,31 @@ void _Thread_Create_idle( void ) * WARNING!!! This is necessary to "kick" start the system and * MUST be done before _Thread_Start is invoked. */ - _Thread_Heir = - _Thread_Executing = _Thread_Idle; + _Per_CPU_Information[ cpu ].idle = + _Per_CPU_Information[ cpu ].heir = + _Per_CPU_Information[ cpu ].executing = idle; _Thread_Start( - _Thread_Idle, + idle, THREAD_START_NUMERIC, Configuration.idle_task, NULL, 0 ); +} + +void _Thread_Create_idle( void ) +{ + #if defined(RTEMS_SMP) + int cpu; + for ( cpu=0 ; cpu < _SMP_Processor_count ; cpu++ ) { + _Thread_Create_idle_helper( + _Objects_Build_name( 'I', 'D', 'L', 'E' ), + cpu + ); + } + #else + _Thread_Create_idle_helper(_Objects_Build_name( 'I', 'D', 'L', 'E' ), 0); + #endif } |