From 54867c2ff66f81f355c71c88abdbd2613caf3c8f Mon Sep 17 00:00:00 2001 From: xuchang-vivo Date: Tue, 24 Mar 2026 09:25:50 +0800 Subject: [PATCH 01/10] add NUM_IRQS --- kernel/src/boards/gd32e507_eval/Kconfig | 2 +- kernel/src/boards/gd32vw553_eval/Kconfig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/src/boards/gd32e507_eval/Kconfig b/kernel/src/boards/gd32e507_eval/Kconfig index 7c8148b8..3b661b41 100644 --- a/kernel/src/boards/gd32e507_eval/Kconfig +++ b/kernel/src/boards/gd32e507_eval/Kconfig @@ -25,4 +25,4 @@ config SOC_GD32E507Z select CORTEX_M config NUM_IRQS - default 88 \ No newline at end of file + default 88 diff --git a/kernel/src/boards/gd32vw553_eval/Kconfig b/kernel/src/boards/gd32vw553_eval/Kconfig index e03bbcc8..91cab662 100644 --- a/kernel/src/boards/gd32vw553_eval/Kconfig +++ b/kernel/src/boards/gd32vw553_eval/Kconfig @@ -7,4 +7,4 @@ config SOC_GD32VW553 select HAS_PLIC config NUM_IRQS - default 116 \ No newline at end of file + default 116 From e7d392ec063c63f238fcfe01ee7d09f83fe7f1db Mon Sep 17 00:00:00 2001 From: xuchang-vivo Date: Wed, 25 Mar 2026 17:29:05 +0800 Subject: [PATCH 02/10] modify InterruptTable --- kernel/src/boards/qemu_virt64_aarch64/Kconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/src/boards/qemu_virt64_aarch64/Kconfig b/kernel/src/boards/qemu_virt64_aarch64/Kconfig index 76605e6c..36425690 100644 --- a/kernel/src/boards/qemu_virt64_aarch64/Kconfig +++ b/kernel/src/boards/qemu_virt64_aarch64/Kconfig @@ -8,5 +8,10 @@ config SOC_VIRT_AARCH64 default y select AARCH64 +config SOC_VIRT_AARCH64 + bool "Virt Aarch64" + default y + select AARCH64 + config NUM_IRQS default 220 From 7dbc5e1c92f7980548214f34f6ac79069e692a99 Mon Sep 17 00:00:00 2001 From: xuchang-vivo Date: Mon, 30 Mar 2026 10:54:16 +0800 Subject: [PATCH 03/10] minor --- kernel/src/boards/gd32e507_eval/Kconfig | 2 +- kernel/src/boards/gd32vw553_eval/Kconfig | 2 +- kernel/src/boards/qemu_virt64_aarch64/Kconfig | 5 ----- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/kernel/src/boards/gd32e507_eval/Kconfig b/kernel/src/boards/gd32e507_eval/Kconfig index 3b661b41..7c8148b8 100644 --- a/kernel/src/boards/gd32e507_eval/Kconfig +++ b/kernel/src/boards/gd32e507_eval/Kconfig @@ -25,4 +25,4 @@ config SOC_GD32E507Z select CORTEX_M config NUM_IRQS - default 88 + default 88 \ No newline at end of file diff --git a/kernel/src/boards/gd32vw553_eval/Kconfig b/kernel/src/boards/gd32vw553_eval/Kconfig index 91cab662..e03bbcc8 100644 --- a/kernel/src/boards/gd32vw553_eval/Kconfig +++ b/kernel/src/boards/gd32vw553_eval/Kconfig @@ -7,4 +7,4 @@ config SOC_GD32VW553 select HAS_PLIC config NUM_IRQS - default 116 + default 116 \ No newline at end of file diff --git a/kernel/src/boards/qemu_virt64_aarch64/Kconfig b/kernel/src/boards/qemu_virt64_aarch64/Kconfig index 36425690..76605e6c 100644 --- a/kernel/src/boards/qemu_virt64_aarch64/Kconfig +++ b/kernel/src/boards/qemu_virt64_aarch64/Kconfig @@ -8,10 +8,5 @@ config SOC_VIRT_AARCH64 default y select AARCH64 -config SOC_VIRT_AARCH64 - bool "Virt Aarch64" - default y - select AARCH64 - config NUM_IRQS default 220 From 357bf6beabdb6a179e4357411b30180e3f963cc3 Mon Sep 17 00:00:00 2001 From: xuchang-vivo Date: Mon, 30 Mar 2026 19:40:46 +0800 Subject: [PATCH 04/10] minor --- kernel/src/arch/arm/irq.rs | 102 ++++++---- kernel/src/boards/gd32e507_eval/handler.rs | 37 ---- kernel/src/boards/gd32e507_eval/mod.rs | 7 +- kernel/src/boards/qemu_mps2_an385/handlers.rs | 183 ------------------ kernel/src/boards/qemu_mps2_an385/mod.rs | 11 +- kernel/src/boards/qemu_mps3_an547/handlers.rs | 178 ----------------- kernel/src/boards/qemu_mps3_an547/mod.rs | 12 +- .../boards/raspberry_pico2_cortexm/handler.rs | 37 ---- .../src/boards/raspberry_pico2_cortexm/mod.rs | 5 +- 9 files changed, 82 insertions(+), 490 deletions(-) delete mode 100644 kernel/src/boards/gd32e507_eval/handler.rs delete mode 100644 kernel/src/boards/qemu_mps2_an385/handlers.rs delete mode 100644 kernel/src/boards/qemu_mps3_an547/handlers.rs delete mode 100644 kernel/src/boards/raspberry_pico2_cortexm/handler.rs diff --git a/kernel/src/arch/arm/irq.rs b/kernel/src/arch/arm/irq.rs index d89df32a..fc58b22f 100644 --- a/kernel/src/arch/arm/irq.rs +++ b/kernel/src/arch/arm/irq.rs @@ -114,50 +114,72 @@ pub union Vector { pub reserved: usize, } +pub const INTERRUPT_TABLE_LEN: usize = blueos_kconfig::CONFIG_NUM_IRQS as usize; + /// Interrupt vector table configuration for ARM Cortex-M processors. /// -/// Users must define their own `__INTERRUPTS` based on their specific device requirements. -/// The interrupt vector table should be placed in the `.vector_table.interrupts` section. -/// -/// # Example -/// -/// ```rust -/// -/// #[used] -/// #[link_section = ".interrupt.vectors"] -/// #[no_mangle] -/// pub static __INTERRUPT_HANDLERS__: InterruptTable = { -/// let mut tbl = [Vector { reserved: 0 }; INTERRUPT_TABLE_LEN]; -/// tbl[0] = Vector { -/// handler: uart0rx_handler, -/// }; -/// tbl[1] = Vector { -/// handler: uart0tx_handler, -/// }; -/// tbl[2] = Vector { -/// handler: uart1rx_handler, -/// }; -/// tbl -/// }; -/// -/// // Declare external interrupt handlers -/// extern "C" { -/// fn uart0rx_handler(); -/// fn uart0tx_handler(); -/// fn uart1rx_handler(); -/// } -/// ``` -/// -/// # Architecture-specific Details -/// -/// Maximum number of device-specific interrupts for different ARM Cortex-M architectures: -/// - ARMv6-M: 32 interrupts -/// - ARMv7-M/ARMv7E-M: 240 interrupts -/// - ARMv8-M: 496 interrupts +/// There are two types of ISRs, one is the RAW type, which is no different +/// from general interrupt service handling. The other is the more flexible +/// SWI type. SWI type interrupt service handling implements the trait object +/// IsrDesc. For some complex processing scenarios, consider using IsrDesc +/// to encapsulate relevant data, such as Shared ISR, Async ISR, Nested ISR, +/// and so on. /// /// # Safety /// /// The interrupt vector table must be properly aligned and contain valid function pointers /// for all used interrupt vectors. Incorrect configuration may lead to undefined behavior. -pub const INTERRUPT_TABLE_LEN: usize = blueos_kconfig::CONFIG_NUM_IRQS as usize; -pub type InterruptTable = [Vector; blueos_kconfig::CONFIG_NUM_IRQS as usize]; +#[used] +#[link_section = ".interrupt.handlers"] +static mut __INTERRUPT_HANDLERS__: [Vector; blueos_kconfig::CONFIG_NUM_IRQS as usize] = [Vector { + handler: _generic_isr_handler, +}; + INTERRUPT_TABLE_LEN]; + +extern "C" fn _generic_isr_handler() { + use cortex_m::peripheral::NVIC; + // Get the current ISR index from the IPSR register + let ipsr: u32; + unsafe { + core::arch::asm!("mrs {}, ipsr", out(reg) ipsr, options(nomem, nostack, preserves_flags)); + } + let isr_index = (ipsr & 0x1FF) + .checked_sub(16) + .expect("Invalid ISR index, IPSR value: {ipsr:#X}"); + + // ISR_DESC[isr_index as usize].as_ref()s + + #[cfg(round_robin)] + { + // If the scheduler is preemptive, trigger PendSV to perform + // a context switch after handling the current interrupt. + cortex_m::peripheral::SCB::set_pendsv(); + } +} + +pub trait IsrDesc: Sync + 'static { + fn service_isr(&self); +} + +static mut ISR_DESC: [Option<&dyn IsrDesc>; INTERRUPT_TABLE_LEN] = [None; INTERRUPT_TABLE_LEN]; + +/// Safety: ISR_DESC only be read in the interrupt handler, and only be written in the register_swi_isr, +/// which is called in the init process while irq is disabled, so it's safe to use unsafe to write it. +/// Be careful to call register_swi_isr in the init process while irq is disabled, otherwise it may cause race condition. +pub fn register_swi_isr(irq: IrqNumber, desc: &'static dyn IsrDesc) { + unsafe { + ISR_DESC[irq.0 as usize] = Some(desc); + } +} + +/// This function is used to register the raw interrupt handler for the given irq number. +/// The handler should be defined in the assembly file, and the caller should ensure that +/// the handler is properly defined and linked. This function is unsafe because it allows +/// registering a raw interrupt handler, which may lead to undefined behavior if not used +/// correctly. +/// Safety: race condition may occur if this function is called while the corresponding +/// interrupt is enabled and can be triggered, so the caller should ensure that the interrupt +/// is disabled before calling this function, and enable it after the handler is registered. +pub unsafe fn register_raw_isr(irq: IrqNumber, handler: unsafe extern "C" fn()) { + __INTERRUPT_HANDLERS__[irq.0 as usize] = Vector { handler }; +} diff --git a/kernel/src/boards/gd32e507_eval/handler.rs b/kernel/src/boards/gd32e507_eval/handler.rs deleted file mode 100644 index 547b6023..00000000 --- a/kernel/src/boards/gd32e507_eval/handler.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2025 vivo Mobile Communication Co., Ltd. -// -// 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 -// -// http://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. - -use crate::{ - arch::{ - self, - irq::{InterruptTable, Vector, INTERRUPT_TABLE_LEN}, - }, - boot::_start, - time, -}; - -extern "C" { - fn uart0_handler(); -} - -#[doc(hidden)] -#[link_section = ".interrupt.handlers"] -#[no_mangle] -static __INTERRUPT_HANDLERS__: InterruptTable = { - let mut tbl = [Vector { reserved: 0 }; INTERRUPT_TABLE_LEN]; - tbl[37] = Vector { - handler: uart0_handler, - }; // USART0 - tbl -}; diff --git a/kernel/src/boards/gd32e507_eval/mod.rs b/kernel/src/boards/gd32e507_eval/mod.rs index 4e1ffd30..43e1a142 100644 --- a/kernel/src/boards/gd32e507_eval/mod.rs +++ b/kernel/src/boards/gd32e507_eval/mod.rs @@ -13,7 +13,6 @@ // limitations under the License. mod config; -mod handler; use crate::{ arch, arch::irq::IrqNumber, boot, boot::INIT_BSS_DONE, devices::clock::systick, irq::IrqTrace, @@ -84,6 +83,9 @@ pub(crate) fn init() { unsafe { boot::init_heap() }; arch::irq::init(); + unsafe { + arch::irq::register_raw_isr(config::USBFS_IRQn, uart0_handler); + } arch::irq::enable_irq_with_priority(config::USBFS_IRQn, arch::irq::Priority::Normal); ClockImpl::init(); } @@ -123,8 +125,7 @@ crate::define_pin_states! { ) } -#[no_mangle] -pub unsafe extern "C" fn uart0_handler() { +unsafe extern "C" fn uart0_handler() { let _trace = IrqTrace::new(config::USBFS_IRQn); use blueos_hal::HasInterruptReg; let uart = get_device!(console_uart); diff --git a/kernel/src/boards/qemu_mps2_an385/handlers.rs b/kernel/src/boards/qemu_mps2_an385/handlers.rs deleted file mode 100644 index 06ba097e..00000000 --- a/kernel/src/boards/qemu_mps2_an385/handlers.rs +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright (c) 2025 vivo Mobile Communication Co., Ltd. -// -// 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 -// -// http://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. - -use crate::{ - arch, - arch::irq::{InterruptTable, Vector, INTERRUPT_TABLE_LEN}, - boot::_start, - time, -}; - -extern "C" { - pub fn uart0rx_handler(); - pub fn uart0tx_handler(); -} - -#[no_mangle] -#[linkage = "weak"] -pub unsafe extern "C" fn uart1rx_handler() { - unreachable!("Should not reach here"); -} - -#[no_mangle] -#[linkage = "weak"] -pub unsafe extern "C" fn uart1tx_handler() { - unreachable!("Should not reach here"); -} - -unsafe extern "C" fn do_nothing() {} - -unsafe extern "C" fn busy() { - unreachable!("Should not be busy!"); -} - -macro_rules! default_irq_handler { - ($handler_name:ident) => { - unsafe extern "C" fn $handler_name() { - $crate::debug!("{}", stringify!($handler_name)); - } - }; -} -default_irq_handler!(uart2rx_handler); -default_irq_handler!(uart2tx_handler); -default_irq_handler!(gpio0all_handler); -default_irq_handler!(gpio1all_handler); -default_irq_handler!(timer0_handler); -default_irq_handler!(timer1_handler); -default_irq_handler!(dualtimer_handler); -default_irq_handler!(spi_0_1_handler); -default_irq_handler!(uart_0_1_2_ovf_handler); -default_irq_handler!(ethernet_handler); -default_irq_handler!(i2s_handler); -default_irq_handler!(touchscreen_handler); -default_irq_handler!(gpio2_handler); -default_irq_handler!(gpio3_handler); -default_irq_handler!(uart3rx_handler); -default_irq_handler!(uart3tx_handler); -default_irq_handler!(uart4rx_handler); -default_irq_handler!(uart4tx_handler); -default_irq_handler!(spi_2_handler); -default_irq_handler!(spi_3_4_handler); -default_irq_handler!(gpio0_0_handler); -default_irq_handler!(gpio0_1_handler); -default_irq_handler!(gpio0_2_handler); -default_irq_handler!(gpio0_3_handler); -default_irq_handler!(gpio0_4_handler); -default_irq_handler!(gpio0_5_handler); -default_irq_handler!(gpio0_6_handler); -default_irq_handler!(gpio0_7_handler); - -#[used] -#[link_section = ".interrupt.handlers"] -#[no_mangle] -pub static __INTERRUPT_HANDLERS__: InterruptTable = { - let mut tbl = [Vector { reserved: 0 }; INTERRUPT_TABLE_LEN]; - tbl[0] = Vector { - handler: uart0rx_handler, - }; - tbl[1] = Vector { - handler: uart0tx_handler, - }; - tbl[2] = Vector { - handler: uart1rx_handler, - }; - tbl[3] = Vector { - handler: uart1tx_handler, - }; - tbl[4] = Vector { - handler: uart2rx_handler, - }; - tbl[5] = Vector { - handler: uart2tx_handler, - }; - tbl[6] = Vector { - handler: gpio0all_handler, - }; - tbl[7] = Vector { - handler: gpio1all_handler, - }; - tbl[8] = Vector { - handler: timer0_handler, - }; - tbl[9] = Vector { - handler: timer1_handler, - }; - tbl[10] = Vector { - handler: dualtimer_handler, - }; - tbl[11] = Vector { - handler: spi_0_1_handler, - }; - tbl[12] = Vector { - handler: uart_0_1_2_ovf_handler, - }; - tbl[13] = Vector { - handler: ethernet_handler, - }; - tbl[14] = Vector { - handler: i2s_handler, - }; - tbl[15] = Vector { - handler: touchscreen_handler, - }; - tbl[16] = Vector { - handler: gpio2_handler, - }; - tbl[17] = Vector { - handler: gpio3_handler, - }; - tbl[18] = Vector { - handler: uart3rx_handler, - }; - tbl[19] = Vector { - handler: uart3tx_handler, - }; - tbl[20] = Vector { - handler: uart4rx_handler, - }; - tbl[21] = Vector { - handler: uart4tx_handler, - }; - tbl[22] = Vector { - handler: spi_2_handler, - }; - tbl[23] = Vector { - handler: spi_3_4_handler, - }; - tbl[24] = Vector { - handler: gpio0_0_handler, - }; - tbl[25] = Vector { - handler: gpio0_1_handler, - }; - tbl[26] = Vector { - handler: gpio0_2_handler, - }; - tbl[27] = Vector { - handler: gpio0_3_handler, - }; - tbl[28] = Vector { - handler: gpio0_4_handler, - }; - tbl[29] = Vector { - handler: gpio0_5_handler, - }; - tbl[30] = Vector { - handler: gpio0_6_handler, - }; - tbl[31] = Vector { - handler: gpio0_7_handler, - }; - tbl -}; diff --git a/kernel/src/boards/qemu_mps2_an385/mod.rs b/kernel/src/boards/qemu_mps2_an385/mod.rs index 5b9b30ce..68decfb9 100644 --- a/kernel/src/boards/qemu_mps2_an385/mod.rs +++ b/kernel/src/boards/qemu_mps2_an385/mod.rs @@ -13,7 +13,6 @@ // limitations under the License. pub mod config; -mod handlers; use crate::{ arch, boards::config::{ @@ -85,6 +84,10 @@ pub(crate) fn init() { unsafe { boot::init_heap() }; arch::irq::init(); ClockImpl::init(); + unsafe { + arch::irq::register_raw_isr(UART0RX_IRQn, uart0rx_handler); + arch::irq::register_raw_isr(UART0TX_IRQn, uart0tx_handler); + } arch::irq::enable_irq_with_priority(UART0RX_IRQn, arch::irq::Priority::Normal); arch::irq::enable_irq_with_priority(UART0TX_IRQn, arch::irq::Priority::Normal); } @@ -101,8 +104,7 @@ crate::define_peripheral! { )}), } -#[no_mangle] -pub unsafe extern "C" fn uart0rx_handler() { +unsafe extern "C" fn uart0rx_handler() { let _trace = IrqTrace::new(UART0RX_IRQn); let uart = get_device!(console_uart); if let Some(handler) = unsafe { @@ -115,8 +117,7 @@ pub unsafe extern "C" fn uart0rx_handler() { uart.clear_interrupt(blueos_driver::uart::InterruptType::Rx); } -#[no_mangle] -pub unsafe extern "C" fn uart0tx_handler() { +unsafe extern "C" fn uart0tx_handler() { let _trace = IrqTrace::new(UART0TX_IRQn); let uart = get_device!(console_uart); if let Some(handler) = unsafe { diff --git a/kernel/src/boards/qemu_mps3_an547/handlers.rs b/kernel/src/boards/qemu_mps3_an547/handlers.rs deleted file mode 100644 index 984b5367..00000000 --- a/kernel/src/boards/qemu_mps3_an547/handlers.rs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) 2025 vivo Mobile Communication Co., Ltd. -// -// 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 -// -// http://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. - -use crate::{ - arch::{ - self, - irq::{InterruptTable, Vector, INTERRUPT_TABLE_LEN}, - }, - boot::_start, - time, -}; - -unsafe extern "C" fn do_nothing() {} - -unsafe extern "C" fn busy() { - unreachable!("Should not be busy!") -} - -macro_rules! default_irq_handler { - ($handler_name:ident) => { - unsafe extern "C" fn $handler_name() { - $crate::debug!("{}", stringify!($handler_name)); - } - }; -} - -extern "C" { - fn uart0rx_handler(); - fn uart0tx_handler(); -} -default_irq_handler!(nonsec_watchdog_reset_req_handler); -default_irq_handler!(nonsec_watchdog_handler); -default_irq_handler!(slowclk_timer_handler); -default_irq_handler!(tfm_timer0_irq_handler); -default_irq_handler!(timer1_handler); -default_irq_handler!(timer2_handler); -default_irq_handler!(mpc_handler); -default_irq_handler!(ppc_handler); -default_irq_handler!(msc_handler); -default_irq_handler!(bridge_error_handler); -default_irq_handler!(mgmt_ppu_handler); -default_irq_handler!(sys_ppu_handler); -default_irq_handler!(cpu0_ppu_handler); -default_irq_handler!(debug_ppu_handler); -default_irq_handler!(timer3_aon_handler); -default_irq_handler!(cpu0_cti_0_handler); -default_irq_handler!(cpu0_cti_1_handler); -default_irq_handler!(system_timestamp_counter_handler); -default_irq_handler!(uartrx1_handler); -default_irq_handler!(uarttx1_handler); -default_irq_handler!(uartrx2_handler); -default_irq_handler!(uarttx2_handler); -default_irq_handler!(uartrx3_handler); -default_irq_handler!(uarttx3_handler); -default_irq_handler!(uartrx4_handler); -default_irq_handler!(uarttx4_handler); - -#[doc(hidden)] -#[link_section = ".interrupt.handlers"] -#[no_mangle] -static __INTERRUPT_HANDLERS__: InterruptTable = { - let mut tbl = [Vector { reserved: 0 }; INTERRUPT_TABLE_LEN]; - - tbl[0] = Vector { - handler: nonsec_watchdog_reset_req_handler, - }; - tbl[1] = Vector { - handler: nonsec_watchdog_handler, - }; - tbl[2] = Vector { - handler: slowclk_timer_handler, - }; - tbl[3] = Vector { - handler: tfm_timer0_irq_handler, - }; - tbl[4] = Vector { - handler: timer1_handler, - }; - tbl[5] = Vector { - handler: timer2_handler, - }; - tbl[6] = Vector { reserved: 0 }; - tbl[7] = Vector { reserved: 0 }; - tbl[8] = Vector { reserved: 0 }; - tbl[9] = Vector { - handler: mpc_handler, - }; - tbl[10] = Vector { - handler: ppc_handler, - }; - tbl[11] = Vector { - handler: msc_handler, - }; - tbl[12] = Vector { - handler: bridge_error_handler, - }; - tbl[13] = Vector { reserved: 0 }; - tbl[14] = Vector { - handler: mgmt_ppu_handler, - }; - tbl[15] = Vector { - handler: sys_ppu_handler, - }; - tbl[16] = Vector { - handler: cpu0_ppu_handler, - }; - tbl[17] = Vector { reserved: 0 }; - tbl[18] = Vector { reserved: 0 }; - tbl[19] = Vector { reserved: 0 }; - tbl[20] = Vector { reserved: 0 }; - tbl[21] = Vector { reserved: 0 }; - tbl[22] = Vector { reserved: 0 }; - tbl[23] = Vector { reserved: 0 }; - tbl[24] = Vector { reserved: 0 }; - tbl[25] = Vector { - handler: debug_ppu_handler, - }; - tbl[27] = Vector { - handler: timer3_aon_handler, - }; - tbl[28] = Vector { - handler: cpu0_cti_0_handler, - }; - tbl[29] = Vector { - handler: cpu0_cti_1_handler, - }; - tbl[30] = Vector { reserved: 0 }; - tbl[31] = Vector { reserved: 0 }; - tbl[32] = Vector { - handler: system_timestamp_counter_handler, - }; - // In the new version of QEMU (9.20), the UART RX interrupt and TX interrupt have been swapped. - // For details, see `fix RX/TX interrupts order `_ - // default set as new version of QEMU - tbl[33] = Vector { - handler: uart0rx_handler, - }; - tbl[34] = Vector { - handler: uart0tx_handler, - }; - tbl[35] = Vector { - handler: uartrx1_handler, - }; - tbl[36] = Vector { - handler: uarttx1_handler, - }; - tbl[37] = Vector { - handler: uartrx2_handler, - }; - tbl[38] = Vector { - handler: uarttx2_handler, - }; - tbl[39] = Vector { - handler: uartrx3_handler, - }; - tbl[40] = Vector { - handler: uarttx3_handler, - }; - tbl[41] = Vector { - handler: uartrx4_handler, - }; - tbl[42] = Vector { - handler: uarttx4_handler, - }; - - tbl -}; diff --git a/kernel/src/boards/qemu_mps3_an547/mod.rs b/kernel/src/boards/qemu_mps3_an547/mod.rs index 5f4561ba..88bb6647 100644 --- a/kernel/src/boards/qemu_mps3_an547/mod.rs +++ b/kernel/src/boards/qemu_mps3_an547/mod.rs @@ -13,7 +13,6 @@ // limitations under the License. pub mod config; -mod handlers; use crate::{ arch, @@ -97,6 +96,10 @@ pub(crate) fn init() { unsafe { boot::init_heap() }; arch::irq::init(); ClockImpl::init(); + unsafe { + arch::irq::register_raw_isr(UART0RX_IRQn, uart0rx_handler); + arch::irq::register_raw_isr(UART0TX_IRQn, uart0tx_handler); + } arch::irq::enable_irq_with_priority(UART0RX_IRQn, arch::irq::Priority::Normal); arch::irq::enable_irq_with_priority(UART0TX_IRQn, arch::irq::Priority::Normal); } @@ -113,8 +116,7 @@ crate::define_peripheral! { crate::define_pin_states!(None); -#[no_mangle] -pub unsafe extern "C" fn uart0rx_handler() { +unsafe extern "C" fn uart0rx_handler() { let _trace = IrqTrace::new(UART0RX_IRQn); let uart = get_device!(console_uart); if let Some(handler) = unsafe { @@ -126,8 +128,8 @@ pub unsafe extern "C" fn uart0rx_handler() { } uart.clear_interrupt(blueos_driver::uart::InterruptType::Rx); } -#[no_mangle] -pub unsafe extern "C" fn uart0tx_handler() { + +unsafe extern "C" fn uart0tx_handler() { let _trace = IrqTrace::new(UART0TX_IRQn); let uart = get_device!(console_uart); if let Some(handler) = unsafe { diff --git a/kernel/src/boards/raspberry_pico2_cortexm/handler.rs b/kernel/src/boards/raspberry_pico2_cortexm/handler.rs deleted file mode 100644 index decc66de..00000000 --- a/kernel/src/boards/raspberry_pico2_cortexm/handler.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2025 vivo Mobile Communication Co., Ltd. -// -// 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 -// -// http://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. - -use crate::{ - arch::{ - self, - irq::{InterruptTable, Vector, INTERRUPT_TABLE_LEN}, - }, - boot::_start, - time, -}; - -unsafe extern "C" { - fn uart0_handler(); -} - -#[doc(hidden)] -#[link_section = ".interrupt.handlers"] -#[no_mangle] -static __INTERRUPT_HANDLERS__: InterruptTable = { - let mut tbl = [Vector { reserved: 0 }; INTERRUPT_TABLE_LEN]; - tbl[33] = Vector { - handler: uart0_handler, - }; - tbl -}; diff --git a/kernel/src/boards/raspberry_pico2_cortexm/mod.rs b/kernel/src/boards/raspberry_pico2_cortexm/mod.rs index 7ab026a1..4a7e84b1 100644 --- a/kernel/src/boards/raspberry_pico2_cortexm/mod.rs +++ b/kernel/src/boards/raspberry_pico2_cortexm/mod.rs @@ -13,7 +13,6 @@ // limitations under the License. mod block; -mod handler; use crate::{ arch::{self, irq::IrqNumber}, @@ -99,6 +98,9 @@ pub(crate) fn init() { unsafe { boot::init_heap() }; arch::irq::init(); + unsafe { + arch::irq::register_raw_isr(UART0_IRQN, uart0_handler); + } arch::irq::enable_irq_with_priority(UART0_IRQN, arch::irq::Priority::Normal); ClockImpl::init(); } @@ -141,7 +143,6 @@ crate::define_bus! { ) } -#[no_mangle] pub unsafe extern "C" fn uart0_handler() { use blueos_hal::HasInterruptReg; let _trace = IrqTrace::new(UART0_IRQN); From 7a0a3d261c1c47b49db1d19d366feece5a8591ae Mon Sep 17 00:00:00 2001 From: xuchang-vivo Date: Mon, 30 Mar 2026 19:46:17 +0800 Subject: [PATCH 05/10] add exception --- kernel/src/arch/arm/irq.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kernel/src/arch/arm/irq.rs b/kernel/src/arch/arm/irq.rs index fc58b22f..8900bea7 100644 --- a/kernel/src/arch/arm/irq.rs +++ b/kernel/src/arch/arm/irq.rs @@ -147,7 +147,12 @@ extern "C" fn _generic_isr_handler() { .checked_sub(16) .expect("Invalid ISR index, IPSR value: {ipsr:#X}"); - // ISR_DESC[isr_index as usize].as_ref()s + unsafe { + ISR_DESC[isr_index as usize] + .as_ref() + .expect("No ISR descriptor found for the current interrupt {isr_index}") + .service_isr(); + } #[cfg(round_robin)] { From e4580ec9eaa5e1e436fc43f72eafd6cfbda2cfdf Mon Sep 17 00:00:00 2001 From: xuchang-vivo Date: Mon, 30 Mar 2026 20:18:46 +0800 Subject: [PATCH 06/10] minor --- kernel/src/arch/arm/irq.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/kernel/src/arch/arm/irq.rs b/kernel/src/arch/arm/irq.rs index 8900bea7..c36d4ccd 100644 --- a/kernel/src/arch/arm/irq.rs +++ b/kernel/src/arch/arm/irq.rs @@ -147,11 +147,10 @@ extern "C" fn _generic_isr_handler() { .checked_sub(16) .expect("Invalid ISR index, IPSR value: {ipsr:#X}"); - unsafe { - ISR_DESC[isr_index as usize] - .as_ref() - .expect("No ISR descriptor found for the current interrupt {isr_index}") - .service_isr(); + if let Some(isr_desc) = unsafe { ISR_DESC[isr_index as usize].as_ref() } { + isr_desc.service_isr(); + } else { + // FIXME: If the ISR is not explicitly registered, what should be done? } #[cfg(round_robin)] From c906fa8ed43260e69808ad095ae6a9078ec52f1b Mon Sep 17 00:00:00 2001 From: xuchang-vivo Date: Tue, 31 Mar 2026 17:38:03 +0800 Subject: [PATCH 07/10] add interrupt macro --- arch/arm/cortex_m/link.x | 6 ++ driver/BUILD.gn | 1 + hal/src/isr.rs | 23 +++++ hal/src/lib.rs | 1 + kernel/src/arch/arm/irq.rs | 31 +++++-- kernel/src/boards/gd32e507_eval/mod.rs | 5 +- kernel/src/boards/qemu_mps2_an385/mod.rs | 1 + kernel/src/boards/qemu_mps3_an547/mod.rs | 1 + .../src/boards/raspberry_pico2_cortexm/mod.rs | 1 + macro/src/lib.rs | 92 ++++++++++++++++++- 10 files changed, 150 insertions(+), 12 deletions(-) create mode 100644 hal/src/isr.rs diff --git a/arch/arm/cortex_m/link.x b/arch/arm/cortex_m/link.x index b1081f18..8b1d73b9 100644 --- a/arch/arm/cortex_m/link.x +++ b/arch/arm/cortex_m/link.x @@ -159,6 +159,12 @@ SECTIONS KEEP(*(.bk_app_array)) PROVIDE_HIDDEN (__bk_app_array_end = .); + . = ALIGN(4); + PROVIDE_HIDDEN(__isr_array_start = .); + KEEP(*(SORT_BY_INIT_PRIORITY(.isr.reg.*))) + KEEP(*(.isr.reg)) + PROVIDE_HIDDEN(__isr_array_end = .); + . = ALIGN(4); __start___llvm_prf_cnts = .; KEEP(*(__llvm_prf_cnts)) diff --git a/driver/BUILD.gn b/driver/BUILD.gn index 9daa365f..14fadaca 100644 --- a/driver/BUILD.gn +++ b/driver/BUILD.gn @@ -24,6 +24,7 @@ build_rust("blueos_driver") { "//external/vendor/tock-registers-0.9.0:tock_registers", "//external/vendor/zerocopy-0.8.27:zerocopy", "//kernel/hal:blueos_hal", + "//kernel/macro:blueos_macro", ] + board_deps features = [] diff --git a/hal/src/isr.rs b/hal/src/isr.rs new file mode 100644 index 00000000..c0920bbf --- /dev/null +++ b/hal/src/isr.rs @@ -0,0 +1,23 @@ +// Copyright (c) 2025 vivo Mobile Communication Co., Ltd. +// +// 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 +// +// http://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. + +pub trait IsrDesc: Sync + 'static { + fn service_isr(&self); +} + +#[repr(C)] +pub struct IsrReg { + pub no: usize, + pub desc: &'static dyn IsrDesc, +} diff --git a/hal/src/lib.rs b/hal/src/lib.rs index 9ad0056d..dc4ce29c 100644 --- a/hal/src/lib.rs +++ b/hal/src/lib.rs @@ -23,6 +23,7 @@ use err::Result; pub mod clock; pub mod clock_control; pub mod i2c; +pub mod isr; pub mod pinctrl; pub mod reset; pub mod uart; diff --git a/kernel/src/arch/arm/irq.rs b/kernel/src/arch/arm/irq.rs index c36d4ccd..d19ebb99 100644 --- a/kernel/src/arch/arm/irq.rs +++ b/kernel/src/arch/arm/irq.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use blueos_hal::isr::{IsrDesc, IsrReg}; use cortex_m::{interrupt::InterruptNumber, peripheral::scb::SystemHandler, Peripherals}; #[cfg(irq_priority_bits_2)] @@ -161,18 +162,30 @@ extern "C" fn _generic_isr_handler() { } } -pub trait IsrDesc: Sync + 'static { - fn service_isr(&self); -} - static mut ISR_DESC: [Option<&dyn IsrDesc>; INTERRUPT_TABLE_LEN] = [None; INTERRUPT_TABLE_LEN]; -/// Safety: ISR_DESC only be read in the interrupt handler, and only be written in the register_swi_isr, -/// which is called in the init process while irq is disabled, so it's safe to use unsafe to write it. -/// Be careful to call register_swi_isr in the init process while irq is disabled, otherwise it may cause race condition. -pub fn register_swi_isr(irq: IrqNumber, desc: &'static dyn IsrDesc) { +/// Safety: ISR_DESC only be read in the interrupt handler, +/// and only be written in the boot process early, so it's +/// safe to use unsafe to write it. +pub fn init_interrupt_registry() { + extern "C" { + static __isr_array_start: usize; + static __isr_array_end: usize; + } + unsafe { - ISR_DESC[irq.0 as usize] = Some(desc); + let mut p = core::ptr::addr_of!(__isr_array_start); + while p < core::ptr::addr_of!(__isr_array_end) { + let r = &*(p as *const IsrReg); + assert!( + r.no < INTERRUPT_TABLE_LEN, + "ISR number {} exceeds the maximum limit {}", + r.no, + INTERRUPT_TABLE_LEN + ); + ISR_DESC[r.no] = Some(r.desc); + p = p.add(1); + } } } diff --git a/kernel/src/boards/gd32e507_eval/mod.rs b/kernel/src/boards/gd32e507_eval/mod.rs index 43e1a142..40485c90 100644 --- a/kernel/src/boards/gd32e507_eval/mod.rs +++ b/kernel/src/boards/gd32e507_eval/mod.rs @@ -76,7 +76,10 @@ const HZ: usize = config::PLL_SYS_FREQ as usize; pub type ClockImpl = systick::SysTickClock; pub(crate) fn init() { - unsafe { copy_data() }; + unsafe { + copy_data(); + arch::irq::init_interrupt_registry(); + }; boot::init_runtime(); use blueos_hal::clock_control::ClockControl; blueos_driver::clock_control::gd32_clock_control::Gd32ClockControl::init(); diff --git a/kernel/src/boards/qemu_mps2_an385/mod.rs b/kernel/src/boards/qemu_mps2_an385/mod.rs index 68decfb9..60bd1fcf 100644 --- a/kernel/src/boards/qemu_mps2_an385/mod.rs +++ b/kernel/src/boards/qemu_mps2_an385/mod.rs @@ -79,6 +79,7 @@ pub type ClockImpl = systick::SysTickClock; pub(crate) fn init() { unsafe { copy_data(); + arch::irq::init_interrupt_registry(); } boot::init_runtime(); unsafe { boot::init_heap() }; diff --git a/kernel/src/boards/qemu_mps3_an547/mod.rs b/kernel/src/boards/qemu_mps3_an547/mod.rs index 88bb6647..e3d2ebbe 100644 --- a/kernel/src/boards/qemu_mps3_an547/mod.rs +++ b/kernel/src/boards/qemu_mps3_an547/mod.rs @@ -91,6 +91,7 @@ pub(crate) fn init() { unsafe { copy_data(); + arch::irq::init_interrupt_registry(); } boot::init_runtime(); unsafe { boot::init_heap() }; diff --git a/kernel/src/boards/raspberry_pico2_cortexm/mod.rs b/kernel/src/boards/raspberry_pico2_cortexm/mod.rs index 4a7e84b1..13b9814a 100644 --- a/kernel/src/boards/raspberry_pico2_cortexm/mod.rs +++ b/kernel/src/boards/raspberry_pico2_cortexm/mod.rs @@ -92,6 +92,7 @@ pub(crate) fn init() { SCB_CPACR_PTR.write_volatile(temp); core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); copy_data(); + arch::irq::init_interrupt_registry(); } boot::init_runtime(); blueos_driver::clock_control::rpi_pico::RpiPicoClockControl::init(); diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 17447020..9bb63352 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -13,8 +13,11 @@ // limitations under the License. use proc_macro::TokenStream; -use quote::quote; -use syn::{Data, Field, Fields}; +use quote::{format_ident, quote}; +use syn::{ + parse::Parser, parse_macro_input, punctuated::Punctuated, token::Comma, Expr, ExprLit, + ItemStatic, Lit, Meta, StaticMutability, +}; #[proc_macro] pub fn current_board_mod(_item: TokenStream) -> TokenStream { @@ -30,3 +33,88 @@ pub fn current_board_mod(_item: TokenStream) -> TokenStream { } .into() } + +#[proc_macro_attribute] +pub fn interrupt(attr: TokenStream, item: TokenStream) -> TokenStream { + let no = match parse_interrupt_no(attr) { + Ok(no) => no, + Err(e) => return e.to_compile_error().into(), + }; + + let item_static = parse_macro_input!(item as ItemStatic); + if matches!(item_static.mutability, StaticMutability::Mut(_)) { + return syn::Error::new_spanned( + &item_static, + "#[interrupt] only supports immutable `static` items", + ) + .to_compile_error() + .into(); + } + + let ident = &item_static.ident; + let reg_ident = format_ident!("__BLUEOS_INTERRUPT_REG_{}", ident); + let section = format!(".isr.reg.{no}"); + + quote! { + #item_static + + const _: () = { + #[repr(C)] + struct __BlueOsInterruptReg { + no: usize, + desc: &'static dyn ::blueos_hal::isr::IsrDesc, + } + + #[used] + #[unsafe(link_section = #section)] + static #reg_ident: blueos_hal::isr::IsrReg = blueos_hal::isr::IsrReg { + no: #no, + desc: &#ident as &'static dyn ::blueos_hal::isr::IsrDesc, + }; + }; + } + .into() +} + +fn parse_interrupt_no(attr: TokenStream) -> Result { + let parser = Punctuated::::parse_terminated; + let metas = parser.parse2(attr.into())?; + let mut no: Option = None; + + for meta in metas { + match meta { + Meta::NameValue(name_value) if name_value.path.is_ident("no") => { + if no.is_some() { + return Err(syn::Error::new_spanned( + name_value, + "`no` can only be specified once", + )); + } + let Expr::Lit(ExprLit { + lit: Lit::Int(lit_int), + .. + }) = name_value.value + else { + return Err(syn::Error::new_spanned( + name_value, + "`no` expects an integer literal, e.g. #[interrupt(no = 1)]", + )); + }; + no = Some(lit_int.base10_parse::()?); + } + other => { + return Err(syn::Error::new_spanned( + other, + "unsupported interrupt attribute, expected #[interrupt(no = N)]", + )); + } + } + } + + no.ok_or_else(|| { + syn::Error::new( + proc_macro2::Span::call_site(), + "missing `no` in #[interrupt]", + ) + }) +} From 779c952fe49fd4ca74709e59fa3a1ac28580adb9 Mon Sep 17 00:00:00 2001 From: xuchang-vivo Date: Tue, 31 Mar 2026 18:18:35 +0800 Subject: [PATCH 08/10] add example --- driver/BUILD.gn | 3 ++- macro/src/lib.rs | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/driver/BUILD.gn b/driver/BUILD.gn index 14fadaca..e3c7a9f5 100644 --- a/driver/BUILD.gn +++ b/driver/BUILD.gn @@ -21,11 +21,12 @@ build_rust("blueos_driver") { "//external/vendor/bitflags-2.10.0:bitflags", "//external/vendor/cfg-if-1.0.4:cfg_if", "//external/vendor/safe-mmio-0.2.5:safe_mmio", + "//external/vendor/spin-0.9.8:spin", "//external/vendor/tock-registers-0.9.0:tock_registers", "//external/vendor/zerocopy-0.8.27:zerocopy", "//kernel/hal:blueos_hal", - "//kernel/macro:blueos_macro", ] + board_deps + proc_macro_deps = [ "//kernel/macro:blueos_macro" ] features = [] rustflags = [ diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 9bb63352..93388091 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -34,6 +34,21 @@ pub fn current_board_mod(_item: TokenStream) -> TokenStream { .into() } +/// Defines an interrupt service routine (ISR) and registers it to the specified interrupt number. +/// +/// EXAMPLE: +/// ``` +/// struct RxIsr; +/// +/// #[blueos_macro::interrupt(no = 1)] +/// static RX_ISR: RxIsr = RxIsr; +/// +/// impl blueos_hal::isr::IsrDesc for RxIsr { +/// fn service_isr(&self) { +/// // ISR implementation +/// } +/// } +/// ``` #[proc_macro_attribute] pub fn interrupt(attr: TokenStream, item: TokenStream) -> TokenStream { let no = match parse_interrupt_no(attr) { From 3e46d01f83ecf9469aa38ae2d1e1edc30f903236 Mon Sep 17 00:00:00 2001 From: xuchang-vivo Date: Wed, 1 Apr 2026 11:21:24 +0800 Subject: [PATCH 09/10] test esp --- driver/src/systimer/esp32_sys_timer.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/driver/src/systimer/esp32_sys_timer.rs b/driver/src/systimer/esp32_sys_timer.rs index bf7e5bd4..7976ab09 100644 --- a/driver/src/systimer/esp32_sys_timer.rs +++ b/driver/src/systimer/esp32_sys_timer.rs @@ -192,7 +192,9 @@ impl Clock for Esp32SysTimer u64 { Self::registers().unit0_op.modify(UNIT_OP::UPDATE::SET); - while !Self::registers().unit0_op.is_set(UNIT_OP::VALUE_VALID) {} + while !Self::registers().unit0_op.is_set(UNIT_OP::VALUE_VALID) { + Self::registers().unit0_op.modify(UNIT_OP::UPDATE::SET); + } let mut lo_prev = Self::registers() .unit0_value_lo From 75b93d6ef7c66ca4b3debb1c3b2239b3fceb5588 Mon Sep 17 00:00:00 2001 From: xuchang-vivo Date: Wed, 1 Apr 2026 11:22:26 +0800 Subject: [PATCH 10/10] test esp32 --- driver/src/systimer/esp32_sys_timer.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/driver/src/systimer/esp32_sys_timer.rs b/driver/src/systimer/esp32_sys_timer.rs index 7976ab09..ce06c144 100644 --- a/driver/src/systimer/esp32_sys_timer.rs +++ b/driver/src/systimer/esp32_sys_timer.rs @@ -222,16 +222,16 @@ impl Clock for Esp32SysTimer