From 1368e174b2fe9a082137d6d27ddce9452b8f08da Mon Sep 17 00:00:00 2001 From: Jakub Klimek Date: Thu, 28 Aug 2025 13:15:40 +0200 Subject: [PATCH] libcoredumpsrv: introduction of coredumping Introduces libcoredumpsrv which provides coredump server functionality and configuration options. Created as static library in order to allow including in -multi devices to save memory on chosen targets similar to posixsrv and dummyfs. Standard way of use is standalone executable provided in phoenix-rtos-utils. Exports functions and constants required for configuration eg. via psh applet. JIRA: RTOS-1054 --- Makefile | 2 +- libcoredumpsrv/Makefile | 23 + libcoredumpsrv/coredump.c | 958 +++++++++++++++++++++++++++ libcoredumpsrv/coredump.h | 65 ++ libcoredumpsrv/elf.h | 257 +++++++ libcoredumpsrv/encoding.c | 73 ++ libcoredumpsrv/encoding.h | 46 ++ libcoredumpsrv/hal.h | 52 ++ libcoredumpsrv/hal/aarch64/cpu.c | 70 ++ libcoredumpsrv/hal/aarch64/cpu.h | 90 +++ libcoredumpsrv/hal/armv7a/cpu.c | 97 +++ libcoredumpsrv/hal/armv7a/cpu.h | 123 ++++ libcoredumpsrv/hal/armv7m/cpu.c | 177 +++++ libcoredumpsrv/hal/armv7m/cpu.h | 165 +++++ libcoredumpsrv/hal/armv7r/cpu.c | 97 +++ libcoredumpsrv/hal/armv7r/cpu.h | 124 ++++ libcoredumpsrv/hal/armv8m/cpu.c | 93 +++ libcoredumpsrv/hal/armv8m/cpu.h | 125 ++++ libcoredumpsrv/hal/armv8r/cpu.c | 97 +++ libcoredumpsrv/hal/armv8r/cpu.h | 123 ++++ libcoredumpsrv/hal/ia32/cpu.c | 80 +++ libcoredumpsrv/hal/ia32/cpu.h | 114 ++++ libcoredumpsrv/hal/riscv64/cpu.c | 95 +++ libcoredumpsrv/hal/riscv64/cpu.h | 188 ++++++ libcoredumpsrv/hal/sparcv8leon/cpu.c | 153 +++++ libcoredumpsrv/hal/sparcv8leon/cpu.h | 291 ++++++++ libcoredumpsrv/kernel_comm.c | 216 ++++++ libcoredumpsrv/kernel_comm.h | 41 ++ libcoredumpsrv/settings.c | 338 ++++++++++ 29 files changed, 4372 insertions(+), 1 deletion(-) create mode 100644 libcoredumpsrv/Makefile create mode 100644 libcoredumpsrv/coredump.c create mode 100644 libcoredumpsrv/coredump.h create mode 100644 libcoredumpsrv/elf.h create mode 100644 libcoredumpsrv/encoding.c create mode 100644 libcoredumpsrv/encoding.h create mode 100644 libcoredumpsrv/hal.h create mode 100644 libcoredumpsrv/hal/aarch64/cpu.c create mode 100644 libcoredumpsrv/hal/aarch64/cpu.h create mode 100644 libcoredumpsrv/hal/armv7a/cpu.c create mode 100644 libcoredumpsrv/hal/armv7a/cpu.h create mode 100644 libcoredumpsrv/hal/armv7m/cpu.c create mode 100644 libcoredumpsrv/hal/armv7m/cpu.h create mode 100644 libcoredumpsrv/hal/armv7r/cpu.c create mode 100644 libcoredumpsrv/hal/armv7r/cpu.h create mode 100644 libcoredumpsrv/hal/armv8m/cpu.c create mode 100644 libcoredumpsrv/hal/armv8m/cpu.h create mode 100644 libcoredumpsrv/hal/armv8r/cpu.c create mode 100644 libcoredumpsrv/hal/armv8r/cpu.h create mode 100644 libcoredumpsrv/hal/ia32/cpu.c create mode 100644 libcoredumpsrv/hal/ia32/cpu.h create mode 100644 libcoredumpsrv/hal/riscv64/cpu.c create mode 100644 libcoredumpsrv/hal/riscv64/cpu.h create mode 100644 libcoredumpsrv/hal/sparcv8leon/cpu.c create mode 100644 libcoredumpsrv/hal/sparcv8leon/cpu.h create mode 100644 libcoredumpsrv/kernel_comm.c create mode 100644 libcoredumpsrv/kernel_comm.h create mode 100644 libcoredumpsrv/settings.c diff --git a/Makefile b/Makefile index dfaa850..8b3e506 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ include ../phoenix-rtos-build/Makefile.common # DEFAULT_COMPONENTS are shared between all targets DEFAULT_COMPONENTS := libcgi libvirtio libvga libgraph libstorage \ libmtd libptable libuuid libcache libswdg libmbr libtinyaes libalgo \ - libmodbus + libmodbus libcoredumpsrv # read out all components ALL_MAKES := $(wildcard */Makefile) $(wildcard */*/Makefile) diff --git a/libcoredumpsrv/Makefile b/libcoredumpsrv/Makefile new file mode 100644 index 0000000..5b00122 --- /dev/null +++ b/libcoredumpsrv/Makefile @@ -0,0 +1,23 @@ +# +# Makefile for Phoenix-RTOS coredump_server +# +# Copyright 2025 Phoenix Systems +# + +LOCAL_PATH := $(call my-dir) + +NAME := libcoredumpsrv +SRCS := $(filter-out $(LOCAL_PATH)srv.c, $(wildcard $(LOCAL_PATH)*.c)) +LOCAL_SRCS := hal/$(TARGET_SUFF)/cpu.c +LOCAL_CFLAGS := -Ilibcoredumpsrv -Ilibcoredumpsrv/hal/$(TARGET_SUFF) +LOCAL_HEADERS := coredump.h + +ifneq (, $(filter imxrt117x imxrt106x imxrt105x, $(TARGET_SUBFAMILY))) + LOCAL_CFLAGS += -DCPU_IMXRT +endif + +ifeq ($(COREDUMP_DISABLE), 1) + CPPFLAGS += -DCOREDUMP_DISABLE +endif + +include $(static-lib.mk) diff --git a/libcoredumpsrv/coredump.c b/libcoredumpsrv/coredump.c new file mode 100644 index 0000000..f9fb765 --- /dev/null +++ b/libcoredumpsrv/coredump.c @@ -0,0 +1,958 @@ +/* + * Phoenix-RTOS + * + * Coredump Server Library + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * %LICENSE% + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "coredump.h" +#include "encoding.h" +#include "hal.h" +#include "kernel_comm.h" + +#define PRINT_ERR(args...) fprintf(stderr, "Coredump server: " args) + +#define min(a, b) ((a) < (b) ? (a) : (b)) +#define max(a, b) ((a) > (b) ? (a) : (b)) + +#define COREDUMP_OUTBUF_SIZE 128 + +#define COREDUMP_START "\n_____________COREDUMP_START_____________\n" +#define COREDUMP_END "\n______________COREDUMP_END______________\n" + +#define PRSTATUS_NAME "CORE" + +#define NT_LMA 0x00414D4C /* ASCII for "LMA" (load memory address) */ +#define NT_LMA_NAME "PHOENIX" + + +struct { + /* Crash Info */ + struct { + void *addr; + size_t size; + } *threadStacks; + size_t threadCnt; + coredump_memseg_t *memList; + size_t memSegCnt; + coredump_reloc_t relocs[8]; + int elfclass32; + + /* File Output */ + FILE *fp; + + /* Text Output */ + char outBuf[COREDUMP_OUTBUF_SIZE]; + size_t outCur; + + __u8 rle_last; + size_t rle_count; + + lib_base64_ctx b64; + crc32_t crc32; + + /* Settings */ + pthread_mutex_t mutex; + coredump_opts_t *opts; + + /* Messaging */ + __u32 memRecvPort; + oid_t settingsDev; +} state; + + +static void coredump_print(const char *data, size_t len) +{ + if (state.opts->print) { + write(STDERR_FILENO, data, len); + usleep(state.opts->printSleep); + } +} + + +static void coredump_writeBuf(const char *data, size_t len) +{ + while (state.outCur + len >= sizeof(state.outBuf) - 1) { + memcpy(state.outBuf + state.outCur, data, sizeof(state.outBuf) - 1 - state.outCur); + state.outBuf[sizeof(state.outBuf) - 1] = '\n'; + coredump_print(state.outBuf, sizeof(state.outBuf)); + data += sizeof(state.outBuf) - 1 - state.outCur; + len -= sizeof(state.outBuf) - 1 - state.outCur; + state.outCur = 0; + } + memcpy(state.outBuf + state.outCur, data, len); + state.outCur += len; + if (state.outCur >= sizeof(state.outBuf) - 1) { + state.outBuf[sizeof(state.outBuf) - 1] = '\n'; + coredump_print(state.outBuf, sizeof(state.outBuf)); + state.outCur = 0; + } +} + + +static void coredump_encodeByte(const __u8 byte) +{ + int n = enc_base64EncodeByte(&state.b64, byte); + coredump_writeBuf(state.b64.outBuf, n); +} + + +static void coredump_encodeRleLength(void) +{ + __u8 byte; + while (state.rle_count > 0) { + byte = state.rle_count & 0x7F; + state.rle_count >>= 7; + if (state.rle_count > 0) { + byte |= 0x80; + } + coredump_encodeByte(byte); + } +} + + +static void coredump_encodeChunk(const __u8 *buf, size_t len) +{ + size_t i; + __u8 byte; + for (i = 0; i < len; i++) { + /* making sure that crc is coherent with dumped data even if the buf is written to by other process */ + byte = buf[i]; + + if (state.fp != NULL) { + fwrite(&byte, 1, 1, state.fp); + } + + state.crc32 = enc_crc32NextByte(state.crc32, byte); + + if (state.rle_last == byte) { + state.rle_count++; + continue; + } + if ((state.rle_count > 3) || ((state.rle_last == 0xFE) && (state.rle_count > 0))) { + coredump_encodeByte(0xFE); + coredump_encodeRleLength(); + coredump_encodeByte(state.rle_last); + } + else { + while (state.rle_count > 0) { + coredump_encodeByte(state.rle_last); + state.rle_count--; + } + } + state.rle_count = 1; + state.rle_last = byte; + } +} + + +static int coredump_dumpMemory(void *startAddr, size_t len) +{ + msg_t msg; + msg_rid_t rid; + + msg.oid.id = 0; + msg.oid.port = state.memRecvPort; + + size_t memChunkSize = len; + if (state.opts->maxMemChunk != 0) { + memChunkSize = min(memChunkSize, state.opts->maxMemChunk); + } + + void *curAddr = startAddr; + + while (curAddr < startAddr + len) { + memChunkSize = min(memChunkSize, startAddr + len - curAddr); + int ret = coredump_getMemory(curAddr, memChunkSize, &msg, &rid); + if (ret != 0) { + PRINT_ERR("coredump_getMemory failed with %d\n", ret); + return ret; + } + coredump_encodeChunk(msg.i.data, memChunkSize); + coredump_putMemory(&msg, rid); + curAddr += memChunkSize; + } + + return 0; +} + + +static void coredump_begin(const char *path, int signal) +{ + state.outCur = 0; + state.rle_last = -1; + state.rle_count = 0; + state.crc32 = LIB_CRC32_INIT; + enc_base64Init(&state.b64); + + coredump_print(COREDUMP_START, sizeof(COREDUMP_START) - 1); + coredump_print(path, strlen(path)); + coredump_print(": ", 2); + coredump_print(strsignal(signal), strlen(strsignal(signal))); + coredump_print(";\n", 2); +} + + +static void coredump_finalize(bool addCrc) +{ + if (addCrc) { + crc32_t crc = enc_crc32Finalize(state.crc32); + coredump_encodeChunk((__u8 *)&crc, sizeof(crc)); + } + + if ((state.rle_count > 3) || (state.rle_last == 0xFE)) { + coredump_encodeByte(0xFE); + coredump_encodeRleLength(); + coredump_encodeByte(state.rle_last); + } + else { + while (state.rle_count > 0) { + coredump_encodeByte(state.rle_last); + state.rle_count--; + } + } + + int n = enc_base64Finalize(&state.b64); + coredump_writeBuf(state.b64.outBuf, n); + + + if (state.outCur > 0) { + state.outBuf[state.outCur++] = '\n'; + coredump_print(state.outBuf, state.outCur); + } + + coredump_print(COREDUMP_END, sizeof(COREDUMP_END) - 1); +} + + +static void coredump_dumpElfHeader32(size_t segCnt) +{ + Elf32_Ehdr hdr; + + memcpy(hdr.e_ident, ELFMAG, sizeof(ELFMAG)); + memset(hdr.e_ident + sizeof(ELFMAG), 0, sizeof(hdr.e_ident) - sizeof(ELFMAG)); + hdr.e_ident[EI_CLASS] = ELFCLASS32; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + hdr.e_ident[EI_DATA] = ELFDATA2LSB; +#else + hdr.e_ident[EI_DATA] = ELFDATA2MSB; +#endif + hdr.e_ident[EI_VERSION] = 1; /* EV_CURRENT */ + hdr.e_ident[EI_OSABI] = ELFOSABI_SYSV; + hdr.e_type = ET_CORE; + hdr.e_machine = HAL_ELF_MACHINE; + hdr.e_version = 1; /* EV_CURRENT */ + hdr.e_phoff = sizeof(Elf32_Ehdr); + hdr.e_ehsize = sizeof(Elf32_Ehdr); + hdr.e_phentsize = sizeof(Elf32_Phdr); + hdr.e_phnum = 1 + segCnt; + hdr.e_shoff = 0; + hdr.e_flags = 0; + hdr.e_shentsize = 0; + hdr.e_shnum = 0; + hdr.e_shstrndx = 0; + hdr.e_entry = 0; + + coredump_encodeChunk((__u8 *)&hdr, sizeof(hdr)); +} + + +static void coredump_dumpElfHeader64(size_t segCnt) +{ + Elf64_Ehdr hdr; + + memcpy(hdr.e_ident, ELFMAG, sizeof(ELFMAG)); + memset(hdr.e_ident + sizeof(ELFMAG), 0, sizeof(hdr.e_ident) - sizeof(ELFMAG)); + hdr.e_ident[EI_CLASS] = ELFCLASS64; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + hdr.e_ident[EI_DATA] = ELFDATA2LSB; +#else + hdr.e_ident[EI_DATA] = ELFDATA2MSB; +#endif + hdr.e_ident[EI_VERSION] = 1; /* EV_CURRENT */ + hdr.e_ident[EI_OSABI] = ELFOSABI_SYSV; + hdr.e_type = ET_CORE; + hdr.e_machine = HAL_ELF_MACHINE; + hdr.e_version = 1; /* EV_CURRENT */ + hdr.e_phoff = sizeof(Elf64_Ehdr); + hdr.e_ehsize = sizeof(Elf64_Ehdr); + hdr.e_phentsize = sizeof(Elf64_Phdr); + hdr.e_phnum = 1 + segCnt; + hdr.e_shoff = 0; + hdr.e_flags = 0; + hdr.e_shentsize = 0; + hdr.e_shnum = 0; + hdr.e_shstrndx = 0; + hdr.e_entry = 0; + + coredump_encodeChunk((__u8 *)&hdr, sizeof(hdr)); +} + + +static void coredump_dumpElfHeader(size_t segCnt) +{ + if (state.elfclass32) { + coredump_dumpElfHeader32(segCnt); + } + else { + coredump_dumpElfHeader64(segCnt); + } +} + + +static size_t align4(size_t size) +{ + return (size + 3) & ~3; +} + + +static int coredump_dumpThreadNotes(coredump_thread_t *threadInfo) +{ + const __u32 zero = 0; + + Elf32_Nhdr nhdr; /* Elf64_Nhdr is identical to Elf32_Nhdr */ + elf_prstatus prstatus; + elf_thread_aux threadAux; + + nhdr.n_namesz = sizeof(PRSTATUS_NAME); + nhdr.n_descsz = sizeof(elf_prstatus); + nhdr.n_type = NT_PRSTATUS; + coredump_encodeChunk((__u8 *)&nhdr, sizeof(nhdr)); + coredump_encodeChunk((__u8 *)PRSTATUS_NAME, sizeof(PRSTATUS_NAME)); + /* alignment */ + coredump_encodeChunk((__u8 *)&zero, align4(sizeof(PRSTATUS_NAME)) - sizeof(PRSTATUS_NAME)); + + memset(&prstatus, 0, sizeof(prstatus)); + int ret = hal_fillPrStatus((cpu_context_t *)threadInfo->context, &prstatus, state.memRecvPort, state.opts); + if (ret != 0) { + PRINT_ERR("hal_fillPrStatus failed with %d\n", ret); + return ret; + } + prstatus.pr_pid = threadInfo->tid; + coredump_encodeChunk((__u8 *)&prstatus, sizeof(prstatus)); + + hal_createThreadAuxNotes((cpu_context_t *)threadInfo->context, &threadAux, state.opts); + coredump_encodeChunk((__u8 *)&threadAux, hal_threadAuxNotesSize(state.opts)); + return 0; +} + + +static int coredump_dumpAllThreadsNotes(int crashedTid) +{ + coredump_thread_t *threadInfo = malloc(sizeof(coredump_thread_t) + sizeof(cpu_context_t)); + if (threadInfo == NULL) { + PRINT_ERR("malloc failed\n"); + return -ENOMEM; + } + + int nextTid = crashedTid; + for (size_t i = 0; i < state.threadCnt; i++) { + int ret = coredump_getThreadContext(nextTid, threadInfo); + if (ret != 0) { + PRINT_ERR("getThreadContext failed\n"); + free(threadInfo); + return ret; + } + + ret = coredump_dumpThreadNotes(threadInfo); + if (ret != 0) { + free(threadInfo); + return ret; + } + + nextTid = threadInfo->nextTid; + } + free(threadInfo); + return 0; +} + + +static size_t coredump_LMANoteSize(void) +{ + +#ifdef NOMMU + int cnt; + + for (cnt = 0; cnt < sizeof(state.relocs) / sizeof(state.relocs[0]); cnt++) { + if (state.relocs[cnt].pbase == NULL) { + break; + } + } + if (cnt > 0) { + return cnt * 2 * sizeof(Elf32_Addr) + sizeof(Elf32_Nhdr) + sizeof(NT_LMA_NAME); + } + +#endif /* NOMMU */ + + return 0; +} + + +static void coredump_dumpLMANote(void) +{ +#ifdef NOMMU + int cnt = 0; + for (cnt = 0; cnt < sizeof(state.relocs) / sizeof(state.relocs[0]); cnt++) { + if (state.relocs[cnt].pbase == NULL) { + break; + } + } + + Elf32_Nhdr nhdr = { + .n_type = NT_LMA, + .n_namesz = sizeof(NT_LMA_NAME), + .n_descsz = cnt * 2 * sizeof(Elf32_Addr) + }; + coredump_encodeChunk((__u8 *)&nhdr, sizeof(nhdr)); + coredump_encodeChunk((__u8 *)NT_LMA_NAME, sizeof(NT_LMA_NAME)); + + coredump_encodeChunk((__u8 *)state.relocs, cnt * 2 * sizeof(Elf32_Addr)); +#endif /* NOMMU */ +} + + +static size_t coredump_findStack(void **currentSP, void *ustack) +{ + for (int i = 0; i < state.memSegCnt; i++) { + if (state.memList[i].startAddr <= ustack && + state.memList[i].endAddr > ustack) { + if ((*currentSP >= (void *)((char *)state.memList[i].endAddr)) || (*currentSP < state.memList[i].startAddr)) { + *currentSP = state.memList[i].startAddr; + } + + size_t stackSize = (char *)state.memList[i].endAddr - (char *)*currentSP; + if (state.opts->maxStackSize > 0) { + stackSize = min(stackSize, state.opts->maxStackSize); + } + return stackSize; + } + } + return 0; +} + + +static int coredump_findAllStacks(int crashedTid) +{ + coredump_thread_t *threadInfo = malloc(sizeof(coredump_thread_t) + sizeof(cpu_context_t)); + if (threadInfo == NULL) { + PRINT_ERR("malloc failed\n"); + return -ENOMEM; + } + + int nextTid = crashedTid; + for (size_t i = 0; i < state.threadCnt; i++) { + int ret = coredump_getThreadContext(nextTid, threadInfo); + if (ret != 0) { + PRINT_ERR("getThreadContext failed\n"); + free(threadInfo); + return ret; + } + + state.threadStacks[i].addr = hal_cpuGetUserSP((cpu_context_t *)threadInfo->context); + state.threadStacks[i].size = coredump_findStack(&state.threadStacks[i].addr, threadInfo->stackAddr); + + nextTid = threadInfo->nextTid; + } + free(threadInfo); + return 0; +} + + +static int coredump_dumpAllMem(void) +{ + int ret; + for (size_t i = 0; i < state.memSegCnt; i++) { + size_t len = (char *)state.memList[i].endAddr - (char *)state.memList[i].startAddr; + ret = coredump_dumpMemory(state.memList[i].startAddr, len); + if (ret != 0) { + PRINT_ERR("Failed to dump memory segment %zu\n", i); + return ret; + } + } + return 0; +} + + +static void coredump_dumpPhdr32(__u32 type, size_t offset, void *vaddr, size_t size) +{ + Elf32_Phdr phdr; + phdr.p_type = type; + phdr.p_offset = offset; + phdr.p_vaddr = (Elf32_Addr)(uintptr_t)vaddr; + phdr.p_paddr = 0; + phdr.p_filesz = size; + phdr.p_memsz = size; + phdr.p_flags = 0; + phdr.p_align = 0; + coredump_encodeChunk((__u8 *)&phdr, sizeof(phdr)); +} + + +static void coredump_dumpPhdr64(__u32 type, size_t offset, void *vaddr, size_t size) +{ + Elf64_Phdr phdr; + phdr.p_type = type; + phdr.p_offset = offset; + phdr.p_vaddr = (Elf64_Addr)(uintptr_t)vaddr; + phdr.p_paddr = 0; + phdr.p_filesz = size; + phdr.p_memsz = size; + phdr.p_flags = 0; + phdr.p_align = 0; + coredump_encodeChunk((__u8 *)&phdr, sizeof(phdr)); +} + + +static void coredump_dumpPhdr(__u32 type, size_t offset, void *vaddr, size_t size) +{ + if (state.elfclass32) { + coredump_dumpPhdr32(type, offset, vaddr, size); + } + else { + coredump_dumpPhdr64(type, offset, vaddr, size); + } +} + + +static void coredump_dumpAllPhdrs(size_t segCnt) +{ + const size_t THREAD_NOTES_SIZE = sizeof(Elf32_Nhdr) + + ((sizeof(PRSTATUS_NAME) + 3) & ~3) + + sizeof(elf_prstatus) + hal_threadAuxNotesSize(state.opts); + const size_t NOTES_SIZE = hal_procAuxNotesSize(state.opts) + state.threadCnt * (THREAD_NOTES_SIZE) + coredump_LMANoteSize(); + size_t currentOffset; + + /* Notes */ + if (state.elfclass32) { + currentOffset = sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr) * (1 + segCnt); + } + else { + currentOffset = sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr) * (1 + segCnt); + } + + coredump_dumpPhdr(PT_NOTE, currentOffset, 0, NOTES_SIZE); + currentOffset += NOTES_SIZE; + + /* Memory */ + if (state.opts->memScope == COREDUMP_MEM_ALL) { + for (size_t i = 0; i < state.memSegCnt; i++) { + coredump_dumpPhdr(PT_LOAD, currentOffset, state.memList[i].startAddr, + state.memList[i].endAddr - state.memList[i].startAddr); + currentOffset += state.memList[i].endAddr - state.memList[i].startAddr; + } + } + else if (state.opts->memScope == COREDUMP_MEM_EXC_STACK) { + coredump_dumpPhdr(PT_LOAD, currentOffset, state.threadStacks[0].addr, state.threadStacks[0].size); + currentOffset += state.threadStacks[0].size; + } + else if (state.opts->memScope == COREDUMP_MEM_ALL_STACKS) { + for (size_t i = 0; i < state.threadCnt; i++) { + coredump_dumpPhdr(PT_LOAD, currentOffset, state.threadStacks[i].addr, state.threadStacks[i].size); + currentOffset += state.threadStacks[i].size; + } + } +} + + +static int coredump_createFile(char *crashPath, int signal) +{ + const static size_t MAX_PATH_SIZE = 256; + if (state.opts->savepath == NULL) { + state.fp = NULL; + return -EINVAL; + } + + /* Check file count in savepath directory */ + DIR *dir = opendir(state.opts->savepath); + if (dir == NULL) { + PRINT_ERR("opendir failed: %s\n", strerror(errno)); + state.fp = NULL; + return -errno; + } + int fileCount = 0; + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) { + if (entry->d_type == DT_REG) { + fileCount++; + } + } + closedir(dir); + if (state.opts->maxFiles > 0 && fileCount >= state.opts->maxFiles) { + PRINT_ERR("Maximum number of coredump files reached (%zu)\n", state.opts->maxFiles); + state.fp = NULL; + return -ENOSPC; + } + + char *base = strrchr(crashPath, '/'); + if (base != NULL) { + crashPath = base + 1; + } + char path[MAX_PATH_SIZE]; + unsigned long long now = time(NULL); + snprintf(path, MAX_PATH_SIZE, "%s/%s!%d@%llu.core", state.opts->savepath, crashPath, signal % 100, now); + state.fp = fopen(path, "wb"); + if (state.fp == NULL) { + PRINT_ERR("fopen failed: %s\n", strerror(errno)); + state.fp = NULL; + return -errno; + } + return 0; +} + + +static void coredump_dump(coredump_general_t *crashInfo) +{ + if (state.opts->savepath != NULL && coredump_createFile(crashInfo->path, crashInfo->signo) != 0) { + PRINT_ERR("Failed to create coredump file, disabling file output!\n"); + free(state.opts->savepath); + state.opts->savepath = NULL; + } + if (state.fp == NULL && state.opts->print == 0) { + PRINT_ERR("All output methods disabled!\n"); + return; + } + + state.elfclass32 = crashInfo->type == COREDUMP_TYPE_32; + state.memSegCnt = crashInfo->memSegCnt; + state.threadCnt = crashInfo->threadCnt; + if (state.opts->maxThreads != 0) { + state.threadCnt = min(state.threadCnt, state.opts->maxThreads); + } + + state.memList = malloc(sizeof(coredump_memseg_t) * crashInfo->memSegCnt); + state.threadStacks = malloc(state.threadCnt * sizeof(state.threadStacks[0])); + if ((crashInfo->memSegCnt > 0 && state.memList == NULL) || state.threadStacks == NULL) { + PRINT_ERR("malloc failed\n"); + goto err; + } + + if (coredump_getMemList(sizeof(coredump_memseg_t) * crashInfo->memSegCnt, state.memList) != 0) { + PRINT_ERR("Failed to get memory list\n"); + goto err; + } + if (coredump_getRelocs(sizeof(state.relocs), state.relocs) != 0) { + PRINT_ERR("Failed to get relocations\n"); + goto err; + } + if (coredump_findAllStacks(crashInfo->tid) != 0) { + PRINT_ERR("Failed to find stacks\n"); + goto err; + } + + int segCnt; + switch (state.opts->memScope) { + case COREDUMP_MEM_ALL: + segCnt = crashInfo->memSegCnt; + break; + case COREDUMP_MEM_ALL_STACKS: + segCnt = state.threadCnt; + break; + case COREDUMP_MEM_EXC_STACK: + segCnt = 1; + break; + default: + segCnt = 0; + break; + } + + coredump_begin(crashInfo->path, crashInfo->signo); + coredump_dumpElfHeader(segCnt); + coredump_dumpAllPhdrs(segCnt); + + if (coredump_dumpAllThreadsNotes(crashInfo->tid) != 0) { + PRINT_ERR("Failed to dump thread notes\n"); + goto err; + } + + elf_proc_aux procAux; + hal_createProcAuxNotes(&procAux, state.opts); + coredump_encodeChunk((__u8 *)&procAux, hal_procAuxNotesSize(state.opts)); + + coredump_dumpLMANote(); + + /* MEMORY */ + switch (state.opts->memScope) { + case COREDUMP_MEM_ALL: + if (coredump_dumpAllMem() != 0) { + PRINT_ERR("Failed to dump all memory\n"); + goto err; + } + break; + case COREDUMP_MEM_EXC_STACK: + /* exception thread is put into threadInfo at index 0 */ + if (coredump_dumpMemory(state.threadStacks[0].addr, state.threadStacks[0].size) != 0) { + PRINT_ERR("Failed to dump exception stack memory\n"); + goto err; + } + break; + case COREDUMP_MEM_ALL_STACKS: + for (int i = 0; (i < state.threadCnt); i++) { + if (coredump_dumpMemory(state.threadStacks[i].addr, state.threadStacks[i].size) != 0) { + PRINT_ERR("Failed to dump thread stack memory for %d. thread\n", i); + goto err; + } + } + break; + default: + break; + } + + coredump_finalize(true); +err: + free(state.memList); + free(state.threadStacks); + + if (state.fp != NULL) { + fclose(state.fp); + state.fp = NULL; + } +} + + +static int initSaveDir(char *path) +{ + struct stat st; + while (stat("/", &st) < 0) { + if (errno == ENOENT) { + usleep(500000); + } + else { + PRINT_ERR("stat failed with: %s\n", strerror(errno)); + return -errno; + } + } + + if ((mkdir(path, 0) != 0) && (errno != EEXIST)) { + return -errno; + } + return 0; +} + + +static void _settingsthr(void *arg) +{ + for (;;) { + msg_t msg; + msg_rid_t rid; + if (msgRecv(state.settingsDev.port, &msg, &rid) < 0) { + PRINT_ERR("portRecv failed\n"); + break; + } + + msg.o.err = EOK; + + if (msg.type == mtSetAttr) { + pthread_mutex_lock(&state.mutex); + switch (msg.i.attr.type) { + case COREDUMP_ATTR_PATH: + if (msg.i.attr.val == 0) { + free(state.opts->savepath); + state.opts->savepath = NULL; + break; + } + if (msg.i.size == 0) { + msg.o.err = -ENOENT; + break; + } + int ret = initSaveDir((char *)msg.i.data); + if (ret != 0) { + msg.o.err = ret; + break; + } + free(state.opts->savepath); + state.opts->savepath = strndup((char *)msg.i.data, msg.i.size); + if (state.opts->savepath == NULL) { + msg.o.err = -ENOMEM; + } + break; + case COREDUMP_ATTR_MAX_THREADS: + if (msg.i.attr.val < 0) { + msg.o.err = -EINVAL; + } + else { + state.opts->maxThreads = msg.i.attr.val; + } + break; + case COREDUMP_ATTR_MAX_STACK_SIZE: + if (msg.i.attr.val < 0) { + msg.o.err = -EINVAL; + } + else { + state.opts->maxStackSize = msg.i.attr.val; + } + break; + case COREDUMP_ATTR_MEM_SCOPE: + if (msg.i.attr.val < COREDUMP_MEM_NONE || msg.i.attr.val > COREDUMP_MEM_ALL) { + msg.o.err = -EINVAL; + } + else { + state.opts->memScope = msg.i.attr.val; + } + break; + case COREDUMP_ATTR_FP_CONTEXT: + if (msg.i.attr.val < 0 || msg.i.attr.val > 1) { + msg.o.err = -EINVAL; + } + else { + state.opts->fpContext = msg.i.attr.val; + } + break; + case COREDUMP_ATTR_PRINT: + if (msg.i.attr.val < 0 || msg.i.attr.val > 1) { + msg.o.err = -EINVAL; + } + else { + state.opts->print = msg.i.attr.val; + } + break; + case COREDUMP_ATTR_PRINT_SLEEP: + if (msg.i.attr.val < 0) { + msg.o.err = -EINVAL; + } + else { + state.opts->printSleep = msg.i.attr.val; + } + break; + case COREDUMP_ATTR_MAX_FILES: + if (msg.i.attr.val < 0) { + msg.o.err = -EINVAL; + } + else { + state.opts->maxFiles = msg.i.attr.val; + } + break; + default: + msg.o.err = -ENOSYS; + break; + } + pthread_mutex_unlock(&state.mutex); + } + else if (msg.type == mtGetAttrAll) { + memcpy(msg.o.data, state.opts, min(sizeof(*state.opts), msg.o.size)); + msg.o.attr.val = sizeof(*state.opts); + if (state.opts->savepath != NULL) { + memcpy(msg.o.data + sizeof(*state.opts), state.opts->savepath, min(strlen(state.opts->savepath) + 1, msg.o.size - sizeof(*state.opts))); + msg.o.attr.val += strlen(state.opts->savepath) + 1; + } + } + else { + msg.o.err = -EOPNOTSUPP; + } + + msgRespond(state.settingsDev.port, &msg, rid); + } +} + + +int coredump_serverthr(coredump_opts_t *opts) +{ + if (portCreate(&state.settingsDev.port) < 0) { + PRINT_ERR("portCreate failed\n"); + return -1; + } + state.settingsDev.id = 0; + if (create_dev(&state.settingsDev, COREDUMP_SETTINGS_DEV) < 0) { + PRINT_ERR("Can't create device! Isn't another instance already running?\n"); + portDestroy(state.settingsDev.port); + return -1; + } + + if (portCreate(&state.memRecvPort) < 0) { + PRINT_ERR("portCreate failed\n"); + portDestroy(state.settingsDev.port); + return -1; + } + + pthread_mutex_init(&state.mutex, NULL); + + state.opts = opts; + if (state.opts->savepath != NULL) { + int ret = initSaveDir(state.opts->savepath); + if (ret == 0) { + state.opts->savepath = strdup(state.opts->savepath); + } + else { + PRINT_ERR("Failed to create save directory (%s), disabled file saving.\n", strerror(ret)); + state.opts->savepath = NULL; + } + } + + pthread_t settingsThread; + if (pthread_create(&settingsThread, NULL, (void *)_settingsthr, NULL) != 0) { + PRINT_ERR("pthread_create failed\n"); + portDestroy(state.memRecvPort); + portDestroy(state.settingsDev.port); + return -1; + } + + for (;;) { + coredump_general_t crashInfo; + if (coredump_waitForCrash(&crashInfo) != 0) { + break; + } + + pthread_mutex_lock(&state.mutex); /* lock settings during dump */ + coredump_dump(&crashInfo); + pthread_mutex_unlock(&state.mutex); + + coredump_closeCrash(); + } + + portDestroy(state.memRecvPort); + portDestroy(state.settingsDev.port); + pthread_cancel(settingsThread); + unlink(COREDUMP_SETTINGS_DEV); + return 0; +} + + +int coredump_printElf(char *path) +{ + printf("Printing ELF file: %s\n", path); + FILE *f = fopen(path, "rb"); + if (f == NULL) { + printf("Error opening file %s: %s\n", path, strerror(errno)); + return -1; + } + + state.fp = NULL; + state.opts = malloc(sizeof(coredump_opts_t)); + state.opts->print = 1; + state.opts->printSleep = 10 * 1000; /* 10ms */ + + int signo = 0; + char *signal = strrchr(path, '!'); + if (signal != NULL) { + *(signal++) = '\0'; + signo = atoi(signal); + } + coredump_begin(strrchr(path, '/'), signo); + + __u8 buff[COREDUMP_OUTBUF_SIZE]; + int ret; + while ((ret = fread(buff, 1, sizeof(buff), f)) > 0) { + coredump_encodeChunk(buff, ret); + } + + coredump_finalize(false); + + fclose(f); + return 0; +} diff --git a/libcoredumpsrv/coredump.h b/libcoredumpsrv/coredump.h new file mode 100644 index 0000000..f89cb5b --- /dev/null +++ b/libcoredumpsrv/coredump.h @@ -0,0 +1,65 @@ +/* + * Phoenix-RTOS + * + * Coredump Server Library + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * %LICENSE% + */ + +#ifndef _COREDUMP_H_ +#define _COREDUMP_H_ + +#include + +#define COREDUMP_SETTINGS_DEV "/dev/coredumpctrl" + + +enum { + COREDUMP_ATTR_MAX_THREADS, /* Maximum number of threads in coredump */ + COREDUMP_ATTR_MAX_STACK_SIZE, /* Maximum stack size for each thread */ + COREDUMP_ATTR_MEM_SCOPE, /* Memory scope for coredump */ + COREDUMP_ATTR_FP_CONTEXT, /* Save floating point context */ + COREDUMP_ATTR_PRINT, /* Print coredump to console */ + COREDUMP_ATTR_PRINT_SLEEP, /* Sleep time between printing coredump chunks */ + COREDUMP_ATTR_PATH, /* Path to save coredumps */ + COREDUMP_ATTR_MAX_FILES, /* Maximum number of coredump files to keep */ +}; + +enum { + COREDUMP_MEM_NONE, + COREDUMP_MEM_EXC_STACK, + COREDUMP_MEM_ALL_STACKS, + COREDUMP_MEM_ALL +}; + + +typedef struct { + size_t maxThreads; /* zero for unlimited */ + size_t maxStackSize; /* zero for unlimited */ + int memScope; + unsigned int fpContext : 1; + + size_t maxMemChunk; /* zero for unlimited */ + + unsigned int print : 1; + useconds_t printSleep; + char *savepath; + size_t maxFiles; +} coredump_opts_t; + + +extern int coredump_serverthr(coredump_opts_t *opts); + + +extern int coredump_configure(char *cmd, int argc, char *argv[]); + + +extern int coredump_parseStartupArgs(int argc, char *argv[], coredump_opts_t *opts); + + +extern int coredump_printElf(char *path); + +#endif /* _COREDUMP_H_ */ diff --git a/libcoredumpsrv/elf.h b/libcoredumpsrv/elf.h new file mode 100644 index 0000000..7ba1096 --- /dev/null +++ b/libcoredumpsrv/elf.h @@ -0,0 +1,257 @@ +/* + * Phoenix-RTOS + * + * Coredump Server Library + * + * ELF definitions + * + * Copyright 2012, 2025 Phoenix Systems + * Copyright 2001, 2005 Pawel Pisarczyk + * Author: Pawel Pisarczyk, Jakub Klimek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _ELF_H_ +#define _ELF_H_ + +#include + + +typedef unsigned short Elf32_Half; +typedef unsigned int Elf32_Word; +typedef unsigned int Elf32_Addr; +typedef unsigned int Elf32_Off; +typedef int Elf32_Sword; + + +typedef __u16 Elf64_Half; +typedef __u32 Elf64_Word; +typedef __u64 Elf64_Addr; +typedef __u64 Elf64_Off; +typedef __s64 Elf64_Sword; +typedef __u64 Elf64_Xword; + +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASSNONE 0 /* Invalid class */ +#define ELFCLASS32 1 /* 32-bit objects */ +#define ELFCLASS64 2 /* 64-bit objects */ +#define ELFCLASSNUM 3 + +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATANONE 0 /* Invalid data encoding */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ +#define ELFDATA2MSB 2 /* 2's complement, big endian */ +#define ELFDATANUM 3 + +#define EI_VERSION 6 /* File version byte index */ + /* Value must be EV_CURRENT */ + +#define EI_OSABI 7 /* OS ABI identification */ +#define ELFOSABI_NONE 0 /* UNIX System V ABI */ +#define ELFOSABI_SYSV 0 /* Alias. */ +#define ELFOSABI_HPUX 1 /* HP-UX */ +#define ELFOSABI_NETBSD 2 /* NetBSD. */ +#define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */ +#define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */ +#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ +#define ELFOSABI_AIX 7 /* IBM AIX. */ +#define ELFOSABI_IRIX 8 /* SGI Irix. */ +#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ +#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ +#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ +#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ +#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */ +#define ELFOSABI_ARM 97 /* ARM */ +#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ + +#define EI_NIDENT 16 + +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Relocatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ +#define ET_NUM 5 /* Number of defined types */ + +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_DYNSYM 11 +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff + +#define STT_LOPROC 13 +#define STT_HIPROC 15 + +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_GNU_STACK 0x6474e551 +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff + +#define PF_X 0x1 +#define PF_W 0x2 +#define PF_R 0x4 + +#define NT_PRSTATUS 1 +#define NT_FPREGSET 2 +#define NT_AUXV 6 +#define NT_ARM_VFP 0x400 + +#define AT_HWCAP 16 +#define AT_NULL 0 + +#define COMPAT_HWCAP_VFP (1 << 6) +#define COMPAT_HWCAP_NEON (1 << 12) +#define COMPAT_HWCAP_VFPv3 (1 << 13) +#define COMPAT_HWCAP_VFPv3D16 (1 << 14) +#define HWCAP_VFPv3 (COMPAT_HWCAP_VFP | COMPAT_HWCAP_NEON | COMPAT_HWCAP_VFPv3) +#define HWCAP_VFPv3D16 (COMPAT_HWCAP_VFP | COMPAT_HWCAP_VFPv3 | COMPAT_HWCAP_VFPv3D16) + +#pragma pack(push, 1) + +typedef struct { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; + + +typedef struct { + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + + +typedef struct { + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + + +typedef struct { + Elf32_Word n_namesz; + Elf32_Word n_descsz; + Elf32_Word n_type; +} Elf32_Nhdr; + + +typedef struct { + __u32 st_name; + Elf32_Addr st_value; + __u32 st_size; + unsigned char st_info; + unsigned char st_other; + __u16 st_shndx; +} Elf32_Sym; + + +typedef struct { + Elf32_Addr r_offset; + __u32 r_info; +} Elf32_Rel; + + +typedef struct { + Elf32_Addr r_offset; + __u32 r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + + +typedef struct { + unsigned char e_ident[EI_NIDENT]; + Elf64_Half e_type; + Elf64_Half e_machine; + Elf64_Word e_version; + Elf64_Addr e_entry; + Elf64_Off e_phoff; + Elf64_Off e_shoff; + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} Elf64_Ehdr; + + +typedef struct { + Elf64_Word p_type; + Elf64_Word p_flags; + Elf64_Off p_offset; + Elf64_Addr p_vaddr; + Elf64_Addr p_paddr; + Elf64_Xword p_filesz; + Elf64_Xword p_memsz; + Elf64_Xword p_align; +} Elf64_Phdr; + + +typedef struct { + Elf64_Word n_namesz; + Elf64_Word n_descsz; + Elf64_Word n_type; +} Elf64_Nhdr; + + +typedef struct { + Elf64_Word sh_name; + Elf64_Word sh_type; + Elf64_Xword sh_flags; + Elf64_Addr sh_addr; + Elf64_Off sh_offset; + Elf64_Xword sh_size; + Elf64_Word sh_link; + Elf64_Word sh_info; + Elf64_Xword sh_addralign; + Elf64_Xword sh_entsize; +} Elf64_Shdr; + + +#pragma pack(pop) + + +#define ELF32_R_SYM(info) ((info) >> 8) +#define ELF32_R_TYPE(info) ((unsigned char)(info)) +#define ELF32_R_INFO(sym, type) (((sym) << 8) + (unsigned char)(type)) + + +#endif diff --git a/libcoredumpsrv/encoding.c b/libcoredumpsrv/encoding.c new file mode 100644 index 0000000..8f32559 --- /dev/null +++ b/libcoredumpsrv/encoding.c @@ -0,0 +1,73 @@ +/* + * Phoenix-RTOS + * + * Coredump Server Library + * + * Binary data text encoding + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * %LICENSE% + */ + +#include "encoding.h" + +#define LIB_CRC32POLY_LE 0xedb88320 + + +crc32_t enc_crc32NextByte(crc32_t crc, __u8 byte) +{ + int b; + crc = (crc ^ (byte & 0xFF)); + for (b = 0; b < 8; b++) { + crc = (crc >> 1) ^ ((crc & 1) ? LIB_CRC32POLY_LE : 0); + } + return crc; +} + + +crc32_t enc_crc32Finalize(crc32_t crc) +{ + return ~crc; +} + + +static const char base64_table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + +void enc_base64Init(lib_base64_ctx *ctx) +{ + ctx->buf = 0; + ctx->bits = 0; +} + + +size_t enc_base64EncodeByte(lib_base64_ctx *ctx, const __u8 byte) +{ + int i = 0; + ctx->buf <<= 8; + ctx->buf |= byte; + ctx->bits += 8; + + while (ctx->bits >= 6) { + ctx->bits -= 6; + ctx->outBuf[i++] = base64_table[(ctx->buf >> ctx->bits) & 0x3F]; + } + return i; +} + + +size_t enc_base64Finalize(lib_base64_ctx *ctx) +{ + int i = 0; + if (ctx->bits > 0) { + ctx->outBuf[i++] = base64_table[(ctx->buf << (6 - ctx->bits)) & 0x3F]; + ctx->outBuf[i++] = '='; + if (ctx->bits == 2) { + ctx->outBuf[i++] = '='; + } + } + return i; +} diff --git a/libcoredumpsrv/encoding.h b/libcoredumpsrv/encoding.h new file mode 100644 index 0000000..7a193b9 --- /dev/null +++ b/libcoredumpsrv/encoding.h @@ -0,0 +1,46 @@ +/* + * Phoenix-RTOS + * + * Coredump Server Library + * + * Binary data text encoding + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * %LICENSE% + */ + +#ifndef _ENCODING_H_ +#define _ENCODING_H_ + +#include "phoenix/types.h" +#include + +typedef __u32 crc32_t; + +#define LIB_CRC32_INIT 0xffffffff + + +extern crc32_t enc_crc32NextByte(crc32_t crc, __u8 byte); + + +extern crc32_t enc_crc32Finalize(crc32_t crc); + + +typedef struct { + __u32 buf; + int bits; + char outBuf[3]; +} lib_base64_ctx; + + +extern void enc_base64Init(lib_base64_ctx *ctx); + + +extern size_t enc_base64EncodeByte(lib_base64_ctx *ctx, const __u8 byte); + + +extern size_t enc_base64Finalize(lib_base64_ctx *ctx); + +#endif /* _ENCODING_H_ */ diff --git a/libcoredumpsrv/hal.h b/libcoredumpsrv/hal.h new file mode 100644 index 0000000..47095e3 --- /dev/null +++ b/libcoredumpsrv/hal.h @@ -0,0 +1,52 @@ +/* + * Phoenix-RTOS + * + * Coredump Server Library + * + * Architecture specific definitions + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * %LICENSE% + */ + +#ifndef _HAL_H_ +#define _HAL_H_ + +#include "cpu.h" +#include "coredump.h" + +#include + + +#define PRINT_ERR(args...) fprintf(stderr, "Coredump server: " args) + + +typedef struct cpu_context_t cpu_context_t; + +typedef struct elf_prstatus elf_prstatus; + +typedef struct elf_thread_aux elf_thread_aux; + +typedef struct elf_proc_aux elf_proc_aux; + + +extern int hal_fillPrStatus(cpu_context_t *ctx, elf_prstatus *prstatus, __u32 memRecvPort, const coredump_opts_t *opts); + + +extern void hal_createThreadAuxNotes(cpu_context_t *ctx, elf_thread_aux *buff, const coredump_opts_t *opts); + + +extern size_t hal_threadAuxNotesSize(const coredump_opts_t *opts); + + +extern void hal_createProcAuxNotes(elf_proc_aux *buff, const coredump_opts_t *opts); + + +extern size_t hal_procAuxNotesSize(const coredump_opts_t *opts); + + +extern void *hal_cpuGetUserSP(cpu_context_t *ctx); + +#endif /* _HAL_H_ */ diff --git a/libcoredumpsrv/hal/aarch64/cpu.c b/libcoredumpsrv/hal/aarch64/cpu.c new file mode 100644 index 0000000..c43141f --- /dev/null +++ b/libcoredumpsrv/hal/aarch64/cpu.c @@ -0,0 +1,70 @@ +/* + * Phoenix-RTOS + * + * Coredump server library + * Architecture specific functions + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include "hal.h" +#include + + +int hal_fillPrStatus(cpu_context_t *ctx, elf_prstatus *prstatus, __u32 memRecvPort, const coredump_opts_t *opts) +{ + memcpy(prstatus->pr_reg.x, ctx->x, sizeof(prstatus->pr_reg.x)); + prstatus->pr_reg.sp = ctx->sp; + prstatus->pr_reg.pc = ctx->pc; + prstatus->pr_reg.psr = ctx->psr; + return 0; +} + + +void hal_createThreadAuxNotes(cpu_context_t *ctx, elf_thread_aux *note, const coredump_opts_t *opts) +{ + static const char FPREGSET_NAME[] = "CORE"; + if (opts->fpContext == 0) { + return; + } + note->nhdr.n_namesz = sizeof(FPREGSET_NAME); + note->nhdr.n_descsz = sizeof(note->fpuContext); + note->nhdr.n_type = NT_FPREGSET; + memcpy(note->name, FPREGSET_NAME, sizeof(FPREGSET_NAME)); + + memcpy(note->fpuContext.freg, &ctx->freg, sizeof(ctx->freg)); + note->fpuContext.fpsr = ctx->fpsr; + note->fpuContext.fpcr = ctx->fpcr; +} + + +void hal_createProcAuxNotes(elf_proc_aux *buff, const coredump_opts_t *opts) +{ + return; +} + + +size_t hal_threadAuxNotesSize(const coredump_opts_t *opts) +{ + if (opts->fpContext) { + return sizeof(elf_thread_aux); + } + return 0; +} + + +size_t hal_procAuxNotesSize(const coredump_opts_t *opts) +{ + return 0; +} + + +void *hal_cpuGetUserSP(cpu_context_t *ctx) +{ + return (void *)ctx->sp; +} diff --git a/libcoredumpsrv/hal/aarch64/cpu.h b/libcoredumpsrv/hal/aarch64/cpu.h new file mode 100644 index 0000000..83cfe38 --- /dev/null +++ b/libcoredumpsrv/hal/aarch64/cpu.h @@ -0,0 +1,90 @@ +/* + * Phoenix-RTOS + * + * Coredump server library + * Architecture specific structures + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _CPU_H_ +#define _CPU_H_ + +#include "elf.h" +#include + +#define HAL_ELF_MACHINE 0xB7 + +struct pr_regs { + __u64 x[31]; /* General purpose registers */ + __u64 sp; + __u64 pc; + __u64 psr; +}; + + +struct elf_prstatus { + struct elf_siginfo { + int si_signo; + int si_code; + int si_errno; + } pr_info; + short pr_cursig; + unsigned long pr_sigpend; + unsigned long pr_sighold; + pid_t pr_pid; + pid_t pr_ppid; + pid_t pr_pgrp; + pid_t pr_sid; + struct timeval { + long tv_sec; + long tv_usec; + } pr_utime; + struct timeval pr_stime; + struct timeval pr_cutime; + struct timeval pr_cstime; + struct pr_regs pr_reg; + int pr_fpvalid; +}; + + +struct elf_thread_aux { + Elf32_Nhdr nhdr; + char name[8]; + + struct { + __u64 freg[2 * 32]; + __u32 fpsr; + __u32 fpcr; + __u64 pad; + } fpuContext; +} __attribute__((packed)); + + +struct elf_proc_aux { +}; + + +/* CPU context saved by interrupt handlers on thread kernel stack */ +struct cpu_context_t { + __u64 savesp; + __u64 cpacr; +#ifndef __SOFTFP__ + /* Advanced SIMD/FPU */ + __u64 fpcr; + __u64 fpsr; + __u64 freg[2 * 32]; +#endif + + __u64 psr; + __u64 pc; + __u64 x[31]; /* General purpose registers */ + __u64 sp; +}; + +#endif /* _CPU_H_ */ diff --git a/libcoredumpsrv/hal/armv7a/cpu.c b/libcoredumpsrv/hal/armv7a/cpu.c new file mode 100644 index 0000000..3f56f5e --- /dev/null +++ b/libcoredumpsrv/hal/armv7a/cpu.c @@ -0,0 +1,97 @@ +/* + * Phoenix-RTOS + * + * Coredump server library + * Architecture specific functions + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include "hal.h" +#include + + +int hal_fillPrStatus(cpu_context_t *ctx, elf_prstatus *prstatus, __u32 memRecvPort, const coredump_opts_t *opts) +{ + prstatus->pr_reg.r0 = ctx->r0; + prstatus->pr_reg.r1 = ctx->r1; + prstatus->pr_reg.r2 = ctx->r2; + prstatus->pr_reg.r3 = ctx->r3; + prstatus->pr_reg.r4 = ctx->r4; + prstatus->pr_reg.r5 = ctx->r5; + prstatus->pr_reg.r6 = ctx->r6; + prstatus->pr_reg.r7 = ctx->r7; + prstatus->pr_reg.r8 = ctx->r8; + prstatus->pr_reg.r9 = ctx->r9; + prstatus->pr_reg.r10 = ctx->r10; + prstatus->pr_reg.fp = ctx->fp; + prstatus->pr_reg.ip = ctx->ip; + prstatus->pr_reg.sp = ctx->sp; + prstatus->pr_reg.lr = ctx->lr; + prstatus->pr_reg.pc = ctx->pc; + prstatus->pr_reg.psr = ctx->psr; + return 0; +} + + +void hal_createThreadAuxNotes(cpu_context_t *ctx, elf_thread_aux *note, const coredump_opts_t *opts) +{ + static const char ARMVFP_NAME[] = "LINUX"; + if (opts->fpContext == 0) { + return; + } + note->nhdr.n_namesz = sizeof(ARMVFP_NAME); + note->nhdr.n_descsz = sizeof(note->fpuContext); + note->nhdr.n_type = NT_ARM_VFP; + memcpy(note->name, ARMVFP_NAME, sizeof(ARMVFP_NAME)); + memcpy(note->fpuContext.freg, ctx->freg, sizeof(ctx->freg)); + note->fpuContext.fpsr = ctx->fpsr; +} + + +void hal_createProcAuxNotes(elf_proc_aux *note, const coredump_opts_t *opts) +{ + static const char AUXV_NAME[] = "CORE"; + if (opts->fpContext == 0) { + return; + } + + note->nhdr.n_namesz = sizeof(AUXV_NAME); + note->nhdr.n_descsz = sizeof(note->auxv); + note->nhdr.n_type = NT_AUXV; + memcpy(note->name, AUXV_NAME, sizeof(AUXV_NAME)); + + note->auxv[0].a_type = AT_HWCAP; + note->auxv[0].a_val = HWCAP_VFPv3; + note->auxv[1].a_type = AT_NULL; + note->auxv[1].a_val = 0; +} + + +size_t hal_threadAuxNotesSize(const coredump_opts_t *opts) +{ + if (opts->fpContext) { + return sizeof(elf_thread_aux); + } + return 0; +} + + +size_t hal_procAuxNotesSize(const coredump_opts_t *opts) +{ + if (opts->fpContext) { + return sizeof(elf_proc_aux); + } + return 0; +} + + +void *hal_cpuGetUserSP(cpu_context_t *ctx) +{ + return (void *)ctx->sp; +} diff --git a/libcoredumpsrv/hal/armv7a/cpu.h b/libcoredumpsrv/hal/armv7a/cpu.h new file mode 100644 index 0000000..54ddfda --- /dev/null +++ b/libcoredumpsrv/hal/armv7a/cpu.h @@ -0,0 +1,123 @@ +/* + * Phoenix-RTOS + * + * Coredump server library + * Architecture specific structures + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _CPU_H_ +#define _CPU_H_ + +#include "elf.h" +#include + +#define HAL_ELF_MACHINE 0x28 + +struct pr_regs { + __u32 r0; + __u32 r1; + __u32 r2; + __u32 r3; + __u32 r4; + __u32 r5; + __u32 r6; + __u32 r7; + __u32 r8; + __u32 r9; + __u32 r10; + __u32 fp; + __u32 ip; + __u32 sp; + __u32 lr; + __u32 pc; + __u32 psr; + + __u32 pad; +}; + + +struct elf_prstatus { + struct elf_siginfo { + int si_signo; + int si_code; + int si_errno; + } pr_info; + short pr_cursig; + unsigned long pr_sigpend; + unsigned long pr_sighold; + pid_t pr_pid; + pid_t pr_ppid; + pid_t pr_pgrp; + pid_t pr_sid; + struct timeval { + long tv_sec; + long tv_usec; + } pr_utime; + struct timeval pr_stime; + struct timeval pr_cutime; + struct timeval pr_cstime; + struct pr_regs pr_reg; + int pr_fpvalid; +}; + + +struct elf_thread_aux { + Elf32_Nhdr nhdr; + char name[8]; + + struct { + __u32 freg[32 * 2]; + __u32 fpsr; + } fpuContext; +}; + + +struct elf_proc_aux { + Elf32_Nhdr nhdr; + char name[8]; + struct { + __u32 a_type; + __u32 a_val; + } auxv[2]; +}; + + +/* CPU context saved by interrupt handlers on thread kernel stack */ +struct cpu_context_t { + __u32 savesp; + __u32 padding; + + /* Floating point coprocessor context */ + __u32 fpsr; + __u32 freg[32 * 2]; + + __u32 psr; + + __u32 r0; + __u32 r1; + __u32 r2; + __u32 r3; + __u32 r4; + __u32 r5; + __u32 r6; + __u32 r7; + __u32 r8; + __u32 r9; + __u32 r10; + + __u32 fp; + __u32 ip; + __u32 sp; + __u32 lr; + + __u32 pc; +}; + +#endif /* _CPU_H_ */ diff --git a/libcoredumpsrv/hal/armv7m/cpu.c b/libcoredumpsrv/hal/armv7m/cpu.c new file mode 100644 index 0000000..d52eeb5 --- /dev/null +++ b/libcoredumpsrv/hal/armv7m/cpu.c @@ -0,0 +1,177 @@ +/* + * Phoenix-RTOS + * + * Coredump server library + * Architecture specific functions + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include "cpu.h" +#include "hal.h" +#include "kernel_comm.h" + +#include + +#ifdef CPU_IMXRT +#define RET_THREAD_PSP 0xffffffed +#define SIZE_FPUCTX 0x48 +#else +#define RET_THREAD_PSP 0xfffffffd +#endif + +int hal_fillPrStatus(cpu_context_t *ctx, elf_prstatus *prstatus, __u32 memRecvPort, const coredump_opts_t *opts) +{ + __u32 psp = ctx->psp; + + /* If we came from userspace HW ctx in on psp stack */ + if (ctx->irq_ret == RET_THREAD_PSP) { + msg_t msg; + msg_rid_t rid; + msg.oid.port = memRecvPort; + msg.oid.id = 0; + + int ret = coredump_getMemory((void *)ctx->psp, sizeof(struct cpu_hwContext_t), &msg, &rid); + if (ret != 0) { + PRINT_ERR("Failed to retrieve missing hw context with error %d\n", ret); + return ret; + } + memcpy(&ctx->hwctx, msg.i.data, sizeof(struct cpu_hwContext_t)); + coredump_putMemory(&msg, rid); + } + psp += sizeof(struct cpu_hwContext_t); +#ifdef CPU_IMXRT /* FIXME - check if FPU was enabled instead */ + psp += SIZE_FPUCTX; +#endif + + prstatus->pr_reg.r0 = ctx->hwctx.r0; + prstatus->pr_reg.r1 = ctx->hwctx.r1; + prstatus->pr_reg.r2 = ctx->hwctx.r2; + prstatus->pr_reg.r3 = ctx->hwctx.r3; + prstatus->pr_reg.r4 = ctx->r4; + prstatus->pr_reg.r5 = ctx->r5; + prstatus->pr_reg.r6 = ctx->r6; + prstatus->pr_reg.r7 = ctx->r7; + prstatus->pr_reg.r8 = ctx->r8; + prstatus->pr_reg.r9 = ctx->r9; + prstatus->pr_reg.r10 = ctx->r10; + prstatus->pr_reg.fp = ctx->r11; + prstatus->pr_reg.ip = ctx->hwctx.r12; + prstatus->pr_reg.sp = psp; + prstatus->pr_reg.lr = ctx->hwctx.lr; + prstatus->pr_reg.pc = ctx->hwctx.pc; + prstatus->pr_reg.psr = ctx->hwctx.psr; + return 0; +} + + +void hal_createThreadAuxNotes(cpu_context_t *ctx, elf_thread_aux *note, const coredump_opts_t *opts) +{ +#ifndef CPU_IMXRT + return; +#else + static const char ARMVFP_NAME[] = "LINUX"; + if (opts->fpContext == 0) { + return; + } + note->nhdr.n_namesz = sizeof(ARMVFP_NAME); + note->nhdr.n_descsz = sizeof(note->fpuContext); + note->nhdr.n_type = NT_ARM_VFP; + memcpy(note->name, ARMVFP_NAME, sizeof(ARMVFP_NAME)); + + note->fpuContext.freg[0] = ctx->s0; + note->fpuContext.freg[1] = ctx->s1; + note->fpuContext.freg[2] = ctx->s2; + note->fpuContext.freg[3] = ctx->s3; + note->fpuContext.freg[4] = ctx->s4; + note->fpuContext.freg[5] = ctx->s5; + note->fpuContext.freg[6] = ctx->s6; + note->fpuContext.freg[7] = ctx->s7; + note->fpuContext.freg[8] = ctx->s8; + note->fpuContext.freg[9] = ctx->s9; + note->fpuContext.freg[10] = ctx->s10; + note->fpuContext.freg[11] = ctx->s11; + note->fpuContext.freg[12] = ctx->s12; + note->fpuContext.freg[13] = ctx->s13; + note->fpuContext.freg[14] = ctx->s14; + note->fpuContext.freg[15] = ctx->s15; + note->fpuContext.freg[16] = ctx->s16; + note->fpuContext.freg[17] = ctx->s17; + note->fpuContext.freg[18] = ctx->s18; + note->fpuContext.freg[19] = ctx->s19; + note->fpuContext.freg[20] = ctx->s20; + note->fpuContext.freg[21] = ctx->s21; + note->fpuContext.freg[22] = ctx->s22; + note->fpuContext.freg[23] = ctx->s23; + note->fpuContext.freg[24] = ctx->s24; + note->fpuContext.freg[25] = ctx->s25; + note->fpuContext.freg[26] = ctx->s26; + note->fpuContext.freg[27] = ctx->s27; + note->fpuContext.freg[28] = ctx->s28; + note->fpuContext.freg[29] = ctx->s29; + note->fpuContext.freg[30] = ctx->s30; + note->fpuContext.freg[31] = ctx->s31; + note->fpuContext.fpsr = ctx->fpscr; +#endif /* CPU_IMXRT */ +} + + +void hal_createProcAuxNotes(elf_proc_aux *note, const coredump_opts_t *opts) +{ +#ifndef CPU_IMXRT + return; +#endif + if (opts->fpContext == 0) { + return; + } + static const char AUXV_NAME[] = "CORE"; + + note->nhdr.n_namesz = sizeof(AUXV_NAME); + note->nhdr.n_descsz = sizeof(note->auxv); + note->nhdr.n_type = NT_AUXV; + memcpy(note->name, AUXV_NAME, sizeof(AUXV_NAME)); + + note->auxv[0].a_type = AT_HWCAP; + note->auxv[0].a_val = HWCAP_VFPv3D16; + note->auxv[1].a_type = AT_NULL; + note->auxv[1].a_val = 0; +} + + +size_t hal_threadAuxNotesSize(const coredump_opts_t *opts) +{ +#ifdef CPU_IMXRT + if (opts->fpContext) { + return sizeof(elf_thread_aux); + } +#endif + return 0; +} + + +size_t hal_procAuxNotesSize(const coredump_opts_t *opts) +{ +#ifdef CPU_IMXRT + if (opts->fpContext) { + return sizeof(elf_proc_aux); + } +#endif + return 0; +} + + +void *hal_cpuGetUserSP(cpu_context_t *ctx) +{ + + __u32 psp = ctx->psp; + psp += sizeof(struct cpu_hwContext_t); +#ifdef CPU_IMXRT /* FIXME - check if FPU was enabled instead */ + psp += SIZE_FPUCTX; +#endif + return (void *)psp; +} diff --git a/libcoredumpsrv/hal/armv7m/cpu.h b/libcoredumpsrv/hal/armv7m/cpu.h new file mode 100644 index 0000000..f104792 --- /dev/null +++ b/libcoredumpsrv/hal/armv7m/cpu.h @@ -0,0 +1,165 @@ +/* + * Phoenix-RTOS + * + * Coredump server library + * Architecture specific structures + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _CPU_H_ +#define _CPU_H_ + +#include "elf.h" +#include + +#define HAL_ELF_MACHINE 0x28 + +struct pr_regs { + __u32 r0; + __u32 r1; + __u32 r2; + __u32 r3; + __u32 r4; + __u32 r5; + __u32 r6; + __u32 r7; + __u32 r8; + __u32 r9; + __u32 r10; + __u32 fp; + __u32 ip; + __u32 sp; + __u32 lr; + __u32 pc; + __u32 psr; + + __u32 pad; +}; + + +struct elf_prstatus { + struct elf_siginfo { + int si_signo; + int si_code; + int si_errno; + } pr_info; + short pr_cursig; + unsigned long pr_sigpend; + unsigned long pr_sighold; + pid_t pr_pid; + pid_t pr_ppid; + pid_t pr_pgrp; + pid_t pr_sid; + struct timeval { + long tv_sec; + long tv_usec; + } pr_utime; + struct timeval pr_stime; + struct timeval pr_cutime; + struct timeval pr_cstime; + struct pr_regs pr_reg; + int pr_fpvalid; +}; + + +struct elf_thread_aux { + Elf32_Nhdr nhdr; + char name[8]; + + struct { + __u32 freg[16 * 2]; + __u32 pad[16 * 2]; + __u32 fpsr; + } fpuContext; +}; + + +struct elf_proc_aux { + Elf32_Nhdr nhdr; + char name[8]; + struct { + __u32 a_type; + __u32 a_val; + } auxv[2]; +}; + + +/* CPU context saved by interrupt handlers on thread kernel stack */ +struct cpu_context_t { + __u32 savesp; + __u32 fpuctx; + + /* Saved by ISR */ + __u32 psp; + __u32 r4; + __u32 r5; + __u32 r6; + __u32 r7; + __u32 r8; + __u32 r9; + __u32 r10; + __u32 r11; + __u32 irq_ret; + + __u32 msp; + __u32 pad0; + +#ifdef CPU_IMXRT + __u32 s16; + __u32 s17; + __u32 s18; + __u32 s19; + __u32 s20; + __u32 s21; + __u32 s22; + __u32 s23; + __u32 s24; + __u32 s25; + __u32 s26; + __u32 s27; + __u32 s28; + __u32 s29; + __u32 s30; + __u32 s31; +#endif + + struct cpu_hwContext_t { + __u32 r0; + __u32 r1; + __u32 r2; + __u32 r3; + __u32 r12; + __u32 lr; + __u32 pc; + __u32 psr; + } hwctx; + +#ifdef CPU_IMXRT + __u32 s0; + __u32 s1; + __u32 s2; + __u32 s3; + __u32 s4; + __u32 s5; + __u32 s6; + __u32 s7; + __u32 s8; + __u32 s9; + __u32 s10; + __u32 s11; + __u32 s12; + __u32 s13; + __u32 s14; + __u32 s15; + __u32 fpscr; + __u32 pad1; +#endif +}; + +#endif /* _CPU_H_ */ diff --git a/libcoredumpsrv/hal/armv7r/cpu.c b/libcoredumpsrv/hal/armv7r/cpu.c new file mode 100644 index 0000000..a1f458c --- /dev/null +++ b/libcoredumpsrv/hal/armv7r/cpu.c @@ -0,0 +1,97 @@ +/* + * Phoenix-RTOS + * + * Coredump server library + * Architecture specific functions + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include "hal.h" +#include + + +int hal_fillPrStatus(cpu_context_t *ctx, elf_prstatus *prstatus, __u32 memRecvPort, const coredump_opts_t *opts) +{ + prstatus->pr_reg.r0 = ctx->r0; + prstatus->pr_reg.r1 = ctx->r1; + prstatus->pr_reg.r2 = ctx->r2; + prstatus->pr_reg.r3 = ctx->r3; + prstatus->pr_reg.r4 = ctx->r4; + prstatus->pr_reg.r5 = ctx->r5; + prstatus->pr_reg.r6 = ctx->r6; + prstatus->pr_reg.r7 = ctx->r7; + prstatus->pr_reg.r8 = ctx->r8; + prstatus->pr_reg.r9 = ctx->r9; + prstatus->pr_reg.r10 = ctx->r10; + prstatus->pr_reg.fp = ctx->fp; + prstatus->pr_reg.ip = ctx->ip; + prstatus->pr_reg.sp = ctx->sp; + prstatus->pr_reg.lr = ctx->lr; + prstatus->pr_reg.pc = ctx->pc; + prstatus->pr_reg.psr = ctx->psr; + return 0; +} + + +void hal_createThreadAuxNotes(cpu_context_t *ctx, elf_thread_aux *note, const coredump_opts_t *opts) +{ + static const char ARMVFP_NAME[] = "LINUX"; + if (opts->fpContext == 0) { + return; + } + note->nhdr.n_namesz = sizeof(ARMVFP_NAME); + note->nhdr.n_descsz = sizeof(note->fpuContext); + note->nhdr.n_type = NT_ARM_VFP; + memcpy(note->name, ARMVFP_NAME, sizeof(ARMVFP_NAME)); + memcpy(note->fpuContext.freg, ctx->freg, sizeof(ctx->freg)); + note->fpuContext.fpsr = ctx->fpsr; +} + + +void hal_createProcAuxNotes(elf_proc_aux *note, const coredump_opts_t *opts) +{ + static const char AUXV_NAME[] = "CORE"; + if (opts->fpContext == 0) { + return; + } + + note->nhdr.n_namesz = sizeof(AUXV_NAME); + note->nhdr.n_descsz = sizeof(note->auxv); + note->nhdr.n_type = NT_AUXV; + memcpy(note->name, AUXV_NAME, sizeof(AUXV_NAME)); + + note->auxv[0].a_type = AT_HWCAP; + note->auxv[0].a_val = HWCAP_VFPv3D16; + note->auxv[1].a_type = AT_NULL; + note->auxv[1].a_val = 0; +} + + +size_t hal_threadAuxNotesSize(const coredump_opts_t *opts) +{ + if (opts->fpContext) { + return sizeof(elf_thread_aux); + } + return 0; +} + + +size_t hal_procAuxNotesSize(const coredump_opts_t *opts) +{ + if (opts->fpContext) { + return sizeof(elf_proc_aux); + } + return 0; +} + + +void *hal_cpuGetUserSP(cpu_context_t *ctx) +{ + return (void *)ctx->sp; +} diff --git a/libcoredumpsrv/hal/armv7r/cpu.h b/libcoredumpsrv/hal/armv7r/cpu.h new file mode 100644 index 0000000..ae860d4 --- /dev/null +++ b/libcoredumpsrv/hal/armv7r/cpu.h @@ -0,0 +1,124 @@ +/* + * Phoenix-RTOS + * + * Coredump server library + * Architecture specific structures + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _CPU_H_ +#define _CPU_H_ + +#include "elf.h" +#include + +#define HAL_ELF_MACHINE 0x28 + +struct pr_regs { + __u32 r0; + __u32 r1; + __u32 r2; + __u32 r3; + __u32 r4; + __u32 r5; + __u32 r6; + __u32 r7; + __u32 r8; + __u32 r9; + __u32 r10; + __u32 fp; + __u32 ip; + __u32 sp; + __u32 lr; + __u32 pc; + __u32 psr; + + __u32 pad; +}; + + +struct elf_prstatus { + struct elf_siginfo { + int si_signo; + int si_code; + int si_errno; + } pr_info; + short pr_cursig; + unsigned long pr_sigpend; + unsigned long pr_sighold; + pid_t pr_pid; + pid_t pr_ppid; + pid_t pr_pgrp; + pid_t pr_sid; + struct timeval { + long tv_sec; + long tv_usec; + } pr_utime; + struct timeval pr_stime; + struct timeval pr_cutime; + struct timeval pr_cstime; + struct pr_regs pr_reg; + int pr_fpvalid; +}; + + +struct elf_thread_aux { + Elf32_Nhdr nhdr; + char name[8]; + + struct { + __u32 freg[16 * 2]; + __u32 pad[16 * 2]; + __u32 fpsr; + } fpuContext; +}; + + +struct elf_proc_aux { + Elf32_Nhdr nhdr; + char name[8]; + struct { + __u32 a_type; + __u32 a_val; + } auxv[2]; +}; + + +/* CPU context saved by interrupt handlers on thread kernel stack */ +struct cpu_context_t { + __u32 savesp; + __u32 padding; + + /* FPU context */ + __u32 fpsr; + __u32 freg[16 * 2]; + + __u32 psr; + + __u32 r0; + __u32 r1; + __u32 r2; + __u32 r3; + __u32 r4; + __u32 r5; + __u32 r6; + __u32 r7; + __u32 r8; + __u32 r9; + __u32 r10; + + __u32 fp; + __u32 ip; + __u32 sp; + __u32 lr; + + __u32 pc; +}; + +#endif /* _CPU_H_ */ diff --git a/libcoredumpsrv/hal/armv8m/cpu.c b/libcoredumpsrv/hal/armv8m/cpu.c new file mode 100644 index 0000000..df90431 --- /dev/null +++ b/libcoredumpsrv/hal/armv8m/cpu.c @@ -0,0 +1,93 @@ +/* + * Phoenix-RTOS + * + * Coredump server library + * Architecture specific functions + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include "cpu.h" +#include "hal.h" +#include "kernel_comm.h" + +#include + +#define RET_THREAD_PSP 0xfffffffdu + +int hal_fillPrStatus(cpu_context_t *ctx, elf_prstatus *prstatus, __u32 memRecvPort, const coredump_opts_t *opts) +{ + __u32 psp = ctx->psp; + + /* If we came from userspace HW ctx in on psp stack */ + if (ctx->irq_ret == RET_THREAD_PSP) { + msg_t msg; + msg_rid_t rid; + msg.oid.port = memRecvPort; + msg.oid.id = 0; + + int ret = coredump_getMemory((void *)ctx->psp, sizeof(struct cpu_hwContext_t), &msg, &rid); + if (ret != 0) { + PRINT_ERR("Failed to retrieve missing hw context with error %d\n", ret); + return ret; + } + memcpy(&ctx->hwctx, msg.i.data, sizeof(struct cpu_hwContext_t)); + coredump_putMemory(&msg, rid); + + psp += sizeof(struct cpu_hwContext_t); + } + + prstatus->pr_reg.r0 = ctx->hwctx.r0; + prstatus->pr_reg.r1 = ctx->hwctx.r1; + prstatus->pr_reg.r2 = ctx->hwctx.r2; + prstatus->pr_reg.r3 = ctx->hwctx.r3; + prstatus->pr_reg.r4 = ctx->r4; + prstatus->pr_reg.r5 = ctx->r5; + prstatus->pr_reg.r6 = ctx->r6; + prstatus->pr_reg.r7 = ctx->r7; + prstatus->pr_reg.r8 = ctx->r8; + prstatus->pr_reg.r9 = ctx->r9; + prstatus->pr_reg.r10 = ctx->r10; + prstatus->pr_reg.fp = ctx->r11; + prstatus->pr_reg.ip = ctx->hwctx.r12; + prstatus->pr_reg.sp = psp; + prstatus->pr_reg.lr = ctx->hwctx.lr; + prstatus->pr_reg.pc = ctx->hwctx.pc; + prstatus->pr_reg.psr = ctx->hwctx.psr; + return 0; +} + + +void hal_createThreadAuxNotes(cpu_context_t *ctx, elf_thread_aux *note, const coredump_opts_t *opts) +{ + return; +} + + +void hal_createProcAuxNotes(elf_proc_aux *note, const coredump_opts_t *opts) +{ + return; +} + + +size_t hal_threadAuxNotesSize(const coredump_opts_t *opts) +{ + return 0; +} + + +size_t hal_procAuxNotesSize(const coredump_opts_t *opts) +{ + return 0; +} + + +void *hal_cpuGetUserSP(cpu_context_t *ctx) +{ + return (void *)ctx->psp; +} diff --git a/libcoredumpsrv/hal/armv8m/cpu.h b/libcoredumpsrv/hal/armv8m/cpu.h new file mode 100644 index 0000000..cfecf17 --- /dev/null +++ b/libcoredumpsrv/hal/armv8m/cpu.h @@ -0,0 +1,125 @@ +/* + * Phoenix-RTOS + * + * Coredump server library + * Architecture specific structures + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _CPU_H_ +#define _CPU_H_ + +#include "elf.h" +#include + +#define HAL_ELF_MACHINE 0x28 + +struct pr_regs { + __u32 r0; + __u32 r1; + __u32 r2; + __u32 r3; + __u32 r4; + __u32 r5; + __u32 r6; + __u32 r7; + __u32 r8; + __u32 r9; + __u32 r10; + __u32 fp; + __u32 ip; + __u32 sp; + __u32 lr; + __u32 pc; + __u32 psr; + + __u32 pad; +}; + + +struct elf_prstatus { + struct elf_siginfo { + int si_signo; + int si_code; + int si_errno; + } pr_info; + short pr_cursig; + unsigned long pr_sigpend; + unsigned long pr_sighold; + pid_t pr_pid; + pid_t pr_ppid; + pid_t pr_pgrp; + pid_t pr_sid; + struct timeval { + long tv_sec; + long tv_usec; + } pr_utime; + struct timeval pr_stime; + struct timeval pr_cutime; + struct timeval pr_cstime; + struct pr_regs pr_reg; + int pr_fpvalid; +}; + + +struct elf_thread_aux { + Elf32_Nhdr nhdr; + char name[8]; + + struct { + __u32 freg[16 * 2]; + __u32 pad[16 * 2]; + __u32 fpsr; + } fpuContext; +}; + + +struct elf_proc_aux { + Elf32_Nhdr nhdr; + char name[8]; + struct { + __u32 a_type; + __u32 a_val; + } auxv[2]; +}; + + +/* CPU context saved by interrupt handlers on thread kernel stack */ +struct cpu_context_t { + __u32 savesp; + __u32 fpuctx; + + /* Saved by ISR */ + __u32 psp; + __u32 r4; + __u32 r5; + __u32 r6; + __u32 r7; + __u32 r8; + __u32 r9; + __u32 r10; + __u32 r11; + __u32 irq_ret; + + __u32 msp; + __u32 pad0; + + struct cpu_hwContext_t { + __u32 r0; + __u32 r1; + __u32 r2; + __u32 r3; + __u32 r12; + __u32 lr; + __u32 pc; + __u32 psr; + } hwctx; +}; + +#endif /* _CPU_H_ */ diff --git a/libcoredumpsrv/hal/armv8r/cpu.c b/libcoredumpsrv/hal/armv8r/cpu.c new file mode 100644 index 0000000..3f56f5e --- /dev/null +++ b/libcoredumpsrv/hal/armv8r/cpu.c @@ -0,0 +1,97 @@ +/* + * Phoenix-RTOS + * + * Coredump server library + * Architecture specific functions + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include "hal.h" +#include + + +int hal_fillPrStatus(cpu_context_t *ctx, elf_prstatus *prstatus, __u32 memRecvPort, const coredump_opts_t *opts) +{ + prstatus->pr_reg.r0 = ctx->r0; + prstatus->pr_reg.r1 = ctx->r1; + prstatus->pr_reg.r2 = ctx->r2; + prstatus->pr_reg.r3 = ctx->r3; + prstatus->pr_reg.r4 = ctx->r4; + prstatus->pr_reg.r5 = ctx->r5; + prstatus->pr_reg.r6 = ctx->r6; + prstatus->pr_reg.r7 = ctx->r7; + prstatus->pr_reg.r8 = ctx->r8; + prstatus->pr_reg.r9 = ctx->r9; + prstatus->pr_reg.r10 = ctx->r10; + prstatus->pr_reg.fp = ctx->fp; + prstatus->pr_reg.ip = ctx->ip; + prstatus->pr_reg.sp = ctx->sp; + prstatus->pr_reg.lr = ctx->lr; + prstatus->pr_reg.pc = ctx->pc; + prstatus->pr_reg.psr = ctx->psr; + return 0; +} + + +void hal_createThreadAuxNotes(cpu_context_t *ctx, elf_thread_aux *note, const coredump_opts_t *opts) +{ + static const char ARMVFP_NAME[] = "LINUX"; + if (opts->fpContext == 0) { + return; + } + note->nhdr.n_namesz = sizeof(ARMVFP_NAME); + note->nhdr.n_descsz = sizeof(note->fpuContext); + note->nhdr.n_type = NT_ARM_VFP; + memcpy(note->name, ARMVFP_NAME, sizeof(ARMVFP_NAME)); + memcpy(note->fpuContext.freg, ctx->freg, sizeof(ctx->freg)); + note->fpuContext.fpsr = ctx->fpsr; +} + + +void hal_createProcAuxNotes(elf_proc_aux *note, const coredump_opts_t *opts) +{ + static const char AUXV_NAME[] = "CORE"; + if (opts->fpContext == 0) { + return; + } + + note->nhdr.n_namesz = sizeof(AUXV_NAME); + note->nhdr.n_descsz = sizeof(note->auxv); + note->nhdr.n_type = NT_AUXV; + memcpy(note->name, AUXV_NAME, sizeof(AUXV_NAME)); + + note->auxv[0].a_type = AT_HWCAP; + note->auxv[0].a_val = HWCAP_VFPv3; + note->auxv[1].a_type = AT_NULL; + note->auxv[1].a_val = 0; +} + + +size_t hal_threadAuxNotesSize(const coredump_opts_t *opts) +{ + if (opts->fpContext) { + return sizeof(elf_thread_aux); + } + return 0; +} + + +size_t hal_procAuxNotesSize(const coredump_opts_t *opts) +{ + if (opts->fpContext) { + return sizeof(elf_proc_aux); + } + return 0; +} + + +void *hal_cpuGetUserSP(cpu_context_t *ctx) +{ + return (void *)ctx->sp; +} diff --git a/libcoredumpsrv/hal/armv8r/cpu.h b/libcoredumpsrv/hal/armv8r/cpu.h new file mode 100644 index 0000000..2e0c51d --- /dev/null +++ b/libcoredumpsrv/hal/armv8r/cpu.h @@ -0,0 +1,123 @@ +/* + * Phoenix-RTOS + * + * Coredump server library + * Architecture specific structures + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _CPU_H_ +#define _CPU_H_ + +#include "elf.h" +#include + +#define HAL_ELF_MACHINE 0x28 + +struct pr_regs { + __u32 r0; + __u32 r1; + __u32 r2; + __u32 r3; + __u32 r4; + __u32 r5; + __u32 r6; + __u32 r7; + __u32 r8; + __u32 r9; + __u32 r10; + __u32 fp; + __u32 ip; + __u32 sp; + __u32 lr; + __u32 pc; + __u32 psr; + + __u32 pad; +}; + + +struct elf_prstatus { + struct elf_siginfo { + int si_signo; + int si_code; + int si_errno; + } pr_info; + short pr_cursig; + unsigned long pr_sigpend; + unsigned long pr_sighold; + pid_t pr_pid; + pid_t pr_ppid; + pid_t pr_pgrp; + pid_t pr_sid; + struct timeval { + long tv_sec; + long tv_usec; + } pr_utime; + struct timeval pr_stime; + struct timeval pr_cutime; + struct timeval pr_cstime; + struct pr_regs pr_reg; + int pr_fpvalid; +}; + + +struct elf_thread_aux { + Elf32_Nhdr nhdr; + char name[8]; + + struct { + __u32 freg[32 * 2]; + __u32 fpsr; + } fpuContext; +}; + + +struct elf_proc_aux { + Elf32_Nhdr nhdr; + char name[8]; + struct { + __u32 a_type; + __u32 a_val; + } auxv[2]; +}; + + +/* CPU context saved by interrupt handlers on thread kernel stack */ +struct cpu_context_t { + __u32 savesp; + __u32 padding; + + /* FPU context */ + __u32 fpsr; + __u32 freg[32 * 2]; + + __u32 psr; + + __u32 r0; + __u32 r1; + __u32 r2; + __u32 r3; + __u32 r4; + __u32 r5; + __u32 r6; + __u32 r7; + __u32 r8; + __u32 r9; + __u32 r10; + + __u32 fp; + __u32 ip; + __u32 sp; + __u32 lr; + + __u32 pc; +}; + +#endif /* _CPU_H_ */ diff --git a/libcoredumpsrv/hal/ia32/cpu.c b/libcoredumpsrv/hal/ia32/cpu.c new file mode 100644 index 0000000..6a60334 --- /dev/null +++ b/libcoredumpsrv/hal/ia32/cpu.c @@ -0,0 +1,80 @@ +/* + * Phoenix-RTOS + * + * Coredump server library + * Architecture specific functions + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include "hal.h" +#include + + +int hal_fillPrStatus(cpu_context_t *ctx, elf_prstatus *prstatus, __u32 memRecvPort, const coredump_opts_t *opts) +{ + prstatus->pr_reg.ebx = ctx->ebx; + prstatus->pr_reg.ecx = ctx->ecx; + prstatus->pr_reg.edx = ctx->edx; + prstatus->pr_reg.esi = ctx->esi; + prstatus->pr_reg.edi = ctx->edi; + prstatus->pr_reg.ebp = ctx->ebp; + prstatus->pr_reg.eax = ctx->eax; + prstatus->pr_reg.ds = ctx->ds; + prstatus->pr_reg.es = ctx->es; + prstatus->pr_reg.fs = ctx->fs; + prstatus->pr_reg.gs = ctx->gs; + prstatus->pr_reg.pad = 0; + prstatus->pr_reg.eip = ctx->eip; + prstatus->pr_reg.cs = ctx->cs; + prstatus->pr_reg.eflags = ctx->eflags; + prstatus->pr_reg.esp = ctx->esp; + prstatus->pr_reg.ss = ctx->ss; + return 0; +} + + +void hal_createThreadAuxNotes(cpu_context_t *ctx, elf_thread_aux *note, const coredump_opts_t *opts) +{ + static const char FPREGSET_NAME[] = "CORE"; + if (opts->fpContext == 0) { + return; + } + note->nhdr.n_namesz = sizeof(FPREGSET_NAME); + note->nhdr.n_descsz = sizeof(ctx->fpuContext); + note->nhdr.n_type = NT_FPREGSET; + memcpy(note->name, FPREGSET_NAME, sizeof(FPREGSET_NAME)); + memcpy(¬e->fpuContext, &ctx->fpuContext, sizeof(ctx->fpuContext)); +} + + +void hal_createProcAuxNotes(elf_proc_aux *buff, const coredump_opts_t *opts) +{ + return; +} + + +size_t hal_threadAuxNotesSize(const coredump_opts_t *opts) +{ + if (opts->fpContext) { + return sizeof(elf_thread_aux); + } + return 0; +} + + +size_t hal_procAuxNotesSize(const coredump_opts_t *opts) +{ + return 0; +} + + +void *hal_cpuGetUserSP(cpu_context_t *ctx) +{ + return (void *)ctx->esp; +} diff --git a/libcoredumpsrv/hal/ia32/cpu.h b/libcoredumpsrv/hal/ia32/cpu.h new file mode 100644 index 0000000..2a4f331 --- /dev/null +++ b/libcoredumpsrv/hal/ia32/cpu.h @@ -0,0 +1,114 @@ +/* + * Phoenix-RTOS + * + * Coredump server library + * Architecture specific structures + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _CPU_H_ +#define _CPU_H_ + +#include "elf.h" +#include + +#define HAL_ELF_MACHINE 0x03 + +struct pr_regs { + __u32 ebx; + __u32 ecx; + __u32 edx; + __u32 esi; + __u32 edi; + __u32 ebp; + __u32 eax; + __u32 ds; + __u32 es; + __u32 fs; + __u32 gs; + __u32 pad; + __u32 eip; + __u32 cs; + __u32 eflags; + __u32 esp; + __u32 ss; +}; + + +struct elf_prstatus { + struct elf_siginfo { + int si_signo; + int si_code; + int si_errno; + } pr_info; + short pr_cursig; + unsigned long pr_sigpend; + unsigned long pr_sighold; + pid_t pr_pid; + pid_t pr_ppid; + pid_t pr_pgrp; + pid_t pr_sid; + struct timeval { + long tv_sec; + long tv_usec; + } pr_utime; + struct timeval pr_stime; + struct timeval pr_cutime; + struct timeval pr_cstime; + struct pr_regs pr_reg; + int pr_fpvalid; +}; + + +typedef struct { + __u16 controlWord, _controlWord; + __u16 statusWord, _statusWord; + __u16 tagWord, _tagWord; + __u32 fip; + __u32 fips; + __u32 fdp; + __u16 fds, _fds; + __u8 fpuContext[80]; +} fpu_context_t; + + +struct elf_thread_aux { + Elf32_Nhdr nhdr; + char name[8]; + fpu_context_t fpuContext; +}; + + +struct elf_proc_aux { +}; + + +struct cpu_context_t { + __u32 savesp; + __u32 edi; + __u32 esi; + __u32 ebp; + __u32 edx; + __u32 ecx; + __u32 ebx; + __u32 eax; + __u16 gs; + __u16 fs; + __u16 es; + __u16 ds; + fpu_context_t fpuContext; + __u32 cr0Bits; + __u32 eip; /* eip, cs, eflags, esp, ss saved by CPU on interrupt */ + __u32 cs; + __u32 eflags; + __u32 esp; + __u32 ss; +}; + +#endif /* _CPU_H_ */ diff --git a/libcoredumpsrv/hal/riscv64/cpu.c b/libcoredumpsrv/hal/riscv64/cpu.c new file mode 100644 index 0000000..f981694 --- /dev/null +++ b/libcoredumpsrv/hal/riscv64/cpu.c @@ -0,0 +1,95 @@ +/* + * Phoenix-RTOS + * + * Coredump server library + * Architecture specific functions + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include "hal.h" +#include + + +int hal_fillPrStatus(cpu_context_t *ctx, elf_prstatus *prstatus, __u32 memRecvPort, const coredump_opts_t *opts) +{ + prstatus->pr_reg.sepc = ctx->sepc; + prstatus->pr_reg.ra = ctx->ra; + prstatus->pr_reg.sp = ctx->sp; + prstatus->pr_reg.gp = ctx->gp; + prstatus->pr_reg.tp = ctx->tp; + prstatus->pr_reg.t0 = ctx->t0; + prstatus->pr_reg.t1 = ctx->t1; + prstatus->pr_reg.t2 = ctx->t2; + prstatus->pr_reg.s0 = ctx->s0; + prstatus->pr_reg.s1 = ctx->s1; + prstatus->pr_reg.a0 = ctx->a0; + prstatus->pr_reg.a1 = ctx->a1; + prstatus->pr_reg.a2 = ctx->a2; + prstatus->pr_reg.a3 = ctx->a3; + prstatus->pr_reg.a4 = ctx->a4; + prstatus->pr_reg.a5 = ctx->a5; + prstatus->pr_reg.a6 = ctx->a6; + prstatus->pr_reg.a7 = ctx->a7; + prstatus->pr_reg.s2 = ctx->s2; + prstatus->pr_reg.s3 = ctx->s3; + prstatus->pr_reg.s4 = ctx->s4; + prstatus->pr_reg.s5 = ctx->s5; + prstatus->pr_reg.s6 = ctx->s6; + prstatus->pr_reg.s7 = ctx->s7; + prstatus->pr_reg.s8 = ctx->s8; + prstatus->pr_reg.s9 = ctx->s9; + prstatus->pr_reg.s10 = ctx->s10; + prstatus->pr_reg.s11 = ctx->s11; + prstatus->pr_reg.t3 = ctx->t3; + prstatus->pr_reg.t4 = ctx->t4; + prstatus->pr_reg.t5 = ctx->t5; + prstatus->pr_reg.t6 = ctx->t6; + return 0; +} + + +void hal_createThreadAuxNotes(cpu_context_t *ctx, elf_thread_aux *note, const coredump_opts_t *opts) +{ + static const char FPREGSET_NAME[] = "CORE"; + if (opts->fpContext == 0) { + return; + } + note->nhdr.n_namesz = sizeof(FPREGSET_NAME); + note->nhdr.n_descsz = sizeof(ctx->fpCtx); + note->nhdr.n_type = NT_FPREGSET; + memcpy(note->name, FPREGSET_NAME, sizeof(FPREGSET_NAME)); + memcpy(¬e->fpCtx, &ctx->fpCtx, sizeof(ctx->fpCtx)); +} + + +void hal_createProcAuxNotes(elf_proc_aux *buff, const coredump_opts_t *opts) +{ + return; +} + + +size_t hal_threadAuxNotesSize(const coredump_opts_t *opts) +{ + if (opts->fpContext) { + return sizeof(elf_thread_aux); + } + return 0; +} + + +size_t hal_procAuxNotesSize(const coredump_opts_t *opts) +{ + return 0; +} + + +void *hal_cpuGetUserSP(cpu_context_t *ctx) +{ + return (void *)ctx->sp; +} diff --git a/libcoredumpsrv/hal/riscv64/cpu.h b/libcoredumpsrv/hal/riscv64/cpu.h new file mode 100644 index 0000000..f27f50b --- /dev/null +++ b/libcoredumpsrv/hal/riscv64/cpu.h @@ -0,0 +1,188 @@ +/* + * Phoenix-RTOS + * + * Coredump server library + * Architecture specific structures + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _CPU_H_ +#define _CPU_H_ + +#include "elf.h" +#include + +#define HAL_ELF_MACHINE 0xF3 + +struct pr_regs { + __u64 sepc; + __u64 ra; + __u64 sp; + __u64 gp; + __u64 tp; + __u64 t0; + __u64 t1; + __u64 t2; + __u64 s0; + __u64 s1; + __u64 a0; + __u64 a1; + __u64 a2; + __u64 a3; + __u64 a4; + __u64 a5; + __u64 a6; + __u64 a7; + __u64 s2; + __u64 s3; + __u64 s4; + __u64 s5; + __u64 s6; + __u64 s7; + __u64 s8; + __u64 s9; + __u64 s10; + __u64 s11; + __u64 t3; + __u64 t4; + __u64 t5; + __u64 t6; +} __attribute__((packed, aligned(8))); + + +struct elf_prstatus { + struct elf_siginfo { + int si_signo; + int si_code; + int si_errno; + } pr_info; + short pr_cursig; + unsigned long pr_sigpend; + unsigned long pr_sighold; + pid_t pr_pid; + pid_t pr_ppid; + pid_t pr_pgrp; + pid_t pr_sid; + struct timeval { + long tv_sec; + long tv_usec; + } pr_utime; + struct timeval pr_stime; + struct timeval pr_cutime; + struct timeval pr_cstime; + struct pr_regs pr_reg; + int pr_fpvalid; +}; + + +typedef struct { + __u64 ft0; + __u64 ft1; + __u64 ft2; + __u64 ft3; + __u64 ft4; + __u64 ft5; + __u64 ft6; + __u64 ft7; + + __u64 fs0; + __u64 fs1; + + __u64 fa0; + __u64 fa1; + __u64 fa2; + __u64 fa3; + __u64 fa4; + __u64 fa5; + __u64 fa6; + __u64 fa7; + + __u64 fs2; + __u64 fs3; + __u64 fs4; + __u64 fs5; + __u64 fs6; + __u64 fs7; + __u64 fs8; + __u64 fs9; + __u64 fs10; + __u64 fs11; + + __u64 ft8; + __u64 ft9; + __u64 ft10; + __u64 ft11; + + __u64 fcsr; +} __attribute__((packed)) cpu_fpContext_t; + + +struct elf_thread_aux { + Elf32_Nhdr nhdr; + char name[8]; + cpu_fpContext_t fpCtx; +} __attribute__((packed)); + + +struct elf_proc_aux { +}; + + +struct cpu_context_t { + __u64 ra; /* x1 */ + __u64 gp; /* x3 */ + + __u64 t0; /* x5 */ + __u64 t1; /* x6 */ + __u64 t2; /* x7 */ + + __u64 s0; /* x8 */ + __u64 s1; /* x9 */ + __u64 a0; /* x10 */ + __u64 a1; /* x11 */ + + __u64 a2; /* x12 */ + __u64 a3; /* x13 */ + __u64 a4; /* x14 */ + __u64 a5; /* x15 */ + + __u64 a6; /* x16 */ + __u64 a7; /* x17 */ + __u64 s2; /* x18 */ + __u64 s3; /* x19 */ + + __u64 s4; /* x20 */ + __u64 s5; /* x21 */ + __u64 s6; /* x22 */ + __u64 s7; /* x23 */ + + __u64 s8; /* x24 */ + __u64 s9; /* x25 */ + __u64 s10; /* x26 */ + __u64 s11; /* x27 */ + + __u64 t3; /* x28 */ + __u64 t4; /* x29 */ + __u64 t5; /* x30 */ + __u64 t6; /* x31 */ + + __u64 ksp; + __u64 sstatus; + __u64 sepc; + __u64 stval; + __u64 scause; + __u64 sscratch; + + __u64 tp; + __u64 sp; + + cpu_fpContext_t fpCtx; +} __attribute__((packed, aligned(8))); + +#endif /* _CPU_H_ */ diff --git a/libcoredumpsrv/hal/sparcv8leon/cpu.c b/libcoredumpsrv/hal/sparcv8leon/cpu.c new file mode 100644 index 0000000..55dc6ed --- /dev/null +++ b/libcoredumpsrv/hal/sparcv8leon/cpu.c @@ -0,0 +1,153 @@ +/* + * Phoenix-RTOS + * + * Coredump server library + * Architecture specific functions + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include "cpu.h" +#include "hal.h" +#include "kernel_comm.h" +#include + +int hal_fillPrStatus(cpu_context_t *ctx, elf_prstatus *prstatus, __u32 memRecvPort, const coredump_opts_t *opts) +{ + prstatus->pr_reg.g1 = ctx->g1; + prstatus->pr_reg.g2 = ctx->g2; + prstatus->pr_reg.g3 = ctx->g3; + prstatus->pr_reg.g4 = ctx->g4; + prstatus->pr_reg.g5 = ctx->g5; + prstatus->pr_reg.g6 = ctx->g6; + prstatus->pr_reg.g7 = ctx->g7; + prstatus->pr_reg.o0 = ctx->o0; + prstatus->pr_reg.o1 = ctx->o1; + prstatus->pr_reg.o2 = ctx->o2; + prstatus->pr_reg.o3 = ctx->o3; + prstatus->pr_reg.o4 = ctx->o4; + prstatus->pr_reg.o5 = ctx->o5; + prstatus->pr_reg.sp = ctx->sp; + prstatus->pr_reg.o7 = ctx->o7; + + prstatus->pr_reg.psr = ctx->psr; + prstatus->pr_reg.pc = ctx->pc; + prstatus->pr_reg.npc = ctx->npc; + prstatus->pr_reg.y = ctx->y; + + /* Fetch cpu_winContext_t from user stack */ + msg_t msg; + msg_rid_t rid; + msg.oid.port = memRecvPort; + msg.oid.id = 0; + + int ret = coredump_getMemory((void *)ctx->sp, sizeof(cpu_winContext_t), &msg, &rid); + if (ret != 0) { + PRINT_ERR("Failed to retrieve missing win context with error %d\n", ret); + return ret; + } + memcpy(&ctx->winCtx, msg.i.data, sizeof(cpu_winContext_t)); + coredump_putMemory(&msg, rid); + + prstatus->pr_reg.l0 = ctx->winCtx.l0; + prstatus->pr_reg.l1 = ctx->winCtx.l1; + prstatus->pr_reg.l2 = ctx->winCtx.l2; + prstatus->pr_reg.l3 = ctx->winCtx.l3; + prstatus->pr_reg.l4 = ctx->winCtx.l4; + prstatus->pr_reg.l5 = ctx->winCtx.l5; + prstatus->pr_reg.l6 = ctx->winCtx.l6; + prstatus->pr_reg.l7 = ctx->winCtx.l7; + prstatus->pr_reg.i0 = ctx->winCtx.i0; + prstatus->pr_reg.i1 = ctx->winCtx.i1; + prstatus->pr_reg.i2 = ctx->winCtx.i2; + prstatus->pr_reg.i3 = ctx->winCtx.i3; + prstatus->pr_reg.i4 = ctx->winCtx.i4; + prstatus->pr_reg.i5 = ctx->winCtx.i5; + prstatus->pr_reg.fp = ctx->winCtx.fp; + prstatus->pr_reg.i7 = ctx->winCtx.i7; + + return 0; +} + + +void hal_createThreadAuxNotes(cpu_context_t *ctx, elf_thread_aux *note, const coredump_opts_t *opts) +{ + static const char FPREGSET_NAME[] = "CORE"; + if (opts->fpContext == 0) { + return; + } + + note->nhdr.n_namesz = sizeof(FPREGSET_NAME); + note->nhdr.n_descsz = sizeof(elf_thread_aux) - offsetof(elf_thread_aux, f0); + note->nhdr.n_type = NT_FPREGSET; + memcpy(¬e->name, FPREGSET_NAME, sizeof(FPREGSET_NAME)); + + note->f0 = ctx->fpCtx.f0; + note->f1 = ctx->fpCtx.f1; + note->f2 = ctx->fpCtx.f2; + note->f3 = ctx->fpCtx.f3; + note->f4 = ctx->fpCtx.f4; + note->f5 = ctx->fpCtx.f5; + note->f6 = ctx->fpCtx.f6; + note->f7 = ctx->fpCtx.f7; + note->f8 = ctx->fpCtx.f8; + note->f9 = ctx->fpCtx.f9; + note->f10 = ctx->fpCtx.f10; + note->f11 = ctx->fpCtx.f11; + note->f12 = ctx->fpCtx.f12; + note->f13 = ctx->fpCtx.f13; + note->f14 = ctx->fpCtx.f14; + note->f15 = ctx->fpCtx.f15; + note->f16 = ctx->fpCtx.f16; + note->f17 = ctx->fpCtx.f17; + note->f18 = ctx->fpCtx.f18; + note->f19 = ctx->fpCtx.f19; + note->f20 = ctx->fpCtx.f20; + note->f21 = ctx->fpCtx.f21; + note->f22 = ctx->fpCtx.f22; + note->f23 = ctx->fpCtx.f23; + note->f24 = ctx->fpCtx.f24; + note->f25 = ctx->fpCtx.f25; + note->f26 = ctx->fpCtx.f26; + note->f27 = ctx->fpCtx.f27; + note->f28 = ctx->fpCtx.f28; + note->f29 = ctx->fpCtx.f29; + note->f30 = ctx->fpCtx.f30; + note->f31 = ctx->fpCtx.f31; + note->pad = 0; + note->fsr = ctx->fpCtx.fsr; + note->ctrl = (__u32)((1 << 8) | (8 << 16)); + memset(note->pad1, 0, sizeof(note->pad1)); +} + + +void hal_createProcAuxNotes(elf_proc_aux *buff, const coredump_opts_t *opts) +{ + return; +} + + +size_t hal_threadAuxNotesSize(const coredump_opts_t *opts) +{ + if (opts->fpContext) { + return sizeof(elf_thread_aux); + } + return 0; +} + + +size_t hal_procAuxNotesSize(const coredump_opts_t *opts) +{ + return 0; +} + + +void *hal_cpuGetUserSP(cpu_context_t *ctx) +{ + return (void *)ctx->sp; +} diff --git a/libcoredumpsrv/hal/sparcv8leon/cpu.h b/libcoredumpsrv/hal/sparcv8leon/cpu.h new file mode 100644 index 0000000..b168544 --- /dev/null +++ b/libcoredumpsrv/hal/sparcv8leon/cpu.h @@ -0,0 +1,291 @@ +/* + * Phoenix-RTOS + * + * Coredump server library + * Architecture specific structures + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _CPU_H_ +#define _CPU_H_ + +#include "elf.h" +#include +#include + +#define HAL_ELF_MACHINE 0x02 + +struct prgregset_t { + __u32 pad; + __u32 g1; + __u32 g2; + __u32 g3; + __u32 g4; + __u32 g5; + __u32 g6; + __u32 g7; + __u32 o0; + __u32 o1; + __u32 o2; + __u32 o3; + __u32 o4; + __u32 o5; + __u32 sp; + __u32 o7; + + __u32 l0; + __u32 l1; + __u32 l2; + __u32 l3; + __u32 l4; + __u32 l5; + __u32 l6; + __u32 l7; + __u32 i0; + __u32 i1; + __u32 i2; + __u32 i3; + __u32 i4; + __u32 i5; + __u32 fp; + __u32 i7; + + __u32 psr; + __u32 pc; + __u32 npc; + __u32 y; + __u32 pad1; + __u32 pad2; +}; + + +/* prstatus_t from opensolaris modified to match GDB: removed pr_sysarg, swapped pr_who and pr_pid */ +struct elf_prstatus { + int pr_flags; /* Flags (see below) */ + short pr_why; /* Reason for process stop (if stopped) */ + short pr_what; /* More detailed reason */ + struct siginfo { + int si_signo; /* signal from signal.h */ + int si_code; /* code from above */ + int si_errno; /* error from errno.h */ + union { + int _pad[((128 / sizeof(int)) - 3)]; /* for future growth */ + struct { /* kill(), SIGCLD */ + long _pid; /* process ID */ + union { + struct { + long _uid; + } _kill; + struct { + long _utime; + int _status; + long _stime; + } _cld; + } _pdata; + } _proc; + struct { /* SIGSEGV, SIGBUS, SIGILL and SIGFPE */ + char *_addr; /* faulting address */ + } _fault; + struct { /* SIGPOLL, SIGXFSZ */ + /* fd not currently available for SIGPOLL */ + int _fd; /* file descriptor */ + long _band; + } _file; + + } _data; + } pr_info; /* Info associated with signal or fault */ + short pr_cursig; /* Current signal */ + __u16 pr_nlwp; /* Number of lwps in the process */ + struct sigset { /* signal set type */ + unsigned int __sigbits[4]; /* signal bits */ + } pr_sigpend; /* Set of signals pending to the process */ + struct sigset pr_sighold; /* Set of signals held (blocked) by the lwp */ + struct sigaltstack { + void *ss_sp; + size_t ss_size; + int ss_flags; + } pr_altstack; /* Alternate signal stack info */ + struct sol_sigaction { + int sa_flags; + void (*sa_handler)(int); + struct sigset sa_mask; + int sa_resv[2]; + } pr_action; /* Signal action for current signal */ + id_t pr_who; /* (originally pr_pid) Specific lwp identifier */ + pid_t pr_ppid; /* Parent process id */ + pid_t pr_pgrp; /* Process group id */ + pid_t pr_sid; /* Session id */ + struct timestruc { /* definition per POSIX.4 */ + time_t tv_sec; /* seconds */ + long tv_nsec; /* and nanoseconds */ + } pr_utime; /* Process user cpu time */ + struct timestruc pr_stime; /* Process system cpu time */ + struct timestruc pr_cutime; /* Sum of children's user times */ + struct timestruc pr_cstime; /* Sum of children's system times */ + char pr_clname[8]; /* Scheduling class name */ + short pr_syscall; /* System call number (if in syscall) */ + short pr_nsysarg; /* Number of arguments to this syscall */ + // long pr_sysarg[8]; /* Arguments to this syscall */ + pid_t pr_pid; /* (originally pr_who) Process id */ + struct sigset pr_lwppend; /* Set of signals pending to the lwp */ + struct ucontext *pr_oldcontext; /* Address of previous ucontext */ + caddr_t pr_brkbase; /* Address of the process heap */ + size_t pr_brksize; /* Size of the process heap, in bytes */ + caddr_t pr_stkbase; /* Address of the process stack */ + size_t pr_stksize; /* Size of the process stack, in bytes */ + short pr_processor; /* processor which last ran this LWP */ + short pr_bind; /* processor LWP bound to or PBIND_NONE */ + long pr_instr; /* Current instruction */ + struct prgregset_t pr_reg; /* General registers */ +} __attribute__((packed)); + + +struct elf_thread_aux { + Elf32_Nhdr nhdr; + char name[8]; + + __u32 f0; + __u32 f1; + __u32 f2; + __u32 f3; + __u32 f4; + __u32 f5; + __u32 f6; + __u32 f7; + __u32 f8; + __u32 f9; + __u32 f10; + __u32 f11; + __u32 f12; + __u32 f13; + __u32 f14; + __u32 f15; + __u32 f16; + __u32 f17; + __u32 f18; + __u32 f19; + __u32 f20; + __u32 f21; + __u32 f22; + __u32 f23; + __u32 f24; + __u32 f25; + __u32 f26; + __u32 f27; + __u32 f28; + __u32 f29; + __u32 f30; + __u32 f31; + + __u32 pad; + __u32 fsr; + + __u32 ctrl; + __u32 pad1[64]; +} __attribute__((packed)); + + +struct elf_proc_aux { +}; + + +typedef struct { + __u32 f0; + __u32 f1; + __u32 f2; + __u32 f3; + __u32 f4; + __u32 f5; + __u32 f6; + __u32 f7; + __u32 f8; + __u32 f9; + __u32 f10; + __u32 f11; + __u32 f12; + __u32 f13; + __u32 f14; + __u32 f15; + __u32 f16; + __u32 f17; + __u32 f18; + __u32 f19; + __u32 f20; + __u32 f21; + __u32 f22; + __u32 f23; + __u32 f24; + __u32 f25; + __u32 f26; + __u32 f27; + __u32 f28; + __u32 f29; + __u32 f30; + __u32 f31; + + __u32 fsr; + __u32 pad; +} cpu_fpContext_t; + + +typedef struct { + /* local */ + __u32 l0; + __u32 l1; + __u32 l2; + __u32 l3; + __u32 l4; + __u32 l5; + __u32 l6; + __u32 l7; + + /* in */ + __u32 i0; + __u32 i1; + __u32 i2; + __u32 i3; + __u32 i4; + __u32 i5; + __u32 fp; + __u32 i7; +} __attribute__((packed)) cpu_winContext_t; + + +struct cpu_context_t { + __u32 savesp; + + __u32 y; + __u32 psr; + __u32 pc; + __u32 npc; + + /* global */ + __u32 g1; + __u32 g2; + __u32 g3; + __u32 g4; + __u32 g5; + __u32 g6; + __u32 g7; + + /* out */ + __u32 o0; + __u32 o1; + __u32 o2; + __u32 o3; + __u32 o4; + __u32 o5; + __u32 sp; + __u32 o7; + + cpu_fpContext_t fpCtx; + cpu_winContext_t winCtx; +} __attribute__((packed)); + +#endif /* _CPU_H_ */ diff --git a/libcoredumpsrv/kernel_comm.c b/libcoredumpsrv/kernel_comm.c new file mode 100644 index 0000000..7c1a0d9 --- /dev/null +++ b/libcoredumpsrv/kernel_comm.c @@ -0,0 +1,216 @@ +/* + * Phoenix-RTOS + * + * Coredump Server Library + * + * Kernel communication + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * %LICENSE% + */ + +#include "kernel_comm.h" + +#include "hal.h" + +#include +#include +#include +#include + +#define PRINT_ERR(args...) fprintf(stderr, "Coredump server: " args) + +int coredump_waitForCrash(coredump_general_t *crashInfo) +{ + msg_t msg; + + msg.oid.id = 0; + msg.oid.port = 1; + msg.type = mtOpen; + msg.i.size = 0; + msg.i.data = NULL; + msg.o.size = sizeof(*crashInfo); + msg.o.data = crashInfo; + + int ret = msgSend(1, &msg); + if (ret != 0) { + PRINT_ERR("msgSend open failed with %d: %s\n", ret, strerror(-ret)); + return ret; + } + if (msg.o.err != 0) { + PRINT_ERR("open failed with %d: %s\n", msg.o.err, strerror(-msg.o.err)); + return msg.o.err; + } + + return 0; +} + + +int coredump_getThreadContext(int tid, coredump_thread_t *resp) +{ + msg_t msg; + + coredump_req_t req = { + .type = COREDUMP_REQ_THREAD, + .thread = { + .tid = tid, + }, + }; + + msg.oid.id = 0; + msg.oid.port = 1; + msg.type = mtRead; + msg.i.size = sizeof(req); + msg.i.data = &req; + msg.o.size = sizeof(coredump_thread_t) + sizeof(cpu_context_t); + msg.o.data = resp; + + int ret = msgSend(1, &msg); + if (ret != 0) { + PRINT_ERR("msgSend req_thread failed with %d: %s\n", ret, strerror(-ret)); + return ret; + } + if (msg.o.err != 0) { + PRINT_ERR("read req_thread failed with %d: %s\n", msg.o.err, strerror(-msg.o.err)); + return msg.o.err; + } + + return 0; +} + + +int coredump_getMemList(size_t bufSize, coredump_memseg_t *resp) +{ + msg_t msg; + coredump_req_t req = { + .type = COREDUMP_REQ_MEMLIST, + }; + + msg.oid.id = 0; + msg.oid.port = 1; + msg.type = mtRead; + msg.i.size = sizeof(req); + msg.i.data = &req; + msg.o.size = bufSize; + msg.o.data = resp; + + int ret = msgSend(1, &msg); + if (ret != 0) { + PRINT_ERR("msgSend req_memlist failed with %d: %s\n", ret, strerror(-ret)); + return ret; + } + if (msg.o.err != 0) { + PRINT_ERR("read req_memlist failed with %d: %s\n", msg.o.err, strerror(-msg.o.err)); + return msg.o.err; + } + return 0; +} + + +int coredump_getRelocs(size_t bufSize, coredump_reloc_t *resp) +{ +#ifdef NOMMU + msg_t msg; + coredump_req_t req = { + .type = COREDUMP_REQ_RELOC, + }; + + msg.oid.id = 0; + msg.oid.port = 1; + msg.type = mtRead; + msg.i.size = sizeof(req); + msg.i.data = &req; + msg.o.size = bufSize; + msg.o.data = resp; + + int ret = msgSend(1, &msg); + if (ret != 0) { + PRINT_ERR("msgSend req_reloc failed with %d: %s\n", ret, strerror(-ret)); + return ret; + } + if (msg.o.err != 0) { + PRINT_ERR("read req_reloc failed with %d: %s\n", msg.o.err, strerror(-msg.o.err)); + return msg.o.err; + } + +#endif + + return 0; +} + + +int coredump_getMemory(void *startAddr, size_t len, msg_t *msg, msg_rid_t *rid) +{ + coredump_req_t req = { + .type = COREDUMP_REQ_MEM, + .mem = { + .startAddr = startAddr, + .size = len, + .responsePort = msg->oid.port, + }, + }; + + msg->type = mtRead; + msg->i.size = sizeof(req); + msg->i.data = &req; + msg->o.size = 0; + + int ret = msgSend(1, msg); + if (ret != 0) { + PRINT_ERR("msgSend req_mem failed with %d: %s\n", ret, strerror(-ret)); + return ret; + } + if (msg->o.err != 0) { + PRINT_ERR("read req_mem failed with %d: %s\n", msg->o.err, strerror(-msg->o.err)); + PRINT_ERR("%p %zu\n", startAddr, len); + return msg->o.err; + } + + ret = msgRecv(msg->oid.port, msg, rid); + if (ret < 0) { + PRINT_ERR("getMemory msgRecv failed with %d: %s\n", ret, strerror(-ret)); + return ret; + } + if (msg->type != mtWrite) { + PRINT_ERR("getMemory msg type %d != mtWrite\n", msg->type); + msg->o.err = -EINVAL; + msgRespond(msg->oid.port, msg, *rid); + return -EINVAL; + } + return 0; +} + + +void coredump_putMemory(msg_t *msg, msg_rid_t rid) +{ + msg->o.err = EOK; + msgRespond(msg->oid.port, msg, rid); +} + + +int coredump_closeCrash(void) +{ + msg_t msg; + + msg.oid.id = 0; + msg.oid.port = 1; + msg.type = mtClose; + msg.i.size = 0; + msg.i.data = NULL; + msg.o.size = 0; + msg.o.data = NULL; + + int ret = msgSend(1, &msg); + if (ret != 0) { + PRINT_ERR("msgSend close failed with %d: %s\n", ret, strerror(-ret)); + return ret; + } + if (msg.o.err != 0) { + PRINT_ERR("close failed with %d: %s\n", msg.o.err, strerror(-msg.o.err)); + return msg.o.err; + } + + return 0; +} diff --git a/libcoredumpsrv/kernel_comm.h b/libcoredumpsrv/kernel_comm.h new file mode 100644 index 0000000..98b8e27 --- /dev/null +++ b/libcoredumpsrv/kernel_comm.h @@ -0,0 +1,41 @@ +/* + * Phoenix-RTOS + * + * Coredump Server Library + * + * Kernel communication + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * %LICENSE% + */ + +#ifndef _KERNEL_COMM_H_ +#define _KERNEL_COMM_H_ + +#include +#include +#include + +extern int coredump_waitForCrash(coredump_general_t *crashInfo); + + +extern int coredump_getThreadContext(int tid, coredump_thread_t *resp); + + +extern int coredump_getMemList(size_t bufSize, coredump_memseg_t *resp); + + +extern int coredump_getRelocs(size_t bufSize, coredump_reloc_t *resp); + + +extern int coredump_getMemory(void *startAddr, size_t len, msg_t *msg, msg_rid_t *rid); + + +extern void coredump_putMemory(msg_t *msg, msg_rid_t rid); + + +extern int coredump_closeCrash(void); + +#endif /* _KERNEL_COMM_H_ */ diff --git a/libcoredumpsrv/settings.c b/libcoredumpsrv/settings.c new file mode 100644 index 0000000..e2e11fb --- /dev/null +++ b/libcoredumpsrv/settings.c @@ -0,0 +1,338 @@ +/* + * Phoenix-RTOS + * + * Coredump Server Library + * + * Configuration functions + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * %LICENSE% + */ + +#include "coredump.h" + +#include +#include +#include +#include +#include +#include + + +static int settings_lookupDev(oid_t *oid) +{ + if (lookup(COREDUMP_SETTINGS_DEV, NULL, oid) < 0) { + printf("Failed to lookup settings device '%s'\n", COREDUMP_SETTINGS_DEV); + return -ENOENT; + } + return 0; +} + + +static int settings_nameToAttr(const char *name) +{ + if (strcmp(name, "MAX_THREADS") == 0) { + return COREDUMP_ATTR_MAX_THREADS; + } + else if (strcmp(name, "MAX_STACK_SIZE") == 0) { + return COREDUMP_ATTR_MAX_STACK_SIZE; + } + else if (strcmp(name, "MEM_SCOPE") == 0) { + return COREDUMP_ATTR_MEM_SCOPE; + } + else if (strcmp(name, "FP_CONTEXT") == 0) { + return COREDUMP_ATTR_FP_CONTEXT; + } + else if (strcmp(name, "PRINT") == 0) { + return COREDUMP_ATTR_PRINT; + } + else if (strcmp(name, "PRINT_SLEEP") == 0) { + return COREDUMP_ATTR_PRINT_SLEEP; + } + else if (strcmp(name, "PATH") == 0) { + return COREDUMP_ATTR_PATH; + } + else if (strcmp(name, "MAX_FILES") == 0) { + return COREDUMP_ATTR_MAX_FILES; + } + return -EINVAL; +} + + +static char *settings_memscopeName(int scope) +{ + switch (scope) { + case COREDUMP_MEM_NONE: + return "none"; + case COREDUMP_MEM_EXC_STACK: + return "exception thread stack"; + case COREDUMP_MEM_ALL_STACKS: + return "all threads stacks"; + case COREDUMP_MEM_ALL: + return "all memory"; + default: + return "invalid"; + } +} + + +static void settings_read(void) +{ + static const int SAVEPATH_MAX = 128; + static const size_t OUT_SIZE = sizeof(coredump_opts_t) + SAVEPATH_MAX; + oid_t oid; + if (settings_lookupDev(&oid) != 0) { + return; + } + + msg_t msg; + msg.oid = oid; + msg.type = mtGetAttrAll; + msg.i.size = 0; + msg.i.data = NULL; + msg.o.size = OUT_SIZE; + msg.o.data = malloc(OUT_SIZE); + int ret = msgSend(oid.port, &msg); + if (ret != 0) { + printf("Failed to read settings: '%s'\n", strerror(-ret)); + free(msg.o.data); + return; + } + if (msg.o.err != 0) { + printf("Failed to read settings: '%s'\n", strerror(msg.o.err)); + free(msg.o.data); + return; + } + + coredump_opts_t *opts = (coredump_opts_t *)msg.o.data; + if (msg.o.attr.val == sizeof(coredump_opts_t)) { + opts->savepath = NULL; + } + else { + opts->savepath = (char *)(opts + 1); + } + + printf("Current settings:\n"); + printf(" Max Threads: %zu\n", opts->maxThreads); + printf(" Max Stack Size: 0x%zx\n", opts->maxStackSize); + printf(" Memory Scope: %s (%d)\n", settings_memscopeName(opts->memScope), opts->memScope); + printf(" FP Context: %s\n", opts->fpContext ? "Enabled" : "Disabled"); + printf(" Max Memory Chunk: %zu\n", opts->maxMemChunk); + printf(" Print: %s\n", opts->print ? "Enabled" : "Disabled"); + printf(" Print Sleep: %u us\n", opts->printSleep); + if (opts->savepath == NULL) { + printf(" Save Path: Disabled\n"); + } + else { + printf(" Save Path: %.*s%s\n", SAVEPATH_MAX, opts->savepath, msg.o.attr.val > OUT_SIZE ? "..." : ""); + } + printf(" Max Files: %zu\n", opts->maxFiles); + printf("\n"); + + free(msg.o.data); +} + + +static int settings_setOpts(char *opt, char *val, coredump_opts_t *opts) +{ + switch (settings_nameToAttr(opt)) { + case COREDUMP_ATTR_MAX_THREADS: + opts->maxThreads = atoi(val); + break; + case COREDUMP_ATTR_MAX_STACK_SIZE: + opts->maxStackSize = atoi(val); + break; + case COREDUMP_ATTR_MEM_SCOPE: + opts->memScope = atoi(val); + break; + case COREDUMP_ATTR_FP_CONTEXT: + opts->fpContext = atoi(val); + break; + case COREDUMP_ATTR_PRINT: + opts->print = atoi(val); + break; + case COREDUMP_ATTR_PRINT_SLEEP: + opts->printSleep = atoi(val); + break; + case COREDUMP_ATTR_PATH: + opts->savepath = val; + break; + case COREDUMP_ATTR_MAX_FILES: + opts->maxFiles = atoi(val); + break; + default: + printf("Unknown setting '%s'\n", opt); + return -EINVAL; + } + return 0; +} + + +static void settings_setDev(char *opt, char *val) +{ + oid_t oid; + if (settings_lookupDev(&oid) != 0) { + return; + } + + int attr = settings_nameToAttr(opt); + if (attr < 0) { + printf("Unknown setting '%s'\n", opt); + return; + } + + int value = atoi(val); + + msg_t msg; + msg.oid = oid; + msg.type = mtSetAttr; + msg.i.attr.type = attr; + if (attr == COREDUMP_ATTR_PATH && *val != '0') { + msg.i.attr.val = 1; + msg.i.size = strlen(val) + 1; + msg.i.data = val; + } + else { + msg.i.attr.val = value; + msg.i.size = 0; + msg.i.data = NULL; + } + msg.o.size = 0; + msg.o.data = NULL; + int ret = msgSend(oid.port, &msg); + if (ret != 0) { + printf("Failed to set setting '%s': %s\n", opt, strerror(-ret)); + return; + } + if (msg.o.err != 0) { + printf("Failed to set setting '%s': %s\n", opt, strerror(msg.o.err)); + return; + } + + if (attr == COREDUMP_ATTR_PATH) { + printf("Changed '%s' to '%s'\n", opt, val); + } + else if (attr == COREDUMP_ATTR_MEM_SCOPE) { + printf("Changed '%s' to '%s' (%d)\n", opt, settings_memscopeName(value), value); + } + else { + printf("Changed '%s' to '%d'\n", opt, value); + } +} + + +static void printHelp(char *cmd, bool config) +{ + if (!config) { + printf("Usage: %s [config] [options]\n", cmd); + printf(" %s\tStart coredump daemon with default options\n", cmd); + printf(" config\tDon't start coredump daemon, just configure running server\n"); + } + printf("Options:\n"); + printf(" -h, --help\t\tShow this help message\n"); + printf(" -s, --set \tSet coredump server setting\n"); + printf(" -g, --get\tGet current coredump server settings\n"); + printf(" -p, --print-elf \tPrint ELF file information\n"); + printf("Settings:\n"); + printf(" MAX_THREADS, MAX_STACK_SIZE, MEM_SCOPE, FP_CONTEXT, PRINT, PRINT_SLEEP, PATH, MAX_FILES\n"); + printf("Example: %s -s MAX_THREADS 8\n", cmd); + printf(" %s -s PATH 0\n", cmd); + printf(" %s -s PATH \"/coredumps\"\n", cmd); + printf("\n"); +} + + +static int settings_parseOption(char *cmd, int argleft, char *arg[], coredump_opts_t *opts) +{ + if (strcmp(arg[0], "-h") == 0 || strcmp(arg[0], "--help") == 0) { + printHelp(cmd, opts == NULL); + return -EAGAIN; + } + + if (strcmp(arg[0], "-g") == 0 || strcmp(arg[0], "--get") == 0) { + if (opts != NULL) { + printf("Error: Cannot use -g/--get when starting server\n"); + return -EINVAL; + } + settings_read(); + return 1; + } + + if (strcmp(arg[0], "-s") == 0 || strcmp(arg[0], "--set") == 0) { + if (argleft < 2) { + printf("Error: Missing option name for -s/--set\n"); + return -EINVAL; + } + if (argleft < 3) { + printf("Error: Missing value for option '%s'\n", arg[1]); + return -EINVAL; + } + if (opts != NULL) { + int ret = settings_setOpts(arg[1], arg[2], opts); + if (ret < 0) { + return ret; + } + } + else { + settings_setDev(arg[1], arg[2]); + } + return 3; + } + + if (strcmp(arg[0], "-p") == 0 || strcmp(arg[0], "--print-elf") == 0) { + if (argleft < 2) { + printf("Error: Missing ELF file path for -p/--print-elf\n"); + return -EINVAL; + } + if (opts != NULL) { + printf("Error: Cannot use -p/--print-elf when starting server\n"); + return -EINVAL; + } + int ret = coredump_printElf(arg[1]); + if (ret < 0) { + return ret; + } + return 2; + } + + printf("Error: Unknown option '%s'\n", arg[0]); + return -EINVAL; +} + + +int coredump_configure(char *cmd, int argc, char *argv[]) +{ + if (argc < 1) { + printHelp(cmd, true); + return 0; + } + + int i = 0; + int ret; + while (i < argc) { + ret = settings_parseOption(cmd, argc - i, &argv[i], NULL); + if (ret < 0) { + return ret; + } + i += ret; + } + return 0; +} + + +int coredump_parseStartupArgs(int argc, char *argv[], coredump_opts_t *opts) +{ + int i = 1; + + int ret; + while (i < argc) { + ret = settings_parseOption(argv[0], argc - i, &argv[i], opts); + if (ret < 0) { + return ret; + } + i += ret; + } + return 0; +}