toaruos/kernel/arch/aarch64/traceback.c
2022-02-22 17:30:17 +09:00

115 lines
3.2 KiB
C

/**
* @file kernel/arch/aarch64/traceback.c
* @brief Kernel fault traceback generator.
*
* @copyright
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2021-2022 K. Lange
*/
#include <stdint.h>
#include <kernel/process.h>
#include <kernel/string.h>
#include <kernel/printf.h>
#include <kernel/mmu.h>
#include <kernel/ksym.h>
#include <kernel/arch/aarch64/regs.h>
extern char end[];
static uintptr_t matching_symbol(uintptr_t ip, char ** name) {
hashmap_t * symbols = ksym_get_map();
uintptr_t best_match = 0;
for (size_t i = 0; i < symbols->size; ++i) {
hashmap_entry_t * x = symbols->entries[i];
while (x) {
void* sym_addr = x->value;
char* sym_name = x->key;
if ((uintptr_t)sym_addr < ip && (uintptr_t)sym_addr > best_match) {
best_match = (uintptr_t)sym_addr;
*name = sym_name;
}
x = x->next;
}
}
return best_match;
}
static int validate_pointer(uintptr_t base, size_t size) {
uintptr_t end = size ? (base + (size - 1)) : base;
uintptr_t page_base = base >> 12;
uintptr_t page_end = end >> 12;
for (uintptr_t page = page_base; page <= page_end; ++page) {
if ((page & 0xffff800000000) != 0 && (page & 0xffff800000000) != 0xffff800000000) return 0;
union PML * page_entry = mmu_get_page_other(this_core->current_process->thread.page_directory->directory, page << 12);
if (!page_entry) return 0;
if (!page_entry->bits.present) return 0;
}
return 1;
}
static void dump_traceback(uintptr_t ip, uintptr_t bp, uintptr_t x30) {
int depth = 0;
int max_depth = 20;
while (bp && ip && depth < max_depth) {
dprintf(" 0x%016zx ", ip);
#if 0
if (ip >= 0xffffffff80000000UL) {
char * name = NULL;
struct LoadedModule * mod = find_module(ip, &name);
if (mod) {
dprintf("\a in module '%s', base address %#zx (offset %#zx)\n",
name, mod->baseAddress, ip - mod->baseAddress);
} else {
dprintf("\a (unknown)\n");
}
#else
if (0) {
#endif
} else if (ip <= 0x800000000000) {
dprintf("\a in userspace\n");
} else if (ip >= 0xffffffff80000000UL && ip <= (uintptr_t)&end) {
/* Find symbol match */
char * name;
uintptr_t addr = matching_symbol(ip, &name);
if (!addr) {
dprintf("\a (no match)\n");
} else {
dprintf("\a %s+0x%zx\n", name, ip-addr);
}
} else {
dprintf("\a (unknown)\n");
}
if (!validate_pointer(bp, sizeof(uintptr_t)) || !validate_pointer(bp + sizeof(uintptr_t), sizeof(uintptr_t))) {
break;
}
if (depth == 0) {
ip = x30;
} else {
ip = *(uintptr_t*)(bp + sizeof(uintptr_t));
bp = *(uintptr_t*)(bp);
}
depth++;
}
}
void aarch64_safe_dump_traceback(uintptr_t elr, struct regs * r) {
dump_traceback(elr, r->x29, r->x30);
}
/* We need to pull the frame address from the caller or this isn't going work, but
* gcc is going to warn that that is unsafe... */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wno-frame-address"
/**
* @brief Display a traceback from the current call context.
*/
void arch_dump_traceback(void) {
dump_traceback((uintptr_t)arch_dump_traceback+1, (uintptr_t)__builtin_frame_address(1), (uintptr_t)__builtin_return_address(0));
}
#pragma GCC diagnostic pop