2018-09-07 16:00:13 +01:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2018 Atmosphère-NX
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms and conditions of the GNU General Public License,
|
|
|
|
* version 2, as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
* more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2018-04-20 11:06:09 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#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!");
|
|
|
|
}
|