/* * Support for writing ELF notes for LoongArch architectures * * Copyright (c) 2023 Loongarch Technology * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2 or later, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . * */ #include "qemu/osdep.h" #include "cpu.h" #include "elf.h" #include "sysemu/dump.h" #include "internals.h" /* struct user_pt_regs from arch/loongarch/include/uapi/asm/ptrace.h */ struct loongarch_user_regs { uint64_t gpr[32]; uint64_t pad1[1]; /* Special CSR registers. */ uint64_t csr_era; uint64_t csr_badv; uint64_t pad2[10]; } QEMU_PACKED; QEMU_BUILD_BUG_ON(sizeof(struct loongarch_user_regs) != 360); /* struct elf_prstatus from include/uapi/linux/elfcore.h */ struct loongarch_elf_prstatus { char pad1[32]; /* 32 == offsetof(struct elf_prstatus, pr_pid) */ uint32_t pr_pid; /* * 76 == offsetof(struct elf_prstatus, pr_reg) - * offsetof(struct elf_prstatus, pr_ppid) */ char pad2[76]; struct loongarch_user_regs pr_reg; uint32_t pr_fpvalid; char pad3[4]; } QEMU_PACKED; QEMU_BUILD_BUG_ON(sizeof(struct loongarch_elf_prstatus) != 480); /* struct user_fp_state from arch/loongarch/include/uapi/asm/ptrace.h */ struct loongarch_fpu_struct { uint64_t fpr[32]; uint64_t fcc; unsigned int fcsr; } QEMU_PACKED; QEMU_BUILD_BUG_ON(sizeof(struct loongarch_fpu_struct) != 268); struct loongarch_note { Elf64_Nhdr hdr; char name[8]; /* align_up(sizeof("CORE"), 4) */ union { struct loongarch_elf_prstatus prstatus; struct loongarch_fpu_struct fpu; }; } QEMU_PACKED; #define LOONGARCH_NOTE_HEADER_SIZE offsetof(struct loongarch_note, prstatus) #define LOONGARCH_PRSTATUS_NOTE_SIZE \ (LOONGARCH_NOTE_HEADER_SIZE + sizeof(struct loongarch_elf_prstatus)) #define LOONGARCH_PRFPREG_NOTE_SIZE \ (LOONGARCH_NOTE_HEADER_SIZE + sizeof(struct loongarch_fpu_struct)) static void loongarch_note_init(struct loongarch_note *note, DumpState *s, const char *name, Elf64_Word namesz, Elf64_Word type, Elf64_Word descsz) { memset(note, 0, sizeof(*note)); note->hdr.n_namesz = cpu_to_dump32(s, namesz); note->hdr.n_descsz = cpu_to_dump32(s, descsz); note->hdr.n_type = cpu_to_dump32(s, type); memcpy(note->name, name, namesz); } static int loongarch_write_elf64_fprpreg(WriteCoreDumpFunction f, CPULoongArchState *env, int cpuid, DumpState *s) { struct loongarch_note note; int ret, i; loongarch_note_init(¬e, s, "CORE", 5, NT_PRFPREG, sizeof(note.fpu)); note.fpu.fcsr = cpu_to_dump64(s, env->fcsr0); for (i = 0; i < 8; i++) { note.fpu.fcc |= env->cf[i] << (8 * i); } note.fpu.fcc = cpu_to_dump64(s, note.fpu.fcc); for (i = 0; i < 32; ++i) { note.fpu.fpr[i] = cpu_to_dump64(s, env->fpr[i].vreg.UD[0]); } ret = f(¬e, LOONGARCH_PRFPREG_NOTE_SIZE, s); if (ret < 0) { return -1; } return 0; } int loongarch_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, DumpState *s) { struct loongarch_note note; CPULoongArchState *env = &LOONGARCH_CPU(cs)->env; int ret, i; loongarch_note_init(¬e, s, "CORE", 5, NT_PRSTATUS, sizeof(note.prstatus)); note.prstatus.pr_pid = cpu_to_dump32(s, cpuid); note.prstatus.pr_fpvalid = cpu_to_dump32(s, 1); for (i = 0; i < 32; ++i) { note.prstatus.pr_reg.gpr[i] = cpu_to_dump64(s, env->gpr[i]); } note.prstatus.pr_reg.csr_era = cpu_to_dump64(s, env->CSR_ERA); note.prstatus.pr_reg.csr_badv = cpu_to_dump64(s, env->CSR_BADV); ret = f(¬e, LOONGARCH_PRSTATUS_NOTE_SIZE, s); if (ret < 0) { return -1; } ret = loongarch_write_elf64_fprpreg(f, env, cpuid, s); if (ret < 0) { return -1; } return ret; } int cpu_get_dump_info(ArchDumpInfo *info, const GuestPhysBlockList *guest_phys_blocks) { info->d_machine = EM_LOONGARCH; info->d_endian = ELFDATA2LSB; info->d_class = ELFCLASS64; return 0; } ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) { size_t note_size = 0; if (class == ELFCLASS64) { note_size = LOONGARCH_PRSTATUS_NOTE_SIZE + LOONGARCH_PRFPREG_NOTE_SIZE; } return note_size * nr_cpus; }