1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
/***********************************************************************************************************************
* Copyright (c) 2020 by the authors
*
* Author: André Borrmann <pspwizard@gmx.de>
* License: Apache License 2.0 / MIT
**********************************************************************************************************************/
#![doc(html_root_url = "https://docs.rs/ruspiro-boot/0.5.4")]
#![no_std]
#![feature(lang_items, linkage)]
// this crate does only compile with valid content if the target architecture is AARCH64!
#![cfg(target_arch = "aarch64")]
//! # RusPiRo Bootstrapping for Raspberry Pi
//!
//! This crates provides the startup routines that are needed to be run from a baremetal kernel on
//! the RaspberryPi before execution could be handed over to Rust code.
//!
//! The crate is only valid when compiled for the
//! target architecture **aarch64**. However, event though this crate might be compiled for this target architecture it
//! is tailor made for the Raspberry Pi 3. This crate may be used in other contexts at your own risk. To build your own
//! Raspberry Pi bare metal kernel or operating system please use the
//! [RusPiRo Turorials](https://github.com/RusPiRo/ruspiro-tutorials) as a starting point.
//!
//! # Usage
//!
//! Put the following code into your main rustfile of the binary that should be build for the
//! Raspberry Pi:
//! ```ignore
//! #[macro_use]
//! extern crate ruspiro_boot;
//!
//! come_alive_with!(alive);
//! run_with!(running);
//!
//! fn alive(core: u32) {
//! // do one-time initialization here
//! }
//!
//! fn running(core: u32) -> ! {
//! // do any processing here and ensure you never return from this function
//! loop {}
//! }
//! ```
//!
//! # Features
//!
//! Feature | Description
//! --------------|------------------------------------------------------------------------------
//! `multicore` | Enables the compilation of the multi core boot sequence. Without it only the main core 0 is running.
//! `panic` | Enables the default panic handler. This feature is enabled by default
//!
//! ## Hint:
//! To successfully build a bare metal binary/kernel that depends on this one to perform the boot
//! strapping part it is **highly recomended** to use the linker script [link64.ld](https://github.com/RusPiRo/ruspiro-boot/blob/v0.5.4/link64.ld) provided by this crate.
//! To conviniently refer to the linker scripts contained in this crate it's recommended to use a
//! specific build script in your project that copies the required file to your current project
//! folder and could then be referred to with the `RUSTFLAG` parameter `-C link-arg=-T./link64.ld`.
//! The build script is a simple `build.rs` rust file in your project root with the following
//! contents:
//!
//! ```no_run
//! use std::{env, fs, path::Path};
//!
//! fn main() {
//! // copy the linker script from the boot crate to the current directory
//! // so it will be invoked by the linker
//! let ld_source = env::var_os("DEP_RUSPIRO_BOOT_LINKERSCRIPT")
//! .expect("error in ruspiro build, `ruspiro-boot` not a dependency?")
//! .to_str()
//! .unwrap()
//! .replace("\\", "/");;
//! let src_file = Path::new(&ld_source);
//! let trg_file = format!(
//! "{}/{}",
//! env::current_dir().unwrap().display(),
//! src_file.file_name().unwrap().to_str().unwrap()
//! );
//! println!("Copy linker script from {:?}, to {:?}", src_file, trg_file);
//! fs::copy(src_file, trg_file).unwrap();
//! }
//! ```
//!
//! To get started you could check out the template projects [here](https://www.github.com/RusPiRo/ruspiro_templates)
//!
mod exception;
mod macros;
mod panic;
pub use self::macros::*;
#[cfg(feature = "multicore")]
use core::ptr;
use ruspiro_cache as cache;
// TODO: verify if the stubs are really required
//#[cfg(not(any(test, doctest)))]
//mod stubs;
extern "C" {
fn __kernel_startup(core: u32);
fn __kernel_run(core: u32) -> !;
fn __boot();
}
/// Entry point that is called by the bootstrapping code.
/// From here we will branch into the kernel code provided by the user of this crate.
/// To conviniently provide those entry points the crate user should use the respective macros
/// `come_alive_with!` and `run_with!`. This entry point is assumed to be always called
/// in EL1(aarch64) or SVC(aarch32) mode
///
#[export_name = "__rust_entry"]
unsafe fn __rust_entry(core: u32) -> ! {
// first step before going any further is to clean the L1 cache to ensure there
// is no garbage remaining that could impact actual execution once the cache is enabled.
cache::invalidate_dcache();
// jump to the function provided by the user of this crate
#[cfg(not(test))]
__kernel_startup(core);
// once the one-time startup of this core has been done kickoff any other core
#[cfg(feature = "multicore")]
kickoff_next_core(core);
// after the one time setup enter the processing loop
#[cfg(not(test))]
__kernel_run(core);
#[cfg(test)]
loop {}
}
#[cfg(feature = "multicore")]
fn kickoff_next_core(core: u32) {
// kicking of another core in arch64 means, writing the jump address for this
// core into a specific memory location
let jump_store: u64 = match core {
0 => 0xe0,
1 => 0xe8,
2 => 0xf0,
_ => return,
};
unsafe {
ptr::write_volatile(jump_store as *mut u64, 0x8_0000);
// as this core may have caches enabled, flush it so the other core
// sees the correct data on memory and the write does not only hit the cache
cache::flush_dcache_range(0xe0, 0x10);
core::arch::asm!("sev"); // trigger an event to wake up the sleeping cores
}
}