Merge branch 'rust-embedded:master' into master

pull/151/head
°~zanez 2 years ago committed by GitHub
commit da305ff04a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -6,6 +6,10 @@
use core::panic::PanicInfo;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
unimplemented!()

@ -327,13 +327,16 @@ diff -uNr 01_wait_forever/src/main.rs 02_runtime_init/src/main.rs
diff -uNr 01_wait_forever/src/panic_wait.rs 02_runtime_init/src/panic_wait.rs
--- 01_wait_forever/src/panic_wait.rs
+++ 02_runtime_init/src/panic_wait.rs
@@ -4,9 +4,10 @@
@@ -4,6 +4,7 @@
//! A panic handler that infinitely waits.
+use crate::cpu;
use core::panic::PanicInfo;
//--------------------------------------------------------------------------------------------------
@@ -12,5 +13,5 @@
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
- unimplemented!()

@ -7,6 +7,10 @@
use crate::cpu;
use core::panic::PanicInfo;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
cpu::wait_forever()

@ -24,9 +24,14 @@ QEMU is no longer running in assembly mode. It will from now on show the output
```console
$ make qemu
[...]
Hello from Rust!
Kernel panic!
Panic location:
File 'src/main.rs', line 126, column 5
Kernel panic: Stopping here.
Stopping here.
```
## Diff to previous
@ -230,7 +235,7 @@ diff -uNr 02_runtime_init/src/main.rs 03_hacky_hello_world/src/main.rs
/// - Only a single core must be active and running this function.
unsafe fn kernel_init() -> ! {
- panic!()
+ println!("[0] Hello from Rust!");
+ println!("Hello from Rust!");
+
+ panic!("Stopping here.")
}
@ -238,7 +243,7 @@ diff -uNr 02_runtime_init/src/main.rs 03_hacky_hello_world/src/main.rs
diff -uNr 02_runtime_init/src/panic_wait.rs 03_hacky_hello_world/src/panic_wait.rs
--- 02_runtime_init/src/panic_wait.rs
+++ 03_hacky_hello_world/src/panic_wait.rs
@@ -4,10 +4,16 @@
@@ -4,14 +4,61 @@
//! A panic handler that infinitely waits.
@ -246,14 +251,59 @@ diff -uNr 02_runtime_init/src/panic_wait.rs 03_hacky_hello_world/src/panic_wait.
+use crate::{cpu, println};
use core::panic::PanicInfo;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
+/// Stop immediately if called a second time.
+///
+/// # Note
+///
+/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
+///
+/// On `AArch64`, which is the only implemented architecture at the time of writing this,
+/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
+/// instructions. They are therefore safe to use even with MMU + caching deactivated.
+///
+/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
+/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
+fn panic_prevent_reenter() {
+ use core::sync::atomic::{AtomicBool, Ordering};
+
+ #[cfg(not(target_arch = "aarch64"))]
+ compile_error!("Add the target_arch to above's check if the following code is safe to use");
+
+ static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
+
+ if !PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
+ PANIC_IN_PROGRESS.store(true, Ordering::Relaxed);
+
+ return;
+ }
+
+ cpu::wait_forever()
+}
+
#[panic_handler]
-fn panic(_info: &PanicInfo) -> ! {
+fn panic(info: &PanicInfo) -> ! {
+ if let Some(args) = info.message() {
+ println!("\nKernel panic: {}", args);
+ } else {
+ println!("\nKernel panic!");
+ }
+ // Protect against panic infinite loops if any of the following code panics itself.
+ panic_prevent_reenter();
+
+ let (location, line, column) = match info.location() {
+ Some(loc) => (loc.file(), loc.line(), loc.column()),
+ _ => ("???", 0, 0),
+ };
+
+ println!(
+ "Kernel panic!\n\n\
+ Panic location:\n File '{}', line {}, column {}\n\n\
+ {}",
+ location,
+ line,
+ column,
+ info.message().unwrap_or(&format_args!("")),
+ );
+
cpu::wait_forever()
}

@ -121,7 +121,7 @@ mod print;
///
/// - Only a single core must be active and running this function.
unsafe fn kernel_init() -> ! {
println!("[0] Hello from Rust!");
println!("Hello from Rust!");
panic!("Stopping here.")
}

@ -7,13 +7,58 @@
use crate::{cpu, println};
use core::panic::PanicInfo;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
/// Stop immediately if called a second time.
///
/// # Note
///
/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
///
/// On `AArch64`, which is the only implemented architecture at the time of writing this,
/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
/// instructions. They are therefore safe to use even with MMU + caching deactivated.
///
/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
fn panic_prevent_reenter() {
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(target_arch = "aarch64"))]
compile_error!("Add the target_arch to above's check if the following code is safe to use");
static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
if !PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
PANIC_IN_PROGRESS.store(true, Ordering::Relaxed);
return;
}
cpu::wait_forever()
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
if let Some(args) = info.message() {
println!("\nKernel panic: {}", args);
} else {
println!("\nKernel panic!");
}
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();
let (location, line, column) = match info.location() {
Some(loc) => (loc.file(), loc.line(), loc.column()),
_ => ("???", 0, 0),
};
println!(
"Kernel panic!\n\n\
Panic location:\n File '{}', line {}, column {}\n\n\
{}",
location,
line,
column,
info.message().unwrap_or(&format_args!("")),
);
cpu::wait_forever()
}

@ -244,11 +244,12 @@ diff -uNr 03_hacky_hello_world/src/main.rs 04_safe_globals/src/main.rs
///
/// - Only a single core must be active and running this function.
unsafe fn kernel_init() -> ! {
- println!("Hello from Rust!");
+ use console::interface::Statistics;
+
println!("[0] Hello from Rust!");
- panic!("Stopping here.")
+ println!("[0] Hello from Rust!");
+
+ println!(
+ "[1] Chars written: {}",
+ bsp::console::console().chars_written()

@ -7,13 +7,58 @@
use crate::{cpu, println};
use core::panic::PanicInfo;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
/// Stop immediately if called a second time.
///
/// # Note
///
/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
///
/// On `AArch64`, which is the only implemented architecture at the time of writing this,
/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
/// instructions. They are therefore safe to use even with MMU + caching deactivated.
///
/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
fn panic_prevent_reenter() {
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(target_arch = "aarch64"))]
compile_error!("Add the target_arch to above's check if the following code is safe to use");
static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
if !PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
PANIC_IN_PROGRESS.store(true, Ordering::Relaxed);
return;
}
cpu::wait_forever()
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
if let Some(args) = info.message() {
println!("\nKernel panic: {}", args);
} else {
println!("\nKernel panic!");
}
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();
let (location, line, column) = match info.location() {
Some(loc) => (loc.file(), loc.line(), loc.column()),
_ => ("???", 0, 0),
};
println!(
"Kernel panic!\n\n\
Panic location:\n File '{}', line {}, column {}\n\n\
{}",
location,
line,
column,
info.message().unwrap_or(&format_args!("")),
);
cpu::wait_forever()
}

@ -1414,7 +1414,7 @@ diff -uNr 04_safe_globals/src/main.rs 05_drivers_gpio_uart/src/main.rs
diff -uNr 04_safe_globals/src/panic_wait.rs 05_drivers_gpio_uart/src/panic_wait.rs
--- 04_safe_globals/src/panic_wait.rs
+++ 05_drivers_gpio_uart/src/panic_wait.rs
@@ -4,15 +4,35 @@
@@ -4,13 +4,29 @@
//! A panic handler that infinitely waits.
@ -1422,11 +1422,11 @@ diff -uNr 04_safe_globals/src/panic_wait.rs 05_drivers_gpio_uart/src/panic_wait.
-use core::panic::PanicInfo;
+use crate::{bsp, cpu};
+use core::{fmt, panic::PanicInfo};
+
+//--------------------------------------------------------------------------------------------------
+// Private Code
+//--------------------------------------------------------------------------------------------------
+
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
+fn _panic_print(args: fmt::Arguments) {
+ use fmt::Write;
+
@ -1442,18 +1442,19 @@ diff -uNr 04_safe_globals/src/panic_wait.rs 05_drivers_gpio_uart/src/panic_wait.
+ _panic_print(format_args_nl!($($arg)*));
+ })
+}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
if let Some(args) = info.message() {
- println!("\nKernel panic: {}", args);
+ panic_println!("\nKernel panic: {}", args);
} else {
- println!("\nKernel panic!");
+ panic_println!("\nKernel panic!");
}
cpu::wait_forever()
+
/// Stop immediately if called a second time.
///
/// # Note
@@ -50,7 +66,7 @@
_ => ("???", 0, 0),
};
- println!(
+ panic_println!(
"Kernel panic!\n\n\
Panic location:\n File '{}', line {}, column {}\n\n\
{}",
diff -uNr 04_safe_globals/tests/boot_test_string.rb 05_drivers_gpio_uart/tests/boot_test_string.rb
--- 04_safe_globals/tests/boot_test_string.rb

@ -27,13 +27,54 @@ macro_rules! panic_println {
})
}
/// Stop immediately if called a second time.
///
/// # Note
///
/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
///
/// On `AArch64`, which is the only implemented architecture at the time of writing this,
/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
/// instructions. They are therefore safe to use even with MMU + caching deactivated.
///
/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
fn panic_prevent_reenter() {
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(target_arch = "aarch64"))]
compile_error!("Add the target_arch to above's check if the following code is safe to use");
static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
if !PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
PANIC_IN_PROGRESS.store(true, Ordering::Relaxed);
return;
}
cpu::wait_forever()
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
if let Some(args) = info.message() {
panic_println!("\nKernel panic: {}", args);
} else {
panic_println!("\nKernel panic!");
}
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();
let (location, line, column) = match info.location() {
Some(loc) => (loc.file(), loc.line(), loc.column()),
_ => ("???", 0, 0),
};
panic_println!(
"Kernel panic!\n\n\
Panic location:\n File '{}', line {}, column {}\n\n\
{}",
location,
line,
column,
info.message().unwrap_or(&format_args!("")),
);
cpu::wait_forever()
}

@ -27,7 +27,8 @@ from SD card all along.
Please bear with me until I find the time to write it all down here elaborately. For the time being,
please see this tutorial as an enabler for a convenience feature that allows booting the following
tutorials in a quick manner.
tutorials in a quick manner. _For those keen to get a deeper understanding, it could make sense to skip forward to [Chapter 15](../15_virtual_mem_part3_precomputed_tables) and read the first half of the README,
where `Load Address != Link Address` is discussed_.
[position independent code]: https://en.wikipedia.org/wiki/Position-independent_code

@ -27,13 +27,54 @@ macro_rules! panic_println {
})
}
/// Stop immediately if called a second time.
///
/// # Note
///
/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
///
/// On `AArch64`, which is the only implemented architecture at the time of writing this,
/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
/// instructions. They are therefore safe to use even with MMU + caching deactivated.
///
/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
fn panic_prevent_reenter() {
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(target_arch = "aarch64"))]
compile_error!("Add the target_arch to above's check if the following code is safe to use");
static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
if !PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
PANIC_IN_PROGRESS.store(true, Ordering::Relaxed);
return;
}
cpu::wait_forever()
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
if let Some(args) = info.message() {
panic_println!("\nKernel panic: {}", args);
} else {
panic_println!("\nKernel panic!");
}
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();
let (location, line, column) = match info.location() {
Some(loc) => (loc.file(), loc.line(), loc.column()),
_ => ("???", 0, 0),
};
panic_println!(
"Kernel panic!\n\n\
Panic location:\n File '{}', line {}, column {}\n\n\
{}",
location,
line,
column,
info.message().unwrap_or(&format_args!("")),
);
cpu::wait_forever()
}

@ -635,32 +635,31 @@ diff -uNr 06_uart_chainloader/src/main.rs 07_timestamps/src/main.rs
diff -uNr 06_uart_chainloader/src/panic_wait.rs 07_timestamps/src/panic_wait.rs
--- 06_uart_chainloader/src/panic_wait.rs
+++ 07_timestamps/src/panic_wait.rs
@@ -29,10 +29,23 @@
@@ -58,18 +58,23 @@
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
+ use crate::time::interface::TimeManager;
+
+ let timestamp = crate::time::time_manager().uptime();
+
if let Some(args) = info.message() {
- panic_println!("\nKernel panic: {}", args);
+ panic_println!(
+ "[ {:>3}.{:06}] Kernel panic: {}",
+ timestamp.as_secs(),
+ timestamp.subsec_micros(),
+ args,
+ );
} else {
- panic_println!("\nKernel panic!");
+ panic_println!(
+ "[ {:>3}.{:06}] Kernel panic!",
+ timestamp.as_secs(),
+ timestamp.subsec_micros(),
+ );
}
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();
cpu::wait_forever()
+ let timestamp = crate::time::time_manager().uptime();
let (location, line, column) = match info.location() {
Some(loc) => (loc.file(), loc.line(), loc.column()),
_ => ("???", 0, 0),
};
panic_println!(
- "Kernel panic!\n\n\
+ "[ {:>3}.{:06}] Kernel panic!\n\n\
Panic location:\n File '{}', line {}, column {}\n\n\
{}",
+ timestamp.as_secs(),
+ timestamp.subsec_micros(),
location,
line,
column,
diff -uNr 06_uart_chainloader/src/print.rs 07_timestamps/src/print.rs
--- 06_uart_chainloader/src/print.rs

@ -27,26 +27,59 @@ macro_rules! panic_println {
})
}
/// Stop immediately if called a second time.
///
/// # Note
///
/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
///
/// On `AArch64`, which is the only implemented architecture at the time of writing this,
/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
/// instructions. They are therefore safe to use even with MMU + caching deactivated.
///
/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
fn panic_prevent_reenter() {
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(target_arch = "aarch64"))]
compile_error!("Add the target_arch to above's check if the following code is safe to use");
static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
if !PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
PANIC_IN_PROGRESS.store(true, Ordering::Relaxed);
return;
}
cpu::wait_forever()
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();
let timestamp = crate::time::time_manager().uptime();
let (location, line, column) = match info.location() {
Some(loc) => (loc.file(), loc.line(), loc.column()),
_ => ("???", 0, 0),
};
if let Some(args) = info.message() {
panic_println!(
"[ {:>3}.{:06}] Kernel panic: {}",
timestamp.as_secs(),
timestamp.subsec_micros(),
args,
);
} else {
panic_println!(
"[ {:>3}.{:06}] Kernel panic!",
timestamp.as_secs(),
timestamp.subsec_micros(),
);
}
panic_println!(
"[ {:>3}.{:06}] Kernel panic!\n\n\
Panic location:\n File '{}', line {}, column {}\n\n\
{}",
timestamp.as_secs(),
timestamp.subsec_micros(),
location,
line,
column,
info.message().unwrap_or(&format_args!("")),
);
cpu::wait_forever()
}

@ -27,26 +27,59 @@ macro_rules! panic_println {
})
}
/// Stop immediately if called a second time.
///
/// # Note
///
/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
///
/// On `AArch64`, which is the only implemented architecture at the time of writing this,
/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
/// instructions. They are therefore safe to use even with MMU + caching deactivated.
///
/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
fn panic_prevent_reenter() {
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(target_arch = "aarch64"))]
compile_error!("Add the target_arch to above's check if the following code is safe to use");
static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
if !PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
PANIC_IN_PROGRESS.store(true, Ordering::Relaxed);
return;
}
cpu::wait_forever()
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();
let timestamp = crate::time::time_manager().uptime();
let (location, line, column) = match info.location() {
Some(loc) => (loc.file(), loc.line(), loc.column()),
_ => ("???", 0, 0),
};
if let Some(args) = info.message() {
panic_println!(
"[ {:>3}.{:06}] Kernel panic: {}",
timestamp.as_secs(),
timestamp.subsec_micros(),
args,
);
} else {
panic_println!(
"[ {:>3}.{:06}] Kernel panic!",
timestamp.as_secs(),
timestamp.subsec_micros(),
);
}
panic_println!(
"[ {:>3}.{:06}] Kernel panic!\n\n\
Panic location:\n File '{}', line {}, column {}\n\n\
{}",
timestamp.as_secs(),
timestamp.subsec_micros(),
location,
line,
column,
info.message().unwrap_or(&format_args!("")),
);
cpu::wait_forever()
}

@ -27,26 +27,59 @@ macro_rules! panic_println {
})
}
/// Stop immediately if called a second time.
///
/// # Note
///
/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
///
/// On `AArch64`, which is the only implemented architecture at the time of writing this,
/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
/// instructions. They are therefore safe to use even with MMU + caching deactivated.
///
/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
fn panic_prevent_reenter() {
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(target_arch = "aarch64"))]
compile_error!("Add the target_arch to above's check if the following code is safe to use");
static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
if !PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
PANIC_IN_PROGRESS.store(true, Ordering::Relaxed);
return;
}
cpu::wait_forever()
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();
let timestamp = crate::time::time_manager().uptime();
let (location, line, column) = match info.location() {
Some(loc) => (loc.file(), loc.line(), loc.column()),
_ => ("???", 0, 0),
};
if let Some(args) = info.message() {
panic_println!(
"[ {:>3}.{:06}] Kernel panic: {}",
timestamp.as_secs(),
timestamp.subsec_micros(),
args,
);
} else {
panic_println!(
"[ {:>3}.{:06}] Kernel panic!",
timestamp.as_secs(),
timestamp.subsec_micros(),
);
}
panic_println!(
"[ {:>3}.{:06}] Kernel panic!\n\n\
Panic location:\n File '{}', line {}, column {}\n\n\
{}",
timestamp.as_secs(),
timestamp.subsec_micros(),
location,
line,
column,
info.message().unwrap_or(&format_args!("")),
);
cpu::wait_forever()
}

@ -27,26 +27,59 @@ macro_rules! panic_println {
})
}
/// Stop immediately if called a second time.
///
/// # Note
///
/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
///
/// On `AArch64`, which is the only implemented architecture at the time of writing this,
/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
/// instructions. They are therefore safe to use even with MMU + caching deactivated.
///
/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
fn panic_prevent_reenter() {
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(target_arch = "aarch64"))]
compile_error!("Add the target_arch to above's check if the following code is safe to use");
static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
if !PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
PANIC_IN_PROGRESS.store(true, Ordering::Relaxed);
return;
}
cpu::wait_forever()
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();
let timestamp = crate::time::time_manager().uptime();
let (location, line, column) = match info.location() {
Some(loc) => (loc.file(), loc.line(), loc.column()),
_ => ("???", 0, 0),
};
if let Some(args) = info.message() {
panic_println!(
"[ {:>3}.{:06}] Kernel panic: {}",
timestamp.as_secs(),
timestamp.subsec_micros(),
args,
);
} else {
panic_println!(
"[ {:>3}.{:06}] Kernel panic!",
timestamp.as_secs(),
timestamp.subsec_micros(),
);
}
panic_println!(
"[ {:>3}.{:06}] Kernel panic!\n\n\
Panic location:\n File '{}', line {}, column {}\n\n\
{}",
timestamp.as_secs(),
timestamp.subsec_micros(),
location,
line,
column,
info.message().unwrap_or(&format_args!("")),
);
cpu::wait_forever()
}

@ -313,7 +313,7 @@ for each exception yet, a default handler is provided:
/// Prints verbose information about the exception and then panics.
fn default_exception_handler(exc: &ExceptionContext) {
panic!(
"\n\nCPU Exception!\n\
"CPU Exception!\n\n\
{}",
exc
);
@ -413,34 +413,37 @@ Minipush 1.0
[MP] ⏩ Pushing 64 KiB =========================================🦀 100% 0 KiB/s Time: 00:00:00
[ML] Loaded! Executing the payload now
[ 0.788994] mingo version 0.11.0
[ 0.789201] Booting on: Raspberry Pi 3
[ 0.789656] MMU online. Special regions:
[ 0.790133] 0x00080000 - 0x0008ffff | 64 KiB | C RO PX | Kernel code and RO data
[ 0.791151] 0x3f000000 - 0x4000ffff | 16 MiB | Dev RW PXN | Device MMIO
[ 0.792040] Current privilege level: EL1
[ 0.792516] Exception handling state:
[ 0.792960] Debug: Masked
[ 0.793350] SError: Masked
[ 0.793740] IRQ: Masked
[ 0.794130] FIQ: Masked
[ 0.794520] Architectural timer resolution: 52 ns
[ 0.795094] Drivers loaded:
[ 0.795430] 1. BCM GPIO
[ 0.795788] 2. BCM PL011 UART
[ 0.796210] Timer test, spinning for 1 second
[ 1.796741]
[ 1.796745] Trying to read from address 8 GiB...
[ 1.797295] ************************************************
[ 1.797987] Whoa! We recovered from a synchronous exception!
[ 1.798680] ************************************************
[ 1.799373]
[ 1.799547] Let's try again
[ 1.799883] Trying to read from address 9 GiB...
Kernel panic:
[ 0.787414] mingo version 0.11.0
[ 0.787621] Booting on: Raspberry Pi 3
[ 0.788076] MMU online. Special regions:
[ 0.788553] 0x00080000 - 0x0008ffff | 64 KiB | C RO PX | Kernel code and RO data
[ 0.789571] 0x3f000000 - 0x4000ffff | 16 MiB | Dev RW PXN | Device MMIO
[ 0.790460] Current privilege level: EL1
[ 0.790936] Exception handling state:
[ 0.791380] Debug: Masked
[ 0.791770] SError: Masked
[ 0.792160] IRQ: Masked
[ 0.792550] FIQ: Masked
[ 0.792940] Architectural timer resolution: 52 ns
[ 0.793514] Drivers loaded:
[ 0.793850] 1. BCM GPIO
[ 0.794208] 2. BCM PL011 UART
[ 0.794630] Timer test, spinning for 1 second
[ 1.795161]
[ 1.795165] Trying to read from address 8 GiB...
[ 1.795715] ************************************************
[ 1.796407] Whoa! We recovered from a synchronous exception!
[ 1.797100] ************************************************
[ 1.797794]
[ 1.797967] Let's try again
[ 1.798303] Trying to read from address 9 GiB...
[ 1.798867] Kernel panic!
Panic location:
File 'src/_arch/aarch64/exception.rs', line 58, column 5
CPU Exception!
ESR_EL1: 0x96000004
Exception Class (EC) : 0x25 - Data Abort, current EL
Instr Specific Syndrome (ISS): 0x4
@ -457,25 +460,25 @@ SPSR_EL1: 0x600003c5
IRQ (I): Masked
FIQ (F): Masked
Illegal Execution State (IL): Not set
ELR_EL1: 0x0000000000082580
ELR_EL1: 0x0000000000082194
General purpose register:
x0 : 0x0000000000000000 x1 : 0x00000000000859b7
x2 : 0x0000000000000027 x3 : 0x0000000000084d3c
x4 : 0x0000000000000003 x5 : 0x3f26329c00000000
x6 : 0x0000000000000000 x7 : 0xd3d18800228d0241
x8 : 0x0000000240000000 x9 : 0x00000000000859b7
x0 : 0x0000000000000000 x1 : 0x0000000000085517
x2 : 0x0000000000000027 x3 : 0x0000000000084380
x4 : 0x0000000000000006 x5 : 0xfb5f341800000000
x6 : 0x0000000000000000 x7 : 0x7f91bc012b2b0209
x8 : 0x0000000240000000 x9 : 0x0000000000085517
x10: 0x0000000000000443 x11: 0x000000003f201000
x12: 0x0000000000000019 x13: 0x0000000000000033
x14: 0x000000000007fd3d x15: 0x0000000000000058
x16: 0x0000000000000078 x17: 0xfd29f02255a847c0
x18: 0x9cd4788000000008 x19: 0x0000000000090008
x20: 0x00000000000857a0 x21: 0x000000003b9aca00
x22: 0x000000000008271c x23: 0x0000000000083314
x24: 0x00000000000003e8 x25: 0xffffffffc4653600
x26: 0x00000000000f4240 x27: 0x0000000000085880
x28: 0x0000000000085170 x29: 0x0000000000086c10
lr : 0x0000000000082574
x12: 0x0000000000000019 x13: 0x00000000ffffd8f0
x14: 0x000000000000147b x15: 0x00000000ffffff9c
x16: 0x000000000007fd38 x17: 0x0000000005f5e0ff
x18: 0x0000000000000030 x19: 0x0000000000090008
x20: 0x0000000000085350 x21: 0x000000003b9aca00
x22: 0x0000000000082e4c x23: 0x0000000000082308
x24: 0x0000000010624dd3 x25: 0xffffffffc4653600
x26: 0x0000000000086638 x27: 0x0000000000085410
x28: 0x0000000000084f90 x29: 0x0000000000086538
lr : 0x0000000000082188
```
## Diff to previous
@ -547,7 +550,7 @@ diff -uNr 10_virtual_mem_part1_identity_mapping/src/_arch/aarch64/exception.rs 1
+/// Prints verbose information about the exception and then panics.
+fn default_exception_handler(exc: &ExceptionContext) {
+ panic!(
+ "\n\nCPU Exception!\n\
+ "CPU Exception!\n\n\
+ {}",
+ exc
+ );

@ -56,7 +56,7 @@ struct ExceptionContext {
/// Prints verbose information about the exception and then panics.
fn default_exception_handler(exc: &ExceptionContext) {
panic!(
"\n\nCPU Exception!\n\
"CPU Exception!\n\n\
{}",
exc
);

@ -27,26 +27,59 @@ macro_rules! panic_println {
})
}
/// Stop immediately if called a second time.
///
/// # Note
///
/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
///
/// On `AArch64`, which is the only implemented architecture at the time of writing this,
/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
/// instructions. They are therefore safe to use even with MMU + caching deactivated.
///
/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
fn panic_prevent_reenter() {
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(target_arch = "aarch64"))]
compile_error!("Add the target_arch to above's check if the following code is safe to use");
static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
if !PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
PANIC_IN_PROGRESS.store(true, Ordering::Relaxed);
return;
}
cpu::wait_forever()
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();
let timestamp = crate::time::time_manager().uptime();
let (location, line, column) = match info.location() {
Some(loc) => (loc.file(), loc.line(), loc.column()),
_ => ("???", 0, 0),
};
if let Some(args) = info.message() {
panic_println!(
"[ {:>3}.{:06}] Kernel panic: {}",
timestamp.as_secs(),
timestamp.subsec_micros(),
args,
);
} else {
panic_println!(
"[ {:>3}.{:06}] Kernel panic!",
timestamp.as_secs(),
timestamp.subsec_micros(),
);
}
panic_println!(
"[ {:>3}.{:06}] Kernel panic!\n\n\
Panic location:\n File '{}', line {}, column {}\n\n\
{}",
timestamp.as_secs(),
timestamp.subsec_micros(),
location,
line,
column,
info.message().unwrap_or(&format_args!("")),
);
cpu::wait_forever()
}

@ -54,3 +54,7 @@ harness = false
[[test]]
name = "02_exception_sync_page_fault"
harness = false
[[test]]
name = "03_exception_restore_sanity"
harness = false

@ -648,6 +648,10 @@ harness = false
[[test]]
name = "02_exception_sync_page_fault"
harness = false
[[test]]
name = "03_exception_restore_sanity"
harness = false
```
#### Overriding Panic Behavior
@ -703,11 +707,11 @@ unsafe fn kernel_init() -> ! {
println!("Testing synchronous exception handling by causing a page fault");
if let Err(string) = memory::mmu::mmu().enable_mmu_and_caching() {
println!("MMU: {}", string);
info!("MMU: {}", string);
cpu::qemu_exit_failure()
}
println!("Writing beyond mapped area to address 9 GiB...");
info!("Writing beyond mapped area to address 9 GiB...");
let big_addr: u64 = 9 * 1024 * 1024 * 1024;
core::ptr::read_volatile(big_addr as *mut u64);
@ -761,7 +765,10 @@ The subtest first sends `"ABC"` over the console to the kernel, and then expects
#![no_main]
#![no_std]
use libkernel::{bsp, console, exception, print};
/// Console tests should time out on the I/O harness in case of panic.
mod panic_wait_forever;
use libkernel::{bsp, console, cpu, exception, print};
#[no_mangle]
unsafe fn kernel_init() -> ! {
@ -849,10 +856,14 @@ Compiling integration test(s) - rpi3
🦀 Testing synchronous exception handling by causing a page fault
-------------------------------------------------------------------
[ 0.163030] Writing beyond mapped area to address 9 GiB...
[ 0.164791] Kernel panic:
[ 0.132792] Writing beyond mapped area to address 9 GiB...
[ 0.134563] Kernel panic!
Panic location:
File 'src/_arch/aarch64/exception.rs', line 58, column 5
CPU Exception!
ESR_EL1: 0x96000004
Exception Class (EC) : 0x25 - Data Abort, current EL
[...]
@ -860,6 +871,24 @@ Compiling integration test(s) - rpi3
-------------------------------------------------------------------
✅ Success: 02_exception_sync_page_fault.rs
-------------------------------------------------------------------
Running tests/03_exception_restore_sanity.rs (target/aarch64-unknown-none-softfloat/release/deps/03_exception_restore_sanity-a56e14285bb26e0e)
-------------------------------------------------------------------
🦀 Running 1 console I/O tests
-------------------------------------------------------------------
1. Exception restore.........................................[ok]
Console log:
Testing exception restore
[ 0.130757] Making a dummy system call
[ 0.132592] Back from system call!
-------------------------------------------------------------------
✅ Success: 03_exception_restore_sanity.rs
-------------------------------------------------------------------
```
## Diff to previous
@ -883,7 +912,7 @@ diff -uNr 11_exceptions_part1_groundwork/Cargo.toml 12_integrated_testing/Cargo.
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
edition = "2021"
@@ -11,20 +11,46 @@
@@ -11,20 +11,50 @@
default = []
bsp_rpi3 = ["tock-registers"]
bsp_rpi4 = ["tock-registers"]
@ -934,6 +963,10 @@ diff -uNr 11_exceptions_part1_groundwork/Cargo.toml 12_integrated_testing/Cargo.
+[[test]]
+name = "02_exception_sync_page_fault"
+harness = false
+
+[[test]]
+name = "03_exception_restore_sanity"
+harness = false
diff -uNr 11_exceptions_part1_groundwork/Makefile 12_integrated_testing/Makefile
--- 11_exceptions_part1_groundwork/Makefile
@ -1070,7 +1103,7 @@ diff -uNr 11_exceptions_part1_groundwork/src/_arch/aarch64/cpu.rs 12_integrated_
diff -uNr 11_exceptions_part1_groundwork/src/_arch/aarch64/exception.rs 12_integrated_testing/src/_arch/aarch64/exception.rs
--- 11_exceptions_part1_groundwork/src/_arch/aarch64/exception.rs
+++ 12_integrated_testing/src/_arch/aarch64/exception.rs
@@ -87,18 +87,6 @@
@@ -87,15 +87,14 @@
#[no_mangle]
unsafe extern "C" fn current_elx_synchronous(e: &mut ExceptionContext) {
@ -1083,12 +1116,30 @@ diff -uNr 11_exceptions_part1_groundwork/src/_arch/aarch64/exception.rs 12_integ
- e.elr_el1 += 4;
-
- return;
- }
- }
-
default_exception_handler(e);
+ #[cfg(feature = "test_build")]
+ {
+ const TEST_SVC_ID: u64 = 0x1337;
+
+ if let Some(ESR_EL1::EC::Value::SVC64) = e.esr_el1.exception_class() {
+ if e.esr_el1.iss() == TEST_SVC_ID {
+ return;
+ }
}
}
@@ -192,6 +191,12 @@
fn exception_class(&self) -> Option<ESR_EL1::EC::Value> {
self.0.read_as_enum(ESR_EL1::EC)
}
+
+ #[cfg(feature = "test_build")]
+ #[inline(always)]
+ fn iss(&self) -> u64 {
+ self.0.read(ESR_EL1::ISS)
+ }
}
/// Human readable ESR_EL1.
diff -uNr 11_exceptions_part1_groundwork/src/_arch/aarch64/memory/mmu/translation_table.rs 12_integrated_testing/src/_arch/aarch64/memory/mmu/translation_table.rs
--- 11_exceptions_part1_groundwork/src/_arch/aarch64/memory/mmu/translation_table.rs
@ -1697,10 +1748,19 @@ diff -uNr 11_exceptions_part1_groundwork/src/panic_wait.rs 12_integrated_testing
/// Prints with a newline - only use from the panic handler.
///
/// Carbon copy from <https://doc.rust-lang.org/src/std/macros.rs.html>
@@ -48,5 +65,5 @@
);
@@ -53,7 +70,7 @@
return;
}
- cpu::wait_forever()
+ _panic_exit()
}
#[panic_handler]
@@ -81,5 +98,5 @@
info.message().unwrap_or(&format_args!("")),
);
- cpu::wait_forever()
+ _panic_exit()
}
@ -1814,7 +1874,7 @@ diff -uNr 11_exceptions_part1_groundwork/tests/00_console_sanity.rb 12_integrate
diff -uNr 11_exceptions_part1_groundwork/tests/00_console_sanity.rs 12_integrated_testing/tests/00_console_sanity.rs
--- 11_exceptions_part1_groundwork/tests/00_console_sanity.rs
+++ 12_integrated_testing/tests/00_console_sanity.rs
@@ -0,0 +1,35 @@
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2019-2022 Andre Richter <andre.o.richter@gmail.com>
@ -1825,6 +1885,9 @@ diff -uNr 11_exceptions_part1_groundwork/tests/00_console_sanity.rs 12_integrate
+#![no_main]
+#![no_std]
+
+/// Console tests should time out on the I/O harness in case of panic.
+mod panic_wait_forever;
+
+use libkernel::{bsp, console, cpu, exception, print};
+
+#[no_mangle]
@ -1953,6 +2016,96 @@ diff -uNr 11_exceptions_part1_groundwork/tests/02_exception_sync_page_fault.rs 1
+ cpu::qemu_exit_failure()
+}
diff -uNr 11_exceptions_part1_groundwork/tests/03_exception_restore_sanity.rb 12_integrated_testing/tests/03_exception_restore_sanity.rb
--- 11_exceptions_part1_groundwork/tests/03_exception_restore_sanity.rb
+++ 12_integrated_testing/tests/03_exception_restore_sanity.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+# SPDX-License-Identifier: MIT OR Apache-2.0
+#
+# Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
+
+require_relative '../../common/tests/console_io_test'
+
+# Verify that exception restore works.
+class ExceptionRestoreTest < SubtestBase
+ def name
+ 'Exception restore'
+ end
+
+ def run(qemu_out, _qemu_in)
+ expect_or_raise(qemu_out, 'Back from system call!')
+ end
+end
+
+##--------------------------------------------------------------------------------------------------
+## Test registration
+##--------------------------------------------------------------------------------------------------
+def subtest_collection
+ [ExceptionRestoreTest.new]
+end
diff -uNr 11_exceptions_part1_groundwork/tests/03_exception_restore_sanity.rs 12_integrated_testing/tests/03_exception_restore_sanity.rs
--- 11_exceptions_part1_groundwork/tests/03_exception_restore_sanity.rs
+++ 12_integrated_testing/tests/03_exception_restore_sanity.rs
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
+
+//! A simple sanity test to see if exception restore code works.
+
+#![feature(format_args_nl)]
+#![no_main]
+#![no_std]
+
+/// Console tests should time out on the I/O harness in case of panic.
+mod panic_wait_forever;
+
+use core::arch::asm;
+use libkernel::{bsp, cpu, exception, info, memory, println};
+
+#[inline(never)]
+fn nested_system_call() {
+ #[cfg(target_arch = "aarch64")]
+ unsafe {
+ asm!("svc #0x1337", options(nomem, nostack, preserves_flags));
+ }
+
+ #[cfg(not(target_arch = "aarch64"))]
+ {
+ info!("Not supported yet");
+ cpu::wait_forever();
+ }
+}
+
+#[no_mangle]
+unsafe fn kernel_init() -> ! {
+ use memory::mmu::interface::MMU;
+
+ exception::handling_init();
+ bsp::console::qemu_bring_up_console();
+
+ // This line will be printed as the test header.
+ println!("Testing exception restore");
+
+ if let Err(string) = memory::mmu::mmu().enable_mmu_and_caching() {
+ info!("MMU: {}", string);
+ cpu::qemu_exit_failure()
+ }
+
+ info!("Making a dummy system call");
+
+ // Calling this inside a function indirectly tests if the link register is restored properly.
+ nested_system_call();
+
+ info!("Back from system call!");
+
+ // The QEMU process running this test will be closed by the I/O test harness.
+ cpu::wait_forever();
+}
diff -uNr 11_exceptions_part1_groundwork/tests/boot_test_string.rb 12_integrated_testing/tests/boot_test_string.rb
--- 11_exceptions_part1_groundwork/tests/boot_test_string.rb
+++ 12_integrated_testing/tests/boot_test_string.rb
@ -1976,6 +2129,20 @@ diff -uNr 11_exceptions_part1_groundwork/tests/panic_exit_success/mod.rs 12_inte
+ libkernel::cpu::qemu_exit_success()
+}
diff -uNr 11_exceptions_part1_groundwork/tests/panic_wait_forever/mod.rs 12_integrated_testing/tests/panic_wait_forever/mod.rs
--- 11_exceptions_part1_groundwork/tests/panic_wait_forever/mod.rs
+++ 12_integrated_testing/tests/panic_wait_forever/mod.rs
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
+
+/// Overwrites libkernel's `panic_wait::_panic_exit()` with wait_forever.
+#[no_mangle]
+fn _panic_exit() -> ! {
+ libkernel::cpu::wait_forever()
+}
diff -uNr 11_exceptions_part1_groundwork/test-types/Cargo.toml 12_integrated_testing/test-types/Cargo.toml
--- 11_exceptions_part1_groundwork/test-types/Cargo.toml
+++ 12_integrated_testing/test-types/Cargo.toml

@ -56,7 +56,7 @@ struct ExceptionContext {
/// Prints verbose information about the exception and then panics.
fn default_exception_handler(exc: &ExceptionContext) {
panic!(
"\n\nCPU Exception!\n\
"CPU Exception!\n\n\
{}",
exc
);
@ -87,6 +87,17 @@ unsafe extern "C" fn current_el0_serror(_e: &mut ExceptionContext) {
#[no_mangle]
unsafe extern "C" fn current_elx_synchronous(e: &mut ExceptionContext) {
#[cfg(feature = "test_build")]
{
const TEST_SVC_ID: u64 = 0x1337;
if let Some(ESR_EL1::EC::Value::SVC64) = e.esr_el1.exception_class() {
if e.esr_el1.iss() == TEST_SVC_ID {
return;
}
}
}
default_exception_handler(e);
}
@ -180,6 +191,12 @@ impl EsrEL1 {
fn exception_class(&self) -> Option<ESR_EL1::EC::Value> {
self.0.read_as_enum(ESR_EL1::EC)
}
#[cfg(feature = "test_build")]
#[inline(always)]
fn iss(&self) -> u64 {
self.0.read(ESR_EL1::ISS)
}
}
/// Human readable ESR_EL1.

@ -44,26 +44,59 @@ macro_rules! panic_println {
})
}
/// Stop immediately if called a second time.
///
/// # Note
///
/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
///
/// On `AArch64`, which is the only implemented architecture at the time of writing this,
/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
/// instructions. They are therefore safe to use even with MMU + caching deactivated.
///
/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
fn panic_prevent_reenter() {
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(target_arch = "aarch64"))]
compile_error!("Add the target_arch to above's check if the following code is safe to use");
static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
if !PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
PANIC_IN_PROGRESS.store(true, Ordering::Relaxed);
return;
}
_panic_exit()
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();
let timestamp = crate::time::time_manager().uptime();
let (location, line, column) = match info.location() {
Some(loc) => (loc.file(), loc.line(), loc.column()),
_ => ("???", 0, 0),
};
if let Some(args) = info.message() {
panic_println!(
"[ {:>3}.{:06}] Kernel panic: {}",
timestamp.as_secs(),
timestamp.subsec_micros(),
args,
);
} else {
panic_println!(
"[ {:>3}.{:06}] Kernel panic!",
timestamp.as_secs(),
timestamp.subsec_micros(),
);
}
panic_println!(
"[ {:>3}.{:06}] Kernel panic!\n\n\
Panic location:\n File '{}', line {}, column {}\n\n\
{}",
timestamp.as_secs(),
timestamp.subsec_micros(),
location,
line,
column,
info.message().unwrap_or(&format_args!("")),
);
_panic_exit()
}

@ -8,6 +8,9 @@
#![no_main]
#![no_std]
/// Console tests should time out on the I/O harness in case of panic.
mod panic_wait_forever;
use libkernel::{bsp, console, cpu, exception, print};
#[no_mangle]

@ -0,0 +1,25 @@
# frozen_string_literal: true
# SPDX-License-Identifier: MIT OR Apache-2.0
#
# Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
require_relative '../../common/tests/console_io_test'
# Verify that exception restore works.
class ExceptionRestoreTest < SubtestBase
def name
'Exception restore'
end
def run(qemu_out, _qemu_in)
expect_or_raise(qemu_out, 'Back from system call!')
end
end
##--------------------------------------------------------------------------------------------------
## Test registration
##--------------------------------------------------------------------------------------------------
def subtest_collection
[ExceptionRestoreTest.new]
end

@ -0,0 +1,55 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
//! A simple sanity test to see if exception restore code works.
#![feature(format_args_nl)]
#![no_main]
#![no_std]
/// Console tests should time out on the I/O harness in case of panic.
mod panic_wait_forever;
use core::arch::asm;
use libkernel::{bsp, cpu, exception, info, memory, println};
#[inline(never)]
fn nested_system_call() {
#[cfg(target_arch = "aarch64")]
unsafe {
asm!("svc #0x1337", options(nomem, nostack, preserves_flags));
}
#[cfg(not(target_arch = "aarch64"))]
{
info!("Not supported yet");
cpu::wait_forever();
}
}
#[no_mangle]
unsafe fn kernel_init() -> ! {
use memory::mmu::interface::MMU;
exception::handling_init();
bsp::console::qemu_bring_up_console();
// This line will be printed as the test header.
println!("Testing exception restore");
if let Err(string) = memory::mmu::mmu().enable_mmu_and_caching() {
info!("MMU: {}", string);
cpu::qemu_exit_failure()
}
info!("Making a dummy system call");
// Calling this inside a function indirectly tests if the link register is restored properly.
nested_system_call();
info!("Back from system call!");
// The QEMU process running this test will be closed by the I/O test harness.
cpu::wait_forever();
}

@ -0,0 +1,9 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
/// Overwrites libkernel's `panic_wait::_panic_exit()` with wait_forever.
#[no_mangle]
fn _panic_exit() -> ! {
libkernel::cpu::wait_forever()
}

@ -54,3 +54,7 @@ harness = false
[[test]]
name = "02_exception_sync_page_fault"
harness = false
[[test]]
name = "03_exception_restore_sanity"
harness = false

@ -913,7 +913,7 @@ diff -uNr 12_integrated_testing/src/_arch/aarch64/exception.rs 13_exceptions_par
use core::{arch::global_asm, cell::UnsafeCell, fmt};
use cortex_a::{asm::barrier, registers::*};
use tock_registers::{
@@ -91,8 +92,11 @@
@@ -102,8 +103,11 @@
}
#[no_mangle]
@ -2511,15 +2511,15 @@ diff -uNr 12_integrated_testing/src/panic_wait.rs 13_exceptions_part2_peripheral
use core::{fmt, panic::PanicInfo};
//--------------------------------------------------------------------------------------------------
@@ -48,6 +48,8 @@
@@ -77,6 +77,8 @@
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
+ unsafe { exception::asynchronous::local_irq_mask() };
+
let timestamp = crate::time::time_manager().uptime();
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();
if let Some(args) = info.message() {
diff -uNr 12_integrated_testing/src/state.rs 13_exceptions_part2_peripheral_IRQs/src/state.rs
--- 12_integrated_testing/src/state.rs
@ -2753,9 +2753,9 @@ diff -uNr 12_integrated_testing/src/synchronization.rs 13_exceptions_part2_perip
+ }
}
diff -uNr 12_integrated_testing/tests/03_exception_irq_sanity.rs 13_exceptions_part2_peripheral_IRQs/tests/03_exception_irq_sanity.rs
--- 12_integrated_testing/tests/03_exception_irq_sanity.rs
+++ 13_exceptions_part2_peripheral_IRQs/tests/03_exception_irq_sanity.rs
diff -uNr 12_integrated_testing/tests/04_exception_irq_sanity.rs 13_exceptions_part2_peripheral_IRQs/tests/04_exception_irq_sanity.rs
--- 12_integrated_testing/tests/04_exception_irq_sanity.rs
+++ 13_exceptions_part2_peripheral_IRQs/tests/04_exception_irq_sanity.rs
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//

@ -57,7 +57,7 @@ struct ExceptionContext {
/// Prints verbose information about the exception and then panics.
fn default_exception_handler(exc: &ExceptionContext) {
panic!(
"\n\nCPU Exception!\n\
"CPU Exception!\n\n\
{}",
exc
);
@ -88,6 +88,17 @@ unsafe extern "C" fn current_el0_serror(_e: &mut ExceptionContext) {
#[no_mangle]
unsafe extern "C" fn current_elx_synchronous(e: &mut ExceptionContext) {
#[cfg(feature = "test_build")]
{
const TEST_SVC_ID: u64 = 0x1337;
if let Some(ESR_EL1::EC::Value::SVC64) = e.esr_el1.exception_class() {
if e.esr_el1.iss() == TEST_SVC_ID {
return;
}
}
}
default_exception_handler(e);
}
@ -184,6 +195,12 @@ impl EsrEL1 {
fn exception_class(&self) -> Option<ESR_EL1::EC::Value> {
self.0.read_as_enum(ESR_EL1::EC)
}
#[cfg(feature = "test_build")]
#[inline(always)]
fn iss(&self) -> u64 {
self.0.read(ESR_EL1::ISS)
}
}
/// Human readable ESR_EL1.

@ -44,28 +44,61 @@ macro_rules! panic_println {
})
}
/// Stop immediately if called a second time.
///
/// # Note
///
/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
///
/// On `AArch64`, which is the only implemented architecture at the time of writing this,
/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
/// instructions. They are therefore safe to use even with MMU + caching deactivated.
///
/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
fn panic_prevent_reenter() {
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(target_arch = "aarch64"))]
compile_error!("Add the target_arch to above's check if the following code is safe to use");
static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
if !PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
PANIC_IN_PROGRESS.store(true, Ordering::Relaxed);
return;
}
_panic_exit()
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
unsafe { exception::asynchronous::local_irq_mask() };
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();
let timestamp = crate::time::time_manager().uptime();
let (location, line, column) = match info.location() {
Some(loc) => (loc.file(), loc.line(), loc.column()),
_ => ("???", 0, 0),
};
if let Some(args) = info.message() {
panic_println!(
"[ {:>3}.{:06}] Kernel panic: {}",
timestamp.as_secs(),
timestamp.subsec_micros(),
args,
);
} else {
panic_println!(
"[ {:>3}.{:06}] Kernel panic!",
timestamp.as_secs(),
timestamp.subsec_micros(),
);
}
panic_println!(
"[ {:>3}.{:06}] Kernel panic!\n\n\
Panic location:\n File '{}', line {}, column {}\n\n\
{}",
timestamp.as_secs(),
timestamp.subsec_micros(),
location,
line,
column,
info.message().unwrap_or(&format_args!("")),
);
_panic_exit()
}

@ -8,6 +8,9 @@
#![no_main]
#![no_std]
/// Console tests should time out on the I/O harness in case of panic.
mod panic_wait_forever;
use libkernel::{bsp, console, cpu, exception, print};
#[no_mangle]

@ -0,0 +1,25 @@
# frozen_string_literal: true
# SPDX-License-Identifier: MIT OR Apache-2.0
#
# Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
require_relative '../../common/tests/console_io_test'
# Verify that exception restore works.
class ExceptionRestoreTest < SubtestBase
def name
'Exception restore'
end
def run(qemu_out, _qemu_in)
expect_or_raise(qemu_out, 'Back from system call!')
end
end
##--------------------------------------------------------------------------------------------------
## Test registration
##--------------------------------------------------------------------------------------------------
def subtest_collection
[ExceptionRestoreTest.new]
end

@ -0,0 +1,55 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
//! A simple sanity test to see if exception restore code works.
#![feature(format_args_nl)]
#![no_main]
#![no_std]
/// Console tests should time out on the I/O harness in case of panic.
mod panic_wait_forever;
use core::arch::asm;
use libkernel::{bsp, cpu, exception, info, memory, println};
#[inline(never)]
fn nested_system_call() {
#[cfg(target_arch = "aarch64")]
unsafe {
asm!("svc #0x1337", options(nomem, nostack, preserves_flags));
}
#[cfg(not(target_arch = "aarch64"))]
{
info!("Not supported yet");
cpu::wait_forever();
}
}
#[no_mangle]
unsafe fn kernel_init() -> ! {
use memory::mmu::interface::MMU;
exception::handling_init();
bsp::console::qemu_bring_up_console();
// This line will be printed as the test header.
println!("Testing exception restore");
if let Err(string) = memory::mmu::mmu().enable_mmu_and_caching() {
info!("MMU: {}", string);
cpu::qemu_exit_failure()
}
info!("Making a dummy system call");
// Calling this inside a function indirectly tests if the link register is restored properly.
nested_system_call();
info!("Back from system call!");
// The QEMU process running this test will be closed by the I/O test harness.
cpu::wait_forever();
}

@ -0,0 +1,9 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
/// Overwrites libkernel's `panic_wait::_panic_exit()` with wait_forever.
#[no_mangle]
fn _panic_exit() -> ! {
libkernel::cpu::wait_forever()
}

@ -54,3 +54,7 @@ harness = false
[[test]]
name = "02_exception_sync_page_fault"
harness = false
[[test]]
name = "03_exception_restore_sanity"
harness = false

@ -3641,4 +3641,52 @@ diff -uNr 13_exceptions_part2_peripheral_IRQs/tests/02_exception_sync_page_fault
info!("Writing beyond mapped area to address 9 GiB...");
let big_addr: u64 = 9 * 1024 * 1024 * 1024;
diff -uNr 13_exceptions_part2_peripheral_IRQs/tests/03_exception_restore_sanity.rs 14_virtual_mem_part2_mmio_remap/tests/03_exception_restore_sanity.rs
--- 13_exceptions_part2_peripheral_IRQs/tests/03_exception_restore_sanity.rs
+++ 14_virtual_mem_part2_mmio_remap/tests/03_exception_restore_sanity.rs
@@ -30,18 +30,40 @@
#[no_mangle]
unsafe fn kernel_init() -> ! {
- use memory::mmu::interface::MMU;
+ use libkernel::driver::interface::DriverManager;
exception::handling_init();
- bsp::console::qemu_bring_up_console();
// This line will be printed as the test header.
println!("Testing exception restore");
- if let Err(string) = memory::mmu::mmu().enable_mmu_and_caching() {
- info!("MMU: {}", string);
+ let phys_kernel_tables_base_addr = match memory::mmu::kernel_map_binary() {
+ Err(string) => {
+ info!("Error mapping kernel binary: {}", string);
+ cpu::qemu_exit_failure()
+ }
+ Ok(addr) => addr,
+ };
+
+ if let Err(e) = memory::mmu::enable_mmu_and_caching(phys_kernel_tables_base_addr) {
+ info!("Enabling MMU failed: {}", e);
cpu::qemu_exit_failure()
}
+ // Printing will silently fail from here on, because the driver's MMIO is not remapped yet.
+
+ memory::mmu::post_enable_init();
+ bsp::console::qemu_bring_up_console();
+
+ // Bring up the drivers needed for printing first.
+ for i in bsp::driver::driver_manager()
+ .early_print_device_drivers()
+ .iter()
+ {
+ // Any encountered errors cannot be printed yet, obviously, so just safely park the CPU.
+ i.init().unwrap_or_else(|_| cpu::qemu_exit_failure());
+ }
+ bsp::driver::driver_manager().post_early_print_device_driver_init();
+ // Printing available again from here on.
info!("Making a dummy system call");
```

@ -57,7 +57,7 @@ struct ExceptionContext {
/// Prints verbose information about the exception and then panics.
fn default_exception_handler(exc: &ExceptionContext) {
panic!(
"\n\nCPU Exception!\n\
"CPU Exception!\n\n\
{}",
exc
);
@ -88,6 +88,17 @@ unsafe extern "C" fn current_el0_serror(_e: &mut ExceptionContext) {
#[no_mangle]
unsafe extern "C" fn current_elx_synchronous(e: &mut ExceptionContext) {
#[cfg(feature = "test_build")]
{
const TEST_SVC_ID: u64 = 0x1337;
if let Some(ESR_EL1::EC::Value::SVC64) = e.esr_el1.exception_class() {
if e.esr_el1.iss() == TEST_SVC_ID {
return;
}
}
}
default_exception_handler(e);
}
@ -184,6 +195,12 @@ impl EsrEL1 {
fn exception_class(&self) -> Option<ESR_EL1::EC::Value> {
self.0.read_as_enum(ESR_EL1::EC)
}
#[cfg(feature = "test_build")]
#[inline(always)]
fn iss(&self) -> u64 {
self.0.read(ESR_EL1::ISS)
}
}
/// Human readable ESR_EL1.

@ -44,28 +44,61 @@ macro_rules! panic_println {
})
}
/// Stop immediately if called a second time.
///
/// # Note
///
/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
///
/// On `AArch64`, which is the only implemented architecture at the time of writing this,
/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
/// instructions. They are therefore safe to use even with MMU + caching deactivated.
///
/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
fn panic_prevent_reenter() {
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(target_arch = "aarch64"))]
compile_error!("Add the target_arch to above's check if the following code is safe to use");
static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
if !PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
PANIC_IN_PROGRESS.store(true, Ordering::Relaxed);
return;
}
_panic_exit()
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
unsafe { exception::asynchronous::local_irq_mask() };
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();
let timestamp = crate::time::time_manager().uptime();
let (location, line, column) = match info.location() {
Some(loc) => (loc.file(), loc.line(), loc.column()),
_ => ("???", 0, 0),
};
if let Some(args) = info.message() {
panic_println!(
"[ {:>3}.{:06}] Kernel panic: {}",
timestamp.as_secs(),
timestamp.subsec_micros(),
args,
);
} else {
panic_println!(
"[ {:>3}.{:06}] Kernel panic!",
timestamp.as_secs(),
timestamp.subsec_micros(),
);
}
panic_println!(
"[ {:>3}.{:06}] Kernel panic!\n\n\
Panic location:\n File '{}', line {}, column {}\n\n\
{}",
timestamp.as_secs(),
timestamp.subsec_micros(),
location,
line,
column,
info.message().unwrap_or(&format_args!("")),
);
_panic_exit()
}

@ -8,6 +8,9 @@
#![no_main]
#![no_std]
/// Console tests should time out on the I/O harness in case of panic.
mod panic_wait_forever;
use libkernel::{bsp, console, cpu, exception, print};
#[no_mangle]

@ -0,0 +1,25 @@
# frozen_string_literal: true
# SPDX-License-Identifier: MIT OR Apache-2.0
#
# Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
require_relative '../../common/tests/console_io_test'
# Verify that exception restore works.
class ExceptionRestoreTest < SubtestBase
def name
'Exception restore'
end
def run(qemu_out, _qemu_in)
expect_or_raise(qemu_out, 'Back from system call!')
end
end
##--------------------------------------------------------------------------------------------------
## Test registration
##--------------------------------------------------------------------------------------------------
def subtest_collection
[ExceptionRestoreTest.new]
end

@ -0,0 +1,77 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
//! A simple sanity test to see if exception restore code works.
#![feature(format_args_nl)]
#![no_main]
#![no_std]
/// Console tests should time out on the I/O harness in case of panic.
mod panic_wait_forever;
use core::arch::asm;
use libkernel::{bsp, cpu, exception, info, memory, println};
#[inline(never)]
fn nested_system_call() {
#[cfg(target_arch = "aarch64")]
unsafe {
asm!("svc #0x1337", options(nomem, nostack, preserves_flags));
}
#[cfg(not(target_arch = "aarch64"))]
{
info!("Not supported yet");
cpu::wait_forever();
}
}
#[no_mangle]
unsafe fn kernel_init() -> ! {
use libkernel::driver::interface::DriverManager;
exception::handling_init();
// This line will be printed as the test header.
println!("Testing exception restore");
let phys_kernel_tables_base_addr = match memory::mmu::kernel_map_binary() {
Err(string) => {
info!("Error mapping kernel binary: {}", string);
cpu::qemu_exit_failure()
}
Ok(addr) => addr,
};
if let Err(e) = memory::mmu::enable_mmu_and_caching(phys_kernel_tables_base_addr) {
info!("Enabling MMU failed: {}", e);
cpu::qemu_exit_failure()
}
// Printing will silently fail from here on, because the driver's MMIO is not remapped yet.
memory::mmu::post_enable_init();
bsp::console::qemu_bring_up_console();
// Bring up the drivers needed for printing first.
for i in bsp::driver::driver_manager()
.early_print_device_drivers()
.iter()
{
// Any encountered errors cannot be printed yet, obviously, so just safely park the CPU.
i.init().unwrap_or_else(|_| cpu::qemu_exit_failure());
}
bsp::driver::driver_manager().post_early_print_device_driver_init();
// Printing available again from here on.
info!("Making a dummy system call");
// Calling this inside a function indirectly tests if the link register is restored properly.
nested_system_call();
info!("Back from system call!");
// The QEMU process running this test will be closed by the I/O test harness.
cpu::wait_forever();
}

@ -0,0 +1,9 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
/// Overwrites libkernel's `panic_wait::_panic_exit()` with wait_forever.
#[no_mangle]
fn _panic_exit() -> ! {
libkernel::cpu::wait_forever()
}

@ -54,3 +54,7 @@ harness = false
[[test]]
name = "02_exception_sync_page_fault"
harness = false
[[test]]
name = "03_exception_restore_sanity"
harness = false

@ -1686,16 +1686,16 @@ diff -uNr 14_virtual_mem_part2_mmio_remap/src/memory/mmu.rs 15_virtual_mem_part3
diff -uNr 14_virtual_mem_part2_mmio_remap/tests/00_console_sanity.rs 15_virtual_mem_part3_precomputed_tables/tests/00_console_sanity.rs
--- 14_virtual_mem_part2_mmio_remap/tests/00_console_sanity.rs
+++ 15_virtual_mem_part3_precomputed_tables/tests/00_console_sanity.rs
@@ -8,7 +8,7 @@
#![no_main]
#![no_std]
@@ -11,7 +11,7 @@
/// Console tests should time out on the I/O harness in case of panic.
mod panic_wait_forever;
-use libkernel::{bsp, console, cpu, exception, print};
+use libkernel::{bsp, console, cpu, exception, memory, print};
#[no_mangle]
unsafe fn kernel_init() -> ! {
@@ -16,6 +16,7 @@
@@ -19,6 +19,7 @@
use console::interface::*;
exception::handling_init();
@ -1770,9 +1770,56 @@ diff -uNr 14_virtual_mem_part2_mmio_remap/tests/02_exception_sync_page_fault.rs
info!("Writing beyond mapped area to address 9 GiB...");
let big_addr: u64 = 9 * 1024 * 1024 * 1024;
diff -uNr 14_virtual_mem_part2_mmio_remap/tests/03_exception_irq_sanity.rs 15_virtual_mem_part3_precomputed_tables/tests/03_exception_irq_sanity.rs
--- 14_virtual_mem_part2_mmio_remap/tests/03_exception_irq_sanity.rs
+++ 15_virtual_mem_part3_precomputed_tables/tests/03_exception_irq_sanity.rs
diff -uNr 14_virtual_mem_part2_mmio_remap/tests/03_exception_restore_sanity.rs 15_virtual_mem_part3_precomputed_tables/tests/03_exception_restore_sanity.rs
--- 14_virtual_mem_part2_mmio_remap/tests/03_exception_restore_sanity.rs
+++ 15_virtual_mem_part3_precomputed_tables/tests/03_exception_restore_sanity.rs
@@ -30,40 +30,12 @@
#[no_mangle]
unsafe fn kernel_init() -> ! {
- use libkernel::driver::interface::DriverManager;
-
exception::handling_init();
-
- // This line will be printed as the test header.
- println!("Testing exception restore");
-
- let phys_kernel_tables_base_addr = match memory::mmu::kernel_map_binary() {
- Err(string) => {
- info!("Error mapping kernel binary: {}", string);
- cpu::qemu_exit_failure()
- }
- Ok(addr) => addr,
- };
-
- if let Err(e) = memory::mmu::enable_mmu_and_caching(phys_kernel_tables_base_addr) {
- info!("Enabling MMU failed: {}", e);
- cpu::qemu_exit_failure()
- }
- // Printing will silently fail from here on, because the driver's MMIO is not remapped yet.
-
memory::mmu::post_enable_init();
bsp::console::qemu_bring_up_console();
- // Bring up the drivers needed for printing first.
- for i in bsp::driver::driver_manager()
- .early_print_device_drivers()
- .iter()
- {
- // Any encountered errors cannot be printed yet, obviously, so just safely park the CPU.
- i.init().unwrap_or_else(|_| cpu::qemu_exit_failure());
- }
- bsp::driver::driver_manager().post_early_print_device_driver_init();
- // Printing available again from here on.
+ // This line will be printed as the test header.
+ println!("Testing exception restore");
info!("Making a dummy system call");
diff -uNr 14_virtual_mem_part2_mmio_remap/tests/04_exception_irq_sanity.rs 15_virtual_mem_part3_precomputed_tables/tests/04_exception_irq_sanity.rs
--- 14_virtual_mem_part2_mmio_remap/tests/04_exception_irq_sanity.rs
+++ 15_virtual_mem_part3_precomputed_tables/tests/04_exception_irq_sanity.rs
@@ -10,11 +10,12 @@
#![reexport_test_harness_main = "test_main"]
#![test_runner(libkernel::test_runner)]

@ -57,7 +57,7 @@ struct ExceptionContext {
/// Prints verbose information about the exception and then panics.
fn default_exception_handler(exc: &ExceptionContext) {
panic!(
"\n\nCPU Exception!\n\
"CPU Exception!\n\n\
{}",
exc
);
@ -88,6 +88,17 @@ unsafe extern "C" fn current_el0_serror(_e: &mut ExceptionContext) {
#[no_mangle]
unsafe extern "C" fn current_elx_synchronous(e: &mut ExceptionContext) {
#[cfg(feature = "test_build")]
{
const TEST_SVC_ID: u64 = 0x1337;
if let Some(ESR_EL1::EC::Value::SVC64) = e.esr_el1.exception_class() {
if e.esr_el1.iss() == TEST_SVC_ID {
return;
}
}
}
default_exception_handler(e);
}
@ -184,6 +195,12 @@ impl EsrEL1 {
fn exception_class(&self) -> Option<ESR_EL1::EC::Value> {
self.0.read_as_enum(ESR_EL1::EC)
}
#[cfg(feature = "test_build")]
#[inline(always)]
fn iss(&self) -> u64 {
self.0.read(ESR_EL1::ISS)
}
}
/// Human readable ESR_EL1.

@ -44,28 +44,61 @@ macro_rules! panic_println {
})
}
/// Stop immediately if called a second time.
///
/// # Note
///
/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
///
/// On `AArch64`, which is the only implemented architecture at the time of writing this,
/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
/// instructions. They are therefore safe to use even with MMU + caching deactivated.
///
/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
fn panic_prevent_reenter() {
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(target_arch = "aarch64"))]
compile_error!("Add the target_arch to above's check if the following code is safe to use");
static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
if !PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
PANIC_IN_PROGRESS.store(true, Ordering::Relaxed);
return;
}
_panic_exit()
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
unsafe { exception::asynchronous::local_irq_mask() };
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();
let timestamp = crate::time::time_manager().uptime();
let (location, line, column) = match info.location() {
Some(loc) => (loc.file(), loc.line(), loc.column()),
_ => ("???", 0, 0),
};
if let Some(args) = info.message() {
panic_println!(
"[ {:>3}.{:06}] Kernel panic: {}",
timestamp.as_secs(),
timestamp.subsec_micros(),
args,
);
} else {
panic_println!(
"[ {:>3}.{:06}] Kernel panic!",
timestamp.as_secs(),
timestamp.subsec_micros(),
);
}
panic_println!(
"[ {:>3}.{:06}] Kernel panic!\n\n\
Panic location:\n File '{}', line {}, column {}\n\n\
{}",
timestamp.as_secs(),
timestamp.subsec_micros(),
location,
line,
column,
info.message().unwrap_or(&format_args!("")),
);
_panic_exit()
}

@ -8,6 +8,9 @@
#![no_main]
#![no_std]
/// Console tests should time out on the I/O harness in case of panic.
mod panic_wait_forever;
use libkernel::{bsp, console, cpu, exception, memory, print};
#[no_mangle]

@ -0,0 +1,25 @@
# frozen_string_literal: true
# SPDX-License-Identifier: MIT OR Apache-2.0
#
# Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
require_relative '../../common/tests/console_io_test'
# Verify that exception restore works.
class ExceptionRestoreTest < SubtestBase
def name
'Exception restore'
end
def run(qemu_out, _qemu_in)
expect_or_raise(qemu_out, 'Back from system call!')
end
end
##--------------------------------------------------------------------------------------------------
## Test registration
##--------------------------------------------------------------------------------------------------
def subtest_collection
[ExceptionRestoreTest.new]
end

@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
//! A simple sanity test to see if exception restore code works.
#![feature(format_args_nl)]
#![no_main]
#![no_std]
/// Console tests should time out on the I/O harness in case of panic.
mod panic_wait_forever;
use core::arch::asm;
use libkernel::{bsp, cpu, exception, info, memory, println};
#[inline(never)]
fn nested_system_call() {
#[cfg(target_arch = "aarch64")]
unsafe {
asm!("svc #0x1337", options(nomem, nostack, preserves_flags));
}
#[cfg(not(target_arch = "aarch64"))]
{
info!("Not supported yet");
cpu::wait_forever();
}
}
#[no_mangle]
unsafe fn kernel_init() -> ! {
exception::handling_init();
memory::mmu::post_enable_init();
bsp::console::qemu_bring_up_console();
// This line will be printed as the test header.
println!("Testing exception restore");
info!("Making a dummy system call");
// Calling this inside a function indirectly tests if the link register is restored properly.
nested_system_call();
info!("Back from system call!");
// The QEMU process running this test will be closed by the I/O test harness.
cpu::wait_forever();
}

@ -0,0 +1,9 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
/// Overwrites libkernel's `panic_wait::_panic_exit()` with wait_forever.
#[no_mangle]
fn _panic_exit() -> ! {
libkernel::cpu::wait_forever()
}

@ -54,3 +54,7 @@ harness = false
[[test]]
name = "02_exception_sync_page_fault"
harness = false
[[test]]
name = "03_exception_restore_sanity"
harness = false

@ -57,7 +57,7 @@ struct ExceptionContext {
/// Prints verbose information about the exception and then panics.
fn default_exception_handler(exc: &ExceptionContext) {
panic!(
"\n\nCPU Exception!\n\
"CPU Exception!\n\n\
{}",
exc
);
@ -88,6 +88,17 @@ unsafe extern "C" fn current_el0_serror(_e: &mut ExceptionContext) {
#[no_mangle]
unsafe extern "C" fn current_elx_synchronous(e: &mut ExceptionContext) {
#[cfg(feature = "test_build")]
{
const TEST_SVC_ID: u64 = 0x1337;
if let Some(ESR_EL1::EC::Value::SVC64) = e.esr_el1.exception_class() {
if e.esr_el1.iss() == TEST_SVC_ID {
return;
}
}
}
default_exception_handler(e);
}
@ -184,6 +195,12 @@ impl EsrEL1 {
fn exception_class(&self) -> Option<ESR_EL1::EC::Value> {
self.0.read_as_enum(ESR_EL1::EC)
}
#[cfg(feature = "test_build")]
#[inline(always)]
fn iss(&self) -> u64 {
self.0.read(ESR_EL1::ISS)
}
}
/// Human readable ESR_EL1.

@ -44,28 +44,61 @@ macro_rules! panic_println {
})
}
/// Stop immediately if called a second time.
///
/// # Note
///
/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
///
/// On `AArch64`, which is the only implemented architecture at the time of writing this,
/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
/// instructions. They are therefore safe to use even with MMU + caching deactivated.
///
/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
fn panic_prevent_reenter() {
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(target_arch = "aarch64"))]
compile_error!("Add the target_arch to above's check if the following code is safe to use");
static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
if !PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
PANIC_IN_PROGRESS.store(true, Ordering::Relaxed);
return;
}
_panic_exit()
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
unsafe { exception::asynchronous::local_irq_mask() };
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();
let timestamp = crate::time::time_manager().uptime();
let (location, line, column) = match info.location() {
Some(loc) => (loc.file(), loc.line(), loc.column()),
_ => ("???", 0, 0),
};
if let Some(args) = info.message() {
panic_println!(
"[ {:>3}.{:06}] Kernel panic: {}",
timestamp.as_secs(),
timestamp.subsec_micros(),
args,
);
} else {
panic_println!(
"[ {:>3}.{:06}] Kernel panic!",
timestamp.as_secs(),
timestamp.subsec_micros(),
);
}
panic_println!(
"[ {:>3}.{:06}] Kernel panic!\n\n\
Panic location:\n File '{}', line {}, column {}\n\n\
{}",
timestamp.as_secs(),
timestamp.subsec_micros(),
location,
line,
column,
info.message().unwrap_or(&format_args!("")),
);
_panic_exit()
}

@ -8,6 +8,9 @@
#![no_main]
#![no_std]
/// Console tests should time out on the I/O harness in case of panic.
mod panic_wait_forever;
use libkernel::{bsp, console, cpu, exception, memory, print};
#[no_mangle]

@ -0,0 +1,25 @@
# frozen_string_literal: true
# SPDX-License-Identifier: MIT OR Apache-2.0
#
# Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
require_relative '../../common/tests/console_io_test'
# Verify that exception restore works.
class ExceptionRestoreTest < SubtestBase
def name
'Exception restore'
end
def run(qemu_out, _qemu_in)
expect_or_raise(qemu_out, 'Back from system call!')
end
end
##--------------------------------------------------------------------------------------------------
## Test registration
##--------------------------------------------------------------------------------------------------
def subtest_collection
[ExceptionRestoreTest.new]
end

@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
//! A simple sanity test to see if exception restore code works.
#![feature(format_args_nl)]
#![no_main]
#![no_std]
/// Console tests should time out on the I/O harness in case of panic.
mod panic_wait_forever;
use core::arch::asm;
use libkernel::{bsp, cpu, exception, info, memory, println};
#[inline(never)]
fn nested_system_call() {
#[cfg(target_arch = "aarch64")]
unsafe {
asm!("svc #0x1337", options(nomem, nostack, preserves_flags));
}
#[cfg(not(target_arch = "aarch64"))]
{
info!("Not supported yet");
cpu::wait_forever();
}
}
#[no_mangle]
unsafe fn kernel_init() -> ! {
exception::handling_init();
memory::mmu::post_enable_init();
bsp::console::qemu_bring_up_console();
// This line will be printed as the test header.
println!("Testing exception restore");
info!("Making a dummy system call");
// Calling this inside a function indirectly tests if the link register is restored properly.
nested_system_call();
info!("Back from system call!");
// The QEMU process running this test will be closed by the I/O test harness.
cpu::wait_forever();
}

@ -0,0 +1,9 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2022 Andre Richter <andre.o.richter@gmail.com>
/// Overwrites libkernel's `panic_wait::_panic_exit()` with wait_forever.
#[no_mangle]
fn _panic_exit() -> ! {
libkernel::cpu::wait_forever()
}

Binary file not shown.

Binary file not shown.

@ -27,26 +27,59 @@ macro_rules! panic_println {
})
}
/// Stop immediately if called a second time.
///
/// # Note
///
/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
///
/// On `AArch64`, which is the only implemented architecture at the time of writing this,
/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
/// instructions. They are therefore safe to use even with MMU + caching deactivated.
///
/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
fn panic_prevent_reenter() {
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(not(target_arch = "aarch64"))]
compile_error!("Add the target_arch to above's check if the following code is safe to use");
static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
if !PANIC_IN_PROGRESS.load(Ordering::Relaxed) {
PANIC_IN_PROGRESS.store(true, Ordering::Relaxed);
return;
}
cpu::wait_forever()
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();
let timestamp = crate::time::time_manager().uptime();
let (location, line, column) = match info.location() {
Some(loc) => (loc.file(), loc.line(), loc.column()),
_ => ("???", 0, 0),
};
if let Some(args) = info.message() {
panic_println!(
"[ {:>3}.{:06}] Kernel panic: {}",
timestamp.as_secs(),
timestamp.subsec_micros(),
args,
);
} else {
panic_println!(
"[ {:>3}.{:06}] Kernel panic!",
timestamp.as_secs(),
timestamp.subsec_micros(),
);
}
panic_println!(
"[ {:>3}.{:06}] Kernel panic!\n\n\
Panic location:\n File '{}', line {}, column {}\n\n\
{}",
timestamp.as_secs(),
timestamp.subsec_micros(),
location,
line,
column,
info.message().unwrap_or(&format_args!("")),
);
cpu::wait_forever()
}

@ -8,6 +8,7 @@
class Test
INDENT = ' '
# rubocop:disable Style/RedundantInitialize
def initialize
# Template instance variables.
# @test_name
@ -15,6 +16,7 @@ class Test
# @test_output
# @test_error
end
# rubocop:enable Style/RedundantInitialize
private

@ -8,6 +8,7 @@ DIFF=$(
diff -uNr \
-x README.md \
-x README.CN.md \
-x README.ES.md \
-x kernel \
-x kernel8.img \
-x Cargo.lock \
@ -16,7 +17,7 @@ DIFF=$(
| sed -r "s/[12][90][127][0-9]-[0-9][0-9]-[0-9][0-9] .*//g" \
| sed -r "s/[[:space:]]*$//g" \
| sed -r "s/%/modulo/g" \
| sed -r "s/diff -uNr -x README.md -x README.CN.md -x kernel -x kernel8.img -x Cargo.lock -x target/\ndiff -uNr/g"
| sed -r "s/diff -uNr -x README.md -x README.CN.md -x README.ES.md -x kernel -x kernel8.img -x Cargo.lock -x target/\ndiff -uNr/g"
)
HEADER="## Diff to previous"

Loading…
Cancel
Save