From 7d374adad20471c98ba0b0404a07ffa0dc6b2afb Mon Sep 17 00:00:00 2001 From: Andre Richter Date: Tue, 5 Apr 2022 23:00:47 +0200 Subject: [PATCH] Tests: Exception restore sanity --- 11_exceptions_part1_groundwork/README.md | 83 +++++---- 12_integrated_testing/Cargo.toml | 4 + 12_integrated_testing/README.md | 174 +++++++++++++++++- .../src/_arch/aarch64/exception.rs | 17 ++ .../tests/00_console_sanity.rs | 3 + .../tests/03_exception_restore_sanity.rb | 25 +++ .../tests/03_exception_restore_sanity.rs | 55 ++++++ .../tests/panic_wait_forever/mod.rs | 9 + .../Cargo.toml | 4 + 13_exceptions_part2_peripheral_IRQs/README.md | 8 +- .../src/_arch/aarch64/exception.rs | 17 ++ .../tests/00_console_sanity.rs | 3 + .../tests/03_exception_restore_sanity.rb | 25 +++ .../tests/03_exception_restore_sanity.rs | 55 ++++++ ...q_sanity.rs => 04_exception_irq_sanity.rs} | 0 .../tests/panic_wait_forever/mod.rs | 9 + 14_virtual_mem_part2_mmio_remap/Cargo.toml | 4 + 14_virtual_mem_part2_mmio_remap/README.md | 48 +++++ .../src/_arch/aarch64/exception.rs | 17 ++ .../tests/00_console_sanity.rs | 3 + .../tests/03_exception_restore_sanity.rb | 25 +++ .../tests/03_exception_restore_sanity.rs | 77 ++++++++ ...q_sanity.rs => 04_exception_irq_sanity.rs} | 0 .../tests/panic_wait_forever/mod.rs | 9 + .../Cargo.toml | 4 + .../README.md | 61 +++++- .../src/_arch/aarch64/exception.rs | 17 ++ .../tests/00_console_sanity.rs | 3 + .../tests/03_exception_restore_sanity.rb | 25 +++ .../tests/03_exception_restore_sanity.rs | 49 +++++ ...q_sanity.rs => 04_exception_irq_sanity.rs} | 0 .../tests/panic_wait_forever/mod.rs | 9 + .../Cargo.toml | 4 + .../src/_arch/aarch64/exception.rs | 17 ++ .../tests/00_console_sanity.rs | 3 + .../tests/03_exception_restore_sanity.rb | 25 +++ .../tests/03_exception_restore_sanity.rs | 49 +++++ ...q_sanity.rs => 04_exception_irq_sanity.rs} | 0 .../tests/panic_wait_forever/mod.rs | 9 + 39 files changed, 886 insertions(+), 63 deletions(-) create mode 100644 12_integrated_testing/tests/03_exception_restore_sanity.rb create mode 100644 12_integrated_testing/tests/03_exception_restore_sanity.rs create mode 100644 12_integrated_testing/tests/panic_wait_forever/mod.rs create mode 100644 13_exceptions_part2_peripheral_IRQs/tests/03_exception_restore_sanity.rb create mode 100644 13_exceptions_part2_peripheral_IRQs/tests/03_exception_restore_sanity.rs rename 13_exceptions_part2_peripheral_IRQs/tests/{03_exception_irq_sanity.rs => 04_exception_irq_sanity.rs} (100%) create mode 100644 13_exceptions_part2_peripheral_IRQs/tests/panic_wait_forever/mod.rs create mode 100644 14_virtual_mem_part2_mmio_remap/tests/03_exception_restore_sanity.rb create mode 100644 14_virtual_mem_part2_mmio_remap/tests/03_exception_restore_sanity.rs rename 14_virtual_mem_part2_mmio_remap/tests/{03_exception_irq_sanity.rs => 04_exception_irq_sanity.rs} (100%) create mode 100644 14_virtual_mem_part2_mmio_remap/tests/panic_wait_forever/mod.rs create mode 100644 15_virtual_mem_part3_precomputed_tables/tests/03_exception_restore_sanity.rb create mode 100644 15_virtual_mem_part3_precomputed_tables/tests/03_exception_restore_sanity.rs rename 15_virtual_mem_part3_precomputed_tables/tests/{03_exception_irq_sanity.rs => 04_exception_irq_sanity.rs} (100%) create mode 100644 15_virtual_mem_part3_precomputed_tables/tests/panic_wait_forever/mod.rs create mode 100644 16_virtual_mem_part4_higher_half_kernel/tests/03_exception_restore_sanity.rb create mode 100644 16_virtual_mem_part4_higher_half_kernel/tests/03_exception_restore_sanity.rs rename 16_virtual_mem_part4_higher_half_kernel/tests/{03_exception_irq_sanity.rs => 04_exception_irq_sanity.rs} (100%) create mode 100644 16_virtual_mem_part4_higher_half_kernel/tests/panic_wait_forever/mod.rs diff --git a/11_exceptions_part1_groundwork/README.md b/11_exceptions_part1_groundwork/README.md index 95aa2724..8d18d616 100644 --- a/11_exceptions_part1_groundwork/README.md +++ b/11_exceptions_part1_groundwork/README.md @@ -413,32 +413,31 @@ 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.789853] mingo version 0.11.0 +[ 0.790060] Booting on: Raspberry Pi 3 +[ 0.790515] MMU online. Special regions: +[ 0.790992] 0x00080000 - 0x0008ffff | 64 KiB | C RO PX | Kernel code and RO data +[ 0.792010] 0x3f000000 - 0x4000ffff | 16 MiB | Dev RW PXN | Device MMIO +[ 0.792899] Current privilege level: EL1 +[ 0.793375] Exception handling state: +[ 0.793819] Debug: Masked +[ 0.794209] SError: Masked +[ 0.794599] IRQ: Masked +[ 0.794989] FIQ: Masked +[ 0.795379] Architectural timer resolution: 52 ns +[ 0.795954] Drivers loaded: +[ 0.796289] 1. BCM GPIO +[ 0.796647] 2. BCM PL011 UART +[ 0.797070] Timer test, spinning for 1 second +[ 1.797600] +[ 1.797604] Trying to read from address 8 GiB... +[ 1.798154] ************************************************ +[ 1.798846] Whoa! We recovered from a synchronous exception! +[ 1.799539] ************************************************ +[ 1.800233] +[ 1.800406] Let's try again +[ 1.800742] Trying to read from address 9 GiB... +[ 1.801306] Kernel panic: CPU Exception! ESR_EL1: 0x96000004 @@ -457,25 +456,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 : 0x000000000008555f + x2 : 0x0000000000000027 x3 : 0x000000000008435c + x4 : 0x0000000000000006 x5 : 0x3f27329c00000000 + x6 : 0x0000000000000000 x7 : 0xd3d1b900228f0241 + x8 : 0x0000000240000000 x9 : 0x000000000008555f 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: 0x0000000000000034 x19: 0x0000000000090008 + x20: 0x0000000000085398 x21: 0x000000003b9aca00 + x22: 0x0000000000082e30 x23: 0x0000000000082308 + x24: 0x0000000010624dd3 x25: 0xffffffffc4653600 + x26: 0x00000000000866b8 x27: 0x0000000000085458 + x28: 0x0000000000084fe0 x29: 0x0000000000086770 + lr : 0x0000000000082188 ``` ## Diff to previous diff --git a/12_integrated_testing/Cargo.toml b/12_integrated_testing/Cargo.toml index 80a3a0bc..e768e678 100644 --- a/12_integrated_testing/Cargo.toml +++ b/12_integrated_testing/Cargo.toml @@ -54,3 +54,7 @@ harness = false [[test]] name = "02_exception_sync_page_fault" harness = false + +[[test]] +name = "03_exception_restore_sanity" +harness = false diff --git a/12_integrated_testing/README.md b/12_integrated_testing/README.md index 71ee3342..b5649cd0 100644 --- a/12_integrated_testing/README.md +++ b/12_integrated_testing/README.md @@ -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() -> ! { @@ -860,6 +867,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 +908,7 @@ diff -uNr 11_exceptions_part1_groundwork/Cargo.toml 12_integrated_testing/Cargo. authors = ["Andre Richter "] edition = "2021" -@@ -11,20 +11,46 @@ +@@ -11,20 +11,50 @@ default = [] bsp_rpi3 = ["tock-registers"] bsp_rpi4 = ["tock-registers"] @@ -934,6 +959,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 +1099,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 +1112,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 { + 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 @@ -1814,7 +1861,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 @@ -1825,6 +1872,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 +2003,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 ++ ++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 ++ ++//! 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 +2116,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 ++ ++/// 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 diff --git a/12_integrated_testing/src/_arch/aarch64/exception.rs b/12_integrated_testing/src/_arch/aarch64/exception.rs index eb73a2cc..e15dc7a0 100644 --- a/12_integrated_testing/src/_arch/aarch64/exception.rs +++ b/12_integrated_testing/src/_arch/aarch64/exception.rs @@ -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 { 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 --git a/12_integrated_testing/tests/00_console_sanity.rs b/12_integrated_testing/tests/00_console_sanity.rs index 6aa3de18..dccb6cc2 100644 --- a/12_integrated_testing/tests/00_console_sanity.rs +++ b/12_integrated_testing/tests/00_console_sanity.rs @@ -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] diff --git a/12_integrated_testing/tests/03_exception_restore_sanity.rb b/12_integrated_testing/tests/03_exception_restore_sanity.rb new file mode 100644 index 00000000..c3c725ed --- /dev/null +++ b/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 + +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 --git a/12_integrated_testing/tests/03_exception_restore_sanity.rs b/12_integrated_testing/tests/03_exception_restore_sanity.rs new file mode 100644 index 00000000..f25ed645 --- /dev/null +++ b/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 + +//! 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 --git a/12_integrated_testing/tests/panic_wait_forever/mod.rs b/12_integrated_testing/tests/panic_wait_forever/mod.rs new file mode 100644 index 00000000..7a4effa5 --- /dev/null +++ b/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 + +/// Overwrites libkernel's `panic_wait::_panic_exit()` with wait_forever. +#[no_mangle] +fn _panic_exit() -> ! { + libkernel::cpu::wait_forever() +} diff --git a/13_exceptions_part2_peripheral_IRQs/Cargo.toml b/13_exceptions_part2_peripheral_IRQs/Cargo.toml index b4572cb2..7ef55831 100644 --- a/13_exceptions_part2_peripheral_IRQs/Cargo.toml +++ b/13_exceptions_part2_peripheral_IRQs/Cargo.toml @@ -54,3 +54,7 @@ harness = false [[test]] name = "02_exception_sync_page_fault" harness = false + +[[test]] +name = "03_exception_restore_sanity" +harness = false diff --git a/13_exceptions_part2_peripheral_IRQs/README.md b/13_exceptions_part2_peripheral_IRQs/README.md index 42b984b7..72b3fa5e 100644 --- a/13_exceptions_part2_peripheral_IRQs/README.md +++ b/13_exceptions_part2_peripheral_IRQs/README.md @@ -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] @@ -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 +// diff --git a/13_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/exception.rs b/13_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/exception.rs index 1e9c3f99..18441018 100644 --- a/13_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/exception.rs +++ b/13_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/exception.rs @@ -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 { 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 --git a/13_exceptions_part2_peripheral_IRQs/tests/00_console_sanity.rs b/13_exceptions_part2_peripheral_IRQs/tests/00_console_sanity.rs index 6aa3de18..dccb6cc2 100644 --- a/13_exceptions_part2_peripheral_IRQs/tests/00_console_sanity.rs +++ b/13_exceptions_part2_peripheral_IRQs/tests/00_console_sanity.rs @@ -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] diff --git a/13_exceptions_part2_peripheral_IRQs/tests/03_exception_restore_sanity.rb b/13_exceptions_part2_peripheral_IRQs/tests/03_exception_restore_sanity.rb new file mode 100644 index 00000000..c3c725ed --- /dev/null +++ b/13_exceptions_part2_peripheral_IRQs/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 + +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 --git a/13_exceptions_part2_peripheral_IRQs/tests/03_exception_restore_sanity.rs b/13_exceptions_part2_peripheral_IRQs/tests/03_exception_restore_sanity.rs new file mode 100644 index 00000000..f25ed645 --- /dev/null +++ b/13_exceptions_part2_peripheral_IRQs/tests/03_exception_restore_sanity.rs @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2022 Andre Richter + +//! 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 --git a/13_exceptions_part2_peripheral_IRQs/tests/03_exception_irq_sanity.rs b/13_exceptions_part2_peripheral_IRQs/tests/04_exception_irq_sanity.rs similarity index 100% rename from 13_exceptions_part2_peripheral_IRQs/tests/03_exception_irq_sanity.rs rename to 13_exceptions_part2_peripheral_IRQs/tests/04_exception_irq_sanity.rs diff --git a/13_exceptions_part2_peripheral_IRQs/tests/panic_wait_forever/mod.rs b/13_exceptions_part2_peripheral_IRQs/tests/panic_wait_forever/mod.rs new file mode 100644 index 00000000..7a4effa5 --- /dev/null +++ b/13_exceptions_part2_peripheral_IRQs/tests/panic_wait_forever/mod.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2022 Andre Richter + +/// Overwrites libkernel's `panic_wait::_panic_exit()` with wait_forever. +#[no_mangle] +fn _panic_exit() -> ! { + libkernel::cpu::wait_forever() +} diff --git a/14_virtual_mem_part2_mmio_remap/Cargo.toml b/14_virtual_mem_part2_mmio_remap/Cargo.toml index 760dbce2..3146ad05 100644 --- a/14_virtual_mem_part2_mmio_remap/Cargo.toml +++ b/14_virtual_mem_part2_mmio_remap/Cargo.toml @@ -54,3 +54,7 @@ harness = false [[test]] name = "02_exception_sync_page_fault" harness = false + +[[test]] +name = "03_exception_restore_sanity" +harness = false diff --git a/14_virtual_mem_part2_mmio_remap/README.md b/14_virtual_mem_part2_mmio_remap/README.md index 3f6c78e9..ccec183c 100644 --- a/14_virtual_mem_part2_mmio_remap/README.md +++ b/14_virtual_mem_part2_mmio_remap/README.md @@ -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"); + ``` diff --git a/14_virtual_mem_part2_mmio_remap/src/_arch/aarch64/exception.rs b/14_virtual_mem_part2_mmio_remap/src/_arch/aarch64/exception.rs index 1e9c3f99..18441018 100644 --- a/14_virtual_mem_part2_mmio_remap/src/_arch/aarch64/exception.rs +++ b/14_virtual_mem_part2_mmio_remap/src/_arch/aarch64/exception.rs @@ -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 { 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 --git a/14_virtual_mem_part2_mmio_remap/tests/00_console_sanity.rs b/14_virtual_mem_part2_mmio_remap/tests/00_console_sanity.rs index 6aa3de18..dccb6cc2 100644 --- a/14_virtual_mem_part2_mmio_remap/tests/00_console_sanity.rs +++ b/14_virtual_mem_part2_mmio_remap/tests/00_console_sanity.rs @@ -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] diff --git a/14_virtual_mem_part2_mmio_remap/tests/03_exception_restore_sanity.rb b/14_virtual_mem_part2_mmio_remap/tests/03_exception_restore_sanity.rb new file mode 100644 index 00000000..c3c725ed --- /dev/null +++ b/14_virtual_mem_part2_mmio_remap/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 + +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 --git a/14_virtual_mem_part2_mmio_remap/tests/03_exception_restore_sanity.rs b/14_virtual_mem_part2_mmio_remap/tests/03_exception_restore_sanity.rs new file mode 100644 index 00000000..c6ff7b3d --- /dev/null +++ b/14_virtual_mem_part2_mmio_remap/tests/03_exception_restore_sanity.rs @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2022 Andre Richter + +//! 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(); +} diff --git a/14_virtual_mem_part2_mmio_remap/tests/03_exception_irq_sanity.rs b/14_virtual_mem_part2_mmio_remap/tests/04_exception_irq_sanity.rs similarity index 100% rename from 14_virtual_mem_part2_mmio_remap/tests/03_exception_irq_sanity.rs rename to 14_virtual_mem_part2_mmio_remap/tests/04_exception_irq_sanity.rs diff --git a/14_virtual_mem_part2_mmio_remap/tests/panic_wait_forever/mod.rs b/14_virtual_mem_part2_mmio_remap/tests/panic_wait_forever/mod.rs new file mode 100644 index 00000000..7a4effa5 --- /dev/null +++ b/14_virtual_mem_part2_mmio_remap/tests/panic_wait_forever/mod.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2022 Andre Richter + +/// Overwrites libkernel's `panic_wait::_panic_exit()` with wait_forever. +#[no_mangle] +fn _panic_exit() -> ! { + libkernel::cpu::wait_forever() +} diff --git a/15_virtual_mem_part3_precomputed_tables/Cargo.toml b/15_virtual_mem_part3_precomputed_tables/Cargo.toml index ea20f0db..6a41bd03 100644 --- a/15_virtual_mem_part3_precomputed_tables/Cargo.toml +++ b/15_virtual_mem_part3_precomputed_tables/Cargo.toml @@ -54,3 +54,7 @@ harness = false [[test]] name = "02_exception_sync_page_fault" harness = false + +[[test]] +name = "03_exception_restore_sanity" +harness = false diff --git a/15_virtual_mem_part3_precomputed_tables/README.md b/15_virtual_mem_part3_precomputed_tables/README.md index 4d7cc40e..77d93f59 100644 --- a/15_virtual_mem_part3_precomputed_tables/README.md +++ b/15_virtual_mem_part3_precomputed_tables/README.md @@ -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)] diff --git a/15_virtual_mem_part3_precomputed_tables/src/_arch/aarch64/exception.rs b/15_virtual_mem_part3_precomputed_tables/src/_arch/aarch64/exception.rs index 1e9c3f99..18441018 100644 --- a/15_virtual_mem_part3_precomputed_tables/src/_arch/aarch64/exception.rs +++ b/15_virtual_mem_part3_precomputed_tables/src/_arch/aarch64/exception.rs @@ -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 { 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 --git a/15_virtual_mem_part3_precomputed_tables/tests/00_console_sanity.rs b/15_virtual_mem_part3_precomputed_tables/tests/00_console_sanity.rs index e038cd47..6595aac1 100644 --- a/15_virtual_mem_part3_precomputed_tables/tests/00_console_sanity.rs +++ b/15_virtual_mem_part3_precomputed_tables/tests/00_console_sanity.rs @@ -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] diff --git a/15_virtual_mem_part3_precomputed_tables/tests/03_exception_restore_sanity.rb b/15_virtual_mem_part3_precomputed_tables/tests/03_exception_restore_sanity.rb new file mode 100644 index 00000000..c3c725ed --- /dev/null +++ b/15_virtual_mem_part3_precomputed_tables/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 + +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 --git a/15_virtual_mem_part3_precomputed_tables/tests/03_exception_restore_sanity.rs b/15_virtual_mem_part3_precomputed_tables/tests/03_exception_restore_sanity.rs new file mode 100644 index 00000000..983d488f --- /dev/null +++ b/15_virtual_mem_part3_precomputed_tables/tests/03_exception_restore_sanity.rs @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2022 Andre Richter + +//! 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(); +} diff --git a/15_virtual_mem_part3_precomputed_tables/tests/03_exception_irq_sanity.rs b/15_virtual_mem_part3_precomputed_tables/tests/04_exception_irq_sanity.rs similarity index 100% rename from 15_virtual_mem_part3_precomputed_tables/tests/03_exception_irq_sanity.rs rename to 15_virtual_mem_part3_precomputed_tables/tests/04_exception_irq_sanity.rs diff --git a/15_virtual_mem_part3_precomputed_tables/tests/panic_wait_forever/mod.rs b/15_virtual_mem_part3_precomputed_tables/tests/panic_wait_forever/mod.rs new file mode 100644 index 00000000..7a4effa5 --- /dev/null +++ b/15_virtual_mem_part3_precomputed_tables/tests/panic_wait_forever/mod.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2022 Andre Richter + +/// Overwrites libkernel's `panic_wait::_panic_exit()` with wait_forever. +#[no_mangle] +fn _panic_exit() -> ! { + libkernel::cpu::wait_forever() +} diff --git a/16_virtual_mem_part4_higher_half_kernel/Cargo.toml b/16_virtual_mem_part4_higher_half_kernel/Cargo.toml index 07a1c57e..4964df47 100644 --- a/16_virtual_mem_part4_higher_half_kernel/Cargo.toml +++ b/16_virtual_mem_part4_higher_half_kernel/Cargo.toml @@ -54,3 +54,7 @@ harness = false [[test]] name = "02_exception_sync_page_fault" harness = false + +[[test]] +name = "03_exception_restore_sanity" +harness = false diff --git a/16_virtual_mem_part4_higher_half_kernel/src/_arch/aarch64/exception.rs b/16_virtual_mem_part4_higher_half_kernel/src/_arch/aarch64/exception.rs index 1e9c3f99..18441018 100644 --- a/16_virtual_mem_part4_higher_half_kernel/src/_arch/aarch64/exception.rs +++ b/16_virtual_mem_part4_higher_half_kernel/src/_arch/aarch64/exception.rs @@ -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 { 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 --git a/16_virtual_mem_part4_higher_half_kernel/tests/00_console_sanity.rs b/16_virtual_mem_part4_higher_half_kernel/tests/00_console_sanity.rs index e038cd47..6595aac1 100644 --- a/16_virtual_mem_part4_higher_half_kernel/tests/00_console_sanity.rs +++ b/16_virtual_mem_part4_higher_half_kernel/tests/00_console_sanity.rs @@ -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] diff --git a/16_virtual_mem_part4_higher_half_kernel/tests/03_exception_restore_sanity.rb b/16_virtual_mem_part4_higher_half_kernel/tests/03_exception_restore_sanity.rb new file mode 100644 index 00000000..c3c725ed --- /dev/null +++ b/16_virtual_mem_part4_higher_half_kernel/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 + +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 --git a/16_virtual_mem_part4_higher_half_kernel/tests/03_exception_restore_sanity.rs b/16_virtual_mem_part4_higher_half_kernel/tests/03_exception_restore_sanity.rs new file mode 100644 index 00000000..983d488f --- /dev/null +++ b/16_virtual_mem_part4_higher_half_kernel/tests/03_exception_restore_sanity.rs @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2022 Andre Richter + +//! 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(); +} diff --git a/16_virtual_mem_part4_higher_half_kernel/tests/03_exception_irq_sanity.rs b/16_virtual_mem_part4_higher_half_kernel/tests/04_exception_irq_sanity.rs similarity index 100% rename from 16_virtual_mem_part4_higher_half_kernel/tests/03_exception_irq_sanity.rs rename to 16_virtual_mem_part4_higher_half_kernel/tests/04_exception_irq_sanity.rs diff --git a/16_virtual_mem_part4_higher_half_kernel/tests/panic_wait_forever/mod.rs b/16_virtual_mem_part4_higher_half_kernel/tests/panic_wait_forever/mod.rs new file mode 100644 index 00000000..7a4effa5 --- /dev/null +++ b/16_virtual_mem_part4_higher_half_kernel/tests/panic_wait_forever/mod.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2022 Andre Richter + +/// Overwrites libkernel's `panic_wait::_panic_exit()` with wait_forever. +#[no_mangle] +fn _panic_exit() -> ! { + libkernel::cpu::wait_forever() +}