]>
Commit | Line | Data |
---|---|---|
da2b9140 AJ |
1 | /* Support for writing ELF notes for ARM architectures |
2 | * | |
3 | * Copyright (C) 2015 Red Hat Inc. | |
4 | * | |
5 | * Author: Andrew Jones <[email protected]> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License along | |
18 | * with this program; if not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
20 | ||
8ef94f0b | 21 | #include "qemu/osdep.h" |
da2b9140 AJ |
22 | #include "cpu.h" |
23 | #include "elf.h" | |
24 | #include "sysemu/dump.h" | |
25 | ||
26 | /* struct user_pt_regs from arch/arm64/include/uapi/asm/ptrace.h */ | |
27 | struct aarch64_user_regs { | |
28 | uint64_t regs[31]; | |
29 | uint64_t sp; | |
30 | uint64_t pc; | |
31 | uint64_t pstate; | |
32 | } QEMU_PACKED; | |
33 | ||
34 | QEMU_BUILD_BUG_ON(sizeof(struct aarch64_user_regs) != 272); | |
35 | ||
36 | /* struct elf_prstatus from include/uapi/linux/elfcore.h */ | |
37 | struct aarch64_elf_prstatus { | |
38 | char pad1[32]; /* 32 == offsetof(struct elf_prstatus, pr_pid) */ | |
39 | uint32_t pr_pid; | |
40 | char pad2[76]; /* 76 == offsetof(struct elf_prstatus, pr_reg) - | |
41 | offsetof(struct elf_prstatus, pr_ppid) */ | |
42 | struct aarch64_user_regs pr_reg; | |
43 | uint32_t pr_fpvalid; | |
44 | char pad3[4]; | |
45 | } QEMU_PACKED; | |
46 | ||
47 | QEMU_BUILD_BUG_ON(sizeof(struct aarch64_elf_prstatus) != 392); | |
48 | ||
bada8e44 AJ |
49 | /* struct user_fpsimd_state from arch/arm64/include/uapi/asm/ptrace.h |
50 | * | |
51 | * While the vregs member of user_fpsimd_state is of type __uint128_t, | |
52 | * QEMU uses an array of uint64_t, where the high half of the 128-bit | |
53 | * value is always in the 2n+1'th index. Thus we also break the 128- | |
54 | * bit values into two halves in this reproduction of user_fpsimd_state. | |
55 | */ | |
56 | struct aarch64_user_vfp_state { | |
57 | uint64_t vregs[64]; | |
58 | uint32_t fpsr; | |
59 | uint32_t fpcr; | |
60 | char pad[8]; | |
61 | } QEMU_PACKED; | |
62 | ||
63 | QEMU_BUILD_BUG_ON(sizeof(struct aarch64_user_vfp_state) != 528); | |
64 | ||
da2b9140 AJ |
65 | struct aarch64_note { |
66 | Elf64_Nhdr hdr; | |
67 | char name[8]; /* align_up(sizeof("CORE"), 4) */ | |
bada8e44 AJ |
68 | union { |
69 | struct aarch64_elf_prstatus prstatus; | |
70 | struct aarch64_user_vfp_state vfp; | |
71 | }; | |
da2b9140 AJ |
72 | } QEMU_PACKED; |
73 | ||
bada8e44 AJ |
74 | #define AARCH64_NOTE_HEADER_SIZE offsetof(struct aarch64_note, prstatus) |
75 | #define AARCH64_PRSTATUS_NOTE_SIZE \ | |
76 | (AARCH64_NOTE_HEADER_SIZE + sizeof(struct aarch64_elf_prstatus)) | |
77 | #define AARCH64_PRFPREG_NOTE_SIZE \ | |
78 | (AARCH64_NOTE_HEADER_SIZE + sizeof(struct aarch64_user_vfp_state)) | |
da2b9140 AJ |
79 | |
80 | static void aarch64_note_init(struct aarch64_note *note, DumpState *s, | |
81 | const char *name, Elf64_Word namesz, | |
82 | Elf64_Word type, Elf64_Word descsz) | |
83 | { | |
84 | memset(note, 0, sizeof(*note)); | |
85 | ||
86 | note->hdr.n_namesz = cpu_to_dump32(s, namesz); | |
87 | note->hdr.n_descsz = cpu_to_dump32(s, descsz); | |
88 | note->hdr.n_type = cpu_to_dump32(s, type); | |
89 | ||
90 | memcpy(note->name, name, namesz); | |
91 | } | |
92 | ||
bada8e44 AJ |
93 | static int aarch64_write_elf64_prfpreg(WriteCoreDumpFunction f, |
94 | CPUARMState *env, int cpuid, | |
95 | DumpState *s) | |
96 | { | |
97 | struct aarch64_note note; | |
98 | int ret, i; | |
99 | ||
100 | aarch64_note_init(¬e, s, "CORE", 5, NT_PRFPREG, sizeof(note.vfp)); | |
101 | ||
102 | for (i = 0; i < 64; ++i) { | |
103 | note.vfp.vregs[i] = cpu_to_dump64(s, float64_val(env->vfp.regs[i])); | |
104 | } | |
105 | ||
106 | if (s->dump_info.d_endian == ELFDATA2MSB) { | |
107 | /* For AArch64 we must always swap the vfp.regs's 2n and 2n+1 | |
108 | * entries when generating BE notes, because even big endian | |
109 | * hosts use 2n+1 for the high half. | |
110 | */ | |
111 | for (i = 0; i < 32; ++i) { | |
112 | uint64_t tmp = note.vfp.vregs[2*i]; | |
113 | note.vfp.vregs[2*i] = note.vfp.vregs[2*i+1]; | |
114 | note.vfp.vregs[2*i+1] = tmp; | |
115 | } | |
116 | } | |
117 | ||
118 | note.vfp.fpsr = cpu_to_dump32(s, vfp_get_fpsr(env)); | |
119 | note.vfp.fpcr = cpu_to_dump32(s, vfp_get_fpcr(env)); | |
120 | ||
121 | ret = f(¬e, AARCH64_PRFPREG_NOTE_SIZE, s); | |
122 | if (ret < 0) { | |
123 | return -1; | |
124 | } | |
125 | ||
126 | return 0; | |
127 | } | |
128 | ||
da2b9140 AJ |
129 | int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, |
130 | int cpuid, void *opaque) | |
131 | { | |
132 | struct aarch64_note note; | |
133 | CPUARMState *env = &ARM_CPU(cs)->env; | |
134 | DumpState *s = opaque; | |
135 | uint64_t pstate, sp; | |
136 | int ret, i; | |
137 | ||
138 | aarch64_note_init(¬e, s, "CORE", 5, NT_PRSTATUS, sizeof(note.prstatus)); | |
139 | ||
140 | note.prstatus.pr_pid = cpu_to_dump32(s, cpuid); | |
bada8e44 | 141 | note.prstatus.pr_fpvalid = cpu_to_dump32(s, 1); |
da2b9140 AJ |
142 | |
143 | if (!is_a64(env)) { | |
144 | aarch64_sync_32_to_64(env); | |
145 | pstate = cpsr_read(env); | |
146 | sp = 0; | |
147 | } else { | |
148 | pstate = pstate_read(env); | |
149 | sp = env->xregs[31]; | |
150 | } | |
151 | ||
152 | for (i = 0; i < 31; ++i) { | |
153 | note.prstatus.pr_reg.regs[i] = cpu_to_dump64(s, env->xregs[i]); | |
154 | } | |
155 | note.prstatus.pr_reg.sp = cpu_to_dump64(s, sp); | |
156 | note.prstatus.pr_reg.pc = cpu_to_dump64(s, env->pc); | |
157 | note.prstatus.pr_reg.pstate = cpu_to_dump64(s, pstate); | |
158 | ||
bada8e44 | 159 | ret = f(¬e, AARCH64_PRSTATUS_NOTE_SIZE, s); |
da2b9140 AJ |
160 | if (ret < 0) { |
161 | return -1; | |
162 | } | |
163 | ||
bada8e44 | 164 | return aarch64_write_elf64_prfpreg(f, env, cpuid, s); |
da2b9140 AJ |
165 | } |
166 | ||
167 | /* struct pt_regs from arch/arm/include/asm/ptrace.h */ | |
168 | struct arm_user_regs { | |
169 | uint32_t regs[17]; | |
170 | char pad[4]; | |
171 | } QEMU_PACKED; | |
172 | ||
173 | QEMU_BUILD_BUG_ON(sizeof(struct arm_user_regs) != 72); | |
174 | ||
175 | /* struct elf_prstatus from include/uapi/linux/elfcore.h */ | |
176 | struct arm_elf_prstatus { | |
177 | char pad1[24]; /* 24 == offsetof(struct elf_prstatus, pr_pid) */ | |
178 | uint32_t pr_pid; | |
179 | char pad2[44]; /* 44 == offsetof(struct elf_prstatus, pr_reg) - | |
180 | offsetof(struct elf_prstatus, pr_ppid) */ | |
181 | struct arm_user_regs pr_reg; | |
182 | uint32_t pr_fpvalid; | |
183 | } QEMU_PACKED arm_elf_prstatus; | |
184 | ||
185 | QEMU_BUILD_BUG_ON(sizeof(struct arm_elf_prstatus) != 148); | |
186 | ||
ade0d0c0 AJ |
187 | /* struct user_vfp from arch/arm/include/asm/user.h */ |
188 | struct arm_user_vfp_state { | |
189 | uint64_t vregs[32]; | |
190 | uint32_t fpscr; | |
191 | } QEMU_PACKED; | |
192 | ||
193 | QEMU_BUILD_BUG_ON(sizeof(struct arm_user_vfp_state) != 260); | |
194 | ||
da2b9140 AJ |
195 | struct arm_note { |
196 | Elf32_Nhdr hdr; | |
ade0d0c0 AJ |
197 | char name[8]; /* align_up(sizeof("LINUX"), 4) */ |
198 | union { | |
199 | struct arm_elf_prstatus prstatus; | |
200 | struct arm_user_vfp_state vfp; | |
201 | }; | |
da2b9140 AJ |
202 | } QEMU_PACKED; |
203 | ||
bada8e44 AJ |
204 | #define ARM_NOTE_HEADER_SIZE offsetof(struct arm_note, prstatus) |
205 | #define ARM_PRSTATUS_NOTE_SIZE \ | |
206 | (ARM_NOTE_HEADER_SIZE + sizeof(struct arm_elf_prstatus)) | |
ade0d0c0 AJ |
207 | #define ARM_VFP_NOTE_SIZE \ |
208 | (ARM_NOTE_HEADER_SIZE + sizeof(struct arm_user_vfp_state)) | |
da2b9140 AJ |
209 | |
210 | static void arm_note_init(struct arm_note *note, DumpState *s, | |
211 | const char *name, Elf32_Word namesz, | |
212 | Elf32_Word type, Elf32_Word descsz) | |
213 | { | |
214 | memset(note, 0, sizeof(*note)); | |
215 | ||
216 | note->hdr.n_namesz = cpu_to_dump32(s, namesz); | |
217 | note->hdr.n_descsz = cpu_to_dump32(s, descsz); | |
218 | note->hdr.n_type = cpu_to_dump32(s, type); | |
219 | ||
220 | memcpy(note->name, name, namesz); | |
221 | } | |
222 | ||
ade0d0c0 AJ |
223 | static int arm_write_elf32_vfp(WriteCoreDumpFunction f, CPUARMState *env, |
224 | int cpuid, DumpState *s) | |
225 | { | |
226 | struct arm_note note; | |
227 | int ret, i; | |
228 | ||
229 | arm_note_init(¬e, s, "LINUX", 6, NT_ARM_VFP, sizeof(note.vfp)); | |
230 | ||
231 | for (i = 0; i < 32; ++i) { | |
232 | note.vfp.vregs[i] = cpu_to_dump64(s, float64_val(env->vfp.regs[i])); | |
233 | } | |
234 | ||
235 | note.vfp.fpscr = cpu_to_dump32(s, vfp_get_fpscr(env)); | |
236 | ||
237 | ret = f(¬e, ARM_VFP_NOTE_SIZE, s); | |
238 | if (ret < 0) { | |
239 | return -1; | |
240 | } | |
241 | ||
242 | return 0; | |
243 | } | |
244 | ||
da2b9140 AJ |
245 | int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, |
246 | int cpuid, void *opaque) | |
247 | { | |
248 | struct arm_note note; | |
249 | CPUARMState *env = &ARM_CPU(cs)->env; | |
250 | DumpState *s = opaque; | |
ade0d0c0 | 251 | int ret, i, fpvalid = !!arm_feature(env, ARM_FEATURE_VFP); |
da2b9140 AJ |
252 | |
253 | arm_note_init(¬e, s, "CORE", 5, NT_PRSTATUS, sizeof(note.prstatus)); | |
254 | ||
255 | note.prstatus.pr_pid = cpu_to_dump32(s, cpuid); | |
ade0d0c0 | 256 | note.prstatus.pr_fpvalid = cpu_to_dump32(s, fpvalid); |
da2b9140 AJ |
257 | |
258 | for (i = 0; i < 16; ++i) { | |
259 | note.prstatus.pr_reg.regs[i] = cpu_to_dump32(s, env->regs[i]); | |
260 | } | |
261 | note.prstatus.pr_reg.regs[16] = cpu_to_dump32(s, cpsr_read(env)); | |
262 | ||
bada8e44 | 263 | ret = f(¬e, ARM_PRSTATUS_NOTE_SIZE, s); |
da2b9140 AJ |
264 | if (ret < 0) { |
265 | return -1; | |
ade0d0c0 AJ |
266 | } else if (fpvalid) { |
267 | return arm_write_elf32_vfp(f, env, cpuid, s); | |
da2b9140 AJ |
268 | } |
269 | ||
270 | return 0; | |
271 | } | |
272 | ||
273 | int cpu_get_dump_info(ArchDumpInfo *info, | |
274 | const GuestPhysBlockList *guest_phys_blocks) | |
275 | { | |
6dba6340 LV |
276 | ARMCPU *cpu; |
277 | CPUARMState *env; | |
da2b9140 AJ |
278 | GuestPhysBlock *block; |
279 | hwaddr lowest_addr = ULLONG_MAX; | |
280 | ||
6dba6340 LV |
281 | if (first_cpu == NULL) { |
282 | return -1; | |
283 | } | |
284 | ||
285 | cpu = ARM_CPU(first_cpu); | |
286 | env = &cpu->env; | |
287 | ||
da2b9140 AJ |
288 | /* Take a best guess at the phys_base. If we get it wrong then crash |
289 | * will need '--machdep phys_offset=<phys-offset>' added to its command | |
290 | * line, which isn't any worse than assuming we can use zero, but being | |
291 | * wrong. This is the same algorithm the crash utility uses when | |
292 | * attempting to guess as it loads non-dumpfile formatted files. | |
293 | */ | |
294 | QTAILQ_FOREACH(block, &guest_phys_blocks->head, next) { | |
295 | if (block->target_start < lowest_addr) { | |
296 | lowest_addr = block->target_start; | |
297 | } | |
298 | } | |
299 | ||
300 | if (arm_feature(env, ARM_FEATURE_AARCH64)) { | |
301 | info->d_machine = EM_AARCH64; | |
302 | info->d_class = ELFCLASS64; | |
303 | info->page_size = (1 << 16); /* aarch64 max pagesize */ | |
304 | if (lowest_addr != ULLONG_MAX) { | |
305 | info->phys_base = lowest_addr; | |
306 | } | |
307 | } else { | |
308 | info->d_machine = EM_ARM; | |
309 | info->d_class = ELFCLASS32; | |
310 | info->page_size = (1 << 12); | |
311 | if (lowest_addr < UINT_MAX) { | |
312 | info->phys_base = lowest_addr; | |
313 | } | |
314 | } | |
315 | ||
316 | /* We assume the relevant endianness is that of EL1; this is right | |
317 | * for kernels, but might give the wrong answer if you're trying to | |
318 | * dump a hypervisor that happens to be running an opposite-endian | |
319 | * kernel. | |
320 | */ | |
321 | info->d_endian = (env->cp15.sctlr_el[1] & SCTLR_EE) != 0 | |
322 | ? ELFDATA2MSB : ELFDATA2LSB; | |
323 | ||
324 | return 0; | |
325 | } | |
326 | ||
327 | ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) | |
328 | { | |
ade0d0c0 AJ |
329 | ARMCPU *cpu = ARM_CPU(first_cpu); |
330 | CPUARMState *env = &cpu->env; | |
da2b9140 AJ |
331 | size_t note_size; |
332 | ||
333 | if (class == ELFCLASS64) { | |
bada8e44 AJ |
334 | note_size = AARCH64_PRSTATUS_NOTE_SIZE; |
335 | note_size += AARCH64_PRFPREG_NOTE_SIZE; | |
da2b9140 | 336 | } else { |
bada8e44 | 337 | note_size = ARM_PRSTATUS_NOTE_SIZE; |
ade0d0c0 AJ |
338 | if (arm_feature(env, ARM_FEATURE_VFP)) { |
339 | note_size += ARM_VFP_NOTE_SIZE; | |
340 | } | |
da2b9140 AJ |
341 | } |
342 | ||
343 | return note_size * nr_cpus; | |
344 | } |