mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 12:21:18 +00:00
thermosphere: add in basic hypervisor skeleton
This commit is contained in:
parent
60c0df032d
commit
d104ff61ca
15 changed files with 2989 additions and 0 deletions
69
thermosphere/Makefile
Normal file
69
thermosphere/Makefile
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
|
||||||
|
|
||||||
|
ifeq ($(strip $(DEVKITPRO)),)
|
||||||
|
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>devkitPro")
|
||||||
|
endif
|
||||||
|
|
||||||
|
include $(DEVKITPRO)/devkitA64/base_tools
|
||||||
|
|
||||||
|
name := thermosphere
|
||||||
|
|
||||||
|
dir_source := src
|
||||||
|
dir_build := build
|
||||||
|
dir_out := out
|
||||||
|
|
||||||
|
ARCH := -march=armv8-a -mtune=cortex-a57
|
||||||
|
|
||||||
|
ASFLAGS := -g $(ARCH)
|
||||||
|
|
||||||
|
CFLAGS = \
|
||||||
|
$(ARCH) \
|
||||||
|
-g \
|
||||||
|
-O2 \
|
||||||
|
-ffunction-sections \
|
||||||
|
-fdata-sections \
|
||||||
|
-mgeneral-regs-only \
|
||||||
|
-fomit-frame-pointer \
|
||||||
|
-std=gnu11 \
|
||||||
|
-Werror \
|
||||||
|
-Wall \
|
||||||
|
-Wno-main
|
||||||
|
|
||||||
|
LDFLAGS = -specs=linker.specs -g $(ARCH)
|
||||||
|
|
||||||
|
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||||
|
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||||
|
$(patsubst $(dir_source)/%.S, $(dir_build)/%.o, \
|
||||||
|
$(call rwildcard, $(dir_source), *.s *.c *.S))))
|
||||||
|
|
||||||
|
|
||||||
|
define bin2o
|
||||||
|
bin2s $< | $(AS) -o $(@)
|
||||||
|
endef
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: $(dir_out)/$(name).bin
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
@rm -rf $(dir_build)
|
||||||
|
@rm -rf $(dir_out)
|
||||||
|
|
||||||
|
$(dir_out)/$(name).bin: $(dir_build)/$(name).elf
|
||||||
|
@mkdir -p "$(@D)"
|
||||||
|
$(OBJCOPY) -S -O binary $< $@
|
||||||
|
|
||||||
|
$(dir_build)/$(name).elf: $(objects)
|
||||||
|
$(LINK.o) $(OUTPUT_OPTION) $^
|
||||||
|
|
||||||
|
$(dir_build)/%.bin.o: $(dir_build)/%.bin
|
||||||
|
@$(bin2o)
|
||||||
|
|
||||||
|
$(dir_build)/%.o: $(dir_source)/%.c
|
||||||
|
@mkdir -p "$(@D)"
|
||||||
|
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
|
$(dir_build)/%.o: $(dir_source)/%.s
|
||||||
|
@mkdir -p "$(@D)"
|
||||||
|
$(COMPILE.c) -x assembler-with-cpp $(OUTPUT_OPTION) $<
|
||||||
|
|
6
thermosphere/README.md
Normal file
6
thermosphere/README.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
Thermoshère
|
||||||
|
=====
|
||||||
|
|
||||||
|
![License](https://img.shields.io/badge/License-GPLv2-blue.svg)
|
||||||
|
|
||||||
|
Thermoshère is a hypervisor for the Nintendo Switch.
|
55
thermosphere/linker.ld
Normal file
55
thermosphere/linker.ld
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
OUTPUT_FORMAT("elf64-littleaarch64")
|
||||||
|
OUTPUT_ARCH(aarch64)
|
||||||
|
ENTRY(_start)
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 0x800D0000;
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
.text : {
|
||||||
|
PROVIDE(lds_thermo_start = .);
|
||||||
|
build/start.o (.text*)
|
||||||
|
*(.text*)
|
||||||
|
}
|
||||||
|
|
||||||
|
. = ALIGN(8);
|
||||||
|
.rodata : {
|
||||||
|
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*)))
|
||||||
|
}
|
||||||
|
|
||||||
|
. = ALIGN(8);
|
||||||
|
.data : {
|
||||||
|
*(.data*)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Uninitialised data */
|
||||||
|
. = ALIGN(8);
|
||||||
|
PROVIDE(lds_bss_start = .);
|
||||||
|
.bss (NOLOAD) : {
|
||||||
|
*(.bss*) . = ALIGN(8);
|
||||||
|
}
|
||||||
|
PROVIDE(lds_bss_end = .);
|
||||||
|
|
||||||
|
/* EL2 stack */
|
||||||
|
. = ALIGN(16);
|
||||||
|
. += 0x10000; /* 64 KiB stack */
|
||||||
|
el2_stack_end = .;
|
||||||
|
|
||||||
|
/* Page align the end of binary */
|
||||||
|
. = ALIGN(512);
|
||||||
|
PROVIDE(lds_el2_thermo_end = .);
|
||||||
|
|
||||||
|
/* EL1 stack */
|
||||||
|
. = ALIGN(16);
|
||||||
|
. += 0x10000; /* 64 KiB stack */
|
||||||
|
el1_stack_end = .;
|
||||||
|
|
||||||
|
lds_thermo_end = .;
|
||||||
|
|
||||||
|
/DISCARD/ : { *(.dynstr*) }
|
||||||
|
/DISCARD/ : { *(.dynamic*) }
|
||||||
|
/DISCARD/ : { *(.plt*) }
|
||||||
|
/DISCARD/ : { *(.interp*) }
|
||||||
|
/DISCARD/ : { *(.gnu*) }
|
||||||
|
}
|
7
thermosphere/linker.specs
Normal file
7
thermosphere/linker.specs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
%rename link old_link
|
||||||
|
|
||||||
|
*link:
|
||||||
|
%(old_link) -T linker.ld --nmagic --gc-sections
|
||||||
|
|
||||||
|
*startfile:
|
||||||
|
crti%O%s crtbegin%O%s
|
99
thermosphere/src/exceptions.c
Normal file
99
thermosphere/src/exceptions.c
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
176
thermosphere/src/exceptions.h
Normal file
176
thermosphere/src/exceptions.h
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
/**
|
||||||
|
* Thermosphère: exception handler
|
||||||
|
* Handles all exceptions, including a return to EL2.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Kate J. Temkin <k@ktemkin.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __EXCEPTION_H__
|
||||||
|
#define __EXCEPTION_H__
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Borrowed fom Xen (not copyrightable as these are facts).
|
||||||
|
* Description of the EL2 exception syndrome register.
|
||||||
|
*/
|
||||||
|
#define HSR_EC_UNKNOWN 0x00
|
||||||
|
#define HSR_EC_WFI_WFE 0x01
|
||||||
|
#define HSR_EC_CP15_32 0x03
|
||||||
|
#define HSR_EC_CP15_64 0x04
|
||||||
|
#define HSR_EC_CP14_32 0x05 /* Trapped MCR or MRC access to CP14 */
|
||||||
|
#define HSR_EC_CP14_DBG 0x06 /* Trapped LDC/STC access to CP14 (only for debug registers) */
|
||||||
|
#define HSR_EC_CP 0x07 /* HCPTR-trapped access to CP0-CP13 */
|
||||||
|
#define HSR_EC_CP10 0x08
|
||||||
|
#define HSR_EC_JAZELLE 0x09
|
||||||
|
#define HSR_EC_BXJ 0x0a
|
||||||
|
#define HSR_EC_CP14_64 0x0c
|
||||||
|
#define HSR_EC_SVC32 0x11
|
||||||
|
#define HSR_EC_HVC32 0x12
|
||||||
|
#define HSR_EC_SMC32 0x13
|
||||||
|
#define HSR_EC_HVC64 0x16
|
||||||
|
#define HSR_EC_SMC64 0x17
|
||||||
|
#define HSR_EC_SYSREG 0x18
|
||||||
|
#define HSR_EC_INSTR_ABORT_LOWER_EL 0x20
|
||||||
|
#define HSR_EC_INSTR_ABORT_CURR_EL 0x21
|
||||||
|
#define HSR_EC_DATA_ABORT_LOWER_EL 0x24
|
||||||
|
#define HSR_EC_DATA_ABORT_CURR_EL 0x25
|
||||||
|
#define HSR_EC_BRK 0x3c
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Borrowed fom Xen (not copyrightable as these are facts).
|
||||||
|
* Description of the EL2 exception syndrome register.
|
||||||
|
*/
|
||||||
|
union esr {
|
||||||
|
uint32_t bits;
|
||||||
|
struct {
|
||||||
|
unsigned long iss:25; /* Instruction Specific Syndrome */
|
||||||
|
unsigned long len:1; /* Instruction length */
|
||||||
|
unsigned long ec:6; /* Exception Class */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Common to all conditional exception classes (0x0N, except 0x00). */
|
||||||
|
struct hsr_cond {
|
||||||
|
unsigned long iss:20; /* Instruction Specific Syndrome */
|
||||||
|
unsigned long cc:4; /* Condition Code */
|
||||||
|
unsigned long ccvalid:1;/* CC Valid */
|
||||||
|
unsigned long len:1; /* Instruction length */
|
||||||
|
unsigned long ec:6; /* Exception Class */
|
||||||
|
} cond;
|
||||||
|
|
||||||
|
struct hsr_wfi_wfe {
|
||||||
|
unsigned long ti:1; /* Trapped instruction */
|
||||||
|
unsigned long sbzp:19;
|
||||||
|
unsigned long cc:4; /* Condition Code */
|
||||||
|
unsigned long ccvalid:1;/* CC Valid */
|
||||||
|
unsigned long len:1; /* Instruction length */
|
||||||
|
unsigned long ec:6; /* Exception Class */
|
||||||
|
} wfi_wfe;
|
||||||
|
|
||||||
|
/* reg, reg0, reg1 are 4 bits on AArch32, the fifth bit is sbzp. */
|
||||||
|
struct hsr_cp32 {
|
||||||
|
unsigned long read:1; /* Direction */
|
||||||
|
unsigned long crm:4; /* CRm */
|
||||||
|
unsigned long reg:5; /* Rt */
|
||||||
|
unsigned long crn:4; /* CRn */
|
||||||
|
unsigned long op1:3; /* Op1 */
|
||||||
|
unsigned long op2:3; /* Op2 */
|
||||||
|
unsigned long cc:4; /* Condition Code */
|
||||||
|
unsigned long ccvalid:1;/* CC Valid */
|
||||||
|
unsigned long len:1; /* Instruction length */
|
||||||
|
unsigned long ec:6; /* Exception Class */
|
||||||
|
} cp32; /* HSR_EC_CP15_32, CP14_32, CP10 */
|
||||||
|
|
||||||
|
struct hsr_cp64 {
|
||||||
|
unsigned long read:1; /* Direction */
|
||||||
|
unsigned long crm:4; /* CRm */
|
||||||
|
unsigned long reg1:5; /* Rt1 */
|
||||||
|
unsigned long reg2:5; /* Rt2 */
|
||||||
|
unsigned long sbzp2:1;
|
||||||
|
unsigned long op1:4; /* Op1 */
|
||||||
|
unsigned long cc:4; /* Condition Code */
|
||||||
|
unsigned long ccvalid:1;/* CC Valid */
|
||||||
|
unsigned long len:1; /* Instruction length */
|
||||||
|
unsigned long ec:6; /* Exception Class */
|
||||||
|
} cp64; /* HSR_EC_CP15_64, HSR_EC_CP14_64 */
|
||||||
|
|
||||||
|
struct hsr_cp {
|
||||||
|
unsigned long coproc:4; /* Number of coproc accessed */
|
||||||
|
unsigned long sbz0p:1;
|
||||||
|
unsigned long tas:1; /* Trapped Advanced SIMD */
|
||||||
|
unsigned long res0:14;
|
||||||
|
unsigned long cc:4; /* Condition Code */
|
||||||
|
unsigned long ccvalid:1;/* CC Valid */
|
||||||
|
unsigned long len:1; /* Instruction length */
|
||||||
|
unsigned long ec:6; /* Exception Class */
|
||||||
|
} cp; /* HSR_EC_CP */
|
||||||
|
|
||||||
|
struct hsr_sysreg {
|
||||||
|
unsigned long read:1; /* Direction */
|
||||||
|
unsigned long crm:4; /* CRm */
|
||||||
|
unsigned long reg:5; /* Rt */
|
||||||
|
unsigned long crn:4; /* CRn */
|
||||||
|
unsigned long op1:3; /* Op1 */
|
||||||
|
unsigned long op2:3; /* Op2 */
|
||||||
|
unsigned long op0:2; /* Op0 */
|
||||||
|
unsigned long res0:3;
|
||||||
|
unsigned long len:1; /* Instruction length */
|
||||||
|
unsigned long ec:6;
|
||||||
|
} sysreg; /* HSR_EC_SYSREG */
|
||||||
|
|
||||||
|
struct hsr_iabt {
|
||||||
|
unsigned long ifsc:6; /* Instruction fault status code */
|
||||||
|
unsigned long res0:1;
|
||||||
|
unsigned long s1ptw:1; /* Stage 2 fault during stage 1 translation */
|
||||||
|
unsigned long res1:1;
|
||||||
|
unsigned long eat:1; /* External abort type */
|
||||||
|
unsigned long res2:15;
|
||||||
|
unsigned long len:1; /* Instruction length */
|
||||||
|
unsigned long ec:6; /* Exception Class */
|
||||||
|
} iabt; /* HSR_EC_INSTR_ABORT_* */
|
||||||
|
|
||||||
|
struct hsr_dabt {
|
||||||
|
unsigned long dfsc:6; /* Data Fault Status Code */
|
||||||
|
unsigned long write:1; /* Write / not Read */
|
||||||
|
unsigned long s1ptw:1; /* Stage 2 fault during stage 1 translation */
|
||||||
|
unsigned long cache:1; /* Cache Maintenance */
|
||||||
|
unsigned long eat:1; /* External Abort Type */
|
||||||
|
unsigned long sbzp0:4;
|
||||||
|
unsigned long ar:1; /* Acquire Release */
|
||||||
|
unsigned long sf:1; /* Sixty Four bit register */
|
||||||
|
unsigned long reg:5; /* Register */
|
||||||
|
unsigned long sign:1; /* Sign extend */
|
||||||
|
unsigned long size:2; /* Access Size */
|
||||||
|
unsigned long valid:1; /* Syndrome Valid */
|
||||||
|
unsigned long len:1; /* Instruction length */
|
||||||
|
unsigned long ec:6; /* Exception Class */
|
||||||
|
} dabt; /* HSR_EC_DATA_ABORT_* */
|
||||||
|
|
||||||
|
struct hsr_brk {
|
||||||
|
unsigned long comment:16; /* Comment */
|
||||||
|
unsigned long res0:9;
|
||||||
|
unsigned long len:1; /* Instruction length */
|
||||||
|
unsigned long ec:6; /* Exception Class */
|
||||||
|
} brk;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure that stores the saved register values on a hypercall.
|
||||||
|
*/
|
||||||
|
struct guest_state {
|
||||||
|
uint64_t pc;
|
||||||
|
uint64_t cpsr;
|
||||||
|
|
||||||
|
uint64_t elr_el1;
|
||||||
|
uint64_t spsr_el1;
|
||||||
|
|
||||||
|
uint64_t sp_el0;
|
||||||
|
uint64_t sp_el1;
|
||||||
|
|
||||||
|
union esr esr_el2;
|
||||||
|
uint64_t x[31];
|
||||||
|
}
|
||||||
|
__attribute__((packed));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
269
thermosphere/src/lib/ini.c
Normal file
269
thermosphere/src/lib/ini.c
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
/* inih -- simple .INI file parser
|
||||||
|
|
||||||
|
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||||
|
home page for more info:
|
||||||
|
|
||||||
|
https://github.com/benhoyt/inih
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "ini.h"
|
||||||
|
|
||||||
|
#if !INI_USE_STACK
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAX_SECTION 50
|
||||||
|
#define MAX_NAME 50
|
||||||
|
|
||||||
|
/* Used by ini_parse_string() to keep track of string parsing state. */
|
||||||
|
typedef struct {
|
||||||
|
const char* ptr;
|
||||||
|
size_t num_left;
|
||||||
|
} ini_parse_string_ctx;
|
||||||
|
|
||||||
|
/* Strip whitespace chars off end of given string, in place. Return s. */
|
||||||
|
static char* rstrip(char* s)
|
||||||
|
{
|
||||||
|
char* p = s + strlen(s);
|
||||||
|
while (p > s && isspace((unsigned char)(*--p)))
|
||||||
|
*p = '\0';
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return pointer to first non-whitespace char in given string. */
|
||||||
|
static char* lskip(const char* s)
|
||||||
|
{
|
||||||
|
while (*s && isspace((unsigned char)(*s)))
|
||||||
|
s++;
|
||||||
|
return (char*)s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return pointer to first char (of chars) or inline comment in given string,
|
||||||
|
or pointer to null at end of string if neither found. Inline comment must
|
||||||
|
be prefixed by a whitespace character to register as a comment. */
|
||||||
|
static char* find_chars_or_comment(const char* s, const char* chars)
|
||||||
|
{
|
||||||
|
#if INI_ALLOW_INLINE_COMMENTS
|
||||||
|
int was_space = 0;
|
||||||
|
while (*s && (!chars || !strchr(chars, *s)) &&
|
||||||
|
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
|
||||||
|
was_space = isspace((unsigned char)(*s));
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
while (*s && (!chars || !strchr(chars, *s))) {
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return (char*)s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
|
||||||
|
static char* strncpy0(char* dest, const char* src, size_t size)
|
||||||
|
{
|
||||||
|
strncpy(dest, src, size);
|
||||||
|
dest[size - 1] = '\0';
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See documentation in header file. */
|
||||||
|
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
||||||
|
void* user)
|
||||||
|
{
|
||||||
|
/* Uses a fair bit of stack (use heap instead if you need to) */
|
||||||
|
#if INI_USE_STACK
|
||||||
|
char line[INI_MAX_LINE];
|
||||||
|
int max_line = INI_MAX_LINE;
|
||||||
|
#else
|
||||||
|
char* line;
|
||||||
|
int max_line = INI_INITIAL_ALLOC;
|
||||||
|
#endif
|
||||||
|
#if INI_ALLOW_REALLOC
|
||||||
|
char* new_line;
|
||||||
|
int offset;
|
||||||
|
#endif
|
||||||
|
char section[MAX_SECTION] = "";
|
||||||
|
char prev_name[MAX_NAME] = "";
|
||||||
|
|
||||||
|
char* start;
|
||||||
|
char* end;
|
||||||
|
char* name;
|
||||||
|
char* value;
|
||||||
|
int lineno = 0;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
#if !INI_USE_STACK
|
||||||
|
line = (char*)malloc(INI_INITIAL_ALLOC);
|
||||||
|
if (!line) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if INI_HANDLER_LINENO
|
||||||
|
#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
|
||||||
|
#else
|
||||||
|
#define HANDLER(u, s, n, v) handler(u, s, n, v)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Scan through stream line by line */
|
||||||
|
while (reader(line, max_line, stream) != NULL) {
|
||||||
|
#if INI_ALLOW_REALLOC
|
||||||
|
offset = strlen(line);
|
||||||
|
while (offset == max_line - 1 && line[offset - 1] != '\n') {
|
||||||
|
max_line *= 2;
|
||||||
|
if (max_line > INI_MAX_LINE)
|
||||||
|
max_line = INI_MAX_LINE;
|
||||||
|
new_line = realloc(line, max_line);
|
||||||
|
if (!new_line) {
|
||||||
|
free(line);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
line = new_line;
|
||||||
|
if (reader(line + offset, max_line - offset, stream) == NULL)
|
||||||
|
break;
|
||||||
|
if (max_line >= INI_MAX_LINE)
|
||||||
|
break;
|
||||||
|
offset += strlen(line + offset);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
lineno++;
|
||||||
|
|
||||||
|
start = line;
|
||||||
|
#if INI_ALLOW_BOM
|
||||||
|
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
|
||||||
|
(unsigned char)start[1] == 0xBB &&
|
||||||
|
(unsigned char)start[2] == 0xBF) {
|
||||||
|
start += 3;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
start = lskip(rstrip(start));
|
||||||
|
|
||||||
|
if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
|
||||||
|
/* Start-of-line comment */
|
||||||
|
}
|
||||||
|
#if INI_ALLOW_MULTILINE
|
||||||
|
else if (*prev_name && *start && start > line) {
|
||||||
|
/* Non-blank line with leading whitespace, treat as continuation
|
||||||
|
of previous name's value (as per Python configparser). */
|
||||||
|
if (!HANDLER(user, section, prev_name, start) && !error)
|
||||||
|
error = lineno;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else if (*start == '[') {
|
||||||
|
/* A "[section]" line */
|
||||||
|
end = find_chars_or_comment(start + 1, "]");
|
||||||
|
if (*end == ']') {
|
||||||
|
*end = '\0';
|
||||||
|
strncpy0(section, start + 1, sizeof(section));
|
||||||
|
*prev_name = '\0';
|
||||||
|
}
|
||||||
|
else if (!error) {
|
||||||
|
/* No ']' found on section line */
|
||||||
|
error = lineno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (*start) {
|
||||||
|
/* Not a comment, must be a name[=:]value pair */
|
||||||
|
end = find_chars_or_comment(start, "=:");
|
||||||
|
if (*end == '=' || *end == ':') {
|
||||||
|
*end = '\0';
|
||||||
|
name = rstrip(start);
|
||||||
|
value = end + 1;
|
||||||
|
#if INI_ALLOW_INLINE_COMMENTS
|
||||||
|
end = find_chars_or_comment(value, NULL);
|
||||||
|
if (*end)
|
||||||
|
*end = '\0';
|
||||||
|
#endif
|
||||||
|
value = lskip(value);
|
||||||
|
rstrip(value);
|
||||||
|
|
||||||
|
/* Valid name[=:]value pair found, call handler */
|
||||||
|
strncpy0(prev_name, name, sizeof(prev_name));
|
||||||
|
if (!HANDLER(user, section, name, value) && !error)
|
||||||
|
error = lineno;
|
||||||
|
}
|
||||||
|
else if (!error) {
|
||||||
|
/* No '=' or ':' found on name[=:]value line */
|
||||||
|
error = lineno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if INI_STOP_ON_FIRST_ERROR
|
||||||
|
if (error)
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !INI_USE_STACK
|
||||||
|
free(line);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See documentation in header file. */
|
||||||
|
int ini_parse_file(FILE* file, ini_handler handler, void* user)
|
||||||
|
{
|
||||||
|
return ini_parse_stream((ini_reader)fgets, file, handler, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See documentation in header file. */
|
||||||
|
int ini_parse(const char* filename, ini_handler handler, void* user)
|
||||||
|
{
|
||||||
|
FILE* file;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
file = fopen(filename, "r");
|
||||||
|
if (!file)
|
||||||
|
return -1;
|
||||||
|
error = ini_parse_file(file, handler, user);
|
||||||
|
fclose(file);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* An ini_reader function to read the next line from a string buffer. This
|
||||||
|
is the fgets() equivalent used by ini_parse_string(). */
|
||||||
|
static char* ini_reader_string(char* str, int num, void* stream) {
|
||||||
|
ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream;
|
||||||
|
const char* ctx_ptr = ctx->ptr;
|
||||||
|
size_t ctx_num_left = ctx->num_left;
|
||||||
|
char* strp = str;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
if (ctx_num_left == 0 || num < 2)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
while (num > 1 && ctx_num_left != 0) {
|
||||||
|
c = *ctx_ptr++;
|
||||||
|
ctx_num_left--;
|
||||||
|
*strp++ = c;
|
||||||
|
if (c == '\n')
|
||||||
|
break;
|
||||||
|
num--;
|
||||||
|
}
|
||||||
|
|
||||||
|
*strp = '\0';
|
||||||
|
ctx->ptr = ctx_ptr;
|
||||||
|
ctx->num_left = ctx_num_left;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See documentation in header file. */
|
||||||
|
int ini_parse_string(const char* string, ini_handler handler, void* user) {
|
||||||
|
ini_parse_string_ctx ctx;
|
||||||
|
|
||||||
|
ctx.ptr = string;
|
||||||
|
ctx.num_left = strlen(string);
|
||||||
|
return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
|
||||||
|
user);
|
||||||
|
}
|
130
thermosphere/src/lib/ini.h
Normal file
130
thermosphere/src/lib/ini.h
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
/* inih -- simple .INI file parser
|
||||||
|
|
||||||
|
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||||
|
home page for more info:
|
||||||
|
|
||||||
|
https://github.com/benhoyt/inih
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __INI_H__
|
||||||
|
#define __INI_H__
|
||||||
|
|
||||||
|
/* Make this header file easier to include in C++ code */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/* Nonzero if ini_handler callback should accept lineno parameter. */
|
||||||
|
#ifndef INI_HANDLER_LINENO
|
||||||
|
#define INI_HANDLER_LINENO 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Typedef for prototype of handler function. */
|
||||||
|
#if INI_HANDLER_LINENO
|
||||||
|
typedef int (*ini_handler)(void* user, const char* section,
|
||||||
|
const char* name, const char* value,
|
||||||
|
int lineno);
|
||||||
|
#else
|
||||||
|
typedef int (*ini_handler)(void* user, const char* section,
|
||||||
|
const char* name, const char* value);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Typedef for prototype of fgets-style reader function. */
|
||||||
|
typedef char* (*ini_reader)(char* str, int num, void* stream);
|
||||||
|
|
||||||
|
/* Parse given INI-style file. May have [section]s, name=value pairs
|
||||||
|
(whitespace stripped), and comments starting with ';' (semicolon). Section
|
||||||
|
is "" if name=value pair parsed before any section heading. name:value
|
||||||
|
pairs are also supported as a concession to Python's configparser.
|
||||||
|
|
||||||
|
For each name=value pair parsed, call handler function with given user
|
||||||
|
pointer as well as section, name, and value (data only valid for duration
|
||||||
|
of handler call). Handler should return nonzero on success, zero on error.
|
||||||
|
|
||||||
|
Returns 0 on success, line number of first error on parse error (doesn't
|
||||||
|
stop on first error), -1 on file open error, or -2 on memory allocation
|
||||||
|
error (only when INI_USE_STACK is zero).
|
||||||
|
*/
|
||||||
|
int ini_parse(const char* filename, ini_handler handler, void* user);
|
||||||
|
|
||||||
|
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
|
||||||
|
close the file when it's finished -- the caller must do that. */
|
||||||
|
int ini_parse_file(FILE* file, ini_handler handler, void* user);
|
||||||
|
|
||||||
|
/* Same as ini_parse(), but takes an ini_reader function pointer instead of
|
||||||
|
filename. Used for implementing custom or string-based I/O (see also
|
||||||
|
ini_parse_string). */
|
||||||
|
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
||||||
|
void* user);
|
||||||
|
|
||||||
|
/* Same as ini_parse(), but takes a zero-terminated string with the INI data
|
||||||
|
instead of a file. Useful for parsing INI data from a network socket or
|
||||||
|
already in memory. */
|
||||||
|
int ini_parse_string(const char* string, ini_handler handler, void* user);
|
||||||
|
|
||||||
|
/* Nonzero to allow multi-line value parsing, in the style of Python's
|
||||||
|
configparser. If allowed, ini_parse() will call the handler with the same
|
||||||
|
name for each subsequent line parsed. */
|
||||||
|
#ifndef INI_ALLOW_MULTILINE
|
||||||
|
#define INI_ALLOW_MULTILINE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
|
||||||
|
the file. See http://code.google.com/p/inih/issues/detail?id=21 */
|
||||||
|
#ifndef INI_ALLOW_BOM
|
||||||
|
#define INI_ALLOW_BOM 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Chars that begin a start-of-line comment. Per Python configparser, allow
|
||||||
|
both ; and # comments at the start of a line by default. */
|
||||||
|
#ifndef INI_START_COMMENT_PREFIXES
|
||||||
|
#define INI_START_COMMENT_PREFIXES ";#"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Nonzero to allow inline comments (with valid inline comment characters
|
||||||
|
specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
|
||||||
|
Python 3.2+ configparser behaviour. */
|
||||||
|
#ifndef INI_ALLOW_INLINE_COMMENTS
|
||||||
|
#define INI_ALLOW_INLINE_COMMENTS 1
|
||||||
|
#endif
|
||||||
|
#ifndef INI_INLINE_COMMENT_PREFIXES
|
||||||
|
#define INI_INLINE_COMMENT_PREFIXES ";"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */
|
||||||
|
#ifndef INI_USE_STACK
|
||||||
|
#define INI_USE_STACK 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Maximum line length for any line in INI file (stack or heap). Note that
|
||||||
|
this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */
|
||||||
|
#ifndef INI_MAX_LINE
|
||||||
|
#define INI_MAX_LINE 200
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Nonzero to allow heap line buffer to grow via realloc(), zero for a
|
||||||
|
fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is
|
||||||
|
zero. */
|
||||||
|
#ifndef INI_ALLOW_REALLOC
|
||||||
|
#define INI_ALLOW_REALLOC 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK
|
||||||
|
is zero. */
|
||||||
|
#ifndef INI_INITIAL_ALLOC
|
||||||
|
#define INI_INITIAL_ALLOC 200
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Stop parsing on first error (default is to keep parsing). */
|
||||||
|
#ifndef INI_STOP_ON_FIRST_ERROR
|
||||||
|
#define INI_STOP_ON_FIRST_ERROR 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __INI_H__ */
|
25
thermosphere/src/lib/printk.c
Normal file
25
thermosphere/src/lib/printk.c
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/**
|
||||||
|
* Kernel print functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "printk.h"
|
||||||
|
|
||||||
|
#include "vsprintf.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Temporary stand-in main printk.
|
||||||
|
*
|
||||||
|
* TODO: This should print via UART, console framebuffer, and to a ring for
|
||||||
|
* consumption by Horizon
|
||||||
|
*/
|
||||||
|
void printk(char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list list;
|
||||||
|
char buf[512];
|
||||||
|
va_start(list, fmt);
|
||||||
|
vsnprintf(buf, sizeof(buf), fmt, list);
|
||||||
|
|
||||||
|
/* FIXME: print via UART */
|
||||||
|
|
||||||
|
va_end(list);
|
||||||
|
}
|
2
thermosphere/src/lib/printk.h
Normal file
2
thermosphere/src/lib/printk.h
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
void printk(char *fmt, ...);
|
1676
thermosphere/src/lib/vsprintf.c
Normal file
1676
thermosphere/src/lib/vsprintf.c
Normal file
File diff suppressed because it is too large
Load diff
25
thermosphere/src/lib/vsprintf.h
Normal file
25
thermosphere/src/lib/vsprintf.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Andrei Warkentin <andrey.warkentin@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software ; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifndef VSPRINTF_H
|
||||||
|
#define VSPRINTF_H
|
||||||
|
|
||||||
|
struct va_format {
|
||||||
|
const char *fmt;
|
||||||
|
va_list *va;
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base);
|
||||||
|
|
||||||
|
int vsnprintf(char *buf, size_t size, const char *fmt, va_list args);
|
||||||
|
int sscanf(const char *buf, const char *fmt, ...);
|
||||||
|
|
||||||
|
#endif /* VSPRINTF_H */
|
134
thermosphere/src/main.c
Normal file
134
thermosphere/src/main.c
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
/**
|
||||||
|
* 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!");
|
||||||
|
}
|
62
thermosphere/src/regs.h
Normal file
62
thermosphere/src/regs.h
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/**
|
||||||
|
* Thermosphère: register access primitives
|
||||||
|
* Copyright (c) Kate J. Temkin <k@ktemkin.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __REGS_H__
|
||||||
|
#define __REGS_H__
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access to system registers.
|
||||||
|
*/
|
||||||
|
#define WRITE_SYSREG(sysreg, val, type) \
|
||||||
|
asm volatile ("msr "#sysreg", %0\n" : : "r"((type)(val)))
|
||||||
|
#define READ_SYSREG(sysreg, val, type) \
|
||||||
|
asm volatile ("mrs %0, "#sysreg"\n" : "=r"((type)(val)))
|
||||||
|
|
||||||
|
#define READ_SYSREG_32(sysreg, val) READ_SYSREG(sysreg, val, uint32_t)
|
||||||
|
#define WRITE_SYSREG_32(sysreg, val) WRITE_SYSREG(sysreg, val, uint32_t)
|
||||||
|
|
||||||
|
#define READ_SYSREG_64(sysreg, val) READ_SYSREG(sysreg, val, uint64_t)
|
||||||
|
#define WRITE_SYSREG_64(sysreg, val) WRITE_SYSREG(sysreg, val, uint64_t)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the system's current Execution Level (EL).
|
||||||
|
*/
|
||||||
|
inline static uint32_t get_current_el(void) {
|
||||||
|
uint32_t val;
|
||||||
|
|
||||||
|
// Read the CurrentEl register, and extract the bits that tell us our EL.
|
||||||
|
READ_SYSREG_32(CurrentEl, val);
|
||||||
|
return val >> 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the base address of the EL2 exception table.
|
||||||
|
*/
|
||||||
|
inline static void set_vbar_el2(void * address) {
|
||||||
|
WRITE_SYSREG_64(vbar_el2, (uint64_t)address);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the address to 'return to' when leaving EL2.
|
||||||
|
*/
|
||||||
|
inline static void set_elr_el2(void * address) {
|
||||||
|
WRITE_SYSREG_64(elr_el2, (uint64_t)address);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the MMU status bit from the SCTLR register.
|
||||||
|
*/
|
||||||
|
inline static uint32_t get_el2_mmu_status(void) {
|
||||||
|
uint32_t val;
|
||||||
|
|
||||||
|
// Read the CurrentEl register, and extract the bits that tell us our EL.
|
||||||
|
READ_SYSREG_32(sctlr_el2, val);
|
||||||
|
return val & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
254
thermosphere/src/start.s
Normal file
254
thermosphere/src/start.s
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
/**
|
||||||
|
* Thermosphère hypervisor entry points
|
||||||
|
* This file contains all entry points (e.g. when launching or switching ELs).
|
||||||
|
*
|
||||||
|
* Copyright (c) Kate J. Temkin <k@ktemkin.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
.section ".text"
|
||||||
|
.global _start
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple macro to help with generating vector table entries.
|
||||||
|
*/
|
||||||
|
.macro ventry label
|
||||||
|
.align 7
|
||||||
|
b \label
|
||||||
|
.endm
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start of day code. This is the first code that executes after we're launched
|
||||||
|
* by the bootloader. We use this only to set up a C environment.
|
||||||
|
*/
|
||||||
|
_start:
|
||||||
|
|
||||||
|
// Create a simple stack for the hypervisor, while executing in EL2.
|
||||||
|
ldr x1, =el2_stack_end
|
||||||
|
mov sp, x1
|
||||||
|
|
||||||
|
// Clear out our binary's bss.
|
||||||
|
stp x0, x1, [sp, #-16]!
|
||||||
|
bl _clear_bss
|
||||||
|
ldp x0, x1, [sp], #16
|
||||||
|
|
||||||
|
// Run the main routine. This shouldn't return.
|
||||||
|
b main
|
||||||
|
|
||||||
|
// We shouldn't ever reach here; trap.
|
||||||
|
1: b 1b
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Vector table for interrupts/exceptions that reach EL2.
|
||||||
|
*/
|
||||||
|
.align 11
|
||||||
|
.global el2_vector_table;
|
||||||
|
el2_vector_table:
|
||||||
|
ventry _unhandled_vector // Synchronous EL2t
|
||||||
|
ventry _unhandled_vector // IRQ EL2t
|
||||||
|
ventry _unhandled_vector // FIQ EL2t
|
||||||
|
ventry _unhandled_vector // Error EL2t
|
||||||
|
|
||||||
|
ventry _unhandled_vector // Synchronous EL2h
|
||||||
|
ventry _unhandled_vector // IRQ EL2h
|
||||||
|
ventry _unhandled_vector // FIQ EL2h
|
||||||
|
ventry _unhandled_vector // Error EL2h
|
||||||
|
|
||||||
|
ventry _handle_hypercall // Synchronous 64-bit EL0/EL1
|
||||||
|
ventry _unhandled_vector // IRQ 64-bit EL0/EL1
|
||||||
|
ventry _unhandled_vector // FIQ 64-bit EL0/EL1
|
||||||
|
ventry _unhandled_vector // Error 64-bit EL0/EL1
|
||||||
|
|
||||||
|
ventry _unhandled_vector // Synchronous 32-bit EL0/EL1
|
||||||
|
ventry _unhandled_vector // IRQ 32-bit EL0/EL1
|
||||||
|
ventry _unhandled_vector // FIQ 32-bit EL0/EL1
|
||||||
|
ventry _unhandled_vector // Error 32-bit EL0/EL1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Switch down to EL1 and then execute the second half of our stub.
|
||||||
|
* Implemented in assembly, as this manipulates the stack.
|
||||||
|
*
|
||||||
|
* Obliterates the stack, but leaves the rest of memory intact. This should be
|
||||||
|
* fine, as we should be hiding the EL2 memory from the rest of the system.
|
||||||
|
*
|
||||||
|
* x0: The location of the device tree to be passed into EL0.
|
||||||
|
*/
|
||||||
|
.global switch_to_el1
|
||||||
|
switch_to_el1:
|
||||||
|
|
||||||
|
// Set up a post-EL1-switch return address...
|
||||||
|
ldr x2, =_post_el1_switch
|
||||||
|
msr elr_el2, x2
|
||||||
|
|
||||||
|
// .. and set up the CPSR after we switch to EL1.
|
||||||
|
// We overwrite the saved program status register. Note that setting
|
||||||
|
// this with the EL = EL1 is what actually causes the switch.
|
||||||
|
mov x2, #0x3c5 // EL1_SP1 | D | A | I | F
|
||||||
|
msr spsr_el2, x2
|
||||||
|
|
||||||
|
// Reset the stack pointer to the very end of the stack, so it's
|
||||||
|
// fresh and clean for when we jump back up into EL2.
|
||||||
|
ldr x2, =el2_stack_end
|
||||||
|
mov sp, x2
|
||||||
|
|
||||||
|
// ... and switch down to EL1. (This essentially asks the processor
|
||||||
|
// to switch down to EL1 and then load ELR_EL2 to the PC.)
|
||||||
|
eret
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Entry point after the switch to EL1.
|
||||||
|
*/
|
||||||
|
.global _post_el1_switch
|
||||||
|
_post_el1_switch:
|
||||||
|
|
||||||
|
// Create a simple stack for us to use while at EL1.
|
||||||
|
// We use this temporarily to launch Horizon.
|
||||||
|
ldr x2, =el1_stack_end
|
||||||
|
mov sp, x2
|
||||||
|
|
||||||
|
// Run the main routine. This shouldn't return.
|
||||||
|
b main_el1
|
||||||
|
|
||||||
|
// We shouldn't ever reach here; trap.
|
||||||
|
1: b 1b
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push and pop 'psuedo-op' macros that simplify the ARM syntax to make the below pretty.
|
||||||
|
*/
|
||||||
|
.macro push, xreg1, xreg2
|
||||||
|
stp \xreg1, \xreg2, [sp, #-16]!
|
||||||
|
.endm
|
||||||
|
.macro pop, xreg1, xreg2
|
||||||
|
ldp \xreg1, \xreg2, [sp], #16
|
||||||
|
.endm
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Macro that saves registers onto the stack when entering an exception handler--
|
||||||
|
* effectively saving the guest state. Once this method is complete, *sp will
|
||||||
|
* point to a struct guest_state.
|
||||||
|
*
|
||||||
|
* You can modify this to save whatever you'd like, but:
|
||||||
|
* 1) We can only push in pairs due to armv8 architecture quirks.
|
||||||
|
* 2) Be careful not to trounce registers until after you've saved them.
|
||||||
|
* 3) r31 is your stack pointer, and doesn't need to be saved. You'll want to
|
||||||
|
* save the lesser EL's stack pointers separately.
|
||||||
|
* 4) Make sure any changes you make are reflected both in _restore_registers_
|
||||||
|
* and in struct guest_state, or things will break pretty badly.
|
||||||
|
*/
|
||||||
|
.macro save_registers
|
||||||
|
// General purpose registers x1 - x30
|
||||||
|
push x29, x30
|
||||||
|
push x27, x28
|
||||||
|
push x25, x26
|
||||||
|
push x23, x24
|
||||||
|
push x21, x22
|
||||||
|
push x19, x20
|
||||||
|
push x17, x18
|
||||||
|
push x15, x16
|
||||||
|
push x13, x14
|
||||||
|
push x11, x12
|
||||||
|
push x9, x10
|
||||||
|
push x7, x8
|
||||||
|
push x5, x6
|
||||||
|
push x3, x4
|
||||||
|
push x1, x2
|
||||||
|
|
||||||
|
// x0 and the el2_esr
|
||||||
|
mrs x20, esr_el2
|
||||||
|
push x20, x0
|
||||||
|
|
||||||
|
// the el1_sp and el0_sp
|
||||||
|
mrs x0, sp_el0
|
||||||
|
mrs x1, sp_el1
|
||||||
|
push x0, x1
|
||||||
|
|
||||||
|
// the el1 elr/spsr
|
||||||
|
mrs x0, elr_el1
|
||||||
|
mrs x1, spsr_el1
|
||||||
|
push x0, x1
|
||||||
|
|
||||||
|
// the el2 elr/spsr
|
||||||
|
mrs x0, elr_el2
|
||||||
|
mrs x1, spsr_el2
|
||||||
|
push x0, x1
|
||||||
|
|
||||||
|
.endm
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Macro that restores registers when returning from EL2.
|
||||||
|
* Mirrors save_registers.
|
||||||
|
*/
|
||||||
|
.macro restore_registers
|
||||||
|
// the el2 elr/spsr
|
||||||
|
pop x0, x1
|
||||||
|
msr elr_el2, x0
|
||||||
|
msr spsr_el2, x1
|
||||||
|
|
||||||
|
// the el1 elr/spsr
|
||||||
|
pop x0, x1
|
||||||
|
msr elr_el1, x0
|
||||||
|
msr spsr_el1, x1
|
||||||
|
|
||||||
|
// the el1_sp and el0_sp
|
||||||
|
pop x0, x1
|
||||||
|
msr sp_el0, x0
|
||||||
|
msr sp_el1, x1
|
||||||
|
|
||||||
|
// x0, and the el2_esr
|
||||||
|
// Note that we don't restore el2_esr, as this wouldn't
|
||||||
|
// have any meaning.
|
||||||
|
pop x20, x0
|
||||||
|
|
||||||
|
// General purpose registers x1 - x30
|
||||||
|
pop x1, x2
|
||||||
|
pop x3, x4
|
||||||
|
pop x5, x6
|
||||||
|
pop x7, x8
|
||||||
|
pop x9, x10
|
||||||
|
pop x11, x12
|
||||||
|
pop x13, x14
|
||||||
|
pop x15, x16
|
||||||
|
pop x17, x18
|
||||||
|
pop x19, x20
|
||||||
|
pop x21, x22
|
||||||
|
pop x23, x24
|
||||||
|
pop x25, x26
|
||||||
|
pop x27, x28
|
||||||
|
pop x29, x30
|
||||||
|
.endm
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handler for any vector we're not equipped to handle.
|
||||||
|
*/
|
||||||
|
_unhandled_vector:
|
||||||
|
// TODO: Save interrupt state and turn off interrupts.
|
||||||
|
save_registers
|
||||||
|
|
||||||
|
// Point x0 at our saved registers, and then call our C handler.
|
||||||
|
mov x0, sp
|
||||||
|
bl unhandled_vector
|
||||||
|
|
||||||
|
restore_registers
|
||||||
|
eret
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handler for any synchronous event coming from the guest (any trap-to-EL2).
|
||||||
|
* This _stub_ only uses this to handle hypercalls-- hence the name.
|
||||||
|
*/
|
||||||
|
_handle_hypercall:
|
||||||
|
// TODO: Save interrupt state and turn off interrupts.
|
||||||
|
save_registers
|
||||||
|
|
||||||
|
// Point x0 at our saved registers, and then call our C handler.
|
||||||
|
mov x0, sp
|
||||||
|
bl handle_hypercall
|
||||||
|
|
||||||
|
restore_registers
|
||||||
|
eret
|
Loading…
Reference in a new issue