| /* |
| * gcc_startup_mips.S -- startup file for MIPS. |
| * |
| * Copyright (c) 1995, 1996, 1997, 2001 Cygnus Support |
| * |
| * The authors hereby grant permission to use, copy, modify, distribute, |
| * and license this software and its documentation for any purpose, provided |
| * that existing copyright notices are retained in all copies and that this |
| * notice is included verbatim in any distributions. No written agreement, |
| * license, or royalty fee is required for any of the authorized uses. |
| * Modifications to this software may be copyrighted by their authors |
| * and need not follow the licensing terms described here, provided that |
| * the new terms are clearly indicated on the first page of each file where |
| * they apply. |
| */ |
| |
| /* This file does not use any floating-point ABI. */ |
| .gnu_attribute 4,0 |
| .set nomips16 |
| |
| #include <mips/regdef.h> |
| #include <mips/m32c0.h> |
| #include "abiflags.S" |
| |
| #define STARTUP_STACK_SIZE 0x0400 /* Temporary stack size to run C code */ |
| |
| /* This is for referencing addresses that are not in the .sdata or |
| .sbss section under embedded-pic, or before we've set up gp. */ |
| #ifdef __mips_embedded_pic |
| # ifdef __mips64 |
| # define LA(t,x) la t,x-PICBASE ; daddu t,s0,t |
| # else |
| # define LA(t,x) la t,x-PICBASE ; addu t,s0,t |
| # endif |
| #else /* __mips_embedded_pic */ |
| # if (defined (__mips64) && (defined (_MIPS_SIM) && _MIPS_SIM == _ABI64) \ |
| || defined (__mips_eabi)) |
| # define LA(t,x) dla t,x |
| # define PTR_ADDU daddu |
| # define PTR_L ld |
| # else |
| # define LA(t,x) la t,x |
| # define PTR_ADDU addu |
| # define PTR_L lw |
| # endif |
| #endif /* __mips_embedded_pic */ |
| |
| #if (defined (__mips64) && (_MIPS_SIM == _ABI64 || _MIPS_SIM == _ABIO64 || \ |
| _MIPS_SIM == _ABIN32 || defined (__mips_eabi))) |
| # define REGSIZE 8 |
| # define LD ld |
| # define ST sd |
| #else |
| # define REGSIZE 4 |
| # define LD lw |
| # define ST sw |
| #endif |
| |
| .section .startdata, "aw", @nobits |
| .balign 16 |
| .space STARTUP_STACK_SIZE |
| __lstack: # Points to the end of the stack |
| __ram_extent: |
| .space 8 |
| |
| .data |
| |
| __temp_space: /* Temporary space to save arguments */ |
| .space REGSIZE * 3 |
| |
| .text |
| .align 2 |
| |
| /* Without the following nop, GDB thinks _start0 is a data variable. |
| * This is probably a bug in GDB in handling a symbol that is at the |
| * start of the .text section. |
| */ |
| nop |
| |
| .globl hardware_hazard_hook .text |
| .globl _start0 |
| .ent _start0 |
| _start0: |
| #ifdef __mips_embedded_pic |
| #define PICBASE start_PICBASE |
| .set noreorder |
| PICBASE = .+8 |
| bal PICBASE |
| nop |
| move s0,$31 |
| .set reorder |
| #endif |
| #if __mips<3 |
| # define STATUS_MASK (SR_CU1|SR_PE) |
| #else |
| /* Post-mips2 has no SR_PE bit. */ |
| # ifdef __mips64 |
| /* Turn on 64-bit addressing and additional float regs. */ |
| # define STATUS_MASK (SR_CU1|SR_FR|SR_KX|SR_SX|SR_UX) |
| # else |
| # if __mips_fpr==32 |
| # define STATUS_MASK (SR_CU1) |
| # else |
| /* Turn on additional float regs. */ |
| # define STATUS_MASK (SR_CU1|SR_FR) |
| # endif |
| # endif |
| #endif |
| |
| /* Save argument registers */ |
| LA (t0, __temp_space) |
| ST a0, (REGSIZE * 0)(t0) |
| ST a1, (REGSIZE * 1)(t0) |
| ST a2, (REGSIZE * 2)(t0) |
| |
| /* |
| * Save k0, k1, ra and sp and register |
| * default exception handler. |
| */ |
| .weak __register_excpt_handler |
| LA (t9, __register_excpt_handler) |
| beqz t9, 1f |
| move a0, ra /* save ra */ |
| jalr t9 |
| b 2f |
| 1: |
| /* Clear Cause register. */ |
| mtc0 zero,C0_CAUSE |
| nop |
| move v0,zero /* Mask for C0_SR. */ |
| 2: |
| /* Read MIPS_abiflags structure and set status/config registers |
| accordingly. */ |
| .weak __MIPS_abiflags_start |
| .weak __MIPS_abiflags_end |
| LA (t0,__MIPS_abiflags_start) |
| LA (t1,__MIPS_abiflags_end) |
| PTR_ADDU t1,t1,-24 |
| |
| /* Branch to 1f is the .MIPS.abiflags section is not 24 bytes. This |
| indicates it is either missing or corrupt. */ |
| bne t0,t1,1f |
| |
| /* Check isa_level. */ |
| lbu t1,ABIFlags_isa_level(t0) |
| sltu v1,t1,3 /* Is MIPS < 3? */ |
| xori t1,t1,64 /* Is MIPS64? */ |
| beq v1,zero,4f |
| li v1,SR_PE |
| or v0,v0,v1 /* Enable soft reset. */ |
| 4: |
| li v1,(SR_KX|SR_SX|SR_UX) |
| bne t1,zero,5f |
| or v0,v0,v1 /* Enable extended addressing. */ |
| 5: |
| /* Check DSP,DSP2,MDMX ase. */ |
| lw t1,ABIFlags_ases(t0) |
| andi t1,t1,(AFL_ASE_DSP|AFL_ASE_DSPR2|AFL_ASE_MDMX) |
| li v1,SR_MX |
| beq t1,zero,6f |
| or v0,v0,v1 |
| 6: |
| /* Check fp_abi. */ |
| lbu t1,ABIFlags_fp_abi(t0) |
| xori t1,t1,Val_GNU_MIPS_ABI_FP_SOFT |
| li v1,SR_CU1 |
| beq t1,zero,2f /* Skip MSA and cpr1_size checks. */ |
| or v0,v0,v1 /* Enable co-processor 1. */ |
| |
| /* Check cpr1_size. */ |
| lbu t1,ABIFlags_cpr1_size(t0) |
| xori t1,t1,AFL_REG_64 |
| li v1,SR_FR |
| bne t1,zero,3f |
| or v0,v0,v1 /* Enable 64-bit FPU registers. */ |
| 3: |
| /* Check MSA ase. */ |
| lw t1,ABIFlags_ases(t0) |
| andi t1,t1,AFL_ASE_MSA |
| li v1,SR_FR |
| beq t1,zero,2f |
| or v0,v0,v1 /* Enable 64-bit FPU registers. */ |
| li v1,CFG5_MSAEN |
| .set push |
| .set mips32 |
| mtc0 v1,C0_CONFIG,5 /* Enable MSA. */ |
| .set pop |
| b 2f |
| |
| 1: |
| /* MIPS_abiflags structure is not available. Set status/config |
| registers based on flags defined by compiler. */ |
| #ifdef __mips_soft_float |
| li v0,(STATUS_MASK-(STATUS_MASK & SR_CU1)) |
| #else |
| li v0,STATUS_MASK |
| #endif |
| |
| 2: |
| /* Set C0_SR, */ |
| mtc0 v0,C0_SR |
| nop |
| |
| /* Avoid hazard from C0_SR changes. */ |
| LA (t0, hardware_hazard_hook) |
| beq t0,zero,2f |
| jalr t0 |
| 2: |
| |
| |
| /* Fix high bits, if any, of the PC so that exception handling doesn't get |
| confused. */ |
| LA (v0, 3f) |
| jr v0 |
| 3: |
| LA (gp, _gp) # set the global data pointer |
| .end _start0 |
| |
| /* |
| * zero out the bss section. |
| */ |
| .globl _get_ram_info .text |
| .globl __stack |
| .globl __global |
| .ent zerobss |
| zerobss: |
| LA (v0, _fbss) |
| LA (v1, _end) |
| beq v0,v1,2f |
| 1: |
| PTR_ADDU v0,v0,4 |
| sw zero,-4(v0) |
| bne v0,v1,1b |
| 2: |
| /* setup the stack pointer */ |
| LA (t0, __stack) # is __stack set ? |
| bne t0,zero,4f |
| |
| LA (sp, __lstack) # make a small stack so we can |
| # run some C code |
| li a0,0 # no need for the ram base |
| LA (a1, __ram_extent) # storage for the extent of ram |
| jal _get_ram_range |
| |
| /* NOTE: a0[0] contains the last address+1 of memory. */ |
| LA (a0, __ram_extent) |
| PTR_L t0,0(a0) # the extent of ram |
| lw $0,-4(t0) # check for valid memory |
| /* Allocate 32 bytes for the register parameters. Allocate 16 |
| bytes for a null argv and envp. Round the result up to 64 |
| bytes to preserve alignment. */ |
| 4: |
| PTR_ADDU t0,t0,-64 |
| move sp,t0 # set stack pointer |
| .end zerobss |
| |
| /* |
| * initialize target specific stuff. Only execute these |
| * functions it they exist. |
| */ |
| .globl hardware_init_hook .text |
| .globl software_init_hook .text |
| .globl _start .text |
| .globl atexit .text |
| .globl exit .text |
| .ent init |
| init: |
| LA (t9, hardware_init_hook) # init the hardware if needed |
| beq t9,zero,6f |
| jalr t9 |
| 6: |
| LA (t9, software_init_hook) # init the hardware if needed |
| beq t9,zero,7f |
| jalr t9 |
| 7: |
| LA (a0, 0) |
| jal atexit |
| |
| #ifdef GCRT0 |
| .globl _ftext |
| .globl _extext |
| LA (a0, _ftext) |
| LA (a1, _etext) |
| jal monstartup |
| #endif |
| |
| /* restore argument registers */ |
| LA (t0, __temp_space) |
| LD a0,(REGSIZE * 0)(t0) |
| LD a1,(REGSIZE * 1)(t0) |
| LD a2,(REGSIZE * 2)(t0) |
| |
| /* Convert pointers potentially */ |
| .weak __convert_argv_pointers |
| LA (t0, __convert_argv_pointers) |
| beqz t0, 1f |
| jalr t0 |
| 1: |
| /* if a0 > 0 then we have arguments ready in a0 to a2 registers */ |
| bgtz a0,.Lmain |
| /* if a0 == 0 then no arguments have been set up */ |
| beqz a0, 1f |
| /* if a0 < -1 then we have undefined behaviour so assume no |
| arguments have been set up */ |
| slti a0, a0, -1 |
| bnez a0, 1f |
| |
| /* a0 == -1 */ |
| .weak __getargs |
| LA (t0, __getargs) |
| beqz t0, 1f |
| jalr t0 # get arguments |
| b .Lmain |
| 1: |
| /* no arguments */ |
| move a0,zero # set argc to 0 |
| PTR_ADDU a1,sp,32 # argv = sp + 32 |
| PTR_ADDU a2,sp,40 # envp = sp + 40 |
| ST zero,(a1) # argv[argc] = 0 |
| ST zero,(a2) # envp[0] = 0 |
| |
| .Lmain: |
| jal _start # call the program start function |
| |
| # fall through to the "exit" routine |
| move a0,v0 # pass through the exit code |
| jal exit # call libc exit to run the G++ |
| # destructors |
| .end init |
| |
| |
| /* Assume the PICBASE set up above is no longer valid below here. */ |
| #ifdef __mips_embedded_pic |
| #undef PICBASE |
| #endif |
| |
| /* |
| * _exit -- Exit from the application. Normally we cause a user trap |
| * to return to the ROM monitor for another run. NOTE: This is |
| * the only other routine we provide in the crt0.o object, since |
| * it may be tied to the "_start0" routine. It also allows |
| * executables that contain a complete world to be linked with |
| * just the crt0.o object. |
| */ |
| .globl hardware_exit_hook .text |
| .globl _exit |
| .ent _exit |
| _exit: |
| 7: |
| |
| # save exit code |
| LA (t0, __temp_space) |
| ST a0,0(t0) |
| |
| #ifdef __mips_embedded_pic |
| /* Need to reinit PICBASE, since we might be called via exit() |
| rather than via a return path which would restore old s0. */ |
| #define PICBASE exit_PICBASE |
| .set noreorder |
| PICBASE = .+8 |
| bal PICBASE |
| nop |
| move s0,$31 |
| .set reorder |
| #endif |
| #ifdef GCRT0 |
| LA (t0, _mcleanup) |
| jalr t0 |
| #endif |
| LA (t0, hardware_exit_hook) |
| beq t0,zero,1f |
| jalr t0 |
| 1: |
| |
| # restore return value from main |
| LA (t0, __temp_space) |
| LD a0,0(t0) |
| |
| .global __exit .text |
| jal __exit |
| |
| # break instruction can cope with 0xfffff, but GAS limits the range: |
| break 1023 |
| b 7b # but loop back just in-case |
| .end _exit |
| |
| /* Assume the PICBASE set up above is no longer valid below here. */ |
| #ifdef __mips_embedded_pic |
| #undef PICBASE |
| #endif |
| |
| /* EOF crt0.S */ |