/* * zgetdump - Tool for copying and converting System z dumps * * ELF core dump input format * * Copyright IBM Corp. 2001, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include "zgetdump.h" /* * Read memory for given memory chunk */ static void dfi_elf_mem_chunk_read_fn(struct dfi_mem_chunk *mem_chunk, u64 off, void *buf, u64 cnt) { u64 elf_load_off = *((u64 *) mem_chunk->data); zg_seek(g.fh, elf_load_off + off, ZG_CHECK); zg_read(g.fh, buf, cnt, ZG_CHECK); } /* * Add load (memory chunk) to DFI dump */ static int pt_load_add(Elf64_Phdr *phdr) { u64 *off_ptr; if (phdr->p_paddr != phdr->p_vaddr) { phdr->p_paddr = phdr->p_vaddr; STDERR("Dump file \"%s\" is a user space core dump\n", g.opts.device); } if (phdr->p_filesz == 0) /* Skip null pt loads */ return 0; off_ptr = zg_alloc(sizeof(*off_ptr)); *off_ptr = phdr->p_offset; dfi_mem_chunk_add(phdr->p_paddr, phdr->p_filesz, off_ptr, dfi_elf_mem_chunk_read_fn, zg_free); if (phdr->p_offset + phdr->p_filesz > zg_size(g.fh)) return -EINVAL; return 0; } /* * Skip name of note */ static void nt_name_skip(Elf64_Nhdr *note) { zg_seek_cur(g.fh, ROUNDUP(note->n_namesz, 4), ZG_CHECK); } /* * Read note */ static int nt_read(Elf64_Nhdr *note, void *buf) { off_t buf_len = ROUNDUP(note->n_descsz, 4); char tmp_buf[buf_len]; nt_name_skip(note); if (zg_read(g.fh, tmp_buf, buf_len, ZG_CHECK_ERR) != buf_len) return -EINVAL; if (buf) memcpy(buf, tmp_buf, note->n_descsz); return 0; } /* * Skip note */ static int nt_skip(Elf64_Nhdr *note) { return nt_read(note, NULL); } /* * Ensure that CPU is already defined by prstatus note */ static void check_cpu(struct dfi_cpu *cpu, const char *note_str) { if (cpu) return; ERR_EXIT("Invalid ELF dump (%s before prstatus found)", note_str); } /* * Read prstatus note and return new DFI CPU */ static struct dfi_cpu *nt_prstatus_read(Elf64_Nhdr *note) { struct dfi_cpu *cpu = dfi_cpu_alloc(); struct nt_prstatus_64 nt_prstatus; if (nt_read(note, &nt_prstatus)) return NULL; memcpy(cpu->gprs, &nt_prstatus.gprs, sizeof(cpu->gprs)); memcpy(cpu->psw, &nt_prstatus.psw, sizeof(cpu->psw)); memcpy(cpu->acrs, &nt_prstatus.acrs, sizeof(cpu->acrs)); dfi_cpu_add(cpu); return cpu; } /* * Read fpregset note */ static int nt_fpregset_read(struct dfi_cpu *cpu, Elf64_Nhdr *note) { struct nt_fpregset_64 nt_fpregset; check_cpu(cpu, "FPREGSET"); if (nt_read(note, &nt_fpregset)) return -EINVAL; memcpy(&cpu->fpc, &nt_fpregset.fpc, sizeof(cpu->fpc)); memcpy(cpu->fprs, &nt_fpregset.fprs, sizeof(cpu->fprs)); return 0; } /* * Read s390 timer note */ static int nt_s390_timer_read(struct dfi_cpu *cpu, Elf64_Nhdr *note) { check_cpu(cpu, "S390_TIMER"); return nt_read(note, &cpu->timer); } /* * Read s390 todcmp note */ static int nt_s390_todcmp_read(struct dfi_cpu *cpu, Elf64_Nhdr *note) { check_cpu(cpu, "S390_TODCMP"); return nt_read(note, &cpu->todcmp); } /* * Read s390 todpreg note */ static int nt_s390_todpreg_read(struct dfi_cpu *cpu, Elf64_Nhdr *note) { check_cpu(cpu, "S390_TODPREG"); return nt_read(note, &cpu->todpreg); } /* * Read s390 ctrs note */ static int nt_s390_ctrs_read(struct dfi_cpu *cpu, Elf64_Nhdr *note) { check_cpu(cpu, "S390_CTRS"); return nt_read(note, &cpu->ctrs); } /* * Read s390 prefix note */ static int nt_s390_prefix_read(struct dfi_cpu *cpu, Elf64_Nhdr *note) { check_cpu(cpu, "S390_PREFIX"); return nt_read(note, &cpu->prefix); } /* * Read s390 vxrs_low note */ static int nt_s390_vxrs_low_read(struct dfi_cpu *cpu, Elf64_Nhdr *note) { check_cpu(cpu, "S390_VXRS_LOW"); return nt_read(note, &cpu->vxrs_low); } /* * Read s390 vxrs_high note */ static int nt_s390_vxrs_high_read(struct dfi_cpu *cpu, Elf64_Nhdr *note) { check_cpu(cpu, "S390_VXRS_HIGH"); return nt_read(note, &cpu->vxrs_high); } /* * Add all notes for notes phdr */ static int pt_notes_add(Elf64_Phdr *phdr) { u64 start_off = zg_tell(g.fh, ZG_CHECK); struct dfi_cpu *cpu_current = NULL; u64 notes_start_off; Elf64_Nhdr note; int rc; zg_seek(g.fh, phdr->p_offset, ZG_CHECK); notes_start_off = zg_tell(g.fh, ZG_CHECK); while (zg_tell(g.fh, ZG_CHECK) - notes_start_off < phdr->p_filesz) { rc = zg_read(g.fh, ¬e, sizeof(note), ZG_CHECK_ERR); if (rc != sizeof(note)) return -EINVAL; switch (note.n_type) { case NT_PRSTATUS: cpu_current = nt_prstatus_read(¬e); if (!cpu_current) return -EINVAL; break; case NT_FPREGSET: if (nt_fpregset_read(cpu_current, ¬e)) return -EINVAL; break; case NT_S390_TIMER: if (nt_s390_timer_read(cpu_current, ¬e)) return -EINVAL; break; case NT_S390_TODCMP: if (nt_s390_todcmp_read(cpu_current, ¬e)) return -EINVAL; break; case NT_S390_TODPREG: if (nt_s390_todpreg_read(cpu_current, ¬e)) return -EINVAL; break; case NT_S390_CTRS: if (nt_s390_ctrs_read(cpu_current, ¬e)) return -EINVAL; break; case NT_S390_PREFIX: if (nt_s390_prefix_read(cpu_current, ¬e)) return -EINVAL; break; case NT_S390_VXRS_LOW: if (nt_s390_vxrs_low_read(cpu_current, ¬e)) return -EINVAL; dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_VX); break; case NT_S390_VXRS_HIGH: if (nt_s390_vxrs_high_read(cpu_current, ¬e)) return -EINVAL; dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_VX); break; default: if (nt_skip(¬e)) return -EINVAL; break; } } zg_seek(g.fh, start_off, ZG_CHECK); return 0; } /* * Read ELF header */ static int read_elf_hdr(Elf64_Ehdr *ehdr) { if (zg_size(g.fh) < sizeof(*ehdr)) return -ENODEV; zg_read(g.fh, ehdr, sizeof(*ehdr), ZG_CHECK); if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) return -ENODEV; if (ehdr->e_type != ET_CORE) return -ENODEV; if (ehdr->e_machine != EM_S390 || ehdr->e_ident[EI_CLASS] != ELFCLASS64) ERR_EXIT("Only s390x (64 bit) core dump files are supported"); return 0; } /* * Initialize ELF input dump format */ static int dfi_elf_init(void) { Elf64_Ehdr ehdr; Elf64_Phdr phdr; int i; if (read_elf_hdr(&ehdr) != 0) return -ENODEV; df_elf_ensure_s390x(); dfi_arch_set(DFI_ARCH_64); dfi_cpu_info_init(DFI_CPU_CONTENT_ALL); for (i = 0; i < ehdr.e_phnum; i++) { zg_read(g.fh, &phdr, sizeof(phdr), ZG_CHECK); switch (phdr.p_type) { case PT_LOAD: if (pt_load_add(&phdr)) return -EINVAL; break; case PT_NOTE: if (pt_notes_add(&phdr)) return -EINVAL; break; default: break; } } dfi_attr_version_set(ehdr.e_ident[EI_VERSION]); return 0; } /* * ELF DFI operations */ struct dfi dfi_elf = { .name = "elf", .init = dfi_elf_init, .feat_bits = DFI_FEAT_COPY | DFI_FEAT_SEEK, };