| /* | |
| + * QuickThreads -- Threads-building toolkit. | |
| + * Copyright (c) 1993 by David Keppel | |
| + * | |
| + * Permission to use, copy, modify and distribute this software and | |
| + * its documentation for any purpose and without fee is hereby | |
| + * granted, provided that the above copyright notice and this notice | |
| + * appear in all copies. This software is provided as a | |
| + * proof-of-concept and for demonstration purposes; there is no | |
| + * representation about the suitability of this software for any | |
| + * purpose. | |
| + * | |
| + * PowerPC-Sys5 thread switching module. | |
| + * | |
| + * This software is largely based on the original PowerPC-Linux porting | |
| + * developed by Ken Aaker <kenaaker@silverbacksystems.com> | |
| + * | |
| + * Marco Bucci <marco.bucci@inwind.it> | |
| + * December 2002 | |
| + * | |
| + */ | |
| #ifndef QUICKTHREADS_POWERPC_H | |
| #define QUICKTHREADS_POWERPC_H | |
| /***************************************************************************** | |
| * | |
| * DESCRIPTION | |
| * | |
| * This is the QuickThreads switching module implementation for PowerPC | |
| * running under System V ABI (Application Binary Interface) [2]. It was | |
| * developed by porting the MacOS X version and tested under LinuxPPC. | |
| * | |
| * Notice that this is not the same than the PowerPC Mach ABI used by MacOSX | |
| * [1]. | |
| * | |
| * IMPLEMENTATION NOTES | |
| * | |
| * 1) Porting on System V ABI | |
| * Excluding the variant argument calling convention, Mach and System V ABI | |
| * are enough similar and it could be possible to use some simple macro, to | |
| * adapt the code for both the ABIs. Actually, the only relevant difference | |
| * is in the linkage area structure and in the position where the Link and | |
| * the Condition registers are saved. As to the calling conventions, there | |
| * are differences with floating points argument passing and with variant | |
| * argument lists. Notice that, on Mach, the caller's stack frame allocates | |
| * space to hold all arguments ([1] p.51), while on System V, the caller's | |
| * stack frame allocates space to hold just the arguments that don't fit into | |
| * registers ([2] p.3.18). | |
| * | |
| * 2) Variant argument list implementation | |
| * Variant argument calling on a RISC machine is not easy to implement since | |
| * parameters are passed via registers instead of via stack. In a general | |
| * variant argument implementation, the caller's stack must map the whole | |
| * parameter list following the rules related to the use of the GPR and FPR | |
| * parameter registers and the stack alignment ([2] p.3-21). | |
| * This implementation is quite simple and not general. It works under the | |
| * hypothesis that arguments are 4-bytes aligned integers. | |
| * | |
| * 3) This heather file organisation | |
| * I preferred to not make confusion between macros that are needed (i.e. | |
| * directly used) by QuickThreads and internal "implementation" macros. You | |
| * will find QuickThreds macros in the end of this header. Sometime they just | |
| * refer to an analogous "internal" macro. On the top, there are the macros | |
| * that I used to make more clean (I hope) the implementation. I could include | |
| * some system heather (as to stack layout definitions, prologs and epilogs, | |
| * etc.), but I preferred to have a self-contained heather in order to make | |
| * all more clear for mantaining and for possible porting on another ABI. | |
| * | |
| * | |
| * REFERENCES | |
| * | |
| * [1] - Mach-O Runtime Architecture | |
| * Runtime Concepts and Conventions for Mac OS X Programs | |
| * Preliminary July 2002 | |
| * | |
| * [2] - SYSTEM V APPLICATION BINARY INTERFACE | |
| * PowerPC Processor Supplement | |
| * September 1995 | |
| * | |
| *****************************************************************************/ | |
| /***************************************************************************** | |
| * | |
| * PowerPC System V Stack frame (see [2]) | |
| * | |
| ................ | |
| + + | |
| | | | |
| +--------------------------+ | |
| | | Caller's LR | |
| + CALLER'S LINKAGE AREA + | |
| backchain -> | | Caller's backchain | |
| +==========================+ | |
| | | FPR31 | |
| + FPR SAVE AREA + | |
| .............. | |
| + + | |
| | | FPRn | |
| +--------------------------+ | |
| | | GPR31 | |
| + GPR SAVE AREA + | |
| .............. | |
| + + | |
| | | GPRn | |
| +--------------------------+ | |
| | | | |
| + ALIGNMEBNT PAD + | |
| .............. | |
| + (if needed) + | |
| | | | |
| +--------------------------+ | |
| | CR SAVE | | |
| +--------------------------+ | |
| | | | |
| + LOCAL VARIABLES AREA + | |
| .............. | |
| + + | |
| | | | |
| +--------------------------+ | |
| | | PAR(n-7) | |
| + + | |
| | | | |
| + PARAMETER AREA + | |
| .............. | |
| + for FUTURE call + | |
| | | PAR(9) | |
| + + | |
| SP + 8 -> | | PAR(8) | |
| +--------------------------+ | |
| SP + 4 -> | | LR callee-save for FUTURE call | |
| + LINKAGE AREA + | |
| SP -> | | backchain | |
| +==========================+ | |
| STACK TOP (lower address) | |
| Stack grows down | |
| | | |
| V | |
| * NOTE: | |
| * | |
| * 1) In this figure parameter are supposed to be integer 4-bytes aligned and | |
| * are numbered 0, 1, 2,... n. | |
| * | |
| * 2) Parameter are allocated in the CALLER's parameter area. This area must | |
| * be large enough to hold all parameters that cannot fit in registers (no | |
| * more parameter registers are available); | |
| * | |
| * 3) The callee saves LR in the caller's linkage area. CR as all other | |
| * callee's state are saved in its own stack frame. | |
| * | |
| *****************************************************************************/ | |
| /***************************************************************************** | |
| * | |
| * Stack initialization for a single argument thread | |
| * | |
| top + QUICKTHREADS_STKBASE -> STACK BOTTOM (higher address) | |
| +==========================+ | |
| | | | |
| + + | |
| .............. | |
| + + | |
| | | | |
| +--------------------------+ | |
| top + QUICKTHREADS_ONLY_INDEX * 4 -> | only param | PAR(3) | |
| + + | |
| top + QUICKTHREADS_USER_INDEX * 4 -> | userf param | PAR(2) | |
| + + | |
| top + QUICKTHREADS_ARGT_INDEX * 4 -> | t param | PAR(1) | |
| + + | |
| top + QUICKTHREADS_ARGU_INDEX * 4 -> | u param | PAR(0) | |
| +--------------------------+ | |
| top + QUICKTHREADS_RETURN_INDEX * 4 -> | qt_start | LR save | |
| + + | |
| top + QUICKTHREADS_BLOCKI_FRAME_SIZE -> | top + QUICKTHREADS_STKBASE | backchain | |
| +==========================+ | |
| | | | |
| + + | |
| .............. | |
| + + | |
| | | | |
| +--------------------------+ | |
| | | | |
| + + | |
| top -> |top + QUICKTHREADS_BLOCKI_FRAME_SIZE| backchain | |
| +==========================+ | |
| STACK TOP (lower address) | |
| Stack grows down | |
| | | |
| V | |
| ***************************************************************************** | |
| * | |
| * Stack initialization for a variant argument thread | |
| * | |
| bottom -> STACK BOTTOM (higher address) | |
| +==========================+ | |
| | | | |
| + + | |
| .............. | |
| + + | |
| top + QUICKTHREADS_VSTKBASE -> | arg(0) | PAR(4) | |
| +--------------------------+ | |
| top + QUICKTHREADS_CLEANUP_INDEX * 4 -> | cleanup param | PAR(3) | |
| + + | |
| top + QUICKTHREADS_USER_INDEX * 4 -> | userf param | PAR(2) | |
| + + | |
| top + QUICKTHREADS_VSTARTUP_INDEX * 4 ->| startup param | PAR(1) | |
| + + | |
| top + QUICKTHREADS_ARGT_INDEX * 4 -> | t param | PAR(0) | |
| +--------------------------+ | |
| top + QUICKTHREADS_RETURN_INDEX * 4 -> | qt_start | LR save | |
| + + | |
| top + QUICKTHREADS_BLOCKI_FRAME_SIZE -> | top + QUICKTHREADS_STKBASE | backchain | |
| +==========================+ | |
| | | | |
| + + | |
| .............. | |
| + + | |
| | | | |
| +--------------------------+ | |
| | | | |
| + + | |
| top -> |top + QUICKTHREADS_BLOCKI_FRAME_SIZE| backchain | |
| +==========================+ | |
| STACK TOP (lower address) | |
| Stack grows down | |
| | | |
| V | |
| * NOTE: | |
| * | |
| * Parameters are passed to "qt_start" or to "qt_vstart" putting them into | |
| * the stack frames of "qt_start" or "qt_vstart" themselves. This not a | |
| * conventional parameter passing because parameters should be put into the | |
| * caller's stack, not into the callee's one. Actually we must consider | |
| * that as a preload of the parameter area that "qt_start" or "qt_vstart" | |
| * will use for their own calls. | |
| * Be aware of the fact that, during a call, the caller's parameter area is, | |
| * in a certain sense, volatile. In facts, the callee can save parameter | |
| * registers on the caller's parameter area. | |
| * | |
| *****************************************************************************/ | |
| /***************************************************************************** | |
| Define PowerPC System V related macros | |
| *****************************************************************************/ | |
| typedef unsigned long PPC_W; | |
| /* Stack pointer must always be a multiple of 16 */ | |
| #define PPC_STACK_INCR 16 | |
| #define PPC_ROUND_STACK(length) \ | |
| (((length)+PPC_STACK_INCR-1) & ~(PPC_STACK_INCR-1)) | |
| #define PPC_LINKAGE_AREA 8 | |
| #define PPC_LR_SAVE 4 | |
| #define PPC_PARAM_AREA(n) (4*(n)) | |
| #define PPC_GPR_SAVE_AREA (4*19) /* GPR13-GPR31 must be saved */ | |
| #define PPC_FPR_SAVE_AREA (8*18) /* FPR14-FPR31 must be saved */ | |
| /* Define parameter offset on the stack. | |
| * NOTICE: Parameters are numbered 0, 1, ..., n. | |
| */ | |
| #define PPC_PAR(i) (PPC_LINKAGE_AREA+(i)*4) | |
| /***************************************************************************** | |
| Define stack frames | |
| *****************************************************************************/ | |
| /* Define the "qt_blocki" and "qt_abort" stack frame. We use the same stack | |
| * frame for both. | |
| * | |
| top + S -> | |
| +==========================+ | |
| top + S - 4 -> | | GPR31 | |
| + GPR SAVE AREA + | |
| .............. | |
| + + | |
| top + S - 19 * 4 -> | | GPR13 | |
| +--------------------------+ | |
| | | | |
| + ALIGNMEBNT PAD + | |
| .............. | |
| + (if needed) + | |
| | | | |
| +--------------------------+ | |
| top + 8 -> | CR SAVE | | |
| +--------------------------+ | |
| | | | |
| + LINKAGE AREA + | |
| top -> | | | |
| +==========================+ | |
| */ | |
| #define QUICKTHREADS_BLOCKI_FRAME_SIZE \ | |
| PPC_ROUND_STACK(PPC_LINKAGE_AREA+4+PPC_GPR_SAVE_AREA) | |
| #define QUICKTHREADS_BLOCKI_CR_SAVE 8 | |
| /* Offset to the base of the GPR save area. Save from GPR13 to GPR31 | |
| * increasing address. | |
| */ | |
| #define QUICKTHREADS_BLOCKI_GPR_SAVE(i) (QUICKTHREADS_BLOCKI_FRAME_SIZE-4+(i-31)*4) | |
| /* Define the "qt_block" stack frame. Notice that since "qt_black" calls | |
| * "qt_blocki", GPR registers are saved into "qt_blocki" stack frame. | |
| * | |
| top + S -> | |
| +==========================+ | |
| top + S - 8 -> | | FPR31 | |
| + FPR SAVE AREA + | |
| .............. | |
| + + | |
| top + S - 18 * 8 -> | | FPR14 | |
| +--------------------------+ | |
| | | | |
| + ALIGNMEBNT PAD + | |
| .............. | |
| + (if needed) + | |
| | | | |
| +--------------------------+ | |
| | | | |
| + LINKAGE AREA + | |
| top -> | | | |
| +==========================+ | |
| */ | |
| #define QUICKTHREADS_BLOCK_FRAME_SIZE \ | |
| PPC_ROUND_STACK(PPC_LINKAGE_AREA+PPC_FPR_SAVE_AREA) | |
| /* Offset to the location where registers are saved. | |
| */ | |
| #define QUICKTHREADS_BLOCK_FPR_SAVE(i) (QUICKTHREADS_BLOCK_FRAME_SIZE-8+(i-31)*8) | |
| /* Define the "qt_start" frame size. It consists just of the linkage area and | |
| * the parameter area. | |
| * | |
| +==========================+ | |
| | | | |
| + ALIGNMEBNT PAD + | |
| .............. | |
| + (if needed) + | |
| | | | |
| +--------------------------+ | |
| | | only par | |
| + + | |
| | | userf par | |
| + PARAMETER AREA + | |
| | | t par | |
| + + | |
| top + 8 -> | | u par | |
| +--------------------------+ | |
| | | | |
| + LINKAGE AREA + | |
| top -> | | | |
| +==========================+ | |
| */ | |
| #define QUICKTHREADS_START_FRAME_SIZE PPC_ROUND_STACK(PPC_LINKAGE_AREA+PPC_PARAM_AREA(4)) | |
| /* Define the "qt_vstart" frame. It consists of the linkage area, the fix parameter | |
| * area, the variant argument list and a local variable area used in "qt_vstart" | |
| * implementation. | |
| * | |
| backchain -> | |
| +==========================+ | |
| backchain - 4 -> | | | |
| + LOCAL VARIABLES AREA + | |
| .............. | |
| + + | |
| | | | |
| +--------------------------+ | |
| | | | |
| + ALIGNMEBNT PAD + | |
| .............. | |
| + (if needed) + | |
| | | | |
| +--------------------------+ | |
| | | arg(n) | |
| + + | |
| | | | |
| + VARIABLE ARGUMENT LIST + | |
| .............. | |
| + for userf call + | |
| | | arg(1) | |
| + + | |
| top + 8 + 16 -> | | arg(0) | |
| +--------------------------+ | |
| | | cleanup par | |
| + + | |
| | | userf par | |
| + PARAMETER AREA + | |
| | | startup par | |
| + + | |
| top + 8 -> | | t par | |
| +--------------------------+ | |
| | | | |
| + LINKAGE AREA + | |
| top -> | | | |
| +==========================+ | |
| */ | |
| #define QUICKTHREADS_VARGS_LOCAL_AREA (4*4) /* local variable area */ | |
| /* The offset the stack will be moved back before to call "userf(...)". | |
| * The linckage area must be moved to be adiacent to the part of the variant | |
| * argument list that is in the stack. Notice that, since the first 8 | |
| * parameters are passed via registers, the offset is equal to the size of | |
| * 4+8 parameters. */ | |
| #define QUICKTHREADS_VARGS_BKOFF PPC_PARAM_AREA(4+8) | |
| #define QUICKTHREADS_VSTART_FRAME_SIZE(varbytes) \ | |
| PPC_ROUND_STACK(PPC_LINKAGE_AREA+QUICKTHREADS_VARGS_BKOFF+(varbytes)+ \ | |
| QUICKTHREADS_VARGS_LOCAL_AREA) | |
| /* Offset to the base of the varian argument list */ | |
| #define QUICKTHREADS_VSTART_LIST_BASE (PPC_LINKAGE_AREA+PPC_PARAM_AREA(4)) | |
| /* Notice that qt_start and qt_vstart have no parameters, actually their | |
| * parameters are written in their stack frame during thread initialization | |
| */ | |
| extern void qt_start(void); | |
| extern void qt_vstart(void); | |
| /* Offset (in words) of the location where the block routine saves its return | |
| * address (i.e. LR). SP points the top of the block routine stack and, | |
| * following ppc calling conventions, the return address is saved in the | |
| * previous (caller's) stack frame. | |
| */ | |
| #define QUICKTHREADS_RETURN_INDEX ((QUICKTHREADS_BLOCKI_FRAME_SIZE+PPC_LR_SAVE)/sizeof(PPC_W)) | |
| /* static variable used to get the stack bottom in "VARGS" initialization */ | |
| static void *qt_sp_bottom_save; | |
| #define QUICKTHREADS_ARG_INDEX(i) ((QUICKTHREADS_BLOCKI_FRAME_SIZE+PPC_PAR(i))/sizeof(PPC_W)) | |
| /***************************************************************************** | |
| QuickThreads needed definitions | |
| *****************************************************************************/ | |
| #define QUICKTHREADS_GROW_DOWN | |
| #define QUICKTHREADS_STKALIGN PPC_STACK_INCR | |
| typedef PPC_W qt_word_t; | |
| /* This macro is used by "QUICKTHREADS_ARGS" to initialize a single argument thread. | |
| * - set "qt_start" as the "qt_block" or "qt_blocki" return address; | |
| * - set the top of the stack backchain; | |
| * - set the next backchain (not needed, but just to be "clean"). | |
| */ | |
| #define QUICKTHREADS_ARGS_MD(sp) \ | |
| (QUICKTHREADS_SPUT (sp, QUICKTHREADS_RETURN_INDEX, qt_start), \ | |
| QUICKTHREADS_SPUT (sp, 0, sp+QUICKTHREADS_BLOCKI_FRAME_SIZE), \ | |
| QUICKTHREADS_SPUT (sp, QUICKTHREADS_BLOCKI_FRAME_SIZE/sizeof(PPC_W), \ | |
| sp+QUICKTHREADS_BLOCKI_FRAME_SIZE+QUICKTHREADS_START_FRAME_SIZE)) | |
| /* This macro is used by "QUICKTHREADS_VARGS" to initialize a variant argument thread. | |
| * It returns the pointer to the top of the argument list. | |
| * We also use it to get the stack bottom via a static variable. This is a bit | |
| * "dirty", it could be better to do it in "qt_vargs", but we don't want change | |
| * anything out of this file. | |
| * We need the stack bottom to allocate a local variable area used by | |
| * "qt_vstart". | |
| */ | |
| #define QUICKTHREADS_VARGS_MD0(sp, varbytes) \ | |
| ((qt_sp_bottom_save = sp), \ | |
| ((qt_t *)(((char *)(sp)) - \ | |
| (QUICKTHREADS_VSTART_FRAME_SIZE(varbytes)-QUICKTHREADS_VSTART_LIST_BASE)))) | |
| /* This macro is used by "QUICKTHREADS_VARGS" to initialize a variant argument thread. | |
| * - set "qt_start" as the "qt_block" or "qt_blocki" return address; | |
| * - set the top of the stackback chain; | |
| * - set the next backchain (it points the stack botton). | |
| */ | |
| #define QUICKTHREADS_VARGS_MD1(sp) \ | |
| (QUICKTHREADS_SPUT (sp, QUICKTHREADS_RETURN_INDEX, qt_vstart), \ | |
| QUICKTHREADS_SPUT (sp, 0, sp+QUICKTHREADS_BLOCKI_FRAME_SIZE), \ | |
| QUICKTHREADS_SPUT (sp, (QUICKTHREADS_BLOCKI_FRAME_SIZE)/sizeof(PPC_W), \ | |
| qt_sp_bottom_save)) | |
| /* Activate "qt_vargs" as the initialization routine for the variant | |
| * argument threads | |
| */ | |
| #define QUICKTHREADS_VARGS_DEFAULT | |
| /* Override "qt_vargs" with "qt_vargs_stdarg". | |
| * On LinuxPPC "qt_vargs" doesn't work, "qt_vargs_stdarg" uses a more | |
| * standard way to retrieve arguments from the variant list. | |
| */ | |
| #define QUICKTHREADS_VARGS(sp, nbytes, vargs, pt, startup, vuserf, cleanup) \ | |
| ((qt_t *)qt_vargs_stdarg (sp, nbytes, vargs, pt, startup, vuserf, cleanup)) | |
| /* This macro is used by "QUICKTHREADS_ADJ(sp)" to get the stack top form the stack | |
| * bottom during a single argument thread initialization. | |
| * It is the space we need to allocate for a single argument thread: the stack | |
| * frame for the block routine ("qt_block" or "qt_blocki") and for "qt_start". | |
| */ | |
| #define QUICKTHREADS_STKBASE \ | |
| (QUICKTHREADS_BLOCKI_FRAME_SIZE+QUICKTHREADS_START_FRAME_SIZE) | |
| /* This macro is used by "QUICKTHREADS_VADJ(sp)" to get the stack top from the base | |
| * of the variant argument list during a variant argument thread initialization. | |
| */ | |
| #define QUICKTHREADS_VSTKBASE (QUICKTHREADS_BLOCKI_FRAME_SIZE+QUICKTHREADS_VSTART_LIST_BASE) | |
| /* The *index* (positive offset) of where to put each value. */ | |
| #define QUICKTHREADS_ARGU_INDEX QUICKTHREADS_ARG_INDEX(0) | |
| #define QUICKTHREADS_ARGT_INDEX QUICKTHREADS_ARG_INDEX(1) | |
| #define QUICKTHREADS_USER_INDEX QUICKTHREADS_ARG_INDEX(2) | |
| #define QUICKTHREADS_ONLY_INDEX QUICKTHREADS_ARG_INDEX(3) | |
| #define QUICKTHREADS_VARGT_INDEX QUICKTHREADS_ARG_INDEX(0) | |
| #define QUICKTHREADS_VSTARTUP_INDEX QUICKTHREADS_ARG_INDEX(1) | |
| #define QUICKTHREADS_VUSERF_INDEX QUICKTHREADS_ARG_INDEX(2) | |
| #define QUICKTHREADS_VCLEANUP_INDEX QUICKTHREADS_ARG_INDEX(3) | |
| #endif /* ndef QUICKTHREADS_POWERPC_H */ | |