blob: 60da6f0ace6ea63ac753877d8347ffb2e0933ce6 [file] [log] [blame]
// Copyright Microsoft and CHERIoT Contributors.
// SPDX-License-Identifier: MIT
#include "constants.h"
#include "defines.h"
#include "../switcher/trusted-stack-assembly.h"
#include <cheri-builtins.h>
.include "assembly-helpers.s"
.section .loader_start, "ax", @progbits
.globl start
.p2align 2
.type start,@function
start:
cjal .Lregs_clear
// The common register clearing function will not zero these registers.
zeroRegisters ra, sp, gp, a0
// At this point all registers are cleared.
#if __has_include(<platform-early_boot.inc>)
# include <platform-early_boot.inc>
#endif
la_abs a3, bootStack
li a1, BOOT_STACK_SIZE
cspecialr ca4, mtdc // Keep the RW memory root in ca4 throughout
li a2, ~CHERI_PERM_STORE_LOCAL
li a5, ~CHERI_PERM_GLOBAL
// Keep G in ca2 and SL in ca5.
candperm ca2, ca4, a2
candperm ca5, ca4, a5
csetaddr csp, ca5, a3
csetboundsexact csp, csp, a1
cincoffset csp, csp, a1 // Move to the end and grow downwards.
// Prepare a trusted stack for the loader.
la_abs a3, bootTStack
li a1, BOOT_TSTACK_SIZE
csetaddr ca3, ca4, a3 // ca4 still has the RW (G+SL!) root
csetboundsexact ca3, ca3, a1
li a1, TSTACKOFFSET_FIRSTFRAME
csh a1, TrustedStack_offset_frameoffset(ca3)
cspecialw mtdc, ca3
// Prepare a bounded pointer to the header.
la_abs a1, __compart_headers
la_abs a3, __compart_headers_end
sub a3, a3, a1
csetaddr ca1, ca2, a1 // ca2 still has the G root.
// FIXME: This should be a set bounds exact, but we currently don't have a
// 'pad to capability alignment' command in the linker script and it needs
// to span two sections.
csetbounds ca1, ca1, a3
// Set up $cra to be the loader's C++ entry point.
// We are safe to clobber $cra here because this is the root function on
// the call stack.
// First set the lower bound on the loader's PCC:
auipcc cra, 0
clw s0, IMAGE_HEADER_LOADER_CODE_START_OFFSET(ca1)
csetaddr cra, cra, s0
// Set the size
clhu s0, IMAGE_HEADER_LOADER_CODE_SIZE_OFFSET(ca1)
csetboundsexact cra, cra, s0
// Set the C++ entry point of loader
la_abs s0, loader_entry_point
csetaddr cra, cra, s0
// Base and size of the GP of loader
// Flute doesn't support unaligned loads, so we have to load the base as
// bytes
clbu s0, IMAGE_HEADER_LOADER_DATA_START_OFFSET+3(ca1)
sll s0, s0, 8
clbu s1, IMAGE_HEADER_LOADER_DATA_START_OFFSET+2(ca1)
add s0, s0, s1
sll s0, s0, 8
clbu s1, IMAGE_HEADER_LOADER_DATA_START_OFFSET+1(ca1)
add s0, s0, s1
sll s0, s0, 8
clbu s1, IMAGE_HEADER_LOADER_DATA_START_OFFSET+0(ca1)
add s0, s0, s1
clhu s1, IMAGE_HEADER_LOADER_DATA_SIZE_OFFSET(ca1)
csetaddr cgp, ca5, s0 // ca5 has the SL root.
csetboundsexact cgp, cgp, s1
srli s1, s1, 1
cincoffset cgp, cgp, s1
// We just want to grab the EXE root. Offset in auipcc matters not.
auipcc ca2, 0
cgetbase t1, ca2
csetaddr ca2, ca2, t1
// mscratchc still has the sealing root; take it and stash the RW root
cmove ca3, ca4
cspecialrw ca3, mscratchc, ca3
// ca4 still has the RW memory root; nothing to change
// The return value is SchedEntryInfo, the space for it is in ca0
// See the comment at the start of `loader_entry_point` in `boot.cc`.
la_abs s1, __thread_count
csetaddr cs1, ca4, s1
clhu s1, 0(cs1)
li t0, -BOOT_THREADINFO_SZ
mul s1, s1, t0
addi s1, s1, -SchedulerEntryInfo_offset_threads
cincoffset csp, csp, s1
neg s1, s1
csetbounds ca0, csp, s1
// Jump to loader_entry_point.
cjalr cra
// Load the two return values (pcc and cgp for the scheduler entry point)
clc cs0, 0(csp)
clc cgp, 8(csp)
cincoffset csp, csp, SchedulerEntryInfo_offset_threads
// Reset the stack pointer to point to the top and clear it
cgetbase a0, csp
csetaddr ca0, csp, a0
cmove ca1, csp
cjal .Lfill_block
// Nothing in the loader stores to the stack after this point
// Zero the entire heap and clear roots.
cspecialr ca0, mscratchc // RW root temporarily held here
la_abs a1, __export_mem_heap
csetaddr ca0, ca0, a1
la_abs a1, __export_mem_heap_end
cjal .Lfill_block
// Clear the remaining roots.
// mtdc is serving its purpose since being set above, and mtcc
// has been set by the loader_entry_point.
zeroOne a0
cspecialw mepcc, ca0
cspecialw mscratchc, ca0
// Move the scheduler's PCC into the register we'll jump to later.
cmove cra, cs0
// c1 is cra (new PCC), c2 is csp, c3 is cgp.
// All other registers will be cleared in the clear-regs block
// Pass the array of threadInfos as first argument.
addi s1, s1, -16
csetbounds ca0, csp, s1
.Lregs_clear:
// a0 is used to pass arguments to the scheduler entry.
zeroAllRegistersExcept ra, sp, gp, a0
cjalr cra
// Done scheduler setup. Now prepare an idle thread.
zeroOne sp
li t1, 0x880
// Enable external and timer interrupts.
csrs mie, t1
// Globally enable interrupts.
csrsi mstatus, 0x8
// Yield to the scheduler to start real tasks.
ecall
// The idle thread sleeps and only waits for interrupts.
.Lidle_loop:
// There is a bug in the Flute hardware revoker that means it stops during
// wfi, but we want it to run here. Flute is simulation only at the
// moment, so we don't care that the nop is power-inefficient.
#if defined(TEMPORAL_SAFETY) && defined(FLUTE) && !defined(SOFTWARE_REVOKER)
nop
#else
wfi
#endif
j .Lidle_loop
.Lfill_block:
csc c0, 0(ca0)
cincoffset ca0, ca0, 8
bltu a0, a1, .Lfill_block
cret
.size start, . - start