]>
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 | ||
9a2b5256 RH |
102 | for (i = 0; i < 32; ++i) { |
103 | uint64_t *q = aa64_vfp_qreg(env, i); | |
104 | note.vfp.vregs[2*i + 0] = cpu_to_dump64(s, q[0]); | |
105 | note.vfp.vregs[2*i + 1] = cpu_to_dump64(s, q[1]); | |
bada8e44 AJ |
106 | } |
107 | ||
108 | if (s->dump_info.d_endian == ELFDATA2MSB) { | |
109 | /* For AArch64 we must always swap the vfp.regs's 2n and 2n+1 | |
110 | * entries when generating BE notes, because even big endian | |
111 | * hosts use 2n+1 for the high half. | |
112 | */ | |
113 | for (i = 0; i < 32; ++i) { | |
114 | uint64_t tmp = note.vfp.vregs[2*i]; | |
115 | note.vfp.vregs[2*i] = note.vfp.vregs[2*i+1]; | |
116 | note.vfp.vregs[2*i+1] = tmp; | |
117 | } | |
118 | } | |
119 | ||
120 | note.vfp.fpsr = cpu_to_dump32(s, vfp_get_fpsr(env)); | |
121 | note.vfp.fpcr = cpu_to_dump32(s, vfp_get_fpcr(env)); | |
122 | ||
123 | ret = f(¬e, AARCH64_PRFPREG_NOTE_SIZE, s); | |
124 | if (ret < 0) { | |
125 | return -1; | |
126 | } | |
127 | ||
128 | return 0; | |
129 | } | |
130 | ||
da2b9140 AJ |
131 | int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, |
132 | int cpuid, void *opaque) | |
133 | { | |
134 | struct aarch64_note note; | |
135 | CPUARMState *env = &ARM_CPU(cs)->env; | |
136 | DumpState *s = opaque; | |
137 | uint64_t pstate, sp; | |
138 | int ret, i; | |
139 | ||
140 | aarch64_note_init(¬e, s, "CORE", 5, NT_PRSTATUS, sizeof(note.prstatus)); | |
141 | ||
142 | note.prstatus.pr_pid = cpu_to_dump32(s, cpuid); | |
bada8e44 | 143 | note.prstatus.pr_fpvalid = cpu_to_dump32(s, 1); |
da2b9140 AJ |
144 | |
145 | if (!is_a64(env)) { | |
146 | aarch64_sync_32_to_64(env); | |
147 | pstate = cpsr_read(env); | |
148 | sp = 0; | |
149 | } else { | |
150 | pstate = pstate_read(env); | |
151 | sp = env->xregs[31]; | |
152 | } | |
153 | ||
154 | for (i = 0; i < 31; ++i) { | |
155 | note.prstatus.pr_reg.regs[i] = cpu_to_dump64(s, env->xregs[i]); | |
156 | } | |
157 | note.prstatus.pr_reg.sp = cpu_to_dump64(s, sp); | |
158 | note.prstatus.pr_reg.pc = cpu_to_dump64(s, env->pc); | |
159 | note.prstatus.pr_reg.pstate = cpu_to_dump64(s, pstate); | |
160 | ||
bada8e44 | 161 | ret = f(¬e, AARCH64_PRSTATUS_NOTE_SIZE, s); |
da2b9140 AJ |
162 | if (ret < 0) { |
163 | return -1; | |
164 | } | |
165 | ||
bada8e44 | 166 | return aarch64_write_elf64_prfpreg(f, env, cpuid, s); |
da2b9140 AJ |
167 | } |
168 | ||
169 | /* struct pt_regs from arch/arm/include/asm/ptrace.h */ | |
170 | struct arm_user_regs { | |
171 | uint32_t regs[17]; | |
172 | char pad[4]; | |
173 | } QEMU_PACKED; | |
174 | ||
175 | QEMU_BUILD_BUG_ON(sizeof(struct arm_user_regs) != 72); | |
176 | ||
177 | /* struct elf_prstatus from include/uapi/linux/elfcore.h */ | |
178 | struct arm_elf_prstatus { | |
179 | char pad1[24]; /* 24 == offsetof(struct elf_prstatus, pr_pid) */ | |
180 | uint32_t pr_pid; | |
181 | char pad2[44]; /* 44 == offsetof(struct elf_prstatus, pr_reg) - | |
182 | offsetof(struct elf_prstatus, pr_ppid) */ | |
183 | struct arm_user_regs pr_reg; | |
184 | uint32_t pr_fpvalid; | |
185 | } QEMU_PACKED arm_elf_prstatus; | |
186 | ||
187 | QEMU_BUILD_BUG_ON(sizeof(struct arm_elf_prstatus) != 148); | |
188 | ||
ade0d0c0 AJ |
189 | /* struct user_vfp from arch/arm/include/asm/user.h */ |
190 | struct arm_user_vfp_state { | |
191 | uint64_t vregs[32]; | |
192 | uint32_t fpscr; | |
193 | } QEMU_PACKED; | |
194 | ||
195 | QEMU_BUILD_BUG_ON(sizeof(struct arm_user_vfp_state) != 260); | |
196 | ||
da2b9140 AJ |
197 | struct arm_note { |
198 | Elf32_Nhdr hdr; | |
ade0d0c0 AJ |
199 | char name[8]; /* align_up(sizeof("LINUX"), 4) */ |
200 | union { | |
201 | struct arm_elf_prstatus prstatus; | |
202 | struct arm_user_vfp_state vfp; | |
203 | }; | |
da2b9140 AJ |
204 | } QEMU_PACKED; |
205 | ||
bada8e44 AJ |
206 | #define ARM_NOTE_HEADER_SIZE offsetof(struct arm_note, prstatus) |
207 | #define ARM_PRSTATUS_NOTE_SIZE \ | |
208 | (ARM_NOTE_HEADER_SIZE + sizeof(struct arm_elf_prstatus)) | |
ade0d0c0 AJ |
209 | #define ARM_VFP_NOTE_SIZE \ |
210 | (ARM_NOTE_HEADER_SIZE + sizeof(struct arm_user_vfp_state)) | |
da2b9140 AJ |
211 | |
212 | static void arm_note_init(struct arm_note *note, DumpState *s, | |
213 | const char *name, Elf32_Word namesz, | |
214 | Elf32_Word type, Elf32_Word descsz) | |
215 | { | |
216 | memset(note, 0, sizeof(*note)); | |
217 | ||
218 | note->hdr.n_namesz = cpu_to_dump32(s, namesz); | |
219 | note->hdr.n_descsz = cpu_to_dump32(s, descsz); | |
220 | note->hdr.n_type = cpu_to_dump32(s, type); | |
221 | ||
222 | memcpy(note->name, name, namesz); | |
223 | } | |
224 | ||
ade0d0c0 AJ |
225 | static int arm_write_elf32_vfp(WriteCoreDumpFunction f, CPUARMState *env, |
226 | int cpuid, DumpState *s) | |
227 | { | |
228 | struct arm_note note; | |
229 | int ret, i; | |
230 | ||
231 | arm_note_init(¬e, s, "LINUX", 6, NT_ARM_VFP, sizeof(note.vfp)); | |
232 | ||
233 | for (i = 0; i < 32; ++i) { | |
9a2b5256 | 234 | note.vfp.vregs[i] = cpu_to_dump64(s, *aa32_vfp_dreg(env, i)); |
ade0d0c0 AJ |
235 | } |
236 | ||
237 | note.vfp.fpscr = cpu_to_dump32(s, vfp_get_fpscr(env)); | |
238 | ||
239 | ret = f(¬e, ARM_VFP_NOTE_SIZE, s); | |
240 | if (ret < 0) { | |
241 | return -1; | |
242 | } | |
243 | ||
244 | return 0; | |
245 | } | |
246 | ||
da2b9140 AJ |
247 | int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, |
248 | int cpuid, void *opaque) | |
249 | { | |
250 | struct arm_note note; | |
251 | CPUARMState *env = &ARM_CPU(cs)->env; | |
252 | DumpState *s = opaque; | |
ade0d0c0 | 253 | int ret, i, fpvalid = !!arm_feature(env, ARM_FEATURE_VFP); |
da2b9140 AJ |
254 | |
255 | arm_note_init(¬e, s, "CORE", 5, NT_PRSTATUS, sizeof(note.prstatus)); | |
256 | ||
257 | note.prstatus.pr_pid = cpu_to_dump32(s, cpuid); | |
ade0d0c0 | 258 | note.prstatus.pr_fpvalid = cpu_to_dump32(s, fpvalid); |
da2b9140 AJ |
259 | |
260 | for (i = 0; i < 16; ++i) { | |
261 | note.prstatus.pr_reg.regs[i] = cpu_to_dump32(s, env->regs[i]); | |
262 | } | |
263 | note.prstatus.pr_reg.regs[16] = cpu_to_dump32(s, cpsr_read(env)); | |
264 | ||
bada8e44 | 265 | ret = f(¬e, ARM_PRSTATUS_NOTE_SIZE, s); |
da2b9140 AJ |
266 | if (ret < 0) { |
267 | return -1; | |
ade0d0c0 AJ |
268 | } else if (fpvalid) { |
269 | return arm_write_elf32_vfp(f, env, cpuid, s); | |
da2b9140 AJ |
270 | } |
271 | ||
272 | return 0; | |
273 | } | |
274 | ||
275 | int cpu_get_dump_info(ArchDumpInfo *info, | |
276 | const GuestPhysBlockList *guest_phys_blocks) | |
277 | { | |
6dba6340 LV |
278 | ARMCPU *cpu; |
279 | CPUARMState *env; | |
da2b9140 AJ |
280 | GuestPhysBlock *block; |
281 | hwaddr lowest_addr = ULLONG_MAX; | |
282 | ||
6dba6340 LV |
283 | if (first_cpu == NULL) { |
284 | return -1; | |
285 | } | |
286 | ||
287 | cpu = ARM_CPU(first_cpu); | |
288 | env = &cpu->env; | |
289 | ||
da2b9140 AJ |
290 | /* Take a best guess at the phys_base. If we get it wrong then crash |
291 | * will need '--machdep phys_offset=<phys-offset>' added to its command | |
292 | * line, which isn't any worse than assuming we can use zero, but being | |
293 | * wrong. This is the same algorithm the crash utility uses when | |
294 | * attempting to guess as it loads non-dumpfile formatted files. | |
295 | */ | |
296 | QTAILQ_FOREACH(block, &guest_phys_blocks->head, next) { | |
297 | if (block->target_start < lowest_addr) { | |
298 | lowest_addr = block->target_start; | |
299 | } | |
300 | } | |
301 | ||
302 | if (arm_feature(env, ARM_FEATURE_AARCH64)) { | |
303 | info->d_machine = EM_AARCH64; | |
304 | info->d_class = ELFCLASS64; | |
305 | info->page_size = (1 << 16); /* aarch64 max pagesize */ | |
306 | if (lowest_addr != ULLONG_MAX) { | |
307 | info->phys_base = lowest_addr; | |
308 | } | |
309 | } else { | |
310 | info->d_machine = EM_ARM; | |
311 | info->d_class = ELFCLASS32; | |
312 | info->page_size = (1 << 12); | |
313 | if (lowest_addr < UINT_MAX) { | |
314 | info->phys_base = lowest_addr; | |
315 | } | |
316 | } | |
317 | ||
318 | /* We assume the relevant endianness is that of EL1; this is right | |
319 | * for kernels, but might give the wrong answer if you're trying to | |
320 | * dump a hypervisor that happens to be running an opposite-endian | |
321 | * kernel. | |
322 | */ | |
323 | info->d_endian = (env->cp15.sctlr_el[1] & SCTLR_EE) != 0 | |
324 | ? ELFDATA2MSB : ELFDATA2LSB; | |
325 | ||
326 | return 0; | |
327 | } | |
328 | ||
329 | ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) | |
330 | { | |
ade0d0c0 AJ |
331 | ARMCPU *cpu = ARM_CPU(first_cpu); |
332 | CPUARMState *env = &cpu->env; | |
da2b9140 AJ |
333 | size_t note_size; |
334 | ||
335 | if (class == ELFCLASS64) { | |
bada8e44 AJ |
336 | note_size = AARCH64_PRSTATUS_NOTE_SIZE; |
337 | note_size += AARCH64_PRFPREG_NOTE_SIZE; | |
da2b9140 | 338 | } else { |
bada8e44 | 339 | note_size = ARM_PRSTATUS_NOTE_SIZE; |
ade0d0c0 AJ |
340 | if (arm_feature(env, ARM_FEATURE_VFP)) { |
341 | note_size += ARM_VFP_NOTE_SIZE; | |
342 | } | |
da2b9140 AJ |
343 | } |
344 | ||
345 | return note_size * nr_cpus; | |
346 | } |