| // 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 |