mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-23 12:51:13 +00:00
100 lines
2.5 KiB
C
100 lines
2.5 KiB
C
|
/**
|
||
|
* Thermosphère: exception handler
|
||
|
* Handles all exceptions, including a return to EL2.
|
||
|
*
|
||
|
* Copyright (c) 2018 Kate J. Temkin <k@ktemkin.com>
|
||
|
*/
|
||
|
|
||
|
#include <stdint.h>
|
||
|
#include <stddef.h>
|
||
|
#include "exceptions.h"
|
||
|
|
||
|
#include "lib/printk.h"
|
||
|
|
||
|
/**
|
||
|
* Simple debug function that prints all of our saved registers.
|
||
|
*/
|
||
|
static void print_registers(struct guest_state *regs)
|
||
|
{
|
||
|
// print x0-29
|
||
|
for(int i = 0; i < 30; i += 2) {
|
||
|
printk("x%d:\t0x%p\t", i, regs->x[i]);
|
||
|
printk("x%d:\t0x%p\n", i + 1, regs->x[i + 1]);
|
||
|
}
|
||
|
|
||
|
// print x30; don't bother with x31 (SP), as it's used by the stack that's
|
||
|
// storing this stuff; we really care about the saved SP anyways
|
||
|
printk("x30:\t0x%p\n", regs->x[30]);
|
||
|
|
||
|
// Special registers.
|
||
|
printk("pc:\t0x%p\tcpsr:\t0x%p\n", regs->pc, regs->cpsr);
|
||
|
printk("sp_el1:\t0x%p\tsp_el0:\t0x%p\n", regs->sp_el1, regs->sp_el0);
|
||
|
printk("elr_el1:0x%p\tspsr_el1:0x%p\n", regs->elr_el1, regs->spsr_el1);
|
||
|
|
||
|
// Note that we don't print ESR_EL2, as this isn't really part of the saved state.
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Placeholder function that triggers whenever a vector happens we're not
|
||
|
* expecting. Currently prints out some debug information.
|
||
|
*/
|
||
|
void unhandled_vector(struct guest_state *regs)
|
||
|
{
|
||
|
printk("\nAn unexpected vector happened!\n");
|
||
|
print_registers(regs);
|
||
|
printk("\n\n");
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Handles an HVC call.
|
||
|
*/
|
||
|
static void handle_hvc(struct guest_state *regs, int call_number)
|
||
|
{
|
||
|
|
||
|
switch(call_number) {
|
||
|
|
||
|
|
||
|
default:
|
||
|
printk("Got a HVC call from 64-bit code.\n");
|
||
|
printk("Calling instruction was: hvc %d\n\n", call_number);
|
||
|
printk("Calling context (you can use these regs as hypercall args!):\n");
|
||
|
print_registers(regs);
|
||
|
printk("\n\n");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Placeholder function that triggers whenever a user event triggers a
|
||
|
* synchronous interrupt. Currently, we really only care about 'hvc',
|
||
|
* so that's all we're going to handle here.
|
||
|
*/
|
||
|
|
||
|
void handle_hypercall(struct guest_state *regs)
|
||
|
{
|
||
|
// This is demonstration code.
|
||
|
// In the future, you'd stick your hypercall table here.
|
||
|
|
||
|
switch (regs->esr_el2.ec) {
|
||
|
|
||
|
case HSR_EC_HVC64: {
|
||
|
// Read the hypercall number.
|
||
|
int hvc_nr = regs->esr_el2.iss & 0xFFFF;
|
||
|
|
||
|
// ... and handle the hypercall.
|
||
|
handle_hvc(regs, hvc_nr);
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
printk("Unexpected hypercall! ESR=%p\n", regs->esr_el2.bits);
|
||
|
print_registers(regs);
|
||
|
printk("\n\n");
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|