From 5e65a801457dfbd7aece97f5d4723abdfce85274 Mon Sep 17 00:00:00 2001 From: Andre Richter Date: Thu, 11 Mar 2021 23:14:08 +0100 Subject: [PATCH] Rework conditional compilation for lib.rs for tests --- 13_integrated_testing/Cargo.toml | 4 +- 13_integrated_testing/Makefile | 9 +- 13_integrated_testing/README.md | 237 ++++++++++-------- .../src/_arch/aarch64/cpu.rs | 4 + 13_integrated_testing/src/cpu.rs | 5 +- 13_integrated_testing/src/lib.rs | 1 + 13_integrated_testing/src/panic_wait.rs | 33 +-- .../tests/00_console_sanity.rs | 9 +- .../tests/01_timer_sanity.rs | 5 +- .../tests/02_exception_sync_page_fault.rs | 9 +- .../tests/panic_exit_failure/mod.rs | 9 - .../Cargo.toml | 4 +- 14_exceptions_part2_peripheral_IRQs/Makefile | 9 +- 14_exceptions_part2_peripheral_IRQs/README.md | 4 +- .../src/_arch/aarch64/cpu.rs | 4 + .../src/cpu.rs | 5 +- .../src/lib.rs | 1 + .../src/panic_wait.rs | 33 +-- .../tests/00_console_sanity.rs | 9 +- .../tests/01_timer_sanity.rs | 5 +- .../tests/02_exception_sync_page_fault.rs | 9 +- .../tests/03_exception_irq_sanity.rs | 2 - .../tests/panic_exit_failure/mod.rs | 9 - 15_virtual_mem_part2_mmio_remap/Cargo.toml | 4 +- 15_virtual_mem_part2_mmio_remap/Makefile | 9 +- 15_virtual_mem_part2_mmio_remap/README.md | 8 +- .../src/_arch/aarch64/cpu.rs | 4 + 15_virtual_mem_part2_mmio_remap/src/cpu.rs | 5 +- 15_virtual_mem_part2_mmio_remap/src/lib.rs | 1 + .../src/panic_wait.rs | 33 +-- .../tests/00_console_sanity.rs | 9 +- .../tests/01_timer_sanity.rs | 5 +- .../tests/02_exception_sync_page_fault.rs | 9 +- .../tests/03_exception_irq_sanity.rs | 2 - .../tests/panic_exit_failure/mod.rs | 9 - X1_JTAG_boot/jtag_boot_rpi3.img | Bin 8128 -> 8144 bytes 36 files changed, 249 insertions(+), 268 deletions(-) delete mode 100644 13_integrated_testing/tests/panic_exit_failure/mod.rs delete mode 100644 14_exceptions_part2_peripheral_IRQs/tests/panic_exit_failure/mod.rs delete mode 100644 15_virtual_mem_part2_mmio_remap/tests/panic_exit_failure/mod.rs diff --git a/13_integrated_testing/Cargo.toml b/13_integrated_testing/Cargo.toml index 5d57cf35..206dfc87 100644 --- a/13_integrated_testing/Cargo.toml +++ b/13_integrated_testing/Cargo.toml @@ -7,22 +7,22 @@ edition = "2018" [profile.release] lto = true -# The features section is used to select the target board. [features] default = [] bsp_rpi3 = ["register"] bsp_rpi4 = ["register"] +test_build = ["qemu-exit"] ##-------------------------------------------------------------------------------------------------- ## Dependencies ##-------------------------------------------------------------------------------------------------- [dependencies] -qemu-exit = "1.x.x" test-types = { path = "test-types" } # Optional dependencies register = { version = "1.x.x", features = ["no_std_unit_tests"], optional = true } +qemu-exit = { version = "1.x.x", optional = true } # Platform specific dependencies [target.'cfg(target_arch = "aarch64")'.dependencies] diff --git a/13_integrated_testing/Makefile b/13_integrated_testing/Makefile index 4f7561db..74e67da5 100644 --- a/13_integrated_testing/Makefile +++ b/13_integrated_testing/Makefile @@ -57,9 +57,9 @@ QEMU_MISSING_STRING = "This board is not yet supported for QEMU." RUSTFLAGS = -C link-arg=-T$(LINKER_FILE) $(RUSTC_MISC_ARGS) RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) -D warnings -D missing_docs -FEATURES = bsp_$(BSP) +FEATURES = --features bsp_$(BSP) COMPILER_ARGS = --target=$(TARGET) \ - --features $(FEATURES) \ + $(FEATURES) \ --release RUSTC_CMD = cargo rustc $(COMPILER_ARGS) @@ -124,12 +124,15 @@ qemu: $(KERNEL_BIN) define KERNEL_TEST_RUNNER #!/usr/bin/env bash - $(OBJCOPY_CMD) $$1 $$1.img + TEST_ELF=$$(echo $$1 | sed -e 's/.*target/target/g') TEST_BINARY=$$(echo $$1.img | sed -e 's/.*target/target/g') + + $(OBJCOPY_CMD) $$TEST_ELF $$TEST_BINARY $(DOCKER_TEST) ruby tests/runner.rb $(EXEC_QEMU) $(QEMU_TEST_ARGS) -kernel $$TEST_BINARY endef export KERNEL_TEST_RUNNER +test: FEATURES += --features test_build test: @mkdir -p target @echo "$$KERNEL_TEST_RUNNER" > target/kernel_test_runner.sh diff --git a/13_integrated_testing/README.md b/13_integrated_testing/README.md index 383b0a0d..3f62ef6c 100644 --- a/13_integrated_testing/README.md +++ b/13_integrated_testing/README.md @@ -210,13 +210,13 @@ The convetion is that as long as the test function does not `panic!`, the test w The last of the attributes we added is `#![reexport_test_harness_main = "test_main"]`. Remember that our kernel uses the `no_main` attribute, and that we also set it for the test compilation. We did -that because we wrote our own `_start()` function (in `aarch64.rs`), which kicks off the following -call chain during kernel boot: +that because we wrote our own `_start()` function, which kicks off the following call chain during +kernel boot: | | Function | File | | - | - | - | | 1. | `_start()` | `lib.rs` | -| 2. | (some more arch64 code) | `lib.rs` | +| 2. | (some more aarch64 code) | `lib.rs` | | 3. | `runtime_init()` | `lib.rs` | | 4. | `kernel_init()` | `main.rs` | | 5. | `kernel_main()` | `main.rs` | @@ -237,6 +237,7 @@ implementation in `lib.rs`: #[cfg(test)] #[no_mangle] unsafe fn kernel_init() -> ! { + exception::handling_init(); bsp::console::qemu_bring_up_console(); test_main(); @@ -245,12 +246,12 @@ unsafe fn kernel_init() -> ! { } ``` -Note that we first call `bsp::console::qemu_bring_up_console()`. Since we are running all our tests -inside `QEMU`, we need to ensure that whatever peripheral implements the kernel's `console` -interface is initialized, so that we can print from our tests. If you recall [tutorial 03], bringing -up peripherals in `QEMU` might not need the full initialization as is needed on real hardware -(setting clocks, config registers, etc...) due to the abstractions in `QEMU`'s emulation code. So -this is an opportunity to cut down on setup code. +Note the call to `bsp::console::qemu_bring_up_console()`. Since we are running all our tests inside +`QEMU`, we need to ensure that whatever peripheral implements the kernel's `console` interface is +initialized, so that we can print from our tests. If you recall [tutorial 03], bringing up +peripherals in `QEMU` might not need the full initialization as is needed on real hardware (setting +clocks, config registers, etc...) due to the abstractions in `QEMU`'s emulation code. So this is an +opportunity to cut down on setup code. [tutorial 03]: ../03_hacky_hello_world @@ -290,16 +291,20 @@ following exit calls for the kernel: //-------------------------------------------------------------------------------------------------- // Testing //-------------------------------------------------------------------------------------------------- +#[cfg(feature = "test_build")] use qemu_exit::QEMUExit; +#[cfg(feature = "test_build")] const QEMU_EXIT_HANDLE: qemu_exit::AArch64 = qemu_exit::AArch64::new(); /// Make the host QEMU binary execute `exit(1)`. +#[cfg(feature = "test_build")] pub fn qemu_exit_failure() -> ! { QEMU_EXIT_HANDLE.exit_failure() } /// Make the host QEMU binary execute `exit(0)`. +#[cfg(feature = "test_build")] pub fn qemu_exit_success() -> ! { QEMU_EXIT_HANDLE.exit_success() } @@ -308,6 +313,10 @@ pub fn qemu_exit_success() -> ! { [Click here] in case you are interested in the implementation. Note that for the functions to work, the `-semihosting` flag must be added to the `QEMU` invocation. +You might have also noted the `#[cfg(feature = "test_build")]`. In the `Makefile`, we ensure that +this feature is only enabled when `cargo test` runs. This way, it is ensured that testing-specific +code is conditionally compiled only for testing. + [exit status]: https://en.wikipedia.org/wiki/Exit_status [@phil-opp]: https://github.com/phil-opp [learned how to do this]: https://os.phil-opp.com/testing/#exiting-qemu @@ -322,19 +331,29 @@ Unit test failure shall be triggered by the `panic!` macro, either directly or b safely park the panicked CPU core in a busy loop. This can't be used for the unit tests, because `cargo` would wait forever for `QEMU` to exit and stall the whole test run. Again, conditional compilation is used to differentiate between a release and testing version of how a `panic!` -concludes. Here is the new testing version: +concludes: ```rust -/// The point of exit when the library is compiled for testing. -#[cfg(test)] +/// The point of exit for `libkernel`. +/// +/// It is linked weakly, so that the integration tests can overload its standard behavior. +#[linkage = "weak"] #[no_mangle] fn _panic_exit() -> ! { - cpu::qemu_exit_failure() + #[cfg(not(test_build))] + { + cpu::wait_forever() + } + + #[cfg(test_build)] + { + cpu::qemu_exit_failure() + } } ``` -In case none of the unit tests panicked, `lib.rs`'s `kernel_init()` calls -`cpu::qemu_exit_success()` to successfully conclude the unit test run. +In case none of the unit tests panicked, `lib.rs`'s `kernel_init()` calls `cpu::qemu_exit_success()` +to successfully conclude the unit test run. ### Controlling Test Kernel Execution @@ -363,12 +382,15 @@ The file `kernel_test_runner.sh` does not exist by default. We generate it on de define KERNEL_TEST_RUNNER #!/usr/bin/env bash - $(OBJCOPY_CMD) $$1 $$1.img + TEST_ELF=$$(echo $$1 | sed -e 's/.*target/target/g') TEST_BINARY=$$(echo $$1.img | sed -e 's/.*target/target/g') + + $(OBJCOPY_CMD) $$TEST_ELF $$TEST_BINARY $(DOCKER_TEST) ruby tests/runner.rb $(EXEC_QEMU) $(QEMU_TEST_ARGS) -kernel $$TEST_BINARY endef export KERNEL_TEST_RUNNER +test: FEATURES += --features test_build test: @mkdir -p target @echo "$$KERNEL_TEST_RUNNER" > target/kernel_test_runner.sh @@ -525,14 +547,13 @@ your test code into individual chunks. For example, take a look at `tests/01_tim #![reexport_test_harness_main = "test_main"] #![test_runner(libkernel::test_runner)] -mod panic_exit_failure; - use core::time::Duration; -use libkernel::{bsp, cpu, time, time::interface::TimeManager}; +use libkernel::{bsp, cpu, exception, time, time::interface::TimeManager}; use test_macros::kernel_test; #[no_mangle] unsafe fn kernel_init() -> ! { + exception::handling_init(); bsp::console::qemu_bring_up_console(); // Depending on CPU arch, some timer bring-up code could go here. Not needed for the RPi. @@ -579,30 +600,34 @@ harness = false #### Overriding Panic Behavior -It is also important to understand that the `libkernel` made available to the integration tests is -the _release_ version. Therefore, it won't contain any code attributed with `#[cfg(test)]`! - -One of the implications of this is that the `panic handler` provided by `libkernel` will be the -version from the release kernel that spins forever, and not the test version that exits `QEMU`. - -One way to navigate around this is to declare the _release version of the panic exit function_ in -`lib.rs` as a [weak symbol]: +Did you notice the `#[linkage = "weak"]` attribute some chapters earlier at the `_panic_exit()` +function? This marks the function in `lib.rs` as a [weak symbol]. Let's look at it again: ```rust -#[cfg(not(test))] +/// The point of exit for `libkernel`. +/// +/// It is linked weakly, so that the integration tests can overload its standard behavior. #[linkage = "weak"] #[no_mangle] fn _panic_exit() -> ! { - cpu::wait_forever() + #[cfg(not(test_build))] + { + cpu::wait_forever() + } + + #[cfg(test_build)] + { + cpu::qemu_exit_failure() + } } ``` [weak symbol]: https://en.wikipedia.org/wiki/Weak_symbol -Integration tests in `$CRATE/tests/` can now override it according to their needs, because depending -on the kind of test, a `panic!` could mean success or failure. For example, -`tests/02_exception_sync_page_fault.rs` is intentionally causing a page fault, so the wanted outcome -is a `panic!`. Here is the whole test (minus some inline comments): +This enables integration tests in `$CRATE/tests/` to override this function according to their +needs. This is useful because depending on the kind of test, a `panic!` could mean success or +failure. For example, `tests/02_exception_sync_page_fault.rs` is intentionally causing a page fault, +so the wanted outcome is a `panic!`. Here is the whole test (minus some inline comments): ```rust //! Page faults must result in synchronous exceptions. @@ -619,13 +644,12 @@ use libkernel::{bsp, cpu, exception, memory, println}; unsafe fn kernel_init() -> ! { use memory::mmu::interface::MMU; + exception::handling_init(); bsp::console::qemu_bring_up_console(); println!("Testing synchronous exception handling by causing a page fault"); println!("-------------------------------------------------------------------\n"); - exception::handling_init(); - if let Err(string) = memory::mmu::mmu().init() { println!("MMU: {}", string); cpu::qemu_exit_failure() @@ -638,11 +662,11 @@ unsafe fn kernel_init() -> ! { // If execution reaches here, the memory access above did not cause a page fault exception. cpu::qemu_exit_failure() } + ``` The `_panic_exit()` version that makes `QEMU` return `0` (indicating test success) is pulled in by -`mod panic_exit_success;`. The counterpart would be `mod panic_exit_failure;`. We provide both in -the `tests` folder, so each integration test can import the one that it needs. +`mod panic_exit_success;`, and it will take precedence over the `weak` version from `lib.rs`. ### Console Tests @@ -685,16 +709,15 @@ The subtest first sends `"ABC"` over the console to the kernel, and then expects #![no_main] #![no_std] -mod panic_exit_failure; - -use libkernel::{bsp, console, print}; +use libkernel::{bsp, console, exception, print}; #[no_mangle] unsafe fn kernel_init() -> ! { - use bsp::console::{console, qemu_bring_up_console}; + use bsp::console::console; use console::interface::*; - qemu_bring_up_console(); + exception::handling_init(); + bsp::console::qemu_bring_up_console(); // Handshake assert_eq!(console().read_char(), 'A'); @@ -796,16 +819,28 @@ diff -uNr 12_exceptions_part1_groundwork/.cargo/config.toml 13_integrated_testin diff -uNr 12_exceptions_part1_groundwork/Cargo.toml 13_integrated_testing/Cargo.toml --- 12_exceptions_part1_groundwork/Cargo.toml +++ 13_integrated_testing/Cargo.toml -@@ -18,11 +18,38 @@ +@@ -7,22 +7,49 @@ + [profile.release] + lto = true + +-# The features section is used to select the target board. + [features] + default = [] + bsp_rpi3 = ["register"] + bsp_rpi4 = ["register"] ++test_build = ["qemu-exit"] + + ##-------------------------------------------------------------------------------------------------- + ## Dependencies ##-------------------------------------------------------------------------------------------------- [dependencies] -+qemu-exit = "1.x.x" +test-types = { path = "test-types" } # Optional dependencies -register = { version = "1.x.x", optional = true } +register = { version = "1.x.x", features = ["no_std_unit_tests"], optional = true } ++qemu-exit = { version = "1.x.x", optional = true } # Platform specific dependencies [target.'cfg(target_arch = "aarch64")'.dependencies] @@ -856,7 +891,7 @@ diff -uNr 12_exceptions_part1_groundwork/Makefile 13_integrated_testing/Makefile OBJDUMP_BINARY = aarch64-none-elf-objdump NM_BINARY = aarch64-none-elf-nm OPENOCD_ARG = -f /openocd/tcl/interface/ftdi/olimex-arm-usb-tiny-h.cfg -f /openocd/rpi4.cfg -@@ -41,6 +43,17 @@ +@@ -41,18 +43,30 @@ # Export for build.rs export LINKER_FILE @@ -874,7 +909,14 @@ diff -uNr 12_exceptions_part1_groundwork/Makefile 13_integrated_testing/Makefile RUSTFLAGS = -C link-arg=-T$(LINKER_FILE) $(RUSTC_MISC_ARGS) RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) -D warnings -D missing_docs -@@ -53,6 +66,7 @@ +-FEATURES = bsp_$(BSP) ++FEATURES = --features bsp_$(BSP) + COMPILER_ARGS = --target=$(TARGET) \ +- --features $(FEATURES) \ ++ $(FEATURES) \ + --release + + RUSTC_CMD = cargo rustc $(COMPILER_ARGS) DOC_CMD = cargo doc $(COMPILER_ARGS) CLIPPY_CMD = cargo clippy $(COMPILER_ARGS) CHECK_CMD = cargo check $(COMPILER_ARGS) @@ -901,7 +943,7 @@ diff -uNr 12_exceptions_part1_groundwork/Makefile 13_integrated_testing/Makefile all: $(KERNEL_BIN) -@@ -100,11 +115,26 @@ +@@ -100,11 +115,29 @@ $(DOC_CMD) --document-private-items --open ifeq ($(QEMU_MACHINE_TYPE),) @@ -916,12 +958,15 @@ diff -uNr 12_exceptions_part1_groundwork/Makefile 13_integrated_testing/Makefile +define KERNEL_TEST_RUNNER + #!/usr/bin/env bash + -+ $(OBJCOPY_CMD) $$1 $$1.img ++ TEST_ELF=$$(echo $$1 | sed -e 's/.*target/target/g') + TEST_BINARY=$$(echo $$1.img | sed -e 's/.*target/target/g') ++ ++ $(OBJCOPY_CMD) $$TEST_ELF $$TEST_BINARY + $(DOCKER_TEST) ruby tests/runner.rb $(EXEC_QEMU) $(QEMU_TEST_ARGS) -kernel $$TEST_BINARY +endef + +export KERNEL_TEST_RUNNER ++test: FEATURES += --features test_build +test: + @mkdir -p target + @echo "$$KERNEL_TEST_RUNNER" > target/kernel_test_runner.sh @@ -934,7 +979,7 @@ diff -uNr 12_exceptions_part1_groundwork/Makefile 13_integrated_testing/Makefile diff -uNr 12_exceptions_part1_groundwork/src/_arch/aarch64/cpu.rs 13_integrated_testing/src/_arch/aarch64/cpu.rs --- 12_exceptions_part1_groundwork/src/_arch/aarch64/cpu.rs +++ 13_integrated_testing/src/_arch/aarch64/cpu.rs -@@ -26,3 +26,20 @@ +@@ -26,3 +26,24 @@ asm::wfe() } } @@ -942,16 +987,20 @@ diff -uNr 12_exceptions_part1_groundwork/src/_arch/aarch64/cpu.rs 13_integrated_ +//-------------------------------------------------------------------------------------------------- +// Testing +//-------------------------------------------------------------------------------------------------- ++#[cfg(feature = "test_build")] +use qemu_exit::QEMUExit; + ++#[cfg(feature = "test_build")] +const QEMU_EXIT_HANDLE: qemu_exit::AArch64 = qemu_exit::AArch64::new(); + +/// Make the host QEMU binary execute `exit(1)`. ++#[cfg(feature = "test_build")] +pub fn qemu_exit_failure() -> ! { + QEMU_EXIT_HANDLE.exit_failure() +} + +/// Make the host QEMU binary execute `exit(0)`. ++#[cfg(feature = "test_build")] +pub fn qemu_exit_success() -> ! { + QEMU_EXIT_HANDLE.exit_success() +} @@ -1121,12 +1170,13 @@ diff -uNr 12_exceptions_part1_groundwork/src/bsp/raspberrypi/memory/mmu.rs 13_in diff -uNr 12_exceptions_part1_groundwork/src/cpu.rs 13_integrated_testing/src/cpu.rs --- 12_exceptions_part1_groundwork/src/cpu.rs +++ 13_integrated_testing/src/cpu.rs -@@ -15,4 +15,4 @@ - //-------------------------------------------------------------------------------------------------- +@@ -16,3 +16,6 @@ // Architectural Public Reexports //-------------------------------------------------------------------------------------------------- --pub use arch_cpu::{nop, wait_forever}; -+pub use arch_cpu::{nop, qemu_exit_failure, qemu_exit_success, wait_forever}; + pub use arch_cpu::{nop, wait_forever}; ++ ++#[cfg(feature = "test_build")] ++pub use arch_cpu::{qemu_exit_failure, qemu_exit_success}; diff -uNr 12_exceptions_part1_groundwork/src/exception.rs 13_integrated_testing/src/exception.rs --- 12_exceptions_part1_groundwork/src/exception.rs @@ -1157,7 +1207,7 @@ diff -uNr 12_exceptions_part1_groundwork/src/exception.rs 13_integrated_testing/ diff -uNr 12_exceptions_part1_groundwork/src/lib.rs 13_integrated_testing/src/lib.rs --- 12_exceptions_part1_groundwork/src/lib.rs +++ 13_integrated_testing/src/lib.rs -@@ -0,0 +1,170 @@ +@@ -0,0 +1,171 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2018-2021 Andre Richter @@ -1322,6 +1372,7 @@ diff -uNr 12_exceptions_part1_groundwork/src/lib.rs 13_integrated_testing/src/li +#[cfg(test)] +#[no_mangle] +unsafe fn kernel_init() -> ! { ++ exception::handling_init(); + bsp::console::qemu_bring_up_console(); + + test_main(); @@ -1590,43 +1641,32 @@ diff -uNr 12_exceptions_part1_groundwork/src/panic_wait.rs 13_integrated_testing unsafe { bsp::console::panic_console_out().write_fmt(args).unwrap() }; } -+/// The point of exit for the "standard" (non-testing) `libkernel`. -+/// -+/// This code will be used by the release kernel binary and the `integration tests`. It is linked -+/// weakly, so that the integration tests can overload it to exit `QEMU` instead of spinning -+/// forever. ++/// The point of exit for `libkernel`. +/// -+/// This is one possible approach to solve the problem that `cargo` can not know who the consumer of -+/// the library will be: -+/// - The release kernel binary that should safely park the paniced core, -+/// - or an `integration test` that is executed in QEMU, which should just exit QEMU. -+#[cfg(not(test))] ++/// It is linked weakly, so that the integration tests can overload its standard behavior. +#[linkage = "weak"] +#[no_mangle] +fn _panic_exit() -> ! { -+ cpu::wait_forever() ++ #[cfg(not(test_build))] ++ { ++ cpu::wait_forever() ++ } ++ ++ #[cfg(test_build)] ++ { ++ cpu::qemu_exit_failure() ++ } +} + /// Prints with a newline - only use from the panic handler. /// /// Carbon copy from -@@ -35,5 +52,16 @@ +@@ -35,5 +52,5 @@ panic_println!("\nKernel panic!"); } - cpu::wait_forever() + _panic_exit() -+} -+ -+//-------------------------------------------------------------------------------------------------- -+// Testing -+//-------------------------------------------------------------------------------------------------- -+ -+/// The point of exit when the library is compiled for testing. -+#[cfg(test)] -+#[no_mangle] -+fn _panic_exit() -> ! { -+ cpu::qemu_exit_failure() } diff -uNr 12_exceptions_part1_groundwork/src/runtime_init.rs 13_integrated_testing/src/runtime_init.rs @@ -1757,7 +1797,7 @@ diff -uNr 12_exceptions_part1_groundwork/tests/00_console_sanity.rb 13_integrate diff -uNr 12_exceptions_part1_groundwork/tests/00_console_sanity.rs 13_integrated_testing/tests/00_console_sanity.rs --- 12_exceptions_part1_groundwork/tests/00_console_sanity.rs +++ 13_integrated_testing/tests/00_console_sanity.rs -@@ -0,0 +1,36 @@ +@@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2019-2021 Andre Richter @@ -1768,16 +1808,15 @@ diff -uNr 12_exceptions_part1_groundwork/tests/00_console_sanity.rs 13_integrate +#![no_main] +#![no_std] + -+mod panic_exit_failure; -+ -+use libkernel::{bsp, console, print}; ++use libkernel::{bsp, console, exception, print}; + +#[no_mangle] +unsafe fn kernel_init() -> ! { -+ use bsp::console::{console, qemu_bring_up_console}; ++ use bsp::console::console; + use console::interface::*; + -+ qemu_bring_up_console(); ++ exception::handling_init(); ++ bsp::console::qemu_bring_up_console(); + + // Handshake + assert_eq!(console().read_char(), 'A'); @@ -1798,7 +1837,7 @@ diff -uNr 12_exceptions_part1_groundwork/tests/00_console_sanity.rs 13_integrate diff -uNr 12_exceptions_part1_groundwork/tests/01_timer_sanity.rs 13_integrated_testing/tests/01_timer_sanity.rs --- 12_exceptions_part1_groundwork/tests/01_timer_sanity.rs +++ 13_integrated_testing/tests/01_timer_sanity.rs -@@ -0,0 +1,50 @@ +@@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2019-2021 Andre Richter @@ -1811,14 +1850,13 @@ diff -uNr 12_exceptions_part1_groundwork/tests/01_timer_sanity.rs 13_integrated_ +#![reexport_test_harness_main = "test_main"] +#![test_runner(libkernel::test_runner)] + -+mod panic_exit_failure; -+ +use core::time::Duration; -+use libkernel::{bsp, cpu, time, time::interface::TimeManager}; ++use libkernel::{bsp, cpu, exception, time, time::interface::TimeManager}; +use test_macros::kernel_test; + +#[no_mangle] +unsafe fn kernel_init() -> ! { ++ exception::handling_init(); + bsp::console::qemu_bring_up_console(); + + // Depending on CPU arch, some timer bring-up code could go here. Not needed for the RPi. @@ -1853,7 +1891,7 @@ diff -uNr 12_exceptions_part1_groundwork/tests/01_timer_sanity.rs 13_integrated_ diff -uNr 12_exceptions_part1_groundwork/tests/02_exception_sync_page_fault.rs 13_integrated_testing/tests/02_exception_sync_page_fault.rs --- 12_exceptions_part1_groundwork/tests/02_exception_sync_page_fault.rs +++ 13_integrated_testing/tests/02_exception_sync_page_fault.rs -@@ -0,0 +1,44 @@ +@@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2019-2021 Andre Richter @@ -1864,10 +1902,10 @@ diff -uNr 12_exceptions_part1_groundwork/tests/02_exception_sync_page_fault.rs 1 +#![no_main] +#![no_std] + -+/// Overwrites libkernel's `panic_wait::_panic_exit()` with the QEMU-exit version. ++/// Overwrites libkernel's `panic_wait::_panic_exit()` so that it returns a "success" code. +/// -+/// Reaching this code is a success, because it is called from the synchronous exception handler, -+/// which is what this test wants to achieve. ++/// In this test, teaching the panic is a success, because it is called from the synchronous ++/// exception handler, which is what this test wants to achieve. +/// +/// It also means that this integration test can not use any other code that calls panic!() directly +/// or indirectly. @@ -1879,13 +1917,12 @@ diff -uNr 12_exceptions_part1_groundwork/tests/02_exception_sync_page_fault.rs 1 +unsafe fn kernel_init() -> ! { + use memory::mmu::interface::MMU; + ++ exception::handling_init(); + bsp::console::qemu_bring_up_console(); + + println!("Testing synchronous exception handling by causing a page fault"); + println!("-------------------------------------------------------------------\n"); + -+ exception::handling_init(); -+ + if let Err(string) = memory::mmu::mmu().init() { + println!("MMU: {}", string); + cpu::qemu_exit_failure() @@ -1899,20 +1936,6 @@ diff -uNr 12_exceptions_part1_groundwork/tests/02_exception_sync_page_fault.rs 1 + cpu::qemu_exit_failure() +} -diff -uNr 12_exceptions_part1_groundwork/tests/panic_exit_failure/mod.rs 13_integrated_testing/tests/panic_exit_failure/mod.rs ---- 12_exceptions_part1_groundwork/tests/panic_exit_failure/mod.rs -+++ 13_integrated_testing/tests/panic_exit_failure/mod.rs -@@ -0,0 +1,9 @@ -+// SPDX-License-Identifier: MIT OR Apache-2.0 -+// -+// Copyright (c) 2019-2021 Andre Richter -+ -+/// Overwrites libkernel's `panic_wait::_panic_exit()` with the QEMU-exit version. -+#[no_mangle] -+fn _panic_exit() -> ! { -+ libkernel::cpu::qemu_exit_failure() -+} - diff -uNr 12_exceptions_part1_groundwork/tests/panic_exit_success/mod.rs 13_integrated_testing/tests/panic_exit_success/mod.rs --- 12_exceptions_part1_groundwork/tests/panic_exit_success/mod.rs +++ 13_integrated_testing/tests/panic_exit_success/mod.rs diff --git a/13_integrated_testing/src/_arch/aarch64/cpu.rs b/13_integrated_testing/src/_arch/aarch64/cpu.rs index 948bad74..56443865 100644 --- a/13_integrated_testing/src/_arch/aarch64/cpu.rs +++ b/13_integrated_testing/src/_arch/aarch64/cpu.rs @@ -30,16 +30,20 @@ pub fn wait_forever() -> ! { //-------------------------------------------------------------------------------------------------- // Testing //-------------------------------------------------------------------------------------------------- +#[cfg(feature = "test_build")] use qemu_exit::QEMUExit; +#[cfg(feature = "test_build")] const QEMU_EXIT_HANDLE: qemu_exit::AArch64 = qemu_exit::AArch64::new(); /// Make the host QEMU binary execute `exit(1)`. +#[cfg(feature = "test_build")] pub fn qemu_exit_failure() -> ! { QEMU_EXIT_HANDLE.exit_failure() } /// Make the host QEMU binary execute `exit(0)`. +#[cfg(feature = "test_build")] pub fn qemu_exit_success() -> ! { QEMU_EXIT_HANDLE.exit_success() } diff --git a/13_integrated_testing/src/cpu.rs b/13_integrated_testing/src/cpu.rs index 9c8eb6f6..36b6a9d4 100644 --- a/13_integrated_testing/src/cpu.rs +++ b/13_integrated_testing/src/cpu.rs @@ -15,4 +15,7 @@ pub mod smp; //-------------------------------------------------------------------------------------------------- // Architectural Public Reexports //-------------------------------------------------------------------------------------------------- -pub use arch_cpu::{nop, qemu_exit_failure, qemu_exit_success, wait_forever}; +pub use arch_cpu::{nop, wait_forever}; + +#[cfg(feature = "test_build")] +pub use arch_cpu::{qemu_exit_failure, qemu_exit_success}; diff --git a/13_integrated_testing/src/lib.rs b/13_integrated_testing/src/lib.rs index 3e0a9eea..0fa216a9 100644 --- a/13_integrated_testing/src/lib.rs +++ b/13_integrated_testing/src/lib.rs @@ -162,6 +162,7 @@ pub fn test_runner(tests: &[&test_types::UnitTest]) { #[cfg(test)] #[no_mangle] unsafe fn kernel_init() -> ! { + exception::handling_init(); bsp::console::qemu_bring_up_console(); test_main(); diff --git a/13_integrated_testing/src/panic_wait.rs b/13_integrated_testing/src/panic_wait.rs index 61536ba8..20493a91 100644 --- a/13_integrated_testing/src/panic_wait.rs +++ b/13_integrated_testing/src/panic_wait.rs @@ -17,21 +17,21 @@ fn _panic_print(args: fmt::Arguments) { unsafe { bsp::console::panic_console_out().write_fmt(args).unwrap() }; } -/// The point of exit for the "standard" (non-testing) `libkernel`. +/// The point of exit for `libkernel`. /// -/// This code will be used by the release kernel binary and the `integration tests`. It is linked -/// weakly, so that the integration tests can overload it to exit `QEMU` instead of spinning -/// forever. -/// -/// This is one possible approach to solve the problem that `cargo` can not know who the consumer of -/// the library will be: -/// - The release kernel binary that should safely park the paniced core, -/// - or an `integration test` that is executed in QEMU, which should just exit QEMU. -#[cfg(not(test))] +/// It is linked weakly, so that the integration tests can overload its standard behavior. #[linkage = "weak"] #[no_mangle] fn _panic_exit() -> ! { - cpu::wait_forever() + #[cfg(not(test_build))] + { + cpu::wait_forever() + } + + #[cfg(test_build)] + { + cpu::qemu_exit_failure() + } } /// Prints with a newline - only use from the panic handler. @@ -54,14 +54,3 @@ fn panic(info: &PanicInfo) -> ! { _panic_exit() } - -//-------------------------------------------------------------------------------------------------- -// Testing -//-------------------------------------------------------------------------------------------------- - -/// The point of exit when the library is compiled for testing. -#[cfg(test)] -#[no_mangle] -fn _panic_exit() -> ! { - cpu::qemu_exit_failure() -} diff --git a/13_integrated_testing/tests/00_console_sanity.rs b/13_integrated_testing/tests/00_console_sanity.rs index 08b654ae..c3754aa9 100644 --- a/13_integrated_testing/tests/00_console_sanity.rs +++ b/13_integrated_testing/tests/00_console_sanity.rs @@ -8,16 +8,15 @@ #![no_main] #![no_std] -mod panic_exit_failure; - -use libkernel::{bsp, console, print}; +use libkernel::{bsp, console, exception, print}; #[no_mangle] unsafe fn kernel_init() -> ! { - use bsp::console::{console, qemu_bring_up_console}; + use bsp::console::console; use console::interface::*; - qemu_bring_up_console(); + exception::handling_init(); + bsp::console::qemu_bring_up_console(); // Handshake assert_eq!(console().read_char(), 'A'); diff --git a/13_integrated_testing/tests/01_timer_sanity.rs b/13_integrated_testing/tests/01_timer_sanity.rs index 52dca0fc..f39d8384 100644 --- a/13_integrated_testing/tests/01_timer_sanity.rs +++ b/13_integrated_testing/tests/01_timer_sanity.rs @@ -10,14 +10,13 @@ #![reexport_test_harness_main = "test_main"] #![test_runner(libkernel::test_runner)] -mod panic_exit_failure; - use core::time::Duration; -use libkernel::{bsp, cpu, time, time::interface::TimeManager}; +use libkernel::{bsp, cpu, exception, time, time::interface::TimeManager}; use test_macros::kernel_test; #[no_mangle] unsafe fn kernel_init() -> ! { + exception::handling_init(); bsp::console::qemu_bring_up_console(); // Depending on CPU arch, some timer bring-up code could go here. Not needed for the RPi. diff --git a/13_integrated_testing/tests/02_exception_sync_page_fault.rs b/13_integrated_testing/tests/02_exception_sync_page_fault.rs index bf2ba29f..185089a3 100644 --- a/13_integrated_testing/tests/02_exception_sync_page_fault.rs +++ b/13_integrated_testing/tests/02_exception_sync_page_fault.rs @@ -8,10 +8,10 @@ #![no_main] #![no_std] -/// Overwrites libkernel's `panic_wait::_panic_exit()` with the QEMU-exit version. +/// Overwrites libkernel's `panic_wait::_panic_exit()` so that it returns a "success" code. /// -/// Reaching this code is a success, because it is called from the synchronous exception handler, -/// which is what this test wants to achieve. +/// In this test, teaching the panic is a success, because it is called from the synchronous +/// exception handler, which is what this test wants to achieve. /// /// It also means that this integration test can not use any other code that calls panic!() directly /// or indirectly. @@ -23,13 +23,12 @@ use libkernel::{bsp, cpu, exception, memory, println}; unsafe fn kernel_init() -> ! { use memory::mmu::interface::MMU; + exception::handling_init(); bsp::console::qemu_bring_up_console(); println!("Testing synchronous exception handling by causing a page fault"); println!("-------------------------------------------------------------------\n"); - exception::handling_init(); - if let Err(string) = memory::mmu::mmu().init() { println!("MMU: {}", string); cpu::qemu_exit_failure() diff --git a/13_integrated_testing/tests/panic_exit_failure/mod.rs b/13_integrated_testing/tests/panic_exit_failure/mod.rs deleted file mode 100644 index af2ba378..00000000 --- a/13_integrated_testing/tests/panic_exit_failure/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -// -// Copyright (c) 2019-2021 Andre Richter - -/// Overwrites libkernel's `panic_wait::_panic_exit()` with the QEMU-exit version. -#[no_mangle] -fn _panic_exit() -> ! { - libkernel::cpu::qemu_exit_failure() -} diff --git a/14_exceptions_part2_peripheral_IRQs/Cargo.toml b/14_exceptions_part2_peripheral_IRQs/Cargo.toml index 5d57cf35..206dfc87 100644 --- a/14_exceptions_part2_peripheral_IRQs/Cargo.toml +++ b/14_exceptions_part2_peripheral_IRQs/Cargo.toml @@ -7,22 +7,22 @@ edition = "2018" [profile.release] lto = true -# The features section is used to select the target board. [features] default = [] bsp_rpi3 = ["register"] bsp_rpi4 = ["register"] +test_build = ["qemu-exit"] ##-------------------------------------------------------------------------------------------------- ## Dependencies ##-------------------------------------------------------------------------------------------------- [dependencies] -qemu-exit = "1.x.x" test-types = { path = "test-types" } # Optional dependencies register = { version = "1.x.x", features = ["no_std_unit_tests"], optional = true } +qemu-exit = { version = "1.x.x", optional = true } # Platform specific dependencies [target.'cfg(target_arch = "aarch64")'.dependencies] diff --git a/14_exceptions_part2_peripheral_IRQs/Makefile b/14_exceptions_part2_peripheral_IRQs/Makefile index 4f7561db..74e67da5 100644 --- a/14_exceptions_part2_peripheral_IRQs/Makefile +++ b/14_exceptions_part2_peripheral_IRQs/Makefile @@ -57,9 +57,9 @@ QEMU_MISSING_STRING = "This board is not yet supported for QEMU." RUSTFLAGS = -C link-arg=-T$(LINKER_FILE) $(RUSTC_MISC_ARGS) RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) -D warnings -D missing_docs -FEATURES = bsp_$(BSP) +FEATURES = --features bsp_$(BSP) COMPILER_ARGS = --target=$(TARGET) \ - --features $(FEATURES) \ + $(FEATURES) \ --release RUSTC_CMD = cargo rustc $(COMPILER_ARGS) @@ -124,12 +124,15 @@ qemu: $(KERNEL_BIN) define KERNEL_TEST_RUNNER #!/usr/bin/env bash - $(OBJCOPY_CMD) $$1 $$1.img + TEST_ELF=$$(echo $$1 | sed -e 's/.*target/target/g') TEST_BINARY=$$(echo $$1.img | sed -e 's/.*target/target/g') + + $(OBJCOPY_CMD) $$TEST_ELF $$TEST_BINARY $(DOCKER_TEST) ruby tests/runner.rb $(EXEC_QEMU) $(QEMU_TEST_ARGS) -kernel $$TEST_BINARY endef export KERNEL_TEST_RUNNER +test: FEATURES += --features test_build test: @mkdir -p target @echo "$$KERNEL_TEST_RUNNER" > target/kernel_test_runner.sh diff --git a/14_exceptions_part2_peripheral_IRQs/README.md b/14_exceptions_part2_peripheral_IRQs/README.md index c04cc9ae..1c3cb7f0 100644 --- a/14_exceptions_part2_peripheral_IRQs/README.md +++ b/14_exceptions_part2_peripheral_IRQs/README.md @@ -2627,7 +2627,7 @@ diff -uNr 13_integrated_testing/src/synchronization.rs 14_exceptions_part2_perip diff -uNr 13_integrated_testing/tests/03_exception_irq_sanity.rs 14_exceptions_part2_peripheral_IRQs/tests/03_exception_irq_sanity.rs --- 13_integrated_testing/tests/03_exception_irq_sanity.rs +++ 14_exceptions_part2_peripheral_IRQs/tests/03_exception_irq_sanity.rs -@@ -0,0 +1,68 @@ +@@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2020-2021 Andre Richter @@ -2640,8 +2640,6 @@ diff -uNr 13_integrated_testing/tests/03_exception_irq_sanity.rs 14_exceptions_p +#![reexport_test_harness_main = "test_main"] +#![test_runner(libkernel::test_runner)] + -+mod panic_exit_failure; -+ +use libkernel::{bsp, cpu, exception}; +use test_macros::kernel_test; + diff --git a/14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/cpu.rs b/14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/cpu.rs index 948bad74..56443865 100644 --- a/14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/cpu.rs +++ b/14_exceptions_part2_peripheral_IRQs/src/_arch/aarch64/cpu.rs @@ -30,16 +30,20 @@ pub fn wait_forever() -> ! { //-------------------------------------------------------------------------------------------------- // Testing //-------------------------------------------------------------------------------------------------- +#[cfg(feature = "test_build")] use qemu_exit::QEMUExit; +#[cfg(feature = "test_build")] const QEMU_EXIT_HANDLE: qemu_exit::AArch64 = qemu_exit::AArch64::new(); /// Make the host QEMU binary execute `exit(1)`. +#[cfg(feature = "test_build")] pub fn qemu_exit_failure() -> ! { QEMU_EXIT_HANDLE.exit_failure() } /// Make the host QEMU binary execute `exit(0)`. +#[cfg(feature = "test_build")] pub fn qemu_exit_success() -> ! { QEMU_EXIT_HANDLE.exit_success() } diff --git a/14_exceptions_part2_peripheral_IRQs/src/cpu.rs b/14_exceptions_part2_peripheral_IRQs/src/cpu.rs index 9c8eb6f6..36b6a9d4 100644 --- a/14_exceptions_part2_peripheral_IRQs/src/cpu.rs +++ b/14_exceptions_part2_peripheral_IRQs/src/cpu.rs @@ -15,4 +15,7 @@ pub mod smp; //-------------------------------------------------------------------------------------------------- // Architectural Public Reexports //-------------------------------------------------------------------------------------------------- -pub use arch_cpu::{nop, qemu_exit_failure, qemu_exit_success, wait_forever}; +pub use arch_cpu::{nop, wait_forever}; + +#[cfg(feature = "test_build")] +pub use arch_cpu::{qemu_exit_failure, qemu_exit_success}; diff --git a/14_exceptions_part2_peripheral_IRQs/src/lib.rs b/14_exceptions_part2_peripheral_IRQs/src/lib.rs index fd4bdde5..4ea416fd 100644 --- a/14_exceptions_part2_peripheral_IRQs/src/lib.rs +++ b/14_exceptions_part2_peripheral_IRQs/src/lib.rs @@ -165,6 +165,7 @@ pub fn test_runner(tests: &[&test_types::UnitTest]) { #[cfg(test)] #[no_mangle] unsafe fn kernel_init() -> ! { + exception::handling_init(); bsp::console::qemu_bring_up_console(); test_main(); diff --git a/14_exceptions_part2_peripheral_IRQs/src/panic_wait.rs b/14_exceptions_part2_peripheral_IRQs/src/panic_wait.rs index 8c7770b6..e3a9ed8a 100644 --- a/14_exceptions_part2_peripheral_IRQs/src/panic_wait.rs +++ b/14_exceptions_part2_peripheral_IRQs/src/panic_wait.rs @@ -17,21 +17,21 @@ fn _panic_print(args: fmt::Arguments) { unsafe { bsp::console::panic_console_out().write_fmt(args).unwrap() }; } -/// The point of exit for the "standard" (non-testing) `libkernel`. +/// The point of exit for `libkernel`. /// -/// This code will be used by the release kernel binary and the `integration tests`. It is linked -/// weakly, so that the integration tests can overload it to exit `QEMU` instead of spinning -/// forever. -/// -/// This is one possible approach to solve the problem that `cargo` can not know who the consumer of -/// the library will be: -/// - The release kernel binary that should safely park the paniced core, -/// - or an `integration test` that is executed in QEMU, which should just exit QEMU. -#[cfg(not(test))] +/// It is linked weakly, so that the integration tests can overload its standard behavior. #[linkage = "weak"] #[no_mangle] fn _panic_exit() -> ! { - cpu::wait_forever() + #[cfg(not(test_build))] + { + cpu::wait_forever() + } + + #[cfg(test_build)] + { + cpu::qemu_exit_failure() + } } /// Prints with a newline - only use from the panic handler. @@ -56,14 +56,3 @@ fn panic(info: &PanicInfo) -> ! { _panic_exit() } - -//-------------------------------------------------------------------------------------------------- -// Testing -//-------------------------------------------------------------------------------------------------- - -/// The point of exit when the library is compiled for testing. -#[cfg(test)] -#[no_mangle] -fn _panic_exit() -> ! { - cpu::qemu_exit_failure() -} diff --git a/14_exceptions_part2_peripheral_IRQs/tests/00_console_sanity.rs b/14_exceptions_part2_peripheral_IRQs/tests/00_console_sanity.rs index 08b654ae..c3754aa9 100644 --- a/14_exceptions_part2_peripheral_IRQs/tests/00_console_sanity.rs +++ b/14_exceptions_part2_peripheral_IRQs/tests/00_console_sanity.rs @@ -8,16 +8,15 @@ #![no_main] #![no_std] -mod panic_exit_failure; - -use libkernel::{bsp, console, print}; +use libkernel::{bsp, console, exception, print}; #[no_mangle] unsafe fn kernel_init() -> ! { - use bsp::console::{console, qemu_bring_up_console}; + use bsp::console::console; use console::interface::*; - qemu_bring_up_console(); + exception::handling_init(); + bsp::console::qemu_bring_up_console(); // Handshake assert_eq!(console().read_char(), 'A'); diff --git a/14_exceptions_part2_peripheral_IRQs/tests/01_timer_sanity.rs b/14_exceptions_part2_peripheral_IRQs/tests/01_timer_sanity.rs index 52dca0fc..f39d8384 100644 --- a/14_exceptions_part2_peripheral_IRQs/tests/01_timer_sanity.rs +++ b/14_exceptions_part2_peripheral_IRQs/tests/01_timer_sanity.rs @@ -10,14 +10,13 @@ #![reexport_test_harness_main = "test_main"] #![test_runner(libkernel::test_runner)] -mod panic_exit_failure; - use core::time::Duration; -use libkernel::{bsp, cpu, time, time::interface::TimeManager}; +use libkernel::{bsp, cpu, exception, time, time::interface::TimeManager}; use test_macros::kernel_test; #[no_mangle] unsafe fn kernel_init() -> ! { + exception::handling_init(); bsp::console::qemu_bring_up_console(); // Depending on CPU arch, some timer bring-up code could go here. Not needed for the RPi. diff --git a/14_exceptions_part2_peripheral_IRQs/tests/02_exception_sync_page_fault.rs b/14_exceptions_part2_peripheral_IRQs/tests/02_exception_sync_page_fault.rs index bf2ba29f..185089a3 100644 --- a/14_exceptions_part2_peripheral_IRQs/tests/02_exception_sync_page_fault.rs +++ b/14_exceptions_part2_peripheral_IRQs/tests/02_exception_sync_page_fault.rs @@ -8,10 +8,10 @@ #![no_main] #![no_std] -/// Overwrites libkernel's `panic_wait::_panic_exit()` with the QEMU-exit version. +/// Overwrites libkernel's `panic_wait::_panic_exit()` so that it returns a "success" code. /// -/// Reaching this code is a success, because it is called from the synchronous exception handler, -/// which is what this test wants to achieve. +/// In this test, teaching the panic is a success, because it is called from the synchronous +/// exception handler, which is what this test wants to achieve. /// /// It also means that this integration test can not use any other code that calls panic!() directly /// or indirectly. @@ -23,13 +23,12 @@ use libkernel::{bsp, cpu, exception, memory, println}; unsafe fn kernel_init() -> ! { use memory::mmu::interface::MMU; + exception::handling_init(); bsp::console::qemu_bring_up_console(); println!("Testing synchronous exception handling by causing a page fault"); println!("-------------------------------------------------------------------\n"); - exception::handling_init(); - if let Err(string) = memory::mmu::mmu().init() { println!("MMU: {}", string); cpu::qemu_exit_failure() diff --git a/14_exceptions_part2_peripheral_IRQs/tests/03_exception_irq_sanity.rs b/14_exceptions_part2_peripheral_IRQs/tests/03_exception_irq_sanity.rs index 1f3bb770..ac25d01a 100644 --- a/14_exceptions_part2_peripheral_IRQs/tests/03_exception_irq_sanity.rs +++ b/14_exceptions_part2_peripheral_IRQs/tests/03_exception_irq_sanity.rs @@ -10,8 +10,6 @@ #![reexport_test_harness_main = "test_main"] #![test_runner(libkernel::test_runner)] -mod panic_exit_failure; - use libkernel::{bsp, cpu, exception}; use test_macros::kernel_test; diff --git a/14_exceptions_part2_peripheral_IRQs/tests/panic_exit_failure/mod.rs b/14_exceptions_part2_peripheral_IRQs/tests/panic_exit_failure/mod.rs deleted file mode 100644 index af2ba378..00000000 --- a/14_exceptions_part2_peripheral_IRQs/tests/panic_exit_failure/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -// -// Copyright (c) 2019-2021 Andre Richter - -/// Overwrites libkernel's `panic_wait::_panic_exit()` with the QEMU-exit version. -#[no_mangle] -fn _panic_exit() -> ! { - libkernel::cpu::qemu_exit_failure() -} diff --git a/15_virtual_mem_part2_mmio_remap/Cargo.toml b/15_virtual_mem_part2_mmio_remap/Cargo.toml index 5d57cf35..206dfc87 100644 --- a/15_virtual_mem_part2_mmio_remap/Cargo.toml +++ b/15_virtual_mem_part2_mmio_remap/Cargo.toml @@ -7,22 +7,22 @@ edition = "2018" [profile.release] lto = true -# The features section is used to select the target board. [features] default = [] bsp_rpi3 = ["register"] bsp_rpi4 = ["register"] +test_build = ["qemu-exit"] ##-------------------------------------------------------------------------------------------------- ## Dependencies ##-------------------------------------------------------------------------------------------------- [dependencies] -qemu-exit = "1.x.x" test-types = { path = "test-types" } # Optional dependencies register = { version = "1.x.x", features = ["no_std_unit_tests"], optional = true } +qemu-exit = { version = "1.x.x", optional = true } # Platform specific dependencies [target.'cfg(target_arch = "aarch64")'.dependencies] diff --git a/15_virtual_mem_part2_mmio_remap/Makefile b/15_virtual_mem_part2_mmio_remap/Makefile index 4f7561db..74e67da5 100644 --- a/15_virtual_mem_part2_mmio_remap/Makefile +++ b/15_virtual_mem_part2_mmio_remap/Makefile @@ -57,9 +57,9 @@ QEMU_MISSING_STRING = "This board is not yet supported for QEMU." RUSTFLAGS = -C link-arg=-T$(LINKER_FILE) $(RUSTC_MISC_ARGS) RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) -D warnings -D missing_docs -FEATURES = bsp_$(BSP) +FEATURES = --features bsp_$(BSP) COMPILER_ARGS = --target=$(TARGET) \ - --features $(FEATURES) \ + $(FEATURES) \ --release RUSTC_CMD = cargo rustc $(COMPILER_ARGS) @@ -124,12 +124,15 @@ qemu: $(KERNEL_BIN) define KERNEL_TEST_RUNNER #!/usr/bin/env bash - $(OBJCOPY_CMD) $$1 $$1.img + TEST_ELF=$$(echo $$1 | sed -e 's/.*target/target/g') TEST_BINARY=$$(echo $$1.img | sed -e 's/.*target/target/g') + + $(OBJCOPY_CMD) $$TEST_ELF $$TEST_BINARY $(DOCKER_TEST) ruby tests/runner.rb $(EXEC_QEMU) $(QEMU_TEST_ARGS) -kernel $$TEST_BINARY endef export KERNEL_TEST_RUNNER +test: FEATURES += --features test_build test: @mkdir -p target @echo "$$KERNEL_TEST_RUNNER" > target/kernel_test_runner.sh diff --git a/15_virtual_mem_part2_mmio_remap/README.md b/15_virtual_mem_part2_mmio_remap/README.md index 65127bd6..53eb8047 100644 --- a/15_virtual_mem_part2_mmio_remap/README.md +++ b/15_virtual_mem_part2_mmio_remap/README.md @@ -3022,11 +3022,11 @@ diff -uNr 14_exceptions_part2_peripheral_IRQs/tests/02_exception_sync_page_fault - use memory::mmu::interface::MMU; + use libkernel::driver::interface::DriverManager; - bsp::console::qemu_bring_up_console(); - -@@ -30,10 +30,22 @@ - exception::handling_init(); + bsp::console::qemu_bring_up_console(); +@@ -29,10 +29,22 @@ + println!("Testing synchronous exception handling by causing a page fault"); + println!("-------------------------------------------------------------------\n"); - if let Err(string) = memory::mmu::mmu().init() { - println!("MMU: {}", string); diff --git a/15_virtual_mem_part2_mmio_remap/src/_arch/aarch64/cpu.rs b/15_virtual_mem_part2_mmio_remap/src/_arch/aarch64/cpu.rs index 948bad74..56443865 100644 --- a/15_virtual_mem_part2_mmio_remap/src/_arch/aarch64/cpu.rs +++ b/15_virtual_mem_part2_mmio_remap/src/_arch/aarch64/cpu.rs @@ -30,16 +30,20 @@ pub fn wait_forever() -> ! { //-------------------------------------------------------------------------------------------------- // Testing //-------------------------------------------------------------------------------------------------- +#[cfg(feature = "test_build")] use qemu_exit::QEMUExit; +#[cfg(feature = "test_build")] const QEMU_EXIT_HANDLE: qemu_exit::AArch64 = qemu_exit::AArch64::new(); /// Make the host QEMU binary execute `exit(1)`. +#[cfg(feature = "test_build")] pub fn qemu_exit_failure() -> ! { QEMU_EXIT_HANDLE.exit_failure() } /// Make the host QEMU binary execute `exit(0)`. +#[cfg(feature = "test_build")] pub fn qemu_exit_success() -> ! { QEMU_EXIT_HANDLE.exit_success() } diff --git a/15_virtual_mem_part2_mmio_remap/src/cpu.rs b/15_virtual_mem_part2_mmio_remap/src/cpu.rs index 9c8eb6f6..36b6a9d4 100644 --- a/15_virtual_mem_part2_mmio_remap/src/cpu.rs +++ b/15_virtual_mem_part2_mmio_remap/src/cpu.rs @@ -15,4 +15,7 @@ pub mod smp; //-------------------------------------------------------------------------------------------------- // Architectural Public Reexports //-------------------------------------------------------------------------------------------------- -pub use arch_cpu::{nop, qemu_exit_failure, qemu_exit_success, wait_forever}; +pub use arch_cpu::{nop, wait_forever}; + +#[cfg(feature = "test_build")] +pub use arch_cpu::{qemu_exit_failure, qemu_exit_success}; diff --git a/15_virtual_mem_part2_mmio_remap/src/lib.rs b/15_virtual_mem_part2_mmio_remap/src/lib.rs index 608d4b07..dad5e903 100644 --- a/15_virtual_mem_part2_mmio_remap/src/lib.rs +++ b/15_virtual_mem_part2_mmio_remap/src/lib.rs @@ -167,6 +167,7 @@ pub fn test_runner(tests: &[&test_types::UnitTest]) { #[cfg(test)] #[no_mangle] unsafe fn kernel_init() -> ! { + exception::handling_init(); bsp::console::qemu_bring_up_console(); test_main(); diff --git a/15_virtual_mem_part2_mmio_remap/src/panic_wait.rs b/15_virtual_mem_part2_mmio_remap/src/panic_wait.rs index 8c7770b6..e3a9ed8a 100644 --- a/15_virtual_mem_part2_mmio_remap/src/panic_wait.rs +++ b/15_virtual_mem_part2_mmio_remap/src/panic_wait.rs @@ -17,21 +17,21 @@ fn _panic_print(args: fmt::Arguments) { unsafe { bsp::console::panic_console_out().write_fmt(args).unwrap() }; } -/// The point of exit for the "standard" (non-testing) `libkernel`. +/// The point of exit for `libkernel`. /// -/// This code will be used by the release kernel binary and the `integration tests`. It is linked -/// weakly, so that the integration tests can overload it to exit `QEMU` instead of spinning -/// forever. -/// -/// This is one possible approach to solve the problem that `cargo` can not know who the consumer of -/// the library will be: -/// - The release kernel binary that should safely park the paniced core, -/// - or an `integration test` that is executed in QEMU, which should just exit QEMU. -#[cfg(not(test))] +/// It is linked weakly, so that the integration tests can overload its standard behavior. #[linkage = "weak"] #[no_mangle] fn _panic_exit() -> ! { - cpu::wait_forever() + #[cfg(not(test_build))] + { + cpu::wait_forever() + } + + #[cfg(test_build)] + { + cpu::qemu_exit_failure() + } } /// Prints with a newline - only use from the panic handler. @@ -56,14 +56,3 @@ fn panic(info: &PanicInfo) -> ! { _panic_exit() } - -//-------------------------------------------------------------------------------------------------- -// Testing -//-------------------------------------------------------------------------------------------------- - -/// The point of exit when the library is compiled for testing. -#[cfg(test)] -#[no_mangle] -fn _panic_exit() -> ! { - cpu::qemu_exit_failure() -} diff --git a/15_virtual_mem_part2_mmio_remap/tests/00_console_sanity.rs b/15_virtual_mem_part2_mmio_remap/tests/00_console_sanity.rs index 08b654ae..c3754aa9 100644 --- a/15_virtual_mem_part2_mmio_remap/tests/00_console_sanity.rs +++ b/15_virtual_mem_part2_mmio_remap/tests/00_console_sanity.rs @@ -8,16 +8,15 @@ #![no_main] #![no_std] -mod panic_exit_failure; - -use libkernel::{bsp, console, print}; +use libkernel::{bsp, console, exception, print}; #[no_mangle] unsafe fn kernel_init() -> ! { - use bsp::console::{console, qemu_bring_up_console}; + use bsp::console::console; use console::interface::*; - qemu_bring_up_console(); + exception::handling_init(); + bsp::console::qemu_bring_up_console(); // Handshake assert_eq!(console().read_char(), 'A'); diff --git a/15_virtual_mem_part2_mmio_remap/tests/01_timer_sanity.rs b/15_virtual_mem_part2_mmio_remap/tests/01_timer_sanity.rs index 52dca0fc..f39d8384 100644 --- a/15_virtual_mem_part2_mmio_remap/tests/01_timer_sanity.rs +++ b/15_virtual_mem_part2_mmio_remap/tests/01_timer_sanity.rs @@ -10,14 +10,13 @@ #![reexport_test_harness_main = "test_main"] #![test_runner(libkernel::test_runner)] -mod panic_exit_failure; - use core::time::Duration; -use libkernel::{bsp, cpu, time, time::interface::TimeManager}; +use libkernel::{bsp, cpu, exception, time, time::interface::TimeManager}; use test_macros::kernel_test; #[no_mangle] unsafe fn kernel_init() -> ! { + exception::handling_init(); bsp::console::qemu_bring_up_console(); // Depending on CPU arch, some timer bring-up code could go here. Not needed for the RPi. diff --git a/15_virtual_mem_part2_mmio_remap/tests/02_exception_sync_page_fault.rs b/15_virtual_mem_part2_mmio_remap/tests/02_exception_sync_page_fault.rs index 858357a4..45f12a1f 100644 --- a/15_virtual_mem_part2_mmio_remap/tests/02_exception_sync_page_fault.rs +++ b/15_virtual_mem_part2_mmio_remap/tests/02_exception_sync_page_fault.rs @@ -8,10 +8,10 @@ #![no_main] #![no_std] -/// Overwrites libkernel's `panic_wait::_panic_exit()` with the QEMU-exit version. +/// Overwrites libkernel's `panic_wait::_panic_exit()` so that it returns a "success" code. /// -/// Reaching this code is a success, because it is called from the synchronous exception handler, -/// which is what this test wants to achieve. +/// In this test, teaching the panic is a success, because it is called from the synchronous +/// exception handler, which is what this test wants to achieve. /// /// It also means that this integration test can not use any other code that calls panic!() directly /// or indirectly. @@ -23,13 +23,12 @@ use libkernel::{bsp, cpu, exception, memory, println}; unsafe fn kernel_init() -> ! { use libkernel::driver::interface::DriverManager; + exception::handling_init(); bsp::console::qemu_bring_up_console(); println!("Testing synchronous exception handling by causing a page fault"); println!("-------------------------------------------------------------------\n"); - exception::handling_init(); - if let Err(string) = memory::mmu::kernel_map_binary_and_enable_mmu() { println!("Enabling MMU failed: {}", string); cpu::qemu_exit_failure() diff --git a/15_virtual_mem_part2_mmio_remap/tests/03_exception_irq_sanity.rs b/15_virtual_mem_part2_mmio_remap/tests/03_exception_irq_sanity.rs index 1f3bb770..ac25d01a 100644 --- a/15_virtual_mem_part2_mmio_remap/tests/03_exception_irq_sanity.rs +++ b/15_virtual_mem_part2_mmio_remap/tests/03_exception_irq_sanity.rs @@ -10,8 +10,6 @@ #![reexport_test_harness_main = "test_main"] #![test_runner(libkernel::test_runner)] -mod panic_exit_failure; - use libkernel::{bsp, cpu, exception}; use test_macros::kernel_test; diff --git a/15_virtual_mem_part2_mmio_remap/tests/panic_exit_failure/mod.rs b/15_virtual_mem_part2_mmio_remap/tests/panic_exit_failure/mod.rs deleted file mode 100644 index af2ba378..00000000 --- a/15_virtual_mem_part2_mmio_remap/tests/panic_exit_failure/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -// -// Copyright (c) 2019-2021 Andre Richter - -/// Overwrites libkernel's `panic_wait::_panic_exit()` with the QEMU-exit version. -#[no_mangle] -fn _panic_exit() -> ! { - libkernel::cpu::qemu_exit_failure() -} diff --git a/X1_JTAG_boot/jtag_boot_rpi3.img b/X1_JTAG_boot/jtag_boot_rpi3.img index d4148b720f230a78479ea44cdc8791b9c28bc753..de6648804151541fd045c79f2f5dcf824f6094db 100755 GIT binary patch delta 2333 zcmb7FU2GFq7CzTwC!WOlv9S~5II$gemDq-oU_|1i;4wjEV?d;iTXw5fX{gFiejbuH zDv-SeRfMEft}YD)9vbLFJ65u-7Lf2tEeVyXcD3ClYN`68st#3K|Ae;e_5rIK&z?IT zD(F*pq{-yY`ObIF{l0U@3!xjKX=l950H@vH{&+zc_(bV}B_l|M5a`B~8c0145c5JJ zI$?k-;{etWxX1Pre0Q|0-}kIzQ)O);+KX|&6#~mbg)bL?V7>!_q8_ds;CM+IyRPC* z{l3EuD&B-S{`F;n^_YYBcU#Fi9$X@$il@ zz=E7ehjEu%xcTB*DfOYUv15`ziZ@(okP5@N>q-L@X3@ehk%C+WayPW)T}7!+V{x>a za?9h2Kvd7u0#IF9B3fK0$Y*gT2*6wEuYr0Nn&~Z!UC_blVuMsbyqef-gv)$Zyr7%R z3nV#WTj)I12$m_h+p#QECM&VQHf%7C4Lyww`}I=LV+1;iD(y0ZWdIfK(Lp>G0x_RJ zzi$WTc8FB?22k1rPAdo9lXe89{Rc`rgwpb6Dp1-8N?C!@4&GH_ zTuLq@pjwM05rqWXFC5o-y4ZI@7YVbOni+Og|F+fBMZJsbXr|>IIbBXC_wFhmU zTG}gqa1O_0I?S&BNJLM#18jKB{yor2JwhV7J<&>wKPzYW$EdjXnv6b9pVB9yhd$p~5DikQ z*Jcff21xO>>t#r7SClhq3|@(fXCbvsT{DN()xNZ|;JvxC5F*CBh=!s$(`e{4z*ahl zjN50o(l512e4E~zY{=Ri=P*dOQIY`q4U+u_r#h~KJm?{2{GB9QC7@ojR7eZPq??33 z$q0~43)W<&s9esJgPdrN%f>3H02t%6nd5|dQPCWloh~nvxgs8EGfktVnHIhL2UI54 zVvyAq76T5d#C39Z9g(wm#Ikq{t^X~l@g{4u?f$*yTC4c}IW+?v{~q1D?FV`2Zxhjo z>Eif5@35C_F7*8`woqGm3yq*J*#;;?u2eTY{qgoEqG^TSpuBj25HsWc_#7k7}Rprb=LD|eV zBjBya8~CQvU8Ny?x|{Nn^N8b<$p-=DL*tFOSutrZD)oS$!IxK+dUnF@#C!lYYqxv* zbXW_oRwzB~+IoA>9(UjFw)S@SW8o;y=4rNUuP3jtg8g6SlT9V8nuvdT*=K7zNGCHp zI@&*ND8;Z#_3GBJI9BTQ>s%W<;&6rk->fgRDzzGG{?ccTy=3iMu2S(oI!n*1+B(6) zPFKazy3*UJIyU5N5}y=G%MnJMF7a+{X_aOvXNxD$SX$L-{Jq*5&!>8|GSV1o@6`78 zTle}f8*98>$(7yN*Si;>oeeha`=zp*EjHDgYqgeZv`Q|PZ3?kAS1U=dKe;-@8(RDZ IJL_usA9XyxjsO4v delta 2318 zcmcIlZERCj7=BOhy52kX)vg^|H+EYlSwBG7ijEGjcY|bXjtt8ThLDVjfdPrK`3r-# z1AoAP*yEW@XEcl-!d)O@hQ<;iXSUyLNzF3Va8L1Iz$9|ZTxLf<`70t4*jMC42(ClvrcI-zmwP9pA~ z%!45r$aLHUT}c7PJhs@9Rdc{H2zMIef;6_(prjY^%@H!a(EwdiUP$qS2-Ir>i)03i zJRtO~Km&IY#<9SvENMsEHk;iiPY8W`bXyEZ5p5#ctmtrBkUepMjNS8L$Y@<&v)#u7 zCWKj@GQ^~G%spRcX0$F($ZXX?1)c%~B@pD6ir2O+u=`v@^vKOC`@$$AcVs}j>=3^7 zR4UO6+QLQ`fhgZ-Z@!WijX-DIF>&kRCt8w&$>7?1T3S?5ml*R?1hWv0aQ!A+N zVA=`5$x!@9lrB7%UPa?k0}N%Vl(Z(0EHTshxthRJ@%!Ke!~VD+j_rJCi65wN=>M1a zfeLs0H;K8tna+u0TwXvKCrCI53fnB~F--W`w9rHd8!0)&KFRykI^n18fm)iV`%I0S z2xbOo6FX>JgUIn8jO$ILcD$+R3?ZA?Oz|SyL<5}#5Gh2bsTDxCH!r0JoUEl}9UEK0$A2+AlYz*ehn4IN7^qFNVK>|HCY0?k0ZLZsF_O zEOnfB{J_Wk+oTI4)11Qy-9E+Mvv_TzPI~=)K}{}%w1i%teEG!NrAOG$7TG=OrN?JB zUoOJcy(h$~_L=_6Cwe>-uTLbk1)_skkhhMSRA-nyTIMT``RS&O4YbD?4!#<1U@w$C zyE;}!okBR+qPS@0H*J_dH~ea0QA7sw!ol9JTho$JiMH9SKFJ7CK9}EsXp5!|XKnCi zPGp}VXDrkWd+(?#z`Mi#~x#k6R+v$D5_QE6mUrwT+BI6Py~t1M;m}e>&Qq!0_&yPC9riz|NMti(W^Jh#A{^2%@jCba{sj zUzc7xM7b*XaA$9qyN9ixT|+joowEZ)-G5?i;Ea&YaB+r7XAJmhycST)Jk(N9MGP-u zX(d8N_3ByN0vAxH=eY2lFGw!F8HeI+;~i%?_f_=%LN@3xz!C7?4{}&=@}t6?Pa6YvUIyNH@n2n zSJydf=j3MPx_`eu$J)%c3TB=YsCVgS`?qJE&j#jPLvSVAS+lPGUPMvfq*?dPM+N&G k-Fu0?Zem0Zh&(T?YuY-QBD=^AwpDJFV!DGEQ{}qf0UGO}SpWb4