xref: /openbmc/linux/arch/mips/kernel/elf.c (revision 8684014d)
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 elfhdr *ehdr = _ehdr;
23 	struct elf_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 elfhdr *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 elfhdr *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