| /* |
| * Copyright (c) 2018 MediaTek Inc. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining |
| * a copy of this software and associated documentation files |
| * (the "Software"), to deal in the Software without restriction, |
| * including without limitation the rights to use, copy, modify, merge, |
| * publish, distribute, sublicense, and/or sell copies of the Software, |
| * and to permit persons to whom the Software is furnished to do so, |
| * subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
| * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #include <kernel/vm.h> |
| #include <malloc.h> |
| #include <printf.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include "kdump.h" |
| #include "mrdump_elf64.h" |
| |
| struct memelfnote { |
| const char *name; |
| int type; |
| unsigned int datasz; |
| void *data; |
| }; |
| |
| static int notesize(struct memelfnote *en) |
| { |
| int sz; |
| |
| sz = sizeof(struct elf64_note); |
| sz += ROUNDUP((strlen(en->name) + 1), 4); |
| sz += ROUNDUP(en->datasz, 4); |
| |
| return sz; |
| } |
| |
| static uint8_t *storenote(struct memelfnote *men, uint8_t *bufp) |
| { |
| struct elf64_note en; |
| en.n_namesz = strlen(men->name) + 1; |
| en.n_descsz = men->datasz; |
| en.n_type = men->type; |
| |
| memcpy(bufp, &en, sizeof(en)); |
| bufp += sizeof(en); |
| |
| memcpy(bufp, men->name, en.n_namesz); |
| bufp += en.n_namesz; |
| bufp = (uint8_t *) ROUNDUP((uint64_t)bufp, 4); |
| memcpy(bufp, men->data, men->datasz); |
| bufp += men->datasz; |
| |
| bufp = (uint8_t *) ROUNDUP((uint64_t)bufp, 4); |
| return bufp; |
| } |
| |
| static uint8_t *kdump_core_write_cpu_note(const struct mrdump_control_block *mrdump_cb, int cpu, struct elf64_phdr *nhdr, uint8_t *bufp) |
| { |
| struct memelfnote notes; |
| struct elf_arm64_prstatus64 prstatus; |
| char cpustr[16]; |
| |
| memset(&prstatus, 0, sizeof(struct elf_arm64_prstatus64)); |
| |
| snprintf(cpustr, sizeof(cpustr), "CPU%d", cpu); |
| /* set up the process status */ |
| notes.name = cpustr; |
| notes.type = NT_PRSTATUS; |
| notes.datasz = sizeof(struct elf_arm64_prstatus64); |
| notes.data = &prstatus; |
| |
| prstatus.pr_pid = cpu + 1; |
| memcpy(&prstatus.pr_reg, &mrdump_cb->crash_record.cpu_regs[cpu].arm64_regs, sizeof(elf_arm64_gregset_t)); |
| |
| nhdr->p_filesz += notesize(¬es); |
| return storenote(¬es, bufp); |
| } |
| |
| static uint8_t *kdump_core_write_machdesc(const struct mrdump_control_block *mrdump_cb, struct elf64_phdr *nhdr, uint8_t *bufp) |
| { |
| struct memelfnote notes; |
| struct elf_mrdump_machdesc machdesc; |
| const struct mrdump_machdesc *kparams = &mrdump_cb->machdesc; |
| |
| memset(&machdesc, 0, sizeof(struct elf_mrdump_machdesc)); |
| |
| notes.name = "MRDUMP01"; |
| notes.type = NT_MRDUMP_MACHDESC; |
| notes.datasz = sizeof(struct elf_mrdump_machdesc); |
| notes.data = &machdesc; |
| |
| machdesc.flags = MRDUMP_TYPE_FULL_MEMORY; |
| machdesc.nr_cpus = kparams->nr_cpus; |
| machdesc.phys_offset = kparams->phys_offset; |
| machdesc.page_offset = kparams->page_offset; |
| machdesc.high_memory = kparams->high_memory; |
| machdesc.kimage_vaddr = kparams->kimage_vaddr; |
| machdesc.modules_start = kparams->modules_start; |
| machdesc.modules_end = kparams->modules_end; |
| machdesc.vmalloc_start = kparams->vmalloc_start; |
| machdesc.vmalloc_end = kparams->vmalloc_end; |
| machdesc.master_page_table = kparams->master_page_table; |
| |
| nhdr->p_filesz += notesize(¬es); |
| return storenote(¬es, bufp); |
| } |
| |
| static uint8_t *mrdump_elf64_write_vmcoreinfo(const struct mrdump_control_block *mrdump_cb, struct elf64_phdr *nhdr, uint8_t *bufp) |
| { |
| struct memelfnote notes; |
| const struct mrdump_machdesc *kparams = &mrdump_cb->machdesc; |
| char vmcoreinfo[512]; |
| |
| memset(vmcoreinfo, 0, sizeof(vmcoreinfo)); |
| |
| notes.name = "VMCOREINFO"; |
| notes.type = 0; |
| notes.datasz = 0; |
| notes.data = &vmcoreinfo; |
| snprintf(vmcoreinfo, sizeof(vmcoreinfo), |
| "PAGESIZE=4096\n" |
| "NUMBER(kimage_voffset)=0x%llx\n" |
| "SYMBOL(_stext)=0x%llx\n" |
| "NUMBER(VA_BITS)=%d\n" |
| "NUMBER(PHYS_OFFSET)=0x%llx\n", |
| (uint64_t)kparams->kimage_voffset, |
| (uint64_t)kparams->kimage_stext_real, |
| 39, |
| (uint64_t)kparams->phys_offset); |
| notes.datasz = strlen(vmcoreinfo); |
| |
| nhdr->p_filesz += notesize(¬es); |
| return storenote(¬es, bufp); |
| } |
| |
| static uint8_t *kdump_core_write_mrdump_cb(const struct mrdump_control_block *mrdump_cb, struct elf64_phdr *nhdr, uint8_t *bufp) |
| { |
| struct memelfnote notes; |
| uint64_t mrdump_cb_paddr = (uint64_t)(uintptr_t)kvaddr_to_paddr((void *)mrdump_cb); |
| |
| notes.name = MRDUMP_GO_DUMP; |
| notes.type = NT_MRDUMP_CBLOCK; |
| notes.datasz = sizeof(mrdump_cb_paddr); |
| notes.data = &mrdump_cb_paddr; |
| |
| nhdr->p_filesz += notesize(¬es); |
| return storenote(¬es, bufp); |
| } |
| |
| void kdump_core64_header_init(const struct mrdump_control_block *mrdump_cb, const struct kzip_addlist *memlist) |
| { |
| struct elf64_phdr *nhdr, *phdr; |
| struct elf64_hdr *elf; |
| off_t offset = 0; |
| const struct mrdump_machdesc *kparams = &mrdump_cb->machdesc; |
| |
| uint8_t *bufp = (uint8_t *)(uintptr_t)memlist[0].address; |
| |
| elf = (struct elf64_hdr *) bufp; |
| bufp += sizeof(struct elf64_hdr); |
| offset += sizeof(struct elf64_hdr); |
| |
| nhdr = (struct elf64_phdr *) bufp; |
| bufp += sizeof(struct elf64_phdr); |
| offset += sizeof(struct elf64_phdr); |
| memset(nhdr, 0, sizeof(struct elf64_phdr)); |
| nhdr->p_type = PT_NOTE; |
| |
| unsigned long long foffset = KDUMP_CORE_HEADER_SIZE; |
| int i; |
| for (i = 1; memlist[i].address != 0; i++) { |
| phdr = (struct elf64_phdr *) bufp; |
| bufp += sizeof(struct elf64_phdr); |
| offset += sizeof(struct elf64_phdr); |
| |
| phdr->p_type = PT_LOAD; |
| phdr->p_flags = PF_R|PF_W|PF_X; |
| phdr->p_offset = foffset; |
| phdr->p_vaddr = 0; |
| phdr->p_paddr = (uint64_t)kvaddr_to_paddr((void *)memlist[i].address); |
| phdr->p_filesz = memlist[i].size; |
| phdr->p_memsz = memlist[i].size; |
| phdr->p_align = 0; |
| |
| foffset += phdr->p_filesz; |
| } |
| |
| mrdump_elf_setup_eident(elf->e_ident, ELFCLASS64); |
| mrdump_elf_setup_elfhdr(elf, EM_AARCH64, struct elf64_hdr, struct elf64_phdr, i); |
| dprintf(CRITICAL, "%s after\n", __func__); |
| |
| nhdr->p_offset = offset; |
| |
| /* NT_PRPSINFO */ |
| struct elf_prpsinfo64 prpsinfo; |
| struct memelfnote notes; |
| /* set up the process info */ |
| notes.name = CORE_STR; |
| notes.type = NT_PRPSINFO; |
| notes.datasz = sizeof(struct elf_prpsinfo64); |
| notes.data = &prpsinfo; |
| |
| memset(&prpsinfo, 0, sizeof(struct elf_prpsinfo64)); |
| prpsinfo.pr_state = 0; |
| prpsinfo.pr_sname = 'R'; |
| prpsinfo.pr_zomb = 0; |
| prpsinfo.pr_gid = prpsinfo.pr_uid = mrdump_cb->crash_record.fault_cpu + 1; |
| strlcpy(prpsinfo.pr_fname, "vmlinux", sizeof(prpsinfo.pr_fname)); |
| strlcpy(prpsinfo.pr_psargs, "vmlinux", ELF_PRARGSZ); |
| |
| nhdr->p_filesz += notesize(¬es); |
| bufp = storenote(¬es, bufp); |
| |
| bufp = kdump_core_write_machdesc(mrdump_cb, nhdr, bufp); |
| bufp = mrdump_elf64_write_vmcoreinfo(mrdump_cb, nhdr, bufp); |
| |
| /* Store pre-cpu backtrace */ |
| bufp = kdump_core_write_cpu_note(mrdump_cb, mrdump_cb->crash_record.fault_cpu, nhdr, bufp); |
| for (unsigned int cpu = 0; cpu < kparams->nr_cpus; cpu++) { |
| if (cpu != mrdump_cb->crash_record.fault_cpu) { |
| bufp = kdump_core_write_cpu_note(mrdump_cb, cpu, nhdr, bufp); |
| } |
| } |
| dprintf(CRITICAL, "%s %d\n", __func__, __LINE__); |
| |
| /* store mrdump control block */ |
| bufp = kdump_core_write_mrdump_cb(mrdump_cb, nhdr, bufp); |
| } |