mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-14 17:16:36 +00:00
134 lines
3.5 KiB
C
134 lines
3.5 KiB
C
/**
|
|
* Thermosphère hypervisor -- primary setup code
|
|
* Copyright (c) 2018 Kate J. Temkin <k@ktemkin.com>
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
|
|
#include "regs.h"
|
|
#include "lib/printk.h"
|
|
|
|
/**
|
|
* Switches to EL1, and then calls main_el1.
|
|
* Implemented in assembly in entry.S.
|
|
*/
|
|
void switch_to_el1();
|
|
|
|
/**
|
|
* C entry point for execution at EL1.
|
|
*/
|
|
void main_el1(void * fdt);
|
|
|
|
/**
|
|
* Reference to the EL2 vector table.
|
|
* Note that the type here isn't reprsentative-- we just need the address of the label.
|
|
*/
|
|
extern uint64_t el2_vector_table;
|
|
|
|
/**
|
|
* Clear out the system's bss.
|
|
*/
|
|
void _clear_bss(void)
|
|
{
|
|
// These symbols don't actually have a meaningful type-- instead,
|
|
// we care about the locations at which the linker /placed/ these
|
|
// symbols, which happen to be at the start and end of the BSS.
|
|
// We use chars here to make the math easy. :)
|
|
extern char lds_bss_start, lds_bss_end;
|
|
|
|
memset(&lds_bss_start, 0, &lds_bss_end - &lds_bss_start);
|
|
}
|
|
|
|
|
|
/**
|
|
* Triggered on an unrecoverable condition; prints an error message
|
|
* and terminates execution.
|
|
*/
|
|
void panic(const char * message)
|
|
{
|
|
printk("\n\n");
|
|
printk("-----------------------------------------------------------------\n");
|
|
printk("PANIC: %s\n", message);
|
|
printk("-----------------------------------------------------------------\n");
|
|
|
|
// TODO: This should probably induce a reboot,
|
|
// rather than sticking here.
|
|
while(1);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Launch an executable kernel image. Should be the last thing called by
|
|
* Discharge, as it does not return.
|
|
*
|
|
* @param kernel The kernel to be executed.
|
|
* @param fdt The device tree to be passed to the given kernel.
|
|
*/
|
|
void launch_kernel(const void *kernel)
|
|
{
|
|
// Construct a function pointer to our kernel, which will allow us to
|
|
// jump there immediately. Note that we don't care what this leaves on
|
|
// the stack, as either our entire stack will be ignored, or it'll
|
|
// be torn down by the target kernel anyways.
|
|
void (*target_kernel)(void) = kernel;
|
|
|
|
printk("Launching Horizon kernel...\n");
|
|
target_kernel();
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Core section of the stub-- sets up the hypervisor from up in EL2.
|
|
*/
|
|
int main(int argc, void **argv)
|
|
{
|
|
// Read the currrent execution level...
|
|
uint32_t el = get_current_el();
|
|
|
|
/* Say hello. */
|
|
printk("Welcome to Atmosph\xe8re Thermosph\xe8" "re!\n");
|
|
printk("Running at EL%d.\n", el);
|
|
|
|
// ... and ensure we're in EL2.
|
|
if (el != 2) {
|
|
panic("Thermosph\xe8" "re must be launched from EL2!");
|
|
}
|
|
|
|
// Set up the vector table for EL2, so that the HVC instruction can be used
|
|
// from EL1. This allows us to return to EL2 after starting the EL1 guest.
|
|
set_vbar_el2(&el2_vector_table);
|
|
|
|
// TODO:
|
|
// Insert any setup you want done in EL2, here. For now, EL2 is set up
|
|
// to do almost nothing-- it doesn't take control of any hardware,
|
|
// and it hasn't set up any trap-to-hypervisor features.
|
|
printk("\nSwitching to EL1...\n");
|
|
switch_to_el1();
|
|
}
|
|
|
|
|
|
/**
|
|
* Secondary section of the stub, executed once we've surrendered
|
|
* hypervisor privileges.
|
|
*/
|
|
void main_el1(void * fdt)
|
|
{
|
|
int rc;
|
|
(void)rc;
|
|
|
|
// Read the currrent execution level...
|
|
uint32_t el = get_current_el();
|
|
|
|
// Validate that we're in EL1.
|
|
printk("Now executing from EL%d!\n", el);
|
|
if(el != 1) {
|
|
panic("Executing with more privilege than we expect!");
|
|
}
|
|
|
|
// If we've made it here, we failed to boot, and we can't recover.
|
|
panic("We should launch Horizon, here!");
|
|
}
|