1 /* 2 * Copyright (C) 2014 Imagination Technologies 3 * Author: Paul Burton <paul.burton@imgtec.com> 4 * 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. 9 */ 10 11 #include <linux/elf.h> 12 #include <linux/sched.h> 13 14 enum { 15 FP_ERROR = -1, 16 FP_DOUBLE_64A = -2, 17 }; 18 19 int arch_elf_pt_proc(void *_ehdr, void *_phdr, struct file *elf, 20 bool is_interp, struct arch_elf_state *state) 21 { 22 struct elf32_hdr *ehdr = _ehdr; 23 struct elf32_phdr *phdr = _phdr; 24 struct mips_elf_abiflags_v0 abiflags; 25 int ret; 26 27 if (config_enabled(CONFIG_64BIT) && 28 (ehdr->e_ident[EI_CLASS] != ELFCLASS32)) 29 return 0; 30 if (phdr->p_type != PT_MIPS_ABIFLAGS) 31 return 0; 32 if (phdr->p_filesz < sizeof(abiflags)) 33 return -EINVAL; 34 35 ret = kernel_read(elf, phdr->p_offset, (char *)&abiflags, 36 sizeof(abiflags)); 37 if (ret < 0) 38 return ret; 39 if (ret != sizeof(abiflags)) 40 return -EIO; 41 42 /* Record the required FP ABIs for use by mips_check_elf */ 43 if (is_interp) 44 state->interp_fp_abi = abiflags.fp_abi; 45 else 46 state->fp_abi = abiflags.fp_abi; 47 48 return 0; 49 } 50 51 static inline unsigned get_fp_abi(struct elf32_hdr *ehdr, int in_abi) 52 { 53 /* If the ABI requirement is provided, simply return that */ 54 if (in_abi != -1) 55 return in_abi; 56 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; 60 61 /* Default to MIPS_ABI_FP_DOUBLE */ 62 return MIPS_ABI_FP_DOUBLE; 63 } 64 65 int arch_check_elf(void *_ehdr, bool has_interpreter, 66 struct arch_elf_state *state) 67 { 68 struct elf32_hdr *ehdr = _ehdr; 69 unsigned fp_abi, interp_fp_abi, abi0, abi1; 70 71 /* Ignore non-O32 binaries */ 72 if (config_enabled(CONFIG_64BIT) && 73 (ehdr->e_ident[EI_CLASS] != ELFCLASS32)) 74 return 0; 75 76 fp_abi = get_fp_abi(ehdr, state->fp_abi); 77 78 if (has_interpreter) { 79 interp_fp_abi = get_fp_abi(ehdr, state->interp_fp_abi); 80 81 abi0 = min(fp_abi, interp_fp_abi); 82 abi1 = max(fp_abi, interp_fp_abi); 83 } else { 84 abi0 = abi1 = fp_abi; 85 } 86 87 state->overall_abi = FP_ERROR; 88 89 if (abi0 == abi1) { 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) { 94 switch (abi1) { 95 case MIPS_ABI_FP_XX: 96 state->overall_abi = MIPS_ABI_FP_DOUBLE; 97 break; 98 99 case MIPS_ABI_FP_64A: 100 state->overall_abi = FP_DOUBLE_64A; 101 break; 102 } 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) { 107 switch (abi1) { 108 case MIPS_ABI_FP_XX: 109 case MIPS_ABI_FP_64: 110 case MIPS_ABI_FP_64A: 111 state->overall_abi = MIPS_ABI_FP_64; 112 break; 113 } 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; 118 } 119 120 switch (state->overall_abi) { 121 case MIPS_ABI_FP_64: 122 case MIPS_ABI_FP_64A: 123 case FP_DOUBLE_64A: 124 if (!config_enabled(CONFIG_MIPS_O32_FP64_SUPPORT)) 125 return -ELIBBAD; 126 break; 127 128 case FP_ERROR: 129 return -ELIBBAD; 130 } 131 132 return 0; 133 } 134 135 void mips_set_personality_fp(struct arch_elf_state *state) 136 { 137 if (config_enabled(CONFIG_FP32XX_HYBRID_FPRS)) { 138 /* 139 * Use hybrid FPRs for all code which can correctly execute 140 * with that mode. 141 */ 142 switch (state->overall_abi) { 143 case MIPS_ABI_FP_DOUBLE: 144 case MIPS_ABI_FP_SINGLE: 145 case MIPS_ABI_FP_SOFT: 146 case MIPS_ABI_FP_XX: 147 case MIPS_ABI_FP_ANY: 148 /* FR=1, FRE=1 */ 149 clear_thread_flag(TIF_32BIT_FPREGS); 150 set_thread_flag(TIF_HYBRID_FPREGS); 151 return; 152 } 153 } 154 155 switch (state->overall_abi) { 156 case MIPS_ABI_FP_DOUBLE: 157 case MIPS_ABI_FP_SINGLE: 158 case MIPS_ABI_FP_SOFT: 159 /* FR=0 */ 160 set_thread_flag(TIF_32BIT_FPREGS); 161 clear_thread_flag(TIF_HYBRID_FPREGS); 162 break; 163 164 case FP_DOUBLE_64A: 165 /* FR=1, FRE=1 */ 166 clear_thread_flag(TIF_32BIT_FPREGS); 167 set_thread_flag(TIF_HYBRID_FPREGS); 168 break; 169 170 case MIPS_ABI_FP_64: 171 case MIPS_ABI_FP_64A: 172 /* FR=1, FRE=0 */ 173 clear_thread_flag(TIF_32BIT_FPREGS); 174 clear_thread_flag(TIF_HYBRID_FPREGS); 175 break; 176 177 case MIPS_ABI_FP_XX: 178 case MIPS_ABI_FP_ANY: 179 if (!config_enabled(CONFIG_MIPS_O32_FP64_SUPPORT)) 180 set_thread_flag(TIF_32BIT_FPREGS); 181 else 182 clear_thread_flag(TIF_32BIT_FPREGS); 183 184 clear_thread_flag(TIF_HYBRID_FPREGS); 185 break; 186 187 default: 188 case FP_ERROR: 189 BUG(); 190 } 191 } 192