]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * writing ELF notes for s390x arch | |
3 | * | |
4 | * | |
5 | * Copyright IBM Corp. 2012, 2013 | |
6 | * | |
7 | * Ekaterina Tumanova <tumanova@linux.vnet.ibm.com> | |
8 | * | |
9 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
10 | * See the COPYING file in the top-level directory. | |
11 | * | |
12 | */ | |
13 | ||
14 | #include "qemu/osdep.h" | |
15 | #include "qemu/units.h" | |
16 | #include "cpu.h" | |
17 | #include "s390x-internal.h" | |
18 | #include "elf.h" | |
19 | #include "sysemu/dump.h" | |
20 | #include "kvm/kvm_s390x.h" | |
21 | #include "target/s390x/kvm/pv.h" | |
22 | ||
23 | struct S390xUserRegsStruct { | |
24 | uint64_t psw[2]; | |
25 | uint64_t gprs[16]; | |
26 | uint32_t acrs[16]; | |
27 | } QEMU_PACKED; | |
28 | ||
29 | typedef struct S390xUserRegsStruct S390xUserRegs; | |
30 | ||
31 | struct S390xElfPrstatusStruct { | |
32 | uint8_t pad1[32]; | |
33 | uint32_t pid; | |
34 | uint8_t pad2[76]; | |
35 | S390xUserRegs regs; | |
36 | uint8_t pad3[16]; | |
37 | } QEMU_PACKED; | |
38 | ||
39 | typedef struct S390xElfPrstatusStruct S390xElfPrstatus; | |
40 | ||
41 | struct S390xElfFpregsetStruct { | |
42 | uint32_t fpc; | |
43 | uint32_t pad; | |
44 | uint64_t fprs[16]; | |
45 | } QEMU_PACKED; | |
46 | ||
47 | typedef struct S390xElfFpregsetStruct S390xElfFpregset; | |
48 | ||
49 | struct S390xElfVregsLoStruct { | |
50 | uint64_t vregs[16]; | |
51 | } QEMU_PACKED; | |
52 | ||
53 | typedef struct S390xElfVregsLoStruct S390xElfVregsLo; | |
54 | ||
55 | struct S390xElfVregsHiStruct { | |
56 | uint64_t vregs[16][2]; | |
57 | } QEMU_PACKED; | |
58 | ||
59 | typedef struct S390xElfVregsHiStruct S390xElfVregsHi; | |
60 | ||
61 | struct S390xElfGSCBStruct { | |
62 | uint64_t gsregs[4]; | |
63 | } QEMU_PACKED; | |
64 | ||
65 | typedef struct S390xElfGSCBStruct S390xElfGSCB; | |
66 | ||
67 | typedef struct noteStruct { | |
68 | Elf64_Nhdr hdr; | |
69 | char name[8]; | |
70 | union { | |
71 | S390xElfPrstatus prstatus; | |
72 | S390xElfFpregset fpregset; | |
73 | S390xElfVregsLo vregslo; | |
74 | S390xElfVregsHi vregshi; | |
75 | S390xElfGSCB gscb; | |
76 | uint32_t prefix; | |
77 | uint64_t timer; | |
78 | uint64_t todcmp; | |
79 | uint32_t todpreg; | |
80 | uint64_t ctrs[16]; | |
81 | uint8_t dynamic[1]; /* | |
82 | * Would be a flexible array member, if | |
83 | * that was legal inside a union. Real | |
84 | * size comes from PV info interface. | |
85 | */ | |
86 | } contents; | |
87 | } QEMU_PACKED Note; | |
88 | ||
89 | static bool pv_dump_initialized; | |
90 | ||
91 | static void s390x_write_elf64_prstatus(Note *note, S390CPU *cpu, int id) | |
92 | { | |
93 | int i; | |
94 | S390xUserRegs *regs; | |
95 | ||
96 | note->hdr.n_type = cpu_to_be32(NT_PRSTATUS); | |
97 | ||
98 | regs = &(note->contents.prstatus.regs); | |
99 | regs->psw[0] = cpu_to_be64(cpu->env.psw.mask); | |
100 | regs->psw[1] = cpu_to_be64(cpu->env.psw.addr); | |
101 | for (i = 0; i <= 15; i++) { | |
102 | regs->acrs[i] = cpu_to_be32(cpu->env.aregs[i]); | |
103 | regs->gprs[i] = cpu_to_be64(cpu->env.regs[i]); | |
104 | } | |
105 | note->contents.prstatus.pid = id; | |
106 | } | |
107 | ||
108 | static void s390x_write_elf64_fpregset(Note *note, S390CPU *cpu, int id) | |
109 | { | |
110 | int i; | |
111 | CPUS390XState *cs = &cpu->env; | |
112 | ||
113 | note->hdr.n_type = cpu_to_be32(NT_FPREGSET); | |
114 | note->contents.fpregset.fpc = cpu_to_be32(cpu->env.fpc); | |
115 | for (i = 0; i <= 15; i++) { | |
116 | note->contents.fpregset.fprs[i] = cpu_to_be64(*get_freg(cs, i)); | |
117 | } | |
118 | } | |
119 | ||
120 | static void s390x_write_elf64_vregslo(Note *note, S390CPU *cpu, int id) | |
121 | { | |
122 | int i; | |
123 | ||
124 | note->hdr.n_type = cpu_to_be32(NT_S390_VXRS_LOW); | |
125 | for (i = 0; i <= 15; i++) { | |
126 | note->contents.vregslo.vregs[i] = cpu_to_be64(cpu->env.vregs[i][1]); | |
127 | } | |
128 | } | |
129 | ||
130 | static void s390x_write_elf64_vregshi(Note *note, S390CPU *cpu, int id) | |
131 | { | |
132 | int i; | |
133 | S390xElfVregsHi *temp_vregshi; | |
134 | ||
135 | temp_vregshi = ¬e->contents.vregshi; | |
136 | ||
137 | note->hdr.n_type = cpu_to_be32(NT_S390_VXRS_HIGH); | |
138 | for (i = 0; i <= 15; i++) { | |
139 | temp_vregshi->vregs[i][0] = cpu_to_be64(cpu->env.vregs[i + 16][0]); | |
140 | temp_vregshi->vregs[i][1] = cpu_to_be64(cpu->env.vregs[i + 16][1]); | |
141 | } | |
142 | } | |
143 | ||
144 | static void s390x_write_elf64_gscb(Note *note, S390CPU *cpu, int id) | |
145 | { | |
146 | int i; | |
147 | ||
148 | note->hdr.n_type = cpu_to_be32(NT_S390_GS_CB); | |
149 | for (i = 0; i < 4; i++) { | |
150 | note->contents.gscb.gsregs[i] = cpu_to_be64(cpu->env.gscb[i]); | |
151 | } | |
152 | } | |
153 | ||
154 | static void s390x_write_elf64_timer(Note *note, S390CPU *cpu, int id) | |
155 | { | |
156 | note->hdr.n_type = cpu_to_be32(NT_S390_TIMER); | |
157 | note->contents.timer = cpu_to_be64((uint64_t)(cpu->env.cputm)); | |
158 | } | |
159 | ||
160 | static void s390x_write_elf64_todcmp(Note *note, S390CPU *cpu, int id) | |
161 | { | |
162 | note->hdr.n_type = cpu_to_be32(NT_S390_TODCMP); | |
163 | note->contents.todcmp = cpu_to_be64((uint64_t)(cpu->env.ckc)); | |
164 | } | |
165 | ||
166 | static void s390x_write_elf64_todpreg(Note *note, S390CPU *cpu, int id) | |
167 | { | |
168 | note->hdr.n_type = cpu_to_be32(NT_S390_TODPREG); | |
169 | note->contents.todpreg = cpu_to_be32((uint32_t)(cpu->env.todpr)); | |
170 | } | |
171 | ||
172 | static void s390x_write_elf64_ctrs(Note *note, S390CPU *cpu, int id) | |
173 | { | |
174 | int i; | |
175 | ||
176 | note->hdr.n_type = cpu_to_be32(NT_S390_CTRS); | |
177 | ||
178 | for (i = 0; i <= 15; i++) { | |
179 | note->contents.ctrs[i] = cpu_to_be64(cpu->env.cregs[i]); | |
180 | } | |
181 | } | |
182 | ||
183 | static void s390x_write_elf64_prefix(Note *note, S390CPU *cpu, int id) | |
184 | { | |
185 | note->hdr.n_type = cpu_to_be32(NT_S390_PREFIX); | |
186 | note->contents.prefix = cpu_to_be32((uint32_t)(cpu->env.psa)); | |
187 | } | |
188 | ||
189 | static void s390x_write_elf64_pv(Note *note, S390CPU *cpu, int id) | |
190 | { | |
191 | note->hdr.n_type = cpu_to_be32(NT_S390_PV_CPU_DATA); | |
192 | if (!pv_dump_initialized) { | |
193 | return; | |
194 | } | |
195 | kvm_s390_dump_cpu(cpu, ¬e->contents.dynamic); | |
196 | } | |
197 | ||
198 | typedef struct NoteFuncDescStruct { | |
199 | int contents_size; | |
200 | uint64_t (*note_size_func)(void); /* NULL for non-dynamic sized contents */ | |
201 | void (*note_contents_func)(Note *note, S390CPU *cpu, int id); | |
202 | bool pvonly; | |
203 | } NoteFuncDesc; | |
204 | ||
205 | static const NoteFuncDesc note_core[] = { | |
206 | {sizeof_field(Note, contents.prstatus), NULL, s390x_write_elf64_prstatus, false}, | |
207 | {sizeof_field(Note, contents.fpregset), NULL, s390x_write_elf64_fpregset, false}, | |
208 | { 0, NULL, NULL, false} | |
209 | }; | |
210 | ||
211 | static const NoteFuncDesc note_linux[] = { | |
212 | {sizeof_field(Note, contents.prefix), NULL, s390x_write_elf64_prefix, false}, | |
213 | {sizeof_field(Note, contents.ctrs), NULL, s390x_write_elf64_ctrs, false}, | |
214 | {sizeof_field(Note, contents.timer), NULL, s390x_write_elf64_timer, false}, | |
215 | {sizeof_field(Note, contents.todcmp), NULL, s390x_write_elf64_todcmp, false}, | |
216 | {sizeof_field(Note, contents.todpreg), NULL, s390x_write_elf64_todpreg, false}, | |
217 | {sizeof_field(Note, contents.vregslo), NULL, s390x_write_elf64_vregslo, false}, | |
218 | {sizeof_field(Note, contents.vregshi), NULL, s390x_write_elf64_vregshi, false}, | |
219 | {sizeof_field(Note, contents.gscb), NULL, s390x_write_elf64_gscb, false}, | |
220 | {0, kvm_s390_pv_dmp_get_size_cpu, s390x_write_elf64_pv, true}, | |
221 | { 0, NULL, NULL, false} | |
222 | }; | |
223 | ||
224 | static int s390x_write_elf64_notes(const char *note_name, | |
225 | WriteCoreDumpFunction f, | |
226 | S390CPU *cpu, int id, | |
227 | DumpState *s, | |
228 | const NoteFuncDesc *funcs) | |
229 | { | |
230 | g_autofree Note *notep = NULL; | |
231 | const NoteFuncDesc *nf; | |
232 | int note_size, prev_size = 0, content_size; | |
233 | int ret = -1; | |
234 | ||
235 | assert(strlen(note_name) < sizeof(notep->name)); | |
236 | ||
237 | for (nf = funcs; nf->note_contents_func; nf++) { | |
238 | if (nf->pvonly && !s390_is_pv()) { | |
239 | continue; | |
240 | } | |
241 | ||
242 | content_size = nf->note_size_func ? nf->note_size_func() : nf->contents_size; | |
243 | note_size = sizeof(Note) - sizeof(notep->contents) + content_size; | |
244 | ||
245 | if (prev_size < note_size) { | |
246 | g_free(notep); | |
247 | notep = g_malloc(note_size); | |
248 | prev_size = note_size; | |
249 | } | |
250 | ||
251 | memset(notep, 0, note_size); | |
252 | ||
253 | /* Setup note header data */ | |
254 | notep->hdr.n_descsz = cpu_to_be32(content_size); | |
255 | notep->hdr.n_namesz = cpu_to_be32(strlen(note_name) + 1); | |
256 | g_strlcpy(notep->name, note_name, sizeof(notep->name)); | |
257 | ||
258 | /* Get contents and write them out */ | |
259 | (*nf->note_contents_func)(notep, cpu, id); | |
260 | ret = f(notep, note_size, s); | |
261 | if (ret < 0) { | |
262 | return -1; | |
263 | } | |
264 | } | |
265 | ||
266 | return 0; | |
267 | } | |
268 | ||
269 | ||
270 | int s390_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, | |
271 | int cpuid, DumpState *s) | |
272 | { | |
273 | S390CPU *cpu = S390_CPU(cs); | |
274 | int r; | |
275 | ||
276 | r = s390x_write_elf64_notes("CORE", f, cpu, cpuid, s, note_core); | |
277 | if (r) { | |
278 | return r; | |
279 | } | |
280 | return s390x_write_elf64_notes("LINUX", f, cpu, cpuid, s, note_linux); | |
281 | } | |
282 | ||
283 | /* PV dump section size functions */ | |
284 | static uint64_t get_mem_state_size_from_len(uint64_t len) | |
285 | { | |
286 | return (len / (MiB)) * kvm_s390_pv_dmp_get_size_mem_state(); | |
287 | } | |
288 | ||
289 | static uint64_t get_size_mem_state(DumpState *s) | |
290 | { | |
291 | return get_mem_state_size_from_len(s->total_size); | |
292 | } | |
293 | ||
294 | static uint64_t get_size_completion_data(DumpState *s) | |
295 | { | |
296 | return kvm_s390_pv_dmp_get_size_completion_data(); | |
297 | } | |
298 | ||
299 | /* PV dump section data functions*/ | |
300 | static int get_data_completion(DumpState *s, uint8_t *buff) | |
301 | { | |
302 | int rc; | |
303 | ||
304 | if (!pv_dump_initialized) { | |
305 | return 0; | |
306 | } | |
307 | rc = kvm_s390_dump_completion_data(buff); | |
308 | if (!rc) { | |
309 | pv_dump_initialized = false; | |
310 | } | |
311 | return rc; | |
312 | } | |
313 | ||
314 | static int get_mem_state(DumpState *s, uint8_t *buff) | |
315 | { | |
316 | int64_t memblock_size, memblock_start; | |
317 | GuestPhysBlock *block; | |
318 | uint64_t off; | |
319 | int rc; | |
320 | ||
321 | QTAILQ_FOREACH(block, &s->guest_phys_blocks.head, next) { | |
322 | memblock_start = dump_filtered_memblock_start(block, s->filter_area_begin, | |
323 | s->filter_area_length); | |
324 | if (memblock_start == -1) { | |
325 | continue; | |
326 | } | |
327 | ||
328 | memblock_size = dump_filtered_memblock_size(block, s->filter_area_begin, | |
329 | s->filter_area_length); | |
330 | ||
331 | off = get_mem_state_size_from_len(block->target_start); | |
332 | ||
333 | rc = kvm_s390_dump_mem_state(block->target_start, | |
334 | get_mem_state_size_from_len(memblock_size), | |
335 | buff + off); | |
336 | if (rc) { | |
337 | return rc; | |
338 | } | |
339 | } | |
340 | ||
341 | return 0; | |
342 | } | |
343 | ||
344 | static struct sections { | |
345 | uint64_t (*sections_size_func)(DumpState *s); | |
346 | int (*sections_contents_func)(DumpState *s, uint8_t *buff); | |
347 | char sctn_str[12]; | |
348 | } sections[] = { | |
349 | { get_size_mem_state, get_mem_state, "pv_mem_meta"}, | |
350 | { get_size_completion_data, get_data_completion, "pv_compl"}, | |
351 | {NULL , NULL, ""} | |
352 | }; | |
353 | ||
354 | static uint64_t arch_sections_write_hdr(DumpState *s, uint8_t *buff) | |
355 | { | |
356 | Elf64_Shdr *shdr = (void *)buff; | |
357 | struct sections *sctn = sections; | |
358 | uint64_t off = s->section_offset; | |
359 | ||
360 | if (!pv_dump_initialized) { | |
361 | return 0; | |
362 | } | |
363 | ||
364 | for (; sctn->sections_size_func; off += shdr->sh_size, sctn++, shdr++) { | |
365 | memset(shdr, 0, sizeof(*shdr)); | |
366 | shdr->sh_type = SHT_PROGBITS; | |
367 | shdr->sh_offset = off; | |
368 | shdr->sh_size = sctn->sections_size_func(s); | |
369 | shdr->sh_name = s->string_table_buf->len; | |
370 | g_array_append_vals(s->string_table_buf, sctn->sctn_str, sizeof(sctn->sctn_str)); | |
371 | } | |
372 | ||
373 | return (uintptr_t)shdr - (uintptr_t)buff; | |
374 | } | |
375 | ||
376 | ||
377 | /* Add arch specific number of sections and their respective sizes */ | |
378 | static void arch_sections_add(DumpState *s) | |
379 | { | |
380 | struct sections *sctn = sections; | |
381 | ||
382 | /* | |
383 | * We only do a PV dump if we are running a PV guest, KVM supports | |
384 | * the dump API and we got valid dump length information. | |
385 | */ | |
386 | if (!s390_is_pv() || !kvm_s390_get_protected_dump() || | |
387 | !kvm_s390_pv_info_basic_valid()) { | |
388 | return; | |
389 | } | |
390 | ||
391 | /* | |
392 | * Start the UV dump process by doing the initialize dump call via | |
393 | * KVM as the proxy. | |
394 | */ | |
395 | if (!kvm_s390_dump_init()) { | |
396 | pv_dump_initialized = true; | |
397 | } else { | |
398 | /* | |
399 | * Dump init failed, maybe the guest owner disabled dumping. | |
400 | * We'll continue the non-PV dump process since this is no | |
401 | * reason to crash qemu. | |
402 | */ | |
403 | return; | |
404 | } | |
405 | ||
406 | for (; sctn->sections_size_func; sctn++) { | |
407 | s->shdr_num += 1; | |
408 | s->elf_section_data_size += sctn->sections_size_func(s); | |
409 | } | |
410 | } | |
411 | ||
412 | /* | |
413 | * After the PV dump has been initialized, the CPU data has been | |
414 | * fetched and memory has been dumped, we need to grab the tweak data | |
415 | * and the completion data. | |
416 | */ | |
417 | static int arch_sections_write(DumpState *s, uint8_t *buff) | |
418 | { | |
419 | struct sections *sctn = sections; | |
420 | int rc; | |
421 | ||
422 | if (!pv_dump_initialized) { | |
423 | return -EINVAL; | |
424 | } | |
425 | ||
426 | for (; sctn->sections_size_func; sctn++) { | |
427 | rc = sctn->sections_contents_func(s, buff); | |
428 | buff += sctn->sections_size_func(s); | |
429 | if (rc) { | |
430 | return rc; | |
431 | } | |
432 | } | |
433 | return 0; | |
434 | } | |
435 | ||
436 | int cpu_get_dump_info(ArchDumpInfo *info, | |
437 | const struct GuestPhysBlockList *guest_phys_blocks) | |
438 | { | |
439 | info->d_machine = EM_S390; | |
440 | info->d_endian = ELFDATA2MSB; | |
441 | info->d_class = ELFCLASS64; | |
442 | /* | |
443 | * This is evaluated for each dump so we can freely switch | |
444 | * between PV and non-PV. | |
445 | */ | |
446 | if (s390_is_pv() && kvm_s390_get_protected_dump() && | |
447 | kvm_s390_pv_info_basic_valid()) { | |
448 | info->arch_sections_add_fn = *arch_sections_add; | |
449 | info->arch_sections_write_hdr_fn = *arch_sections_write_hdr; | |
450 | info->arch_sections_write_fn = *arch_sections_write; | |
451 | } else { | |
452 | info->arch_sections_add_fn = NULL; | |
453 | info->arch_sections_write_hdr_fn = NULL; | |
454 | info->arch_sections_write_fn = NULL; | |
455 | } | |
456 | return 0; | |
457 | } | |
458 | ||
459 | ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) | |
460 | { | |
461 | int name_size = 8; /* "LINUX" or "CORE" + pad */ | |
462 | size_t elf_note_size = 0; | |
463 | int note_head_size, content_size; | |
464 | const NoteFuncDesc *nf; | |
465 | ||
466 | assert(class == ELFCLASS64); | |
467 | assert(machine == EM_S390); | |
468 | ||
469 | note_head_size = sizeof(Elf64_Nhdr); | |
470 | ||
471 | for (nf = note_core; nf->note_contents_func; nf++) { | |
472 | elf_note_size = elf_note_size + note_head_size + name_size + nf->contents_size; | |
473 | } | |
474 | for (nf = note_linux; nf->note_contents_func; nf++) { | |
475 | if (nf->pvonly && !s390_is_pv()) { | |
476 | continue; | |
477 | } | |
478 | content_size = nf->contents_size ? nf->contents_size : nf->note_size_func(); | |
479 | elf_note_size = elf_note_size + note_head_size + name_size + | |
480 | content_size; | |
481 | } | |
482 | ||
483 | return (elf_note_size) * nr_cpus; | |
484 | } |