| // Copyright 2025 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| #![no_main] |
| #![no_std] |
| |
| use pw_status::{Error, Result}; |
| use test_interrupts_codegen::{constants, handle}; |
| use test_messages::TestScenario; |
| use userspace::syscall::Signals; |
| use userspace::time::Instant; |
| use userspace::{entry, syscall}; |
| |
| fn read_expected_scenario( |
| expected_scenario: TestScenario, |
| expected_signals: Signals, |
| ) -> Result<()> { |
| // the interrupt listener responds on IPC with the interrupt scenario. |
| let wait_return = syscall::object_wait(handle::IPC, Signals::READABLE, Instant::MAX)?; |
| |
| if !wait_return.pending_signals.contains(Signals::READABLE) || wait_return.user_data != 0 { |
| return Err(Error::Internal); |
| } |
| |
| let mut buffer = [0u8; size_of::<TestScenario>()]; |
| let mut bits = [0u8; size_of::<u32>()]; |
| let len = syscall::channel_read( |
| handle::IPC, |
| 0, |
| &mut [buffer.as_mut_slice(), bits.as_mut_slice()], |
| )?; |
| if len != buffer.len() + bits.len() { |
| return Err(Error::OutOfRange); |
| }; |
| |
| let expected_bits = expected_signals.bits(); |
| let received_scenario = TestScenario::try_from(buffer[0])?; |
| let received_bits = u32::from_le_bytes(bits); |
| if received_scenario != expected_scenario || received_bits != expected_bits { |
| test_logger::step_failed!( |
| "Unexpected test scenario received: {} (expected {}) with bits {:#010x} (expected {:#010x})", |
| received_scenario as u8, |
| expected_scenario as u8, |
| received_bits as u32, |
| expected_bits as u32, |
| ); |
| return Err(Error::Internal); |
| } |
| |
| let response_buffer = [0u8; 0]; |
| syscall::channel_respond(handle::IPC, &response_buffer)?; |
| |
| Ok(()) |
| } |
| |
| fn test_wait_interrupt() -> Result<()> { |
| syscall::debug_trigger_interrupt(constants::TEST_IRQ)?; |
| syscall::debug_trigger_interrupt(constants::TEST_IRQ2)?; |
| // The interrupt signal bits start at bit 16 aka Signals::INTERRUPT_A. |
| // Since we signalled both test_irqs, we'll expect to see both INTERRUPT_A |
| // and INTERRUPT_B signals. |
| read_expected_scenario( |
| TestScenario::WaitInterrupt, |
| Signals::INTERRUPT_A | Signals::INTERRUPT_B, |
| ) |
| } |
| |
| fn test_wait_group_interrupt() -> Result<()> { |
| syscall::debug_trigger_interrupt(constants::TEST_IRQ)?; |
| read_expected_scenario(TestScenario::WaitGroupInterrupt, Signals::INTERRUPT_A) |
| } |
| |
| fn test_interrupts() -> Result<()> { |
| test_wait_interrupt()?; |
| test_wait_group_interrupt() |
| } |
| |
| #[entry] |
| fn entry() -> Result<()> { |
| test_logger::start("User Interrupts Test"); |
| let ret = test_interrupts() |
| .inspect(|_| test_logger::passed("User Interrupts Test")) |
| .inspect_err(|e| { |
| test_logger::failed("User Interrupts Test"); |
| test_logger::step_failed!("status code: {}", *e as u32); |
| }); |
| |
| // Since this is written as a test, shut down with the return status from `main()`. |
| let _ = syscall::debug_shutdown(ret); |
| ret |
| } |
| |
| #[panic_handler] |
| fn panic(_info: &core::panic::PanicInfo) -> ! { |
| loop {} |
| } |