Update for Linux/MIPS.

This commit is contained in:
Ulrich Drepper 1997-07-12 23:22:49 +00:00
parent 6cabe500f3
commit bd4c4968c8
2 changed files with 339 additions and 245 deletions

View File

@ -24,6 +24,34 @@
#define ELF_MACHINE_NAME "MIPS"
#include <assert.h>
#include <entry.h>
#ifndef ENTRY_POINT
#error ENTRY_POINT needs to be defined for MIPS.
#endif
#ifndef _RTLD_PROLOGUE
#ifdef __STDC__
#define _RTLD_PROLOGUE(entry) "\n\t.globl " #entry \
"\n\t.ent " #entry \
"\n\t" #entry ":\n\t"
#else
#define _RTLD_PROLOGUE(entry) "\n\t.globl entry\n\t.ent entry\n\t entry:\n\t"
#endif
#endif
#ifndef _RTLD_EPILOGUE
#ifdef __STDC__
#define _RTLD_EPILOGUE(entry) "\t.end " #entry "\n"
#else
#define _RTLD_EPILOGUE(entry) "\t.end entry\n"
#endif
#endif
/* I have no idea what I am doing. */
#define ELF_MACHINE_RELOC_NOPLT -1
#define elf_machine_lookup_noplt_p(type) (1)
#define elf_machine_lookup_noexec_p(type) (0)
/* Translate a processor specific dynamic tag to the index
in l_info array. */
@ -34,6 +62,18 @@
#define ELF_MACHINE_ALIGN_MASK 0xffff
#endif
/*
* MIPS libraries are usually linked to a non-zero base address. We
* subtrace the base address from the address where we map the object
* to. This results in more efficient address space usage.
*/
#if 0
#define MAP_BASE_ADDR(l) ((l)->l_info[DT_MIPS(BASE_ADDRESS)] ? \
(l)->l_info[DT_MIPS(BASE_ADDRESS)]->d_un.d_ptr : 0)
#else
#define MAP_BASE_ADDR(l) 0x5ffe0000
#endif
/* If there is a DT_MIPS_RLD_MAP entry in the dynamic section, fill it in
with the run-time address of the r_debug structure */
#define ELF_MACHINE_DEBUG_SETUP(l,r) \
@ -43,7 +83,7 @@ do { if ((l)->l_info[DT_MIPS (RLD_MAP)]) \
} while (0)
/* Return nonzero iff E_MACHINE is compatible with the running host. */
static inline int
static inline int __attribute__ ((unused))
elf_machine_matches_host (ElfW(Half) e_machine)
{
switch (e_machine)
@ -56,6 +96,16 @@ elf_machine_matches_host (ElfW(Half) e_machine)
}
}
/* Return the link-time address of _DYNAMIC. Conveniently, this is the
+ first element of the GOT. This must be inlined in a function which
+ uses global data. */
+static inline ElfW(Addr)
+elf_machine_dynamic (void)
+{
+ register ElfW(Addr) gp asm ("$28");
+ return * (ElfW(Addr) *) (gp - 0x7ff0);
+}
+
static inline ElfW(Addr) *
elf_mips_got_from_gpreg (ElfW(Addr) gpreg)
{
@ -75,6 +125,16 @@ elf_machine_got (void)
}
/* Return the link-time address of _DYNAMIC. Conveniently, this is the
first element of the GOT. This must be inlined in a function which
uses global data. */
static inline ElfW(Addr)
elf_machine_dynamic (void)
{
register ElfW(Addr) gp asm ("$28");
return * (ElfW(Addr) *) (gp - 0x7ff0);
}
/* Return the run-time load address of the shared object. */
static inline ElfW(Addr)
elf_machine_load_address (void)
@ -87,7 +147,9 @@ elf_machine_load_address (void)
" nop\n"
"here: dsubu %0, $31, %0\n"
" .set reorder\n"
: "=r" (addr));
: "=r" (addr)
: /* No inputs */
: "$31");
#else
asm (" .set noreorder\n"
" la %0, here\n"
@ -95,7 +157,9 @@ elf_machine_load_address (void)
" nop\n"
"here: subu %0, $31, %0\n"
" .set reorder\n"
: "=r" (addr));
: "=r" (addr)
: /* No inputs */
: "$31");
#endif
return addr;
}
@ -105,7 +169,7 @@ elf_machine_load_address (void)
/* Relocate GOT. */
static inline void
elf_machine_got_rel (struct link_map *map)
elf_machine_got_rel (struct link_map *map, int lazy)
{
ElfW(Addr) *got;
ElfW(Sym) *sym;
@ -119,7 +183,7 @@ elf_machine_got_rel (struct link_map *map)
const ElfW(Sym) *ref = sym; \
ElfW(Addr) sym_loadaddr; \
sym_loadaddr = _dl_lookup_symbol (strtab + sym->st_name, &ref, scope, \
map->l_name, DL_LOOKUP_NOPLT); \
map->l_name, ELF_MACHINE_RELOC_NOPLT);\
(ref)? sym_loadaddr + ref->st_value: 0; \
})
@ -151,7 +215,7 @@ elf_machine_got_rel (struct link_map *map)
{
if (ELFW(ST_TYPE) (sym->st_info) == STT_FUNC)
{
if (sym->st_value /* && maybe_stub (sym->st_value) */)
if (sym->st_value && lazy)
*got = sym->st_value + map->l_addr;
else
*got = RESOLVE_GOTSYM (sym);
@ -163,7 +227,7 @@ elf_machine_got_rel (struct link_map *map)
*got = RESOLVE_GOTSYM (sym);
else if (ELFW(ST_TYPE) (sym->st_info) == STT_FUNC
&& *got != sym->st_value
/* && maybe_stub (*got) */)
&& lazy)
*got += map->l_addr;
else if (ELFW(ST_TYPE) (sym->st_info) == STT_SECTION)
{
@ -193,6 +257,11 @@ elf_machine_runtime_setup (struct link_map *l, int lazy)
extern void _dl_runtime_resolve (ElfW(Word));
extern int _dl_mips_gnu_objects;
#ifdef RTLD_BOOTSTRAP
{
return lazy;
}
#endif
if (lazy)
{
/* The GOT entries for functions have not yet been filled in.
@ -216,7 +285,7 @@ elf_machine_runtime_setup (struct link_map *l, int lazy)
}
/* Relocate global offset table. */
elf_machine_got_rel (l);
elf_machine_got_rel (l, lazy);
return lazy;
}
@ -282,6 +351,7 @@ elf_machine_runtime_link_map (ElfW(Addr) gpreg, ElfW(Addr) stub_pc)
}
_dl_signal_error (0, NULL, "cannot find runtime link map");
return NULL;
}
/* Mips has no PLT but define elf_machine_relplt to be elf_machine_rel. */
@ -295,211 +365,220 @@ elf_machine_runtime_link_map (ElfW(Addr) gpreg, ElfW(Addr) stub_pc)
to usual c arguments. */
#ifdef __mips64
#define ELF_MACHINE_RUNTIME_TRAMPOLINE \
/* The flag _dl_mips_gnu_objects is set if all dynamic objects are \
generated by the gnu linker. */\
int _dl_mips_gnu_objects = 1;\
\
#define ELF_MACHINE_RUNTIME_TRAMPOLINE \
/* The flag _dl_mips_gnu_objects is set if all dynamic objects are \
generated by the gnu linker. */ \
int _dl_mips_gnu_objects = 1; \
\
/* This is called from assembly stubs below which the compiler can't see. */ \
static ElfW(Addr) \
__dl_runtime_resolve (ElfW(Word), ElfW(Word), ElfW(Addr), ElfW(Addr)) \
__attribute__ ((unused)); \
\
static ElfW(Addr) \
__dl_runtime_resolve (ElfW(Word) sym_index,\
ElfW(Word) return_address,\
ElfW(Addr) old_gpreg,\
ElfW(Addr) stub_pc)\
{\
struct link_map *l = elf_machine_runtime_link_map (old_gpreg, stub_pc);\
const ElfW(Sym) *const symtab\
= (const ElfW(Sym) *) (l->l_addr + l->l_info[DT_SYMTAB]->d_un.d_ptr);\
const char *strtab\
= (void *) (l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr);\
const ElfW(Addr) *got\
= (const ElfW(Addr) *) (l->l_addr + l->l_info[DT_PLTGOT]->d_un.d_ptr);\
const ElfW(Word) local_gotno\
= (const ElfW(Word)) l->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val;\
const ElfW(Word) gotsym\
= (const ElfW(Word)) l->l_info[DT_MIPS (GOTSYM)]->d_un.d_val;\
const ElfW(Sym) *definer;\
ElfW(Addr) loadbase;\
ElfW(Addr) funcaddr;\
struct link_map **scope;\
\
/* Look up the symbol's run-time value. */\
scope = _dl_object_relocation_scope (l);\
definer = &symtab[sym_index];\
\
loadbase = _dl_lookup_symbol (strtab + definer->st_name, &definer,\
scope, l->l_name, DL_LOOKUP_NOPLT);\
\
*_dl_global_scope_end = NULL;\
\
/* Apply the relocation with that value. */\
funcaddr = loadbase + definer->st_value;\
*(got + local_gotno + sym_index - gotsym) = funcaddr;\
\
return funcaddr;\
}\
\
asm ("\n\
.text\n\
.align 3\n\
.globl _dl_runtime_resolve\n\
.type _dl_runtime_resolve,@function\n\
.ent _dl_runtime_resolve\n\
_dl_runtime_resolve:\n\
.set noreorder\n\
# Save old GP to $3.\n\
move $3,$28\n\
# Modify t9 ($25) so as to point .cpload instruction.\n\
daddu $25,2*8\n\
# Compute GP.\n\
.cpload $25\n\
.set reorder\n\
# Save slot call pc.\n\
move $2, $31\n\
# Save arguments and sp value in stack.\n\
dsubu $29, 10*8\n\
.cprestore 8*8\n\
sd $15, 9*8($29)\n\
sd $4, 3*8($29)\n\
sd $5, 4*8($29)\n\
sd $6, 5*8($29)\n\
sd $7, 6*8($29)\n\
sd $16, 7*8($29)\n\
move $16, $29\n\
move $4, $24\n\
move $5, $15\n\
move $6, $3\n\
move $7, $2\n\
jal __dl_runtime_resolve\n\
move $29, $16\n\
ld $31, 9*8($29)\n\
ld $4, 3*8($29)\n\
ld $5, 4*8($29)\n\
ld $6, 5*8($29)\n\
ld $7, 6*8($29)\n\
ld $16, 7*8($29)\n\
daddu $29, 10*8\n\
move $25, $2\n\
jr $25\n\
.end _dl_runtime_resolve\n\
static ElfW(Addr) \
__dl_runtime_resolve (ElfW(Word), ElfW(Word), ElfW(Addr), ElfW(Addr)) \
__attribute__ ((unused)); \
\
static ElfW(Addr) \
__dl_runtime_resolve (ElfW(Word) sym_index, \
ElfW(Word) return_address, \
ElfW(Addr) old_gpreg, \
ElfW(Addr) stub_pc) \
{ \
struct link_map *l = elf_machine_runtime_link_map (old_gpreg, stub_pc); \
const ElfW(Sym) *const symtab \
= (const ElfW(Sym) *) (l->l_addr + l->l_info[DT_SYMTAB]->d_un.d_ptr); \
const char *strtab \
= (void *) (l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr); \
const ElfW(Addr) *got \
= (const ElfW(Addr) *) (l->l_addr + l->l_info[DT_PLTGOT]->d_un.d_ptr); \
const ElfW(Word) local_gotno \
= (const ElfW(Word)) l->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val; \
const ElfW(Word) gotsym \
= (const ElfW(Word)) l->l_info[DT_MIPS (GOTSYM)]->d_un.d_val; \
const ElfW(Sym) *definer; \
ElfW(Addr) loadbase; \
ElfW(Addr) funcaddr; \
struct link_map **scope; \
\
/* Look up the symbol's run-time value. */ \
scope = _dl_object_relocation_scope (l); \
definer = &symtab[sym_index]; \
\
loadbase = _dl_lookup_symbol (strtab + definer->st_name, &definer, \
scope, l->l_name, ELF_MACHINE_RELOC_NOPLT); \
\
*_dl_global_scope_end = NULL; \
\
/* Apply the relocation with that value. */ \
funcaddr = loadbase + definer->st_value; \
*(got + local_gotno + sym_index - gotsym) = funcaddr; \
\
return funcaddr; \
} \
\
asm ("\n \
.text\n \
.align 3\n \
.globl _dl_runtime_resolve\n \
.type _dl_runtime_resolve,@function\n \
.ent _dl_runtime_resolve\n \
_dl_runtime_resolve:\n \
.set noreorder\n \
# Save old GP to $3.\n \
move $3,$28\n \
# Modify t9 ($25) so as to point .cpload instruction.\n \
daddu $25,2*8\n \
# Compute GP.\n \
.cpload $25\n \
.set reorder\n \
# Save slot call pc.\n \
move $2, $31\n \
# Save arguments and sp value in stack.\n \
dsubu $29, 10*8\n \
.cprestore 8*8\n \
sd $15, 9*8($29)\n \
sd $4, 3*8($29)\n \
sd $5, 4*8($29)\n \
sd $6, 5*8($29)\n \
sd $7, 6*8($29)\n \
sd $16, 7*8($29)\n \
move $16, $29\n \
move $4, $24\n \
move $5, $15\n \
move $6, $3\n \
move $7, $2\n \
jal __dl_runtime_resolve\n \
move $29, $16\n \
ld $31, 9*8($29)\n \
ld $4, 3*8($29)\n \
ld $5, 4*8($29)\n \
ld $6, 5*8($29)\n \
ld $7, 6*8($29)\n \
ld $16, 7*8($29)\n \
daddu $29, 10*8\n \
move $25, $2\n \
jr $25\n \
.end _dl_runtime_resolve\n \
");
#else
#define ELF_MACHINE_RUNTIME_TRAMPOLINE \
/* The flag _dl_mips_gnu_objects is set if all dynamic objects are \
generated by the gnu linker. */\
int _dl_mips_gnu_objects = 1;\
\
#define ELF_MACHINE_RUNTIME_TRAMPOLINE \
/* The flag _dl_mips_gnu_objects is set if all dynamic objects are \
generated by the gnu linker. */ \
int _dl_mips_gnu_objects = 1; \
\
/* This is called from assembly stubs below which the compiler can't see. */ \
static ElfW(Addr) \
__dl_runtime_resolve (ElfW(Word), ElfW(Word), ElfW(Addr), ElfW(Addr)) \
__attribute__ ((unused)); \
\
static ElfW(Addr) \
__dl_runtime_resolve (ElfW(Word) sym_index,\
ElfW(Word) return_address,\
ElfW(Addr) old_gpreg,\
ElfW(Addr) stub_pc)\
{\
struct link_map *l = elf_machine_runtime_link_map (old_gpreg, stub_pc);\
const ElfW(Sym) *const symtab\
= (const ElfW(Sym) *) (l->l_addr + l->l_info[DT_SYMTAB]->d_un.d_ptr);\
const char *strtab\
= (void *) (l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr);\
const ElfW(Addr) *got\
= (const ElfW(Addr) *) (l->l_addr + l->l_info[DT_PLTGOT]->d_un.d_ptr);\
const ElfW(Word) local_gotno\
= (const ElfW(Word)) l->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val;\
const ElfW(Word) gotsym\
= (const ElfW(Word)) l->l_info[DT_MIPS (GOTSYM)]->d_un.d_val;\
const ElfW(Sym) *definer;\
ElfW(Addr) loadbase;\
ElfW(Addr) funcaddr;\
struct link_map **scope;\
\
/* Look up the symbol's run-time value. */\
scope = _dl_object_relocation_scope (l);\
definer = &symtab[sym_index];\
\
loadbase = _dl_lookup_symbol (strtab + definer->st_name, &definer,\
scope, l->l_name, DL_LOOKUP_NOPLT);\
\
*_dl_global_scope_end = NULL;\
\
/* Apply the relocation with that value. */\
funcaddr = loadbase + definer->st_value;\
*(got + local_gotno + sym_index - gotsym) = funcaddr;\
\
return funcaddr;\
}\
\
asm ("\n\
.text\n\
.align 2\n\
.globl _dl_runtime_resolve\n\
.type _dl_runtime_resolve,@function\n\
.ent _dl_runtime_resolve\n\
_dl_runtime_resolve:\n\
.set noreorder\n\
# Save old GP to $3.\n\
move $3,$28\n\
# Modify t9 ($25) so as to point .cpload instruction.\n\
addu $25,8\n\
# Compute GP.\n\
.cpload $25\n\
.set reorder\n\
# Save slot call pc.\n\
move $2, $31\n\
# Save arguments and sp value in stack.\n\
subu $29, 40\n\
.cprestore 32\n\
sw $15, 36($29)\n\
sw $4, 12($29)\n\
sw $5, 16($29)\n\
sw $6, 20($29)\n\
sw $7, 24($29)\n\
sw $16, 28($29)\n\
move $16, $29\n\
move $4, $24\n\
move $5, $15\n\
move $6, $3\n\
move $7, $2\n\
jal __dl_runtime_resolve\n\
move $29, $16\n\
lw $31, 36($29)\n\
lw $4, 12($29)\n\
lw $5, 16($29)\n\
lw $6, 20($29)\n\
lw $7, 24($29)\n\
lw $16, 28($29)\n\
addu $29, 40\n\
move $25, $2\n\
jr $25\n\
.end _dl_runtime_resolve\n\
static ElfW(Addr) \
__dl_runtime_resolve (ElfW(Word), ElfW(Word), ElfW(Addr), ElfW(Addr)) \
__attribute__ ((unused)); \
\
static ElfW(Addr) \
__dl_runtime_resolve (ElfW(Word) sym_index, \
ElfW(Word) return_address, \
ElfW(Addr) old_gpreg, \
ElfW(Addr) stub_pc) \
{ \
struct link_map *l = elf_machine_runtime_link_map (old_gpreg, stub_pc); \
const ElfW(Sym) *const symtab \
= (const ElfW(Sym) *) (l->l_addr + l->l_info[DT_SYMTAB]->d_un.d_ptr); \
const char *strtab \
= (void *) (l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr); \
const ElfW(Addr) *got \
= (const ElfW(Addr) *) (l->l_addr + l->l_info[DT_PLTGOT]->d_un.d_ptr); \
const ElfW(Word) local_gotno \
= (const ElfW(Word)) l->l_info[DT_MIPS (LOCAL_GOTNO)]->d_un.d_val; \
const ElfW(Word) gotsym \
= (const ElfW(Word)) l->l_info[DT_MIPS (GOTSYM)]->d_un.d_val; \
const ElfW(Sym) *definer; \
ElfW(Addr) loadbase; \
ElfW(Addr) funcaddr; \
struct link_map **scope; \
\
/* Look up the symbol's run-time value. */ \
scope = _dl_object_relocation_scope (l); \
definer = &symtab[sym_index]; \
\
loadbase = _dl_lookup_symbol (strtab + definer->st_name, &definer, \
scope, l->l_name, ELF_MACHINE_RELOC_NOPLT); \
\
*_dl_global_scope_end = NULL; \
\
/* Apply the relocation with that value. */ \
funcaddr = loadbase + definer->st_value; \
*(got + local_gotno + sym_index - gotsym) = funcaddr; \
\
return funcaddr; \
} \
\
asm ("\n \
.text\n \
.align 2\n \
.globl _dl_runtime_resolve\n \
.type _dl_runtime_resolve,@function\n \
.ent _dl_runtime_resolve\n \
_dl_runtime_resolve:\n \
.set noreorder\n \
# Save slot call pc.\n \
move $3, $31\n \
# Modify t9 ($25) so as to point .cpload instruction.\n \
addu $25,8\n \
# Compute GP.\n \
.cpload $25\n \
.set reorder\n \
# Save slot call pc.\n \
move $2, $31\n \
# Save arguments and sp value in stack.\n \
subu $29, 40\n \
.cprestore 32\n \
sw $15, 36($29)\n \
sw $4, 12($29)\n \
sw $5, 16($29)\n \
sw $6, 20($29)\n \
sw $7, 24($29)\n \
sw $16, 28($29)\n \
move $16, $29\n \
move $4, $24\n \
move $5, $15\n \
move $6, $3\n \
move $7, $2\n \
jal __dl_runtime_resolve\n \
move $29, $16\n \
lw $31, 36($29)\n \
lw $4, 12($29)\n \
lw $5, 16($29)\n \
lw $6, 20($29)\n \
lw $7, 24($29)\n \
lw $16, 28($29)\n \
addu $29, 40\n \
move $25, $2\n \
jr $25\n \
.end _dl_runtime_resolve\n \
");
#endif
/* Mask identifying addresses reserved for the user program,
where the dynamic linker should not map anything. */
#define ELF_MACHINE_USER_ADDRESS_MASK 0x00000000UL
#define ELF_MACHINE_USER_ADDRESS_MASK 0x80000000UL
/* Initial entry point code for the dynamic linker.
The C function `_dl_start' is the real entry point;
its return value is the user program's entry point. */
its return value is the user program's entry point.
Note how we have to be careful about two things:
1) That we allocate a minimal stack of 24 bytes for
every function call, the MIPS ABI states that even
if all arguments are passed in registers the procedure
called can use the 16 byte area pointed to by $sp
when it is called to store away the arguments passed
to it.
2) That under Linux the entry is named __start
and not just plain _start. */
#ifdef __mips64
#define RTLD_START asm ("\
.text\n\
.align 3\n\
.globl _start\n\
.globl _dl_start_user\n\
.ent _start\n\
_start:\n\
.align 3\n"\
_RTLD_PROLOGUE (ENTRY_POINT)\
" .globl _dl_start_user\n\
.set noreorder\n\
bltzal $0, 0f\n\
nop\n\
@ -561,16 +640,15 @@ _dl_start_user:\n\
ld $5, 1*8($29)\n\
ld $6, 2*8$29)\n\
ld $7, 3*8($29)\n\
jr $25\n\
.end _start\n\
");
jr $25\n"\
_RTLD_EPILOGUE(ENTRY_POINT) \
);
#else
#define RTLD_START asm ("\
.text\n\
.globl _start\n\
.globl _dl_start_user\n\
.ent _start\n\
_start:\n\
.text\n"\
_RTLD_PROLOGUE(ENTRY_POINT)\
" .globl _dl_start_user\n\
.set noreorder\n\
bltzal $0, 0f\n\
nop\n\
@ -582,7 +660,9 @@ _start:\n\
la $4, _DYNAMIC\n\
sw $4, -0x7ff0($28)\n\
move $4, $29\n\
subu $29, 16\n\
jal _dl_start\n\
addiu $29, 16\n\
# Get the value of label '_dl_start_user' in t9 ($25).\n\
la $25, _dl_start_user\n\
_dl_start_user:\n\
@ -610,7 +690,9 @@ _dl_start_user:\n\
lw $4, 8($2)\n\
# Call _dl_init_next to return the address of an initializer\n\
# function to run.\n\
subu $29, 16\n\
jal _dl_init_next\n\
addiu $29, 16\n\
move $28, $16\n\
# Check for zero return, when out of initializers.\n\
beq $2, $0, 2f\n\
@ -624,16 +706,18 @@ _dl_start_user:\n\
move $28, $16\n\
# Loop to call _dl_init_next for the next initializer.\n\
b 1b\n\
2: # Clear the startup flag. Assumes 32 bit ints.\n\
sw $0, _dl_starting_up\n\
# Pass our finalizer function to the user in ra.\n\
2: la $31, _dl_fini\n\
la $31, _dl_fini\n\
# Jump to the user entry point.\n\
move $25, $17\n\
lw $4, 0($29)\n\
lw $5, 4($29)\n\
lw $6, 8($29)\n\
lw $7, 12($29)\n\
jr $25\n\
.end _start\n\
jr $25\n"\
_RTLD_EPILOGUE(ENTRY_POINT)\
");
#endif

View File

@ -1,7 +1,7 @@
/* FPU control word bits. Mips version.
Copyright (C) 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Olaf Flebbe.
Contributed by Olaf Flebbe and Ralf Baechle.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
@ -21,74 +21,84 @@
#ifndef _FPU_CONTROL_H
#define _FPU_CONTROL_H
/* FP control/status register bit assignments.
/* MIPS FPU floating point control register bits.
*
* 31-25 24 23 22-18 17-12 11-7 6-2 1-0
* (cause) (enables) (flags)
* | reserved | FS | C | reserved | E V Z O U I | V Z O U I | V Z O U I | RM
* 31-25 -> floating point conditions code bits 7-1. These bits are only
* available in MIPS IV.
* 24 -> flush denormalized results to zero instead of
* causing unimplemented operation exception. This bit is only
* available for MIPS III and newer.
* 23 -> Condition bit
* 22-18 -> reserved (read as 0, write with 0)
* 17 -> cause bit for unimplemented operation
* 16 -> cause bit for invalid exception
* 15 -> cause bit for division by zero exception
* 14 -> cause bit for overflow exception
* 13 -> cause bit for underflow exception
* 12 -> cause bit for inexact exception
* 11 -> enable exception for invalid exception
* 10 -> enable exception for division by zero exception
* 9 -> enable exception for overflow exception
* 8 -> enable exception for underflow exception
* 7 -> enable exception for inexact exception
* 6 -> flag invalid exception
* 5 -> flag division by zero exception
* 4 -> flag overflow exception
* 3 -> flag underflow exception
* 2 -> flag inexact exception
* 1-0 -> rounding control
*
* FS: When set, denormalized results are flushed to zero instead of
* causing an unimplemented operation exception.
* C: Condition bit.
* E: Unimplemented Operation.
* V: Invalid Operation.
* Z: Division by zero.
* O: Overflow.
* U: Underflow.
* I: Inexact Operation
* RM: Rounding mode bits
* 00 (RN) - rounding to nearest
* 01 (RZ) - rounding toward zero
* 10 (RP) - rounding down (toward - infinity)
* 11 (RM) - rounding up (toward + infinity)
*
* Rounding Control:
* 00 - rounding to nearest (RN)
* 01 - rounding toward zero (RZ)
* 01 - rounding (up) toward plus infinity (RP)
* 11 - rounding (down)toward minus infinity (RM)
*/
#include <features.h>
/* masking of interrupts */
#define _FPU_MASK_IM (1 << 11)
#define _FPU_MASK_DM (1 << 24) /* XXX */
#define _FPU_MASK_ZM (1 << 10)
#define _FPU_MASK_OM (1 << 9)
#define _FPU_MASK_UM (1 << 8)
#define _FPU_MASK_PM (1 << 7)
#define _FPU_MASK_V 0x0800 /* Invalid operation */
#define _FPU_MASK_Z 0x0400 /* Division by zero */
#define _FPU_MASK_O 0x0200 /* Overflow */
#define _FPU_MASK_U 0x0100 /* Underflow */
#define _FPU_MASK_I 0x0080 /* Inexact operation */
/* precision control */
#define _FPU_EXTENDED 0
#define _FPU_DOUBLE 0
#define _FPU_SINGLE 0
/* flush denormalized numbers to zero */
#define _FPU_FLUSH_TZ 0x1000000
/* rounding control */
#define _FPU_RC_NEAREST 0x0 /* RECOMMENDED */
#define _FPU_RC_DOWN 0x2
#define _FPU_RC_UP 0x3
#define _FPU_RC_NEAREST 0x0 /* RECOMMENDED */
#define _FPU_RC_ZERO 0x1
#define _FPU_RC_UP 0x2
#define _FPU_RC_DOWN 0x3
#define _FPU_RESERVED 0xfe7c0000 /* Reserved bits */
#define _FPU_RESERVED 0xfe3c0000 /* Reserved bits in cw */
/* The fdlibm code requires strict IEEE double precision arithmetic,
and no interrupts for exceptions, rounding to nearest. */
#define _FPU_DEFAULT 0x0
#define _FPU_DEFAULT 0x00000600
/* IEEE: same as above, but exceptions */
#define _FPU_IEEE (0x1f << 7)
#define _FPU_IEEE 0x00000F80
/* Type of the control word. */
typedef unsigned int fpu_control_t;
typedef unsigned int fpu_control_t __attribute__ ((__mode__ (__HI__)));
/* Macros for accessing the hardware control word. */
#define _FPU_GETCW(cw) __asm__ ("cfc1 %0, $31; nop; nop" : "=r" (cw))
#define _FPU_SETCW(cw) __asm__ ("ctc1 %0, $31; nop; nop" : : "r" (cw))
#define _FPU_GETCW(cw) __asm__ ("cfc1 %0,$31" : "=r" (cw) : )
#define _FPU_SETCW(cw) __asm__ ("ctc1 %0,$31" : : "r" (cw))
/* Default control word set at startup. */
extern fpu_control_t __fpu_control;
__BEGIN_DECLS
/* Called at startup. It can be used to manipulate fpu control register. */
/* Called at startup. It can be used to manipulate the fpu control
register. */
extern void __setfpucw __P ((fpu_control_t));
__END_DECLS