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 } 485