2 * Copyright (C) 2014 Imagination Technologies
3 * Author: Paul Burton <paul.burton@imgtec.com>
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
11 #include <linux/elf.h>
12 #include <linux/sched.h>
19 int arch_elf_pt_proc(void *_ehdr, void *_phdr, struct file *elf,
20 bool is_interp, struct arch_elf_state *state)
22 struct elfhdr *ehdr = _ehdr;
23 struct elf_phdr *phdr = _phdr;
24 struct mips_elf_abiflags_v0 abiflags;
27 if (config_enabled(CONFIG_64BIT) &&
28 (ehdr->e_ident[EI_CLASS] != ELFCLASS32))
30 if (phdr->p_type != PT_MIPS_ABIFLAGS)
32 if (phdr->p_filesz < sizeof(abiflags))
35 ret = kernel_read(elf, phdr->p_offset, (char *)&abiflags,
39 if (ret != sizeof(abiflags))
42 /* Record the required FP ABIs for use by mips_check_elf */
44 state->interp_fp_abi = abiflags.fp_abi;
46 state->fp_abi = abiflags.fp_abi;
51 static inline unsigned get_fp_abi(struct elfhdr *ehdr, int in_abi)
53 /* If the ABI requirement is provided, simply return that */
57 /* If the EF_MIPS_FP64 flag was set, return MIPS_ABI_FP_64 */
58 if (ehdr->e_flags & EF_MIPS_FP64)
59 return MIPS_ABI_FP_64;
61 /* Default to MIPS_ABI_FP_DOUBLE */
62 return MIPS_ABI_FP_DOUBLE;
65 int arch_check_elf(void *_ehdr, bool has_interpreter,
66 struct arch_elf_state *state)
68 struct elfhdr *ehdr = _ehdr;
69 unsigned fp_abi, interp_fp_abi, abi0, abi1;
71 /* Ignore non-O32 binaries */
72 if (config_enabled(CONFIG_64BIT) &&
73 (ehdr->e_ident[EI_CLASS] != ELFCLASS32))
76 fp_abi = get_fp_abi(ehdr, state->fp_abi);
78 if (has_interpreter) {
79 interp_fp_abi = get_fp_abi(ehdr, state->interp_fp_abi);
81 abi0 = min(fp_abi, interp_fp_abi);
82 abi1 = max(fp_abi, interp_fp_abi);
87 state->overall_abi = FP_ERROR;
90 state->overall_abi = abi0;
91 } else if (abi0 == MIPS_ABI_FP_ANY) {
92 state->overall_abi = abi1;
93 } else if (abi0 == MIPS_ABI_FP_DOUBLE) {
96 state->overall_abi = MIPS_ABI_FP_DOUBLE;
100 state->overall_abi = FP_DOUBLE_64A;
103 } else if (abi0 == MIPS_ABI_FP_SINGLE ||
104 abi0 == MIPS_ABI_FP_SOFT) {
105 /* Cannot link with other ABIs */
106 } else if (abi0 == MIPS_ABI_FP_OLD_64) {
110 case MIPS_ABI_FP_64A:
111 state->overall_abi = MIPS_ABI_FP_64;
114 } else if (abi0 == MIPS_ABI_FP_XX ||
115 abi0 == MIPS_ABI_FP_64 ||
116 abi0 == MIPS_ABI_FP_64A) {
117 state->overall_abi = MIPS_ABI_FP_64;
120 switch (state->overall_abi) {
122 case MIPS_ABI_FP_64A:
124 if (!config_enabled(CONFIG_MIPS_O32_FP64_SUPPORT))
135 void mips_set_personality_fp(struct arch_elf_state *state)
137 if (config_enabled(CONFIG_FP32XX_HYBRID_FPRS)) {
139 * Use hybrid FPRs for all code which can correctly execute
142 switch (state->overall_abi) {
143 case MIPS_ABI_FP_DOUBLE:
144 case MIPS_ABI_FP_SINGLE:
145 case MIPS_ABI_FP_SOFT:
147 case MIPS_ABI_FP_ANY:
149 clear_thread_flag(TIF_32BIT_FPREGS);
150 set_thread_flag(TIF_HYBRID_FPREGS);
155 switch (state->overall_abi) {
156 case MIPS_ABI_FP_DOUBLE:
157 case MIPS_ABI_FP_SINGLE:
158 case MIPS_ABI_FP_SOFT:
160 set_thread_flag(TIF_32BIT_FPREGS);
161 clear_thread_flag(TIF_HYBRID_FPREGS);
166 clear_thread_flag(TIF_32BIT_FPREGS);
167 set_thread_flag(TIF_HYBRID_FPREGS);
171 case MIPS_ABI_FP_64A:
173 clear_thread_flag(TIF_32BIT_FPREGS);
174 clear_thread_flag(TIF_HYBRID_FPREGS);
178 case MIPS_ABI_FP_ANY:
179 if (!config_enabled(CONFIG_MIPS_O32_FP64_SUPPORT))
180 set_thread_flag(TIF_32BIT_FPREGS);
182 clear_thread_flag(TIF_32BIT_FPREGS);
184 clear_thread_flag(TIF_HYBRID_FPREGS);