diff --git a/hal/aarch64/exceptions.c b/hal/aarch64/exceptions.c index 281dfb05e..56356be55 100644 --- a/hal/aarch64/exceptions.c +++ b/hal/aarch64/exceptions.c @@ -310,3 +310,9 @@ void _hal_exceptionsInit(void) exceptions.handler[i] = exceptions_trampoline; } } + + +cpu_context_t *hal_excToCpuCtx(exc_context_t *ctx) +{ + return &ctx->cpuCtx; +} diff --git a/hal/armv7a/arch/exceptions.h b/hal/armv7a/arch/exceptions.h index 35ddcf704..6569cb780 100644 --- a/hal/armv7a/arch/exceptions.h +++ b/hal/armv7a/arch/exceptions.h @@ -23,7 +23,12 @@ #define EXC_UNDEFINED 1 #define EXC_PAGEFAULT 4 -#define SIZE_CTXDUMP 512 /* Size of dumped context */ +#define SIZE_CTXDUMP 512 /* Size of dumped context */ +#define SIZE_COREDUMP_GREGSET 72 +#define SIZE_COREDUMP_THREADAUX 280 /* vfp context note */ +#define SIZE_COREDUMP_GENAUX 36 /* auxv HWCAP note */ + +#define HAL_ELF_MACHINE 40 /* ARM */ typedef struct _exc_context_t { diff --git a/hal/armv7a/exceptions.c b/hal/armv7a/exceptions.c index fbe6968a9..ae7b56d2c 100644 --- a/hal/armv7a/exceptions.c +++ b/hal/armv7a/exceptions.c @@ -19,6 +19,7 @@ #include "hal/console.h" #include "hal/string.h" #include "include/mman.h" +#include "proc/elf.h" #define EXC_ASYNC_EXTERNAL 0x16 @@ -49,18 +50,23 @@ struct { enum { exc_reset = 0, exc_undef, exc_svc, exc_prefetch, exc_abort }; -void hal_exceptionsDumpContext(char *buff, exc_context_t *ctx, int n) +const char *hal_exceptionMnemonic(int n) { static const char *const mnemonics[] = { - "0 #Reset", "1 #Undef", "2 #Syscall", "3 #Prefetch", - "4 #Abort", "5 #Reserved", "6 #FIRQ", "7 #IRQ" + "0 #Reset", "1 #Undef", "2 #Syscall", "3 #Prefetch", + "4 #Abort", "5 #Reserved", "6 #FIRQ", "7 #IRQ" }; - size_t i = 0; - n &= 0x7; + return mnemonics[n & 0x7]; +} + + +void hal_exceptionsDumpContext(char *buff, exc_context_t *ctx, int n) +{ + size_t i = 0; hal_strcpy(buff, "\nException: "); - hal_strcpy(buff += hal_strlen(buff), mnemonics[n]); + hal_strcpy(buff += hal_strlen(buff), hal_exceptionMnemonic(n)); hal_strcpy(buff += hal_strlen(buff), "\n"); buff += hal_strlen(buff); @@ -232,3 +238,83 @@ void _hal_exceptionsInit(void) exceptions.abortHandler = exceptions_defaultHandler; exceptions.defaultHandler = exceptions_defaultHandler; } + + +cpu_context_t *hal_excToCpuCtx(exc_context_t *ctx) +{ + return &ctx->cpuCtx; +} + + +void hal_coredumpGRegset(void *buff, cpu_context_t *ctx) +{ + u32 *regs = (u32 *)buff; + *(regs++) = ctx->r0; + *(regs++) = ctx->r1; + *(regs++) = ctx->r2; + *(regs++) = ctx->r3; + *(regs++) = ctx->r4; + *(regs++) = ctx->r5; + *(regs++) = ctx->r6; + *(regs++) = ctx->r7; + *(regs++) = ctx->r8; + *(regs++) = ctx->r9; + *(regs++) = ctx->r10; + *(regs++) = ctx->fp; + *(regs++) = ctx->ip; + *(regs++) = ctx->sp; + *(regs++) = ctx->lr; + *(regs++) = ctx->pc; + *(regs++) = ctx->psr; +} + + +void hal_coredumpThreadAux(void *buff, cpu_context_t *ctx) +{ + static const char ARMVFP_NAME[] = "LINUX"; + Elf32_Nhdr nhdr; + nhdr.n_namesz = sizeof(ARMVFP_NAME); + nhdr.n_descsz = sizeof(ctx->freg) + sizeof(ctx->fpsr); + nhdr.n_type = NT_ARM_VFP; + hal_memcpy(buff, &nhdr, sizeof(nhdr)); + buff = (char *)buff + sizeof(nhdr); + hal_memcpy(buff, ARMVFP_NAME, sizeof(ARMVFP_NAME)); + buff = (char *)buff + ((sizeof(ARMVFP_NAME) + 3) & ~3); + hal_memcpy(buff, ctx->freg, sizeof(ctx->freg)); + buff = (char *)buff + sizeof(ctx->freg); + hal_memcpy(buff, &ctx->fpsr, sizeof(ctx->fpsr)); +} + + +#define COMPAT_HWCAP_VFP (1 << 6) +#define COMPAT_HWCAP_NEON (1 << 12) +#define COMPAT_HWCAP_VFPv3 (1 << 13) +#define HWCAP_VFPv3 (COMPAT_HWCAP_VFP | COMPAT_HWCAP_NEON | COMPAT_HWCAP_VFPv3) + +#define AT_HWCAP 16 +#define AT_NULL 0 + + +void hal_coredumpGeneralAux(void *buff) +{ + static const char AUXV_NAME[] = "CORE"; + Elf32_Nhdr nhdr; + struct { + u32 a_type; + u32 a_val; + } auxv[2]; + + nhdr.n_namesz = sizeof(AUXV_NAME); + nhdr.n_descsz = sizeof(auxv); + nhdr.n_type = NT_AUXV; + hal_memcpy(buff, &nhdr, sizeof(nhdr)); + buff = (char *)buff + sizeof(nhdr); + hal_memcpy(buff, AUXV_NAME, sizeof(AUXV_NAME)); + buff = (char *)buff + ((sizeof(AUXV_NAME) + 3) & ~3); + + auxv[0].a_type = AT_HWCAP; + auxv[0].a_val = HWCAP_VFPv3; + auxv[1].a_type = AT_NULL; + auxv[1].a_val = 0; + hal_memcpy(buff, auxv, sizeof(auxv)); +} diff --git a/hal/armv7m/exceptions.c b/hal/armv7m/exceptions.c index 67672b2d4..1ac943616 100644 --- a/hal/armv7m/exceptions.c +++ b/hal/armv7m/exceptions.c @@ -182,3 +182,9 @@ void _hal_exceptionsInit(void) { hal_exception_common.handler = NULL; } + + +extern cpu_context_t *hal_excToCpuCtx(exc_context_t *ctx) +{ + return NULL; /* unsupported */ +} diff --git a/hal/armv7r/exceptions.c b/hal/armv7r/exceptions.c index 1bee4c023..0dc9fb942 100644 --- a/hal/armv7r/exceptions.c +++ b/hal/armv7r/exceptions.c @@ -235,3 +235,9 @@ void _hal_exceptionsInit(void) exceptions.abortHandler = exceptions_defaultHandler; exceptions.defaultHandler = exceptions_defaultHandler; } + + +cpu_context_t *hal_excToCpuCtx(exc_context_t *ctx) +{ + return &ctx->cpuCtx; +} diff --git a/hal/armv8m/exceptions.c b/hal/armv8m/exceptions.c index e6cbddfeb..47b026bd6 100644 --- a/hal/armv8m/exceptions.c +++ b/hal/armv8m/exceptions.c @@ -136,3 +136,9 @@ int hal_exceptionsSetHandler(unsigned int n, void (*handler)(unsigned int, exc_c void _hal_exceptionsInit(void) { } + + +cpu_context_t *hal_excToCpuCtx(exc_context_t *ctx) +{ + return NULL; /* unsupported */ +} diff --git a/hal/armv8r/exceptions.c b/hal/armv8r/exceptions.c index b2b4a0d33..bf0ae41f5 100644 --- a/hal/armv8r/exceptions.c +++ b/hal/armv8r/exceptions.c @@ -235,3 +235,9 @@ void _hal_exceptionsInit(void) exceptions.abortHandler = exceptions_defaultHandler; exceptions.defaultHandler = exceptions_defaultHandler; } + + +cpu_context_t *hal_excToCpuCtx(exc_context_t *ctx) +{ + return &ctx->cpuCtx; +} diff --git a/hal/exceptions.h b/hal/exceptions.h index 793e03523..fec72322f 100644 --- a/hal/exceptions.h +++ b/hal/exceptions.h @@ -28,6 +28,9 @@ extern void *hal_exceptionsFaultAddr(unsigned int n, exc_context_t *ctx); extern ptr_t hal_exceptionsPC(exc_context_t *ctx); +extern const char *hal_exceptionMnemonic(int n); + + extern void hal_exceptionsDumpContext(char *buff, exc_context_t *ctx, int n); @@ -36,4 +39,16 @@ extern int hal_exceptionsSetHandler(unsigned int n, void (*handler)(unsigned int extern void _hal_exceptionsInit(void); + +extern cpu_context_t *hal_excToCpuCtx(exc_context_t *ctx); + + +extern void hal_coredumpGRegset(void *buff, cpu_context_t *ctx); + + +extern void hal_coredumpThreadAux(void *buff, cpu_context_t *ctx); + + +extern void hal_coredumpGeneralAux(void *buff); + #endif diff --git a/hal/ia32/arch/exceptions.h b/hal/ia32/arch/exceptions.h index c4d9ee680..af8d3f3e0 100644 --- a/hal/ia32/arch/exceptions.h +++ b/hal/ia32/arch/exceptions.h @@ -24,7 +24,12 @@ #define EXC_UNDEFINED 6 #define EXC_PAGEFAULT 14 -#define SIZE_CTXDUMP 512 /* Size of dumped context */ +#define SIZE_CTXDUMP 512 /* Size of dumped context */ +#define SIZE_COREDUMP_GREGSET 68 +#define SIZE_COREDUMP_THREADAUX 128 /* vfp context note */ +#define SIZE_COREDUMP_GENAUX 0 + +#define HAL_ELF_MACHINE 3 /* IA32 */ #pragma pack(push, 1) diff --git a/hal/ia32/exceptions.c b/hal/ia32/exceptions.c index efd44e023..00df75c33 100644 --- a/hal/ia32/exceptions.c +++ b/hal/ia32/exceptions.c @@ -23,6 +23,7 @@ #include "include/mman.h" #include "include/errno.h" +#include "proc/elf.h" /* Exception stubs */ @@ -112,19 +113,24 @@ ptr_t hal_exceptionsPC(exc_context_t *ctx) } -void hal_exceptionsDumpContext(char *buff, exc_context_t *ctx, int n) +const char *hal_exceptionMnemonic(int n) { static const char *const mnemonics[] = { - "0 #DE", "1 #DB", "2 #NMI", "3 #BP", "4 #OF", "5 #BR", "6 #UD", "7 #NM", - "8 #BF", "9 #", "10 #TS", "11 #NP", "12 #SS", "13 #GP", "14 #PF", "15 #", - "16 #MF", "17 #AC", "18 #MC", "19 #XM/#XF", "20 #VE", "21 #", "22 #", "23 #", - "24 #", "25 #", "26 #", "27 #", "28 #", "29 #", "30 #SE", "31 #" }; + "0 #DE", "1 #DB", "2 #NMI", "3 #BP", "4 #OF", "5 #BR", "6 #UD", "7 #NM", + "8 #BF", "9 #", "10 #TS", "11 #NP", "12 #SS", "13 #GP", "14 #PF", "15 #", + "16 #MF", "17 #AC", "18 #MC", "19 #XM/#XF", "20 #VE", "21 #", "22 #", "23 #", + "24 #", "25 #", "26 #", "27 #", "28 #", "29 #", "30 #SE", "31 #" + }; + + return mnemonics[n & 0x1f]; +} + +void hal_exceptionsDumpContext(char *buff, exc_context_t *ctx, int n) +{ size_t i = 0; u32 ss; - n &= 0x1f; - /* clang-format off */ __asm__ volatile( "xorl %0, %0\n\t" @@ -135,7 +141,7 @@ void hal_exceptionsDumpContext(char *buff, exc_context_t *ctx, int n) /* clang-format on */ hal_strcpy(buff, "\nException: "); - hal_strcpy(buff += hal_strlen(buff), mnemonics[n]); + hal_strcpy(buff += hal_strlen(buff), hal_exceptionMnemonic(n)); hal_strcpy(buff += hal_strlen(buff), "\n"); buff += hal_strlen(buff); @@ -301,3 +307,52 @@ __attribute__ ((section (".init"))) void _hal_exceptionsInit(void) return; } + + +cpu_context_t *hal_excToCpuCtx(exc_context_t *ctx) +{ + return &ctx->cpuCtx; +} + + +void hal_coredumpGRegset(void *buff, cpu_context_t *ctx) +{ + u32 *regs = (u32 *)buff; + *(regs++) = ctx->ebx; + *(regs++) = ctx->ecx; + *(regs++) = ctx->edx; + *(regs++) = ctx->esi; + *(regs++) = ctx->edi; + *(regs++) = ctx->ebp; + *(regs++) = ctx->eax; + *(regs++) = ctx->ds; + *(regs++) = ctx->es; + *(regs++) = ctx->fs; + *(regs++) = ctx->gs; + *(regs++) = 0; + *(regs++) = ctx->eip; + *(regs++) = ctx->cs; + *(regs++) = ctx->eflags; + *(regs++) = ctx->esp; + *(regs++) = ctx->ss; +} + + +void hal_coredumpThreadAux(void *buff, cpu_context_t *ctx) +{ + static const char FPREGSET_NAME[] = "CORE"; + Elf32_Nhdr nhdr; + nhdr.n_namesz = sizeof(FPREGSET_NAME); + nhdr.n_descsz = sizeof(ctx->fpuContext); + nhdr.n_type = NT_FPREGSET; + hal_memcpy(buff, &nhdr, sizeof(nhdr)); + buff = (char *)buff + sizeof(nhdr); + hal_memcpy(buff, FPREGSET_NAME, sizeof(FPREGSET_NAME)); + buff = (char *)buff + ((sizeof(FPREGSET_NAME) + 3) & ~3); + hal_memcpy(buff, &ctx->fpuContext, sizeof(ctx->fpuContext)); +} + + +void hal_coredumpGeneralAux(void *buff) +{ +} diff --git a/hal/riscv64/arch/cpu.h b/hal/riscv64/arch/cpu.h index b58964f06..d67acf1d3 100644 --- a/hal/riscv64/arch/cpu.h +++ b/hal/riscv64/arch/cpu.h @@ -122,7 +122,7 @@ typedef struct { /* CPU context saved by interrupt handlers on thread kernel stack */ typedef struct { - u64 pc; + u64 ra; /* x1 */ u64 gp; /* x3 */ u64 t0; /* x5 */ diff --git a/hal/riscv64/arch/exceptions.h b/hal/riscv64/arch/exceptions.h index 76516a3a2..b91244fa8 100644 --- a/hal/riscv64/arch/exceptions.h +++ b/hal/riscv64/arch/exceptions.h @@ -23,7 +23,12 @@ #define EXC_UNDEFINED 2 #define EXC_PAGEFAULT 127 -#define SIZE_CTXDUMP 1024 /* Size of dumped context */ +#define SIZE_CTXDUMP 1024 /* Size of dumped context */ +#define SIZE_COREDUMP_GREGSET 256 +#define SIZE_COREDUMP_THREADAUX 284 /* vfp context note */ +#define SIZE_COREDUMP_GENAUX 0 + +#define HAL_ELF_MACHINE 243 /* RISC-V 64-bit */ typedef cpu_context_t exc_context_t; diff --git a/hal/riscv64/cpu.c b/hal/riscv64/cpu.c index f4cb6aeb8..6a947d900 100644 --- a/hal/riscv64/cpu.c +++ b/hal/riscv64/cpu.c @@ -138,7 +138,7 @@ int hal_cpuCreateContext(cpu_context_t **nctx, void *start, void *kstack, size_t __asm__ volatile("sd gp, %0" : "=m"(ctx->gp)); /* clang-format on */ - ctx->pc = (u64)0; + ctx->ra = (u64)0; ctx->sp = (u64)kstack + kstacksz; ctx->t0 = 0; diff --git a/hal/riscv64/exceptions.c b/hal/riscv64/exceptions.c index de194dc6f..457cb12ee 100644 --- a/hal/riscv64/exceptions.c +++ b/hal/riscv64/exceptions.c @@ -20,6 +20,7 @@ #include "hal/string.h" #include "include/mman.h" +#include "proc/elf.h" #define SIZE_EXCEPTIONS 16 @@ -31,10 +32,8 @@ static struct { } exceptions_common; -void hal_exceptionsDumpContext(char *buff, exc_context_t *ctx, int n) +const char *hal_exceptionMnemonic(int n) { - unsigned int i = 0; - static const char *mnemonics[] = { "0 Instruction address missaligned", "1 Instruction access fault", "2 Illegal instruction", "3 Breakpoint", "4 Reserved", "5 Load access fault", "6 AMO address misaligned", "7 Store/AMO access fault", @@ -42,15 +41,21 @@ void hal_exceptionsDumpContext(char *buff, exc_context_t *ctx, int n) "12 Instruction page fault", "13 Load page fault", "14 Reserved", "15 Store/AMO page fault" }; - n &= 0xf; + return mnemonics[n & 0xf]; +} + + +void hal_exceptionsDumpContext(char *buff, exc_context_t *ctx, int n) +{ + unsigned int i = 0; hal_strcpy(buff, "\nException: "); - hal_strcpy(buff += hal_strlen(buff), mnemonics[n]); + hal_strcpy(buff += hal_strlen(buff), hal_exceptionMnemonic(n)); hal_strcpy(buff += hal_strlen(buff), "\n"); buff += hal_strlen(buff); i += hal_i2s("zero: ", &buff[i], 0, 16, 1); - i += hal_i2s(" ra : ", &buff[i], (u64)ctx->pc, 16, 1); + i += hal_i2s(" ra : ", &buff[i], (u64)ctx->ra, 16, 1); i += hal_i2s(" sp : ", &buff[i], (u64)ctx->sp, 16, 1); i += hal_i2s(" gp : ", &buff[i], (u64)ctx->gp, 16, 1); buff[i++] = '\n'; @@ -170,7 +175,7 @@ inline void *hal_exceptionsFaultAddr(unsigned int n, exc_context_t *ctx) inline ptr_t hal_exceptionsPC(exc_context_t *ctx) { - return ctx->pc; + return ctx->sepc; } @@ -246,3 +251,69 @@ __attribute__((section(".init"))) void _hal_exceptionsInit(void) exceptions_common.handlers[k] = exceptions_trampoline; } } + + +cpu_context_t *hal_excToCpuCtx(exc_context_t *ctx) +{ + return ctx; +} + + +void hal_coredumpGRegset(void *buff, cpu_context_t *ctx) +{ + u64 *regs = (u64 *)buff; + + *(regs++) = ctx->sepc; + *(regs++) = ctx->ra; + *(regs++) = ctx->sp; + *(regs++) = ctx->gp; + *(regs++) = ctx->tp; + *(regs++) = ctx->t0; + *(regs++) = ctx->t1; + *(regs++) = ctx->t2; + *(regs++) = ctx->s0; + *(regs++) = ctx->s1; + *(regs++) = ctx->a0; + *(regs++) = ctx->a1; + *(regs++) = ctx->a2; + *(regs++) = ctx->a3; + *(regs++) = ctx->a4; + *(regs++) = ctx->a5; + *(regs++) = ctx->a6; + *(regs++) = ctx->a7; + *(regs++) = ctx->s2; + *(regs++) = ctx->s3; + *(regs++) = ctx->s4; + *(regs++) = ctx->s5; + *(regs++) = ctx->s6; + *(regs++) = ctx->s7; + *(regs++) = ctx->s8; + *(regs++) = ctx->s9; + *(regs++) = ctx->s10; + *(regs++) = ctx->s11; + *(regs++) = ctx->t3; + *(regs++) = ctx->t4; + *(regs++) = ctx->t5; + *(regs++) = ctx->t6; +} + + +void hal_coredumpThreadAux(void *buff, cpu_context_t *ctx) +{ + static const char FPREGSET_NAME[] = "CORE"; + Elf64_Nhdr nhdr; + nhdr.n_namesz = sizeof(FPREGSET_NAME); + nhdr.n_descsz = sizeof(ctx->fpCtx); + nhdr.n_type = NT_FPREGSET; + hal_memcpy(buff, &nhdr, sizeof(nhdr)); + buff = (char *)buff + sizeof(nhdr); + hal_memcpy(buff, FPREGSET_NAME, sizeof(FPREGSET_NAME)); + buff = (char *)buff + ((sizeof(FPREGSET_NAME) + 3) & ~3); + + hal_memcpy(buff, &ctx->fpCtx, sizeof(ctx->fpCtx)); +} + + +void hal_coredumpGeneralAux(void *buff) +{ +} diff --git a/hal/sparcv8leon/exceptions-nommu.c b/hal/sparcv8leon/exceptions-nommu.c index ba5c68aac..a2daba1f7 100644 --- a/hal/sparcv8leon/exceptions-nommu.c +++ b/hal/sparcv8leon/exceptions-nommu.c @@ -169,3 +169,9 @@ int hal_exceptionsSetHandler(unsigned int n, void (*handler)(unsigned int, exc_c void _hal_exceptionsInit(void) { } + + +cpu_context_t *hal_excToCpuCtx(exc_context_t *ctx) +{ + return &ctx->cpuCtx; +} diff --git a/hal/sparcv8leon/exceptions.c b/hal/sparcv8leon/exceptions.c index 7f7d1c118..e8f56c022 100644 --- a/hal/sparcv8leon/exceptions.c +++ b/hal/sparcv8leon/exceptions.c @@ -213,3 +213,9 @@ void _hal_exceptionsInit(void) exceptions_common.defaultHandler = exceptions_defaultHandler; exceptions_common.mmuFaultHandler = exceptions_defaultHandler; } + + +cpu_context_t *hal_excToCpuCtx(exc_context_t *ctx) +{ + return &ctx->cpuCtx; +} diff --git a/lib/Makefile b/lib/Makefile index de858b25d..8fb7d1977 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -5,5 +5,5 @@ # Author: Pawel Pisarczyk # -OBJS += $(addprefix $(PREFIX_O)lib/, assert.o printf.o bsearch.o rand.o strtoul.o rb.o list.o cbuffer.o strutil.o idtree.o) +OBJS += $(addprefix $(PREFIX_O)lib/, assert.o printf.o bsearch.o rand.o strtoul.o rb.o list.o cbuffer.o strutil.o idtree.o encoding.o) diff --git a/lib/encoding.c b/lib/encoding.c new file mode 100644 index 000000000..bb76a3c52 --- /dev/null +++ b/lib/encoding.c @@ -0,0 +1,60 @@ +#include "encoding.h" + +#define LIB_CRC32POLY_LE 0xedb88320 + + +crc32_t lib_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 lib_crc32Finalize(crc32_t crc) +{ + return ~crc; +} + + +static const char base64_table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + +void lib_base64Init(lib_base64_ctx *ctx) +{ + ctx->buf = 0; + ctx->bits = 0; +} + + +size_t lib_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 lib_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/lib/encoding.h b/lib/encoding.h new file mode 100644 index 000000000..b26184ce0 --- /dev/null +++ b/lib/encoding.h @@ -0,0 +1,27 @@ +#include "hal/types.h" + +typedef u32 crc32_t; + +#define LIB_CRC32_INIT 0xffffffff + + +extern crc32_t lib_crc32NextByte(crc32_t crc, u8 byte); + + +extern crc32_t lib_crc32Finalize(crc32_t crc); + + +typedef struct { + u32 buf; + int bits; + char outBuf[3]; +} lib_base64_ctx; + + +extern void lib_base64Init(lib_base64_ctx *ctx); + + +extern size_t lib_base64EncodeByte(lib_base64_ctx *ctx, const u8 byte); + + +extern size_t lib_base64Finalize(lib_base64_ctx *ctx); diff --git a/proc/Makefile b/proc/Makefile index 1d5b6689f..17c61e9d0 100644 --- a/proc/Makefile +++ b/proc/Makefile @@ -5,7 +5,7 @@ # Author: Pawel Pisarczyk # -OBJS += $(addprefix $(PREFIX_O)proc/, proc.o threads.o process.o name.o resource.o mutex.o cond.o userintr.o ports.o) +OBJS += $(addprefix $(PREFIX_O)proc/, proc.o threads.o process.o name.o resource.o mutex.o cond.o userintr.o ports.o coredump.o) ifneq (, $(findstring NOMMU, $(CPPFLAGS))) OBJS += $(PREFIX_O)proc/msg-nommu.o diff --git a/proc/coredump.c b/proc/coredump.c new file mode 100644 index 000000000..c6637c217 --- /dev/null +++ b/proc/coredump.c @@ -0,0 +1,603 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * Process coredump + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * %LICENSE% + */ + +#include "coredump.h" +#include "arch/cpu.h" +#include "arch/exceptions.h" +#include "hal/exceptions.h" +#include "log/log.h" +#include "proc/elf.h" +#include "proc/process.h" +#include "lib/encoding.h" +#include + +#ifndef PROC_COREDUMP + +void coredump_dump(unsigned int n, exc_context_t *ctx) +{ + return; +} + +#else + +#define COREDUMP_OUTBUF_SIZE 128 + +#define MEM_NONE 0 +#define MEM_EXC_STACK 1 +#define MEM_ALL_STACKS 2 +#define MEM_ALL 3 + +#ifndef PROC_COREDUMP_MEM_OPT +#define PROC_COREDUMP_MEM_OPT MEM_EXC_STACK +#endif + +#ifndef PROC_COREDUMP_THREADS_NUM +#define PROC_COREDUMP_THREADS_NUM 1 +#endif + +#define CORE_BUF_SIZE_MAX max(SIZE_COREDUMP_GREGSET, max(SIZE_COREDUMP_THREADAUX, SIZE_COREDUMP_GENAUX)) + +#define COREDUMP_START "\n_____________COREDUMP_START_____________\n" +#define COREDUMP_END "\n______________COREDUMP_END______________\n" + +#define PRSTATUS_NAME "CORE" + +typedef struct { + 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; + char pr_reg[0]; /* actual size depends on architecture */ + int pr_fpvalid; +} elf_prstatus; + +typedef struct { + char outBuf[COREDUMP_OUTBUF_SIZE]; + size_t outCur; + + u8 rle_last; + size_t rle_count; + + lib_base64_ctx b64; + crc32_t crc32; +} coredump_state_t; + + +static void coredump_write(const char *data, size_t len) +{ +#ifdef PROC_COREDUMP_WRITE_SERIAL + hal_consolePrint(ATTR_NORMAL, data); +#endif +#ifdef PROC_COREDUMP_WRITE_LOG + log_write(data, len); +#endif +} + + +static void coredump_writeBuf(coredump_state_t *state, const char *data, size_t len) +{ + while (state->outCur + len >= sizeof(state->outBuf) - 1) { + hal_memcpy(state->outBuf + state->outCur, data, sizeof(state->outBuf) - 1 - state->outCur); + state->outBuf[sizeof(state->outBuf) - 1] = '\0'; + coredump_write(state->outBuf, sizeof(state->outBuf)); + data += sizeof(state->outBuf) - 1 - state->outCur; + len -= sizeof(state->outBuf) - 1 - state->outCur; + state->outCur = 0; + } + hal_memcpy(state->outBuf + state->outCur, data, len); + state->outCur += len; + if (state->outCur >= sizeof(state->outBuf) - 1) { + state->outBuf[sizeof(state->outBuf) - 1] = '\0'; + coredump_write(state->outBuf, sizeof(state->outBuf)); + state->outCur = 0; + } +} + + +static void coredump_nextByte(coredump_state_t *state, const u8 byte) +{ + int n = lib_base64EncodeByte(&state->b64, byte); + coredump_writeBuf(state, state->b64.outBuf, n); +} + + +static void coredump_encodeRleLength(coredump_state_t *state) +{ + u8 byte; + while (state->rle_count > 0) { + byte = state->rle_count & 0x7F; + state->rle_count >>= 7; + if (state->rle_count > 0) { + byte |= 0x80; + } + coredump_nextByte(state, byte); + } +} + + +static void coredump_init(coredump_state_t *state, const char *path, const char *mnemonic) +{ + state->outCur = 0; + state->rle_last = -1; + state->rle_count = 0; + state->crc32 = LIB_CRC32_INIT; + lib_base64Init(&state->b64); + + coredump_write(COREDUMP_START, sizeof(COREDUMP_START) - 1); + coredump_write(path, hal_strlen(path)); + coredump_write(": ", 2); + coredump_write(mnemonic, hal_strlen(mnemonic)); + coredump_write(";\n", 2); +} + + +static void coredump_encodeChunk(coredump_state_t *state, 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]; + state->crc32 = lib_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_nextByte(state, 0xFE); + coredump_encodeRleLength(state); + coredump_nextByte(state, state->rle_last); + } + else { + while (state->rle_count > 0) { + coredump_nextByte(state, state->rle_last); + state->rle_count--; + } + } + state->rle_count = 1; + state->rle_last = byte; + } +} + + +static void coredump_finalize(coredump_state_t *state) +{ + crc32_t crc = lib_crc32Finalize(state->crc32); + coredump_encodeChunk(state, (u8 *)&crc, sizeof(crc)); + + if ((state->rle_count > 3) || (state->rle_last == 0xFE)) { + coredump_nextByte(state, 0xFE); + coredump_encodeRleLength(state); + coredump_nextByte(state, state->rle_last); + } + else { + while (state->rle_count > 0) { + coredump_nextByte(state, state->rle_last); + state->rle_count--; + } + } + + int n = lib_base64Finalize(&state->b64); + coredump_writeBuf(state, state->b64.outBuf, n); + + + if (state->outCur > 0) { + state->outBuf[state->outCur++] = '\0'; + coredump_write(state->outBuf, state->outCur); + } + + coredump_write(COREDUMP_END, sizeof(COREDUMP_END) - 1); +} + + +static int isElfClass32(coredump_state_t *state) +{ + return sizeof(void *) == 8; +} + + +static void coredump_dumpElfHeader32(size_t segCnt, coredump_state_t *state) +{ + Elf32_Ehdr hdr; + + hal_memcpy(hdr.e_ident, ELFMAG, sizeof(ELFMAG)); + hal_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(state, (u8 *)&hdr, sizeof(hdr)); +} + + +static void coredump_dumpElfHeader64(size_t segCnt, coredump_state_t *state) +{ + Elf64_Ehdr hdr; + + hal_memcpy(hdr.e_ident, ELFMAG, sizeof(ELFMAG)); + hal_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(state, (u8 *)&hdr, sizeof(hdr)); +} + + +static void coredump_dumpElfHeader(size_t segCnt, coredump_state_t *state) +{ + if (isElfClass32(state)) { + coredump_dumpElfHeader32(segCnt, state); + } + else { + coredump_dumpElfHeader64(segCnt, state); + } +} + + +static size_t align4(size_t size) +{ + return (size + 3) & ~3; +} + + +static void coredump_dumpThreadNotes(coredump_threadinfo_t *threads, size_t threadCnt, coredump_state_t *state, void *buff) +{ + const u32 zero = 0; + Elf32_Nhdr nhdr; /* Elf64_Nhdr is identical to Elf32_Nhdr */ + size_t i; + elf_prstatus prstatus; + hal_memset(&prstatus, 0, sizeof(prstatus)); + for (i = 0; i < threadCnt; i++) { + nhdr.n_namesz = sizeof(PRSTATUS_NAME); + nhdr.n_descsz = sizeof(elf_prstatus) + SIZE_COREDUMP_GREGSET; + nhdr.n_type = NT_PRSTATUS; + coredump_encodeChunk(state, (u8 *)&nhdr, sizeof(nhdr)); + coredump_encodeChunk(state, (u8 *)PRSTATUS_NAME, sizeof(PRSTATUS_NAME)); + /* alignment */ + coredump_encodeChunk(state, (u8 *)&zero, align4(sizeof(PRSTATUS_NAME)) - sizeof(PRSTATUS_NAME)); + + prstatus.pr_pid = threads[i].tid; + coredump_encodeChunk(state, (u8 *)&prstatus, offsetof(elf_prstatus, pr_reg)); + hal_coredumpGRegset(buff, threads[i].userContext); + coredump_encodeChunk(state, (u8 *)buff, SIZE_COREDUMP_GREGSET); + coredump_encodeChunk(state, (u8 *)&prstatus.pr_reg, sizeof(prstatus) - offsetof(elf_prstatus, pr_reg)); + + hal_coredumpThreadAux(buff, threads[i].userContext); + coredump_encodeChunk(state, (u8 *)buff, SIZE_COREDUMP_THREADAUX); + } +} + + +static size_t coredump_findStack(void **currentSP, void *ustack, process_t *process) +{ + map_entry_t t; + map_entry_t *e; + size_t stackSize; + + t.vaddr = ustack; + t.size = 1; + + proc_lockSet(&process->mapp->lock); + + e = lib_treeof(map_entry_t, linkage, lib_rbFind(&process->mapp->tree, &t.linkage)); + if (e == NULL) { + proc_lockClear(&process->mapp->lock); + return 0; + } + if ((*currentSP >= (void *)((char *)e->vaddr + e->size)) || (*currentSP < e->vaddr)) { + *currentSP = e->vaddr; + } + stackSize = (char *)e->vaddr + e->size - (char *)*currentSP; + + proc_lockClear(&process->mapp->lock); + + return stackSize; +} + + +static void coredump_dumpStack(process_t *process, coredump_threadinfo_t *threadInfo, coredump_state_t *state) +{ + void *userSp; + size_t stackSize; + + userSp = hal_cpuGetUserSP(threadInfo->userContext); + stackSize = coredump_findStack(&userSp, threadInfo->ustack, process); + + coredump_encodeChunk(state, userSp, stackSize); +} + + +static void coredump_dumpPhdr32(u32 type, size_t offset, void *vaddr, size_t size, unsigned short flags, coredump_state_t *state) +{ + Elf32_Phdr phdr; + phdr.p_type = type; + phdr.p_offset = offset; + phdr.p_vaddr = (Elf32_Addr)(ptr_t)vaddr; + phdr.p_paddr = 0; + phdr.p_filesz = size; + phdr.p_memsz = size; + + phdr.p_flags = 0; + if (flags & PROT_READ) { + phdr.p_flags |= PF_R; + } + if (flags & PROT_WRITE) { + phdr.p_flags |= PF_W; + } + if (flags & PROT_EXEC) { + phdr.p_flags |= PF_X; + } + + phdr.p_align = 0; + coredump_encodeChunk(state, (u8 *)&phdr, sizeof(phdr)); +} + + +static void coredump_dumpPhdr64(u32 type, size_t offset, void *vaddr, size_t size, unsigned short flags, coredump_state_t *state) +{ + Elf64_Phdr phdr; + phdr.p_type = type; + phdr.p_offset = offset; + phdr.p_vaddr = (Elf64_Addr)(ptr_t)vaddr; + phdr.p_paddr = 0; + phdr.p_filesz = size; + phdr.p_memsz = size; + + phdr.p_flags = 0; + if (flags & PROT_READ) { + phdr.p_flags |= PF_R; + } + if (flags & PROT_WRITE) { + phdr.p_flags |= PF_W; + } + if (flags & PROT_EXEC) { + phdr.p_flags |= PF_X; + } + + phdr.p_align = 0; + coredump_encodeChunk(state, (u8 *)&phdr, sizeof(phdr)); +} + + +static void coredump_dumpPhdr(u32 type, size_t offset, void *vaddr, size_t size, unsigned short flags, coredump_state_t *state) +{ + if (isElfClass32(state)) { + coredump_dumpPhdr32(type, offset, vaddr, size, flags, state); + } + else { + coredump_dumpPhdr64(type, offset, vaddr, size, flags, state); + } +} + + +static void coredump_dumpAllPhdrs(coredump_threadinfo_t *threadInfo, size_t threadCnt, size_t segCnt, process_t *process, coredump_state_t *state) +{ + static const size_t THREAD_NOTES_SIZE = sizeof(Elf32_Nhdr) + + ((sizeof(PRSTATUS_NAME) + 3) & ~3) + + sizeof(elf_prstatus) + + SIZE_COREDUMP_GREGSET + + SIZE_COREDUMP_THREADAUX; + const size_t NOTES_SIZE = SIZE_COREDUMP_GENAUX + threadCnt * (THREAD_NOTES_SIZE); + size_t currentOffset; + size_t stackSize; + void *userSp; + map_entry_t *e; + size_t i; + + /* Notes */ + if (isElfClass32(state)) { + 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, 0, state); + currentOffset += NOTES_SIZE; + + /* Memory */ + if (PROC_COREDUMP_MEM_OPT == MEM_ALL) { + proc_lockSet(&process->mapp->lock); + e = lib_treeof(map_entry_t, linkage, lib_rbMinimum(process->mapp->tree.root)); + while (e != NULL) { + if ((e->prot & PROT_READ) && (e->prot & PROT_WRITE)) { + coredump_dumpPhdr(PT_LOAD, currentOffset, e->vaddr, e->size, e->prot, state); + currentOffset += e->size; + } + e = lib_treeof(map_entry_t, linkage, lib_rbNext(&e->linkage)); + } + proc_lockClear(&process->mapp->lock); + } + else if (PROC_COREDUMP_MEM_OPT == MEM_EXC_STACK) { + /* exception thread is put into threadInfo at index 0 */ + userSp = hal_cpuGetUserSP(threadInfo[0].userContext); + stackSize = coredump_findStack(&userSp, threadInfo[0].ustack, process); + coredump_dumpPhdr(PT_LOAD, currentOffset, userSp, stackSize, PROT_READ | PROT_WRITE, state); + currentOffset += stackSize; + } + else if (PROC_COREDUMP_MEM_OPT == MEM_ALL_STACKS) { + for (i = 0; i < threadCnt; i++) { + userSp = hal_cpuGetUserSP(threadInfo[i].userContext); + stackSize = coredump_findStack(&userSp, threadInfo[i].ustack, process); + coredump_dumpPhdr(PT_LOAD, currentOffset, userSp, stackSize, PROT_READ | PROT_WRITE, state); + currentOffset += stackSize; + } + } +} + + +static void coredump_dumpAllMemory(process_t *process, coredump_state_t *state) +{ + map_entry_t *e; + + proc_lockSet(&process->mapp->lock); + e = lib_treeof(map_entry_t, linkage, lib_rbMinimum(process->mapp->tree.root)); + while (e != NULL) { + if ((e->prot & PROT_READ) && (e->prot & PROT_WRITE)) { + coredump_encodeChunk(state, e->vaddr, e->size); + } + e = lib_treeof(map_entry_t, linkage, lib_rbNext(&e->linkage)); + } + proc_lockClear(&process->mapp->lock); +} + + +static size_t coredump_segmentCount(process_t *process) +{ + map_entry_t *e; + size_t segCnt = 0; + + proc_lockSet(&process->mapp->lock); + + e = lib_treeof(map_entry_t, linkage, lib_rbMinimum(process->mapp->tree.root)); + while (e != NULL) { + if ((e->prot & PROT_READ) && (e->prot & PROT_WRITE)) { + segCnt++; + } + e = lib_treeof(map_entry_t, linkage, lib_rbNext(&e->linkage)); + } + + proc_lockClear(&process->mapp->lock); + return segCnt; +} + + +static void coredump_currentThreadInfo(cpu_context_t *ctx, unsigned int n, coredump_threadinfo_t *info) +{ + thread_t *current = proc_current(); + + info->tid = proc_getTid(current); + info->cursig = n; + info->userContext = ctx; + info->ustack = current->ustack; +} + + +void coredump_dump(unsigned int n, exc_context_t *ctx) +{ + char buff[CORE_BUF_SIZE_MAX] __attribute__((aligned(8))); + coredump_threadinfo_t threadInfo[PROC_COREDUMP_THREADS_NUM]; + size_t segCnt; + size_t threadCnt; + coredump_state_t state; + process_t *process; + size_t i; + + process = proc_current()->process; + + /* + * Ensure for dumped process that: + * - saved context is coherent with stack memory, + * - thread count, section count are fixed, + * while the rest of the processes can run freely. + */ + proc_freeze(process); + + coredump_currentThreadInfo(hal_excToCpuCtx(ctx), n, &threadInfo[0]); + threadCnt = 1 + coredump_threadsInfo(process, 1, PROC_COREDUMP_THREADS_NUM - 1, &threadInfo[1]); + + switch (PROC_COREDUMP_MEM_OPT) { + case MEM_ALL: + segCnt = coredump_segmentCount(process); + break; + case MEM_ALL_STACKS: + segCnt = threadCnt; + break; + case MEM_EXC_STACK: + segCnt = 1; + break; + default: + segCnt = 0; + break; + } + + coredump_init(&state, process->path, hal_exceptionMnemonic(n)); + coredump_dumpElfHeader(segCnt, &state); + coredump_dumpAllPhdrs(threadInfo, threadCnt, segCnt, process, &state); + + coredump_dumpThreadNotes(threadInfo, threadCnt, &state, buff); + hal_coredumpGeneralAux(buff); + coredump_encodeChunk(&state, (u8 *)buff, SIZE_COREDUMP_GENAUX); + + /* MEMORY */ + if (PROC_COREDUMP_MEM_OPT == MEM_ALL) { + coredump_dumpAllMemory(process, &state); + } + else if (PROC_COREDUMP_MEM_OPT == MEM_EXC_STACK) { + /* exception thread is put into threadInfo at index 0 */ + coredump_dumpStack(process, &threadInfo[0], &state); + } + else if (PROC_COREDUMP_MEM_OPT == MEM_ALL_STACKS) { + for (i = 0; (i < threadCnt) && (i < PROC_COREDUMP_THREADS_NUM); i++) { + coredump_dumpStack(process, &threadInfo[i], &state); + } + } + + coredump_finalize(&state); + + proc_unfreeze(process); +} +#endif /* PROC_COREDUMP */ diff --git a/proc/coredump.h b/proc/coredump.h new file mode 100644 index 000000000..ac2a6b7e3 --- /dev/null +++ b/proc/coredump.h @@ -0,0 +1,33 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * Process coredump + * + * Copyright 2025 Phoenix Systems + * Author: Jakub Klimek + * + * %LICENSE% + */ + +#ifndef _COREDUMP_H_ +#define _COREDUMP_H_ + +#include "arch/exceptions.h" +#include "process.h" + +typedef struct { + int tid; + short cursig; + cpu_context_t *userContext; + void *ustack; +} coredump_threadinfo_t; + + +extern void coredump_dump(unsigned int n, exc_context_t *ctx); + + +extern size_t coredump_threadsInfo(process_t *process, int ignoreCurrent, size_t n, coredump_threadinfo_t *info); + +#endif diff --git a/proc/elf.h b/proc/elf.h index 55382d1b3..f2c3476d5 100644 --- a/proc/elf.h +++ b/proc/elf.h @@ -32,8 +32,50 @@ typedef u64 Elf64_Off; typedef s64 Elf64_Sword; typedef u64 Elf64_Xword; - -#define EI_NIDENT 16 +#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 @@ -45,19 +87,25 @@ typedef u64 Elf64_Xword; #define SHT_LOUSER 0x80000000 #define SHT_HIUSER 0xffffffff -#define STT_LOPROC 13 -#define STT_HIPROC 15 +#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 PT_LOAD 1 -#define PT_DYNAMIC 2 -#define PT_INTERP 3 -#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 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 #pragma pack(push, 1) @@ -67,10 +115,10 @@ typedef struct { Elf32_Half e_machine; Elf32_Word e_version; Elf32_Addr e_entry; - Elf32_Off e_phoff; - Elf32_Off e_shoff; + Elf32_Off e_phoff; + Elf32_Off e_shoff; Elf32_Word e_flags; - Elf32_Half e_hsize; + Elf32_Half e_ehsize; Elf32_Half e_phentsize; Elf32_Half e_phnum; Elf32_Half e_shentsize; @@ -106,12 +154,19 @@ typedef struct { typedef struct { - u32 st_name; - Elf32_Addr st_value; - u32 st_size; + 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; + u16 st_shndx; } Elf32_Sym; @@ -158,6 +213,13 @@ typedef struct { } 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; diff --git a/proc/lock.h b/proc/lock.h index ac4c53f02..5a9091543 100644 --- a/proc/lock.h +++ b/proc/lock.h @@ -20,6 +20,11 @@ #include "include/threads.h" +/* clang-format off */ +enum { LOCK_KERNEL_ONLY = 0, LOCK_USER }; +/* clang-format on */ + + typedef struct _lock_t { spinlock_t spinlock; /* Spinlock */ struct _thread_t *owner; /* Owner thread */ @@ -27,6 +32,7 @@ typedef struct _lock_t { struct _lock_t *prev, *next; /* Doubly linked list */ const char *name; struct lockAttr attr; + unsigned int kind; unsigned int depth; /* Used with recursive locks */ } lock_t; diff --git a/proc/mutex.c b/proc/mutex.c index 8e796b9d8..4deb597c0 100644 --- a/proc/mutex.c +++ b/proc/mutex.c @@ -73,6 +73,7 @@ int proc_mutexCreate(const struct lockAttr *attr) } proc_lockInit(&mutex->lock, attr, "user.mutex"); + mutex->lock.kind = LOCK_USER; (void)resource_put(p, &mutex->resource); diff --git a/proc/process.c b/proc/process.c index 02d2ee150..32ca04273 100644 --- a/proc/process.c +++ b/proc/process.c @@ -194,6 +194,8 @@ int proc_start(void (*initthr)(void *), void *arg, const char *path) process->ghosts = NULL; process->reaper = NULL; process->refs = 1; + process->exit = 0; + process->freeze = RUNNING; proc_lockInit(&process->lock, &proc_lockAttrDefault, "process"); @@ -274,11 +276,13 @@ void process_exception(unsigned int n, exc_context_t *ctx) { thread_t *thread = proc_current(); - process_dumpException(n, ctx); + coredump_dump(n, ctx); if (thread->process == NULL) hal_cpuHalt(); + process_dumpException(n, ctx); + threads_sigpost(thread->process, thread, signal_kill); /* Don't allow current thread to return to the userspace, @@ -297,6 +301,8 @@ static void process_illegal(unsigned int n, exc_context_t *ctx) if (process == NULL) hal_cpuHalt(); + process_dumpException(n, ctx); + threads_sigpost(process, thread, signal_illegal); } diff --git a/proc/process.h b/proc/process.h index c0c881ae3..881bf41fd 100644 --- a/proc/process.h +++ b/proc/process.h @@ -45,6 +45,7 @@ typedef struct _process_t { vm_map_t *imapp; pmap_t *pmapp; int exit; + int freeze : 2; unsigned lazy : 1; unsigned lgap : 1; diff --git a/proc/threads.c b/proc/threads.c index 1798f268f..85b69791f 100644 --- a/proc/threads.c +++ b/proc/threads.c @@ -24,6 +24,7 @@ #include "resource.h" #include "msg.h" #include "ports.h" +#include "coredump.h" const struct lockAttr proc_lockAttrDefault = { .type = PH_LOCK_NORMAL }; @@ -576,8 +577,11 @@ int _threads_schedule(unsigned int n, cpu_context_t *context, void *arg) if (current != NULL) { current->context = context; - /* Move thread to the end of queue */ - if (current->state == READY) { + if ((hal_cpuSupervisorMode(current->context) == 0) && (current->freeze != RUNNING)) { + current->freeze = FROZEN; + } + else if (current->state == READY) { + /* Move thread to the end of queue */ LIST_ADD(&threads_common.ready[current->priority], current); _perf_preempted(current); } @@ -592,6 +596,11 @@ int _threads_schedule(unsigned int n, cpu_context_t *context, void *arg) LIST_REMOVE(&threads_common.ready[i], selected); + if ((hal_cpuSupervisorMode(selected->context) == 0) && (selected->freeze != RUNNING)) { + selected->freeze = FROZEN; + continue; + } + if (selected->exit == 0) { break; } @@ -645,15 +654,15 @@ int _threads_schedule(unsigned int n, cpu_context_t *context, void *arg) #if defined(STACK_CANARY) || !defined(NDEBUG) if ((selected->execkstack == NULL) && (selected->context == selCtx)) { LIB_ASSERT_ALWAYS((char *)selCtx > ((char *)selected->kstack + selected->kstacksz - 9 * selected->kstacksz / 10), - "pid: %d, tid: %d, kstack: 0x%p, context: 0x%p, kernel stack limit exceeded", - (selected->process != NULL) ? process_getPid(selected->process) : 0, proc_getTid(selected), - selected->kstack, selCtx); + "pid: %d, tid: %d, kstack: 0x%p, context: 0x%p, kernel stack limit exceeded", + (selected->process != NULL) ? process_getPid(selected->process) : 0, proc_getTid(selected), + selected->kstack, selCtx); } - LIB_ASSERT_ALWAYS((selected->process == NULL) || (selected->ustack == NULL) || - (hal_memcmp(selected->ustack, threads_common.stackCanary, sizeof(threads_common.stackCanary)) == 0), - "pid: %d, tid: %d, path: %s, user stack corrupted", - process_getPid(selected->process), proc_getTid(selected), selected->process->path); + LIB_ASSERT_ALWAYS((selected->process == NULL) || (selected->ustack == NULL) || hal_cpuSupervisorMode(selected->context) || + (hal_memcmp(selected->ustack, threads_common.stackCanary, sizeof(threads_common.stackCanary)) == 0), + "pid: %d, tid: %d, path: %s, user stack corrupted", + process_getPid(selected->process), proc_getTid(selected), selected->process->path); #endif } @@ -767,9 +776,11 @@ int proc_threadCreate(process_t *process, void (*start)(void *), int *id, unsign t->sigmask = t->sigpend = 0; t->refs = 1; t->interruptible = 0; + t->freeze = RUNNING; t->exit = 0; t->execdata = NULL; t->wait = NULL; + t->waitlock = NULL; t->locks = NULL; t->stick = 0; t->utick = 0; @@ -821,11 +832,18 @@ int proc_threadCreate(process_t *process, void (*start)(void *), int *id, unsign else { hal_spinlockSet(&threads_common.spinlock, &sc); } - /* Insert thread to scheduler queue */ + + if (process != NULL) { + t->exit = process->exit == 0 ? 0 : THREAD_END; + t->freeze = process->freeze == RUNNING ? RUNNING : FROZEN; + } + if (t->freeze != FROZEN) { + /* Insert thread to scheduler queue */ + LIST_ADD(&threads_common.ready[priority], t); + } _perf_begin(t); _perf_waking(t); - LIST_ADD(&threads_common.ready[priority], t); hal_spinlockClear(&threads_common.spinlock, &sc); @@ -1056,6 +1074,10 @@ static void _proc_threadDequeue(thread_t *t) t->state = READY; t->interruptible = 0; + if (t->freeze == FROZEN) { + return; + } + /* MOD */ for (i = 0; i < hal_cpuGetCount(); i++) { if (t == threads_common.current[i]) { @@ -1645,6 +1667,7 @@ static int _proc_lockSet(lock_t *lock, int interruptible, spinlock_ctx_t *scp) _proc_threadSetPriority(lock->owner, current->priority); } + current->waitlock = lock; hal_spinlockClear(&threads_common.spinlock, &sc); do { @@ -1752,6 +1775,7 @@ static int _proc_lockUnlock(lock_t *lock) if (lockPriority < lock->owner->priority) { _proc_threadSetPriority(lock->queue, lockPriority); } + lock->owner->waitlock = NULL; _proc_threadDequeue(lock->owner); LIST_ADD(&lock->owner->locks, lock); ret = 1; @@ -1904,6 +1928,7 @@ int proc_lockInit(lock_t *lock, const struct lockAttr *attr, const char *name) lock->owner = NULL; lock->queue = NULL; lock->name = name; + lock->kind = LOCK_KERNEL_ONLY; hal_memcpy(&lock->attr, attr, sizeof(struct lockAttr)); @@ -2129,3 +2154,158 @@ int _threads_init(vm_map_t *kmap, vm_object_t *kernel) return EOK; } + + +cpu_context_t *_threads_userContext(thread_t *thread) +{ + if (hal_cpuSupervisorMode(thread->context) != 0) { + return (cpu_context_t *)((char *)(thread->kstack + thread->kstacksz) - sizeof(cpu_context_t)); + } + return thread->context; +} + + +int _threads_usesKernelOnlyLock(thread_t *thread) +{ + lock_t *lock; + + if ((thread->waitlock != NULL) && (thread->waitlock->kind == LOCK_KERNEL_ONLY)) { + return 1; + } + + lock = thread->locks; + if (lock == NULL) { + return 0; + } + + do { + if (lock->kind == LOCK_KERNEL_ONLY) { + return 1; + } + lock = lock->next; + } while (lock != thread->locks); + + return 0; +} + + +/* + * Does not freeze current thread until it's rescheduled in userspace! + * Do not enter this function while holding any KERNEL_ONLY locks! + */ +void proc_freeze(process_t *process) +{ + spinlock_ctx_t sc; + thread_t *thread, *current; + int i; + + hal_spinlockSet(&threads_common.spinlock, &sc); + current = _proc_current(); + while (process->freeze != RUNNING) { + current->freeze = FROZEN; + hal_cpuReschedule(&threads_common.spinlock, &sc); + hal_spinlockSet(&threads_common.spinlock, &sc); + } + thread = process->threads; + process->freeze = FREEZING; + + /* Kernelspace and currently executing threads can't be instantly frozen */ + do { + if (((thread->state == READY) && (hal_cpuSupervisorMode(thread->context) != 0)) || + ((thread->state == SLEEP) && (_threads_usesKernelOnlyLock(thread) != 0))) { + thread->freeze = FREEZING; + } + else { + thread->freeze = FROZEN; + } + thread = thread->procnext; + } while (thread != process->threads); + + for (i = 0; i < hal_cpuGetCount(); i++) { + if ((i != hal_cpuGetID()) && (threads_common.current[i]->process == process)) { + threads_common.current[i]->freeze = FREEZING; + } + } + current->freeze = FREEZER; + + /* Threads can become frozen when rescheduled from userspace */ + do { + while ((thread->freeze != FROZEN) && (thread != current)) { + if ((thread->state == SLEEP) && (_threads_usesKernelOnlyLock(thread) == 0)) { + /* Ignore waiting on user locks, as it can lead to deadlock with freezer process */ + thread->freeze = FROZEN; + continue; + } + hal_cpuReschedule(&threads_common.spinlock, &sc); + hal_spinlockSet(&threads_common.spinlock, &sc); + } + thread = thread->procnext; + } while (thread != process->threads); + + process->freeze = FROZEN; + hal_spinlockClear(&threads_common.spinlock, &sc); +} + + +void proc_unfreeze(process_t *process) +{ + spinlock_ctx_t sc; + thread_t *thread; + thread_t *current; + hal_spinlockSet(&threads_common.spinlock, &sc); + current = _proc_current(); + thread = process->threads; + + do { + thread->freeze = RUNNING; + if ((thread->state == READY) && (thread != current) && (LIST_BELONGS(&threads_common.ready[thread->priority], thread) == 0)) { + LIST_ADD(&threads_common.ready[thread->priority], thread); + } + + thread = thread->procnext; + } while (thread != process->threads); + process->freeze = RUNNING; + hal_spinlockClear(&threads_common.spinlock, &sc); +} + + +size_t coredump_threadsInfo(process_t *process, int ignoreCurrent, size_t n, coredump_threadinfo_t *info) +{ + thread_t *thread; + thread_t *current; + spinlock_ctx_t sc; + cpu_context_t *cctx; + size_t i = 0; + + if (n == 0) { + return 0; + } + + hal_spinlockSet(&threads_common.spinlock, &sc); + current = _proc_current(); + + thread = process->threads; + do { + if ((ignoreCurrent != 0) && (thread == current)) { + thread = thread->procnext; + continue; + } + + cctx = _threads_userContext(thread); + if (cctx == NULL) { + thread = thread->procnext; + continue; + } + + info[i].tid = proc_getTid(thread); + info[i].cursig = 0; + info[i].userContext = cctx; + info[i].ustack = thread->ustack; + i++; + + thread = thread->procnext; + } while ((i < n) && (thread != process->threads)); + + hal_spinlockClear(&threads_common.spinlock, &sc); + return i; +} diff --git a/proc/threads.h b/proc/threads.h index cf830ea25..c6de4102b 100644 --- a/proc/threads.h +++ b/proc/threads.h @@ -22,11 +22,14 @@ #include "include/sysinfo.h" #include "process.h" #include "lock.h" +#include "coredump.h" #define MAX_TID MAX_ID #define THREAD_END 1 #define THREAD_END_NOW 2 +/* clang-format off */ + /* Parent thread states */ enum { PREFORK = 0, FORKING = 1, FORKED }; @@ -35,6 +38,10 @@ enum { OWNSTACK = 0, PARENTSTACK }; enum { READY = 0, SLEEP, GHOST }; +enum { RUNNING = 0, FREEZER, FREEZING, FROZEN }; + +/* clang-format on */ + typedef struct _thread_t { struct _thread_t *next; @@ -52,6 +59,7 @@ typedef struct _thread_t { struct _thread_t *blocking; struct _thread_t **wait; + lock_t *waitlock; volatile time_t wakeup; unsigned priorityBase : 4; @@ -59,6 +67,7 @@ typedef struct _thread_t { unsigned state : 2; unsigned exit : 2; unsigned interruptible : 1; + unsigned freeze : 2; unsigned sigmask; unsigned sigpend; @@ -217,4 +226,12 @@ extern int threads_sigsuspend(unsigned int mask); extern void threads_setupUserReturn(void *retval, cpu_context_t *ctx); +extern cpu_context_t *_threads_userContext(thread_t *thread); + + +extern void proc_freeze(process_t *proc); + + +extern void proc_unfreeze(process_t *proc); + #endif diff --git a/vm/map.c b/vm/map.c index 93ea5382c..7154a9b36 100644 --- a/vm/map.c +++ b/vm/map.c @@ -742,6 +742,7 @@ static void map_pageFault(unsigned int n, exc_context_t *ctx) paddr = (void *)((unsigned long)vaddr & ~(SIZE_PAGE - 1)); #ifdef PAGEFAULTSTOP + coredump_dump(n, ctx); process_dumpException(n, ctx); /* clang-format off */ __asm__ volatile ("1: b 1b"); @@ -761,6 +762,7 @@ static void map_pageFault(unsigned int n, exc_context_t *ctx) map = map_common.kmap; if (vm_mapForce(map, paddr, prot)) { + coredump_dump(n, ctx); process_dumpException(n, ctx); if (thread->process == NULL) {