/** * Thermosphère hypervisor -- primary setup code * Copyright (c) 2018 Kate J. Temkin */ #include #include #include #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!"); }