blob: 03836e3ae566a7fdf313a975f18bf08ffbf60449 [file]
/*
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "arch_fault.h"
#include <assert.h>
#include <camkes/fault.h>
#include <limits.h>
#include <sel4/sel4.h>
#include <sel4debug/register_dump.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <inttypes.h>
/* This function is provided by generated code. */
extern const char *get_instance_name(void);
/* Display information about a fault address, situating within the given memory
* map. The memory map is not assumed to be ordered.
*/
static void show_fault_location(const camkes_memory_region_t *memory_map,
uintptr_t address)
{
assert(memory_map != NULL);
/* Calculate the number of entries in the memory map. */
size_t memory_map_sz = 0;
for (const camkes_memory_region_t *reg = memory_map;
reg->start != 0 || reg->end != 0;
reg++, memory_map_sz++);
/* Determine how many characters are needed to print a pointer
* (n-bit compatibility).
*/
int ptr_bytes = snprintf(NULL, 0, "%"PRIxPTR, UINTPTR_MAX);
assert(ptr_bytes > 0 && "0-bit pointers; huh?");
SHOW(" memory map:\n");
/* How many entries we've currently printed. */
size_t printed = 0;
/* The last upper address we saw. We're going to work our way down through
* the regions to give the user a logical display of their address space.
*/
uintptr_t limit = UINTPTR_MAX;
uintptr_t last_start = 0;
while (printed < memory_map_sz) {
const camkes_memory_region_t *current = NULL;
for (const camkes_memory_region_t *reg = memory_map;
reg->start != 0 || reg->end != 0; reg++) {
assert(reg->start <= reg->end && "inverted region in memory map");
/* If the current region is the uppermost thing we've seen thus far
* that we haven't printed...
*/
if (reg->end <= limit && (current == NULL ||
reg->end > current->end)) {
current = reg;
}
}
assert(current != NULL && "overlapping or illegal regions in memory map");
if (current->end != last_start - 1) {
if (limit != UINTPTR_MAX) {
SHOW(" | <undescribed>\n");
}
SHOW(" +-- 0x%.*"PRIxPTR" --\n", ptr_bytes, current->end);
}
SHOW(" | %s\n", current->name);
if (address >= current->start && address <= current->end) {
SHOW(" | FAULT @ 0x%.*"PRIxPTR"\n", ptr_bytes, address);
}
SHOW(" +-- 0x%.*"PRIxPTR" --\n", ptr_bytes, current->start);
last_start = current->start;
limit = current->end - 1;
printed++;
}
}
void camkes_show_fault(seL4_MessageInfo_t info, seL4_CPtr thread_id,
const char *name, bool tcb_caps_available,
const camkes_memory_region_t *memory_map)
{
const char *safe_name = name == NULL ? "<unknown>" : name;
int label = seL4_MessageInfo_get_label(info);
switch (label) {
case seL4_Fault_CapFault: {
/* From section 5.2.1 of the seL4 manual. */
uintptr_t pc = (uintptr_t)seL4_GetMR(0);
seL4_CPtr slot = (seL4_CPtr)seL4_GetMR(1);
bool receive = seL4_GetMR(2) == 1;
/* From section 3.4 of the seL4 manual. */
int cause = seL4_GetMR(3);
switch (cause) {
case seL4_InvalidRoot:
SHOW("cap fault (invalid root) in %s phase from %s.%s (ID "
"0x%"PRIxPTR"), pc = %p, slot = 0x%"PRIxPTR"\n", receive ? "receive" :
"send", get_instance_name(), safe_name, thread_id,
(void *)pc, slot);
break;
case seL4_MissingCapability: {
unsigned unresolved = seL4_GetMR(4);
SHOW("cap fault (missing capability with %u unresolved "
"bits) from %s.%s (ID 0x%"PRIxPTR"), pc = %p, slot = 0x%"PRIxPTR"\n",
unresolved, get_instance_name(), safe_name, thread_id,
(void *)pc, slot);
break;
}
case seL4_DepthMismatch: {
unsigned unresolved = seL4_GetMR(4);
unsigned resolved = seL4_GetMR(5);
SHOW("cap fault (depth mismatch with %u unresolved bits "
"and %u resolved bits) from %s.%s (ID 0x%"PRIxPTR"), pc = %p, "
"slot = 0x%"PRIxPTR"\n", unresolved, resolved, get_instance_name(),
safe_name, thread_id, (void*)pc, slot);
break;
}
case seL4_GuardMismatch: {
unsigned unresolved = seL4_GetMR(4);
seL4_Word guard = seL4_GetMR(5);
unsigned guard_size = seL4_GetMR(6);
SHOW("cap fault (guard mismatch with %u unresolved bits "
"and %u bit guard of 0x%"PRIxPTR") from %s.%s (ID 0x%"PRIxPTR"), pc = "
"%p, slot = 0x%"PRIxPTR"\n", unresolved, guard_size, guard,
get_instance_name(), safe_name, thread_id, (void*)pc, slot);
break;
}
default:
SHOW("cap fault (unknown cause %d; API bug?) from %s.%s "
"(ID 0x%"PRIxPTR"), pc = %p, slot = 0x%"PRIxPTR"\n", label,
get_instance_name(), safe_name, thread_id, (void*)pc,
slot);
break;
}
break;
}
case seL4_Fault_VMFault: {
uintptr_t pc = seL4_GetMR(0);
uintptr_t addr = seL4_GetMR(1);
bool instruction_fault = seL4_GetMR(2) == 1;
uintptr_t fsr = seL4_GetMR(3);
SHOW("%s fault from %s.%s (ID 0x%"PRIxPTR") on address %p, pc = %p, fsr = "
"0x%"PRIxPTR"\n", instruction_fault ? "instruction" : "data",
get_instance_name(), safe_name, thread_id, (void*)addr,
(void*)pc, fsr);
if (tcb_caps_available) {
sel4debug_dump_registers_prefix(thread_id, FAULT_PREFIX);
} else {
SHOW("register dump unavailable (no cap to target TCB)\n");
}
if (memory_map != NULL) {
show_fault_location(memory_map, addr);
}
break;
}
case seL4_Fault_UnknownSyscall:
show_unknown_syscall_fault(thread_id, name);
break;
case seL4_Fault_UserException: {
uintptr_t pc = seL4_GetMR(0);
uintptr_t sp = seL4_GetMR(1);
uintptr_t flags = seL4_GetMR(2);
int exc_no = seL4_GetMR(3);
int exc_code = seL4_GetMR(4);
SHOW("user exception (number %d, code %d) from %s.%s (ID 0x%"PRIxPTR"), "
"pc = %p, sp = %p, flags = %p\n", exc_no, exc_code,
get_instance_name(), safe_name, thread_id, (void*)pc, (void*)sp,
(void*)flags);
break;
}
default:
SHOW("unknown message from %s.%s (ID 0x%"PRIxPTR"); misconfigured fault "
"handler?\n", get_instance_name(), safe_name, thread_id);
}
}