From f9b134e85ffa0a017d5f9ba8a46d7572275c98ec Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Mon, 26 Jul 2021 12:48:59 +0200 Subject: [PATCH 1/9] add set_general_handler macro --- src/structures/idt.rs | 239 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index da93db813..c7efcefdb 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -1120,10 +1120,204 @@ pub enum ExceptionVector { Security = 0x1E, } +#[macro_export] +/// Set a general handler in an [`InterruptDescriptorTable`]. +/// ``` +/// #![feature(abi_x86_interrupt)] +/// use x86_64::set_general_handler; +/// use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; +/// +/// let mut idt = InterruptDescriptorTable::new(); +/// fn my_general_handler( +/// stack_frame: InterruptStackFrame, +/// index: u8, +/// error_code: Option, +/// ) { +/// todo!("handle irq {}", index) +/// } +/// +/// // set only one entry +/// set_general_handler!(&mut idt, my_general_handler, 14); +/// +/// // set a range of entries +/// set_general_handler!(&mut idt, my_general_handler, 32..64); +/// +/// // set all entries +/// set_general_handler!(&mut idt, my_general_handler); +/// ``` +macro_rules! set_general_handler { + ($idt:expr, $handler:ident) => { + $crate::set_general_handler!($idt, $handler, 0..=255); + }; + ($idt:expr, $handler:ident, $idx:literal) => { + $crate::set_general_handler!($idt, $handler, $idx..=$idx); + }; + ($idt:expr, $handler:ident, $range:expr) => {{ + fn set_general_handler( + idt: &mut $crate::structures::idt::InterruptDescriptorTable, + range: impl core::ops::RangeBounds, + ) { + $crate::set_general_handler_recursive_bits!(idt, $handler, range); + } + set_general_handler($idt, $range); + }}; +} + +#[macro_export] +#[doc(hidden)] +/// We can't loop in macros, but we can use recursion. +/// This macro recursivly adds one more bit to it's arguments until we have 8 bits so that we can call set_general_handler_entry. +macro_rules! set_general_handler_recursive_bits { + // if we have 8 all bits, construct the index from the bits, check if the entry is in range and invoke the macro that sets the handler + ($idt:expr, $handler:ident, $range:expr, $bit7:tt, $bit6:tt, $bit5:tt, $bit4:tt, $bit3:tt, $bit2:tt, $bit1:tt, $bit0:tt) => {{ + const IDX: u8 = $bit0 | ($bit1 << 1) | ($bit2 << 2) | ($bit3 << 3) | ($bit4 << 4) | ($bit5 << 5) | ($bit6 << 6) | ($bit7 << 7); + let idx = IDX as usize; + + #[allow(unreachable_code)] + if $range.contains(&idx) { + $crate::set_general_handler_entry!($idt, $handler, IDX, $bit7, $bit6, $bit5, $bit4, $bit3, $bit2, $bit1, $bit0); + } + }}; + // otherwise recursivly invoke the macro adding one more bit + ($idt:expr, $handler:ident, $range:expr $(, $bits:tt)*) => { + $crate::set_general_handler_recursive_bits!($idt, $handler, $range $(, $bits)*, 0); + $crate::set_general_handler_recursive_bits!($idt, $handler, $range $(, $bits)*, 1); + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! set_general_handler_entry { + // special case entries that don't have the `HandlerFunc` signature + ($idt:expr, $handler:ident, $idx:expr, 0, 0, 0, 0, 1, 0, 0, 0) => {{ + extern "x86-interrupt" fn handler( + frame: $crate::structures::idt::InterruptStackFrame, + error_code: u64, + ) -> ! { + $handler(frame, $idx.into(), Some(error_code)); + panic!("General handler returned on double fault"); + } + $idt.double_fault.set_handler_fn(handler); + }}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 0, 1, 0, 1, 0) => {{ + extern "x86-interrupt" fn handler( + frame: $crate::structures::idt::InterruptStackFrame, + error_code: u64, + ) { + $handler(frame, $idx.into(), Some(error_code)); + } + $idt.invalid_tss.set_handler_fn(handler); + }}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 0, 1, 0, 1, 1) => {{ + extern "x86-interrupt" fn handler( + frame: $crate::structures::idt::InterruptStackFrame, + error_code: u64, + ) { + $handler(frame, $idx.into(), Some(error_code)); + } + $idt.segment_not_present.set_handler_fn(handler); + }}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 0, 1, 1, 0, 0) => {{ + extern "x86-interrupt" fn handler( + frame: $crate::structures::idt::InterruptStackFrame, + error_code: u64, + ) { + $handler(frame, $idx.into(), Some(error_code)); + } + $idt.stack_segment_fault.set_handler_fn(handler); + }}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 0, 1, 1, 0, 1) => {{ + extern "x86-interrupt" fn handler( + frame: $crate::structures::idt::InterruptStackFrame, + error_code: u64, + ) { + $handler(frame, $idx.into(), Some(error_code)); + } + $idt.general_protection_fault.set_handler_fn(handler); + }}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 0, 1, 1, 1, 0) => {{ + extern "x86-interrupt" fn handler( + frame: $crate::structures::idt::InterruptStackFrame, + error_code: $crate::structures::idt::PageFaultErrorCode, + ) { + $handler(frame, IDX.into(), Some(error_code.bits())); + } + $idt.page_fault.set_handler_fn(handler); + }}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 0, 0, 0, 1) => {{ + extern "x86-interrupt" fn handler( + frame: $crate::structures::idt::InterruptStackFrame, + error_code: u64, + ) { + $handler(frame, $idx.into(), Some(error_code)); + } + $idt.alignment_check.set_handler_fn(handler); + }}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 0, 0, 1, 0) => {{ + extern "x86-interrupt" fn handler( + frame: $crate::structures::idt::InterruptStackFrame, + ) -> ! { + $handler(frame, $idx.into(), None); + panic!("General handler returned on machine check exception"); + } + $idt.machine_check.set_handler_fn(handler); + }}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 1, 1, 0) => {{ + extern "x86-interrupt" fn handler( + frame: $crate::structures::idt::InterruptStackFrame, + error_code: u64, + ) { + $handler(frame, $idx.into(), Some(error_code)); + } + $idt.security_exception.set_handler_fn(handler); + }}; + + // reserved + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 0, 1, 1, 1, 1) => {}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 0, 1, 0, 1) => {}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 0, 1, 1, 0) => {}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 0, 1, 1, 1) => {}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 0, 0, 0) => {}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 0, 0, 1) => {}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 0, 1, 0) => {}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 0, 1, 1) => {}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 1, 0, 0) => {}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 1, 0, 1) => {}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 1, 1, 0) => {}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 1, 1, 1) => {}; + + // set entries with `HandlerFunc` signature + ($idt:expr, $handler:ident, $idx:ident $(, $_bits:tt)*) => {{ + extern "x86-interrupt" fn handler(frame: $crate::structures::idt::InterruptStackFrame) { + $handler(frame, $idx.into(), None); + } + $idt[$idx as usize].set_handler_fn(handler); + }}; +} + #[cfg(test)] mod test { use super::*; + fn entry_present(idt: &InterruptDescriptorTable, index: usize) -> bool { + let options = match index { + 8 => &idt.double_fault.options, + 10 => &idt.invalid_tss.options, + 11 => &idt.segment_not_present.options, + 12 => &idt.stack_segment_fault.options, + 13 => &idt.general_protection_fault.options, + 14 => &idt.page_fault.options, + 15 => &idt.reserved_1.options, + 17 => &idt.alignment_check.options, + 18 => &idt.machine_check.options, + i @ 21..=29 => &idt.reserved_2[i - 21].options, + 30 => &idt.security_exception.options, + 31 => &idt.reserved_3.options, + other => &idt[other].options, + }; + options.bits.get_bit(15) + } + #[test] fn size_test() { use core::mem::size_of; @@ -1131,6 +1325,51 @@ mod test { assert_eq!(size_of::(), 256 * 16); } + #[test] + fn default_handlers() { + fn general_handler( + _stack_frame: InterruptStackFrame, + _index: u8, + _error_code: Option, + ) { + } + + let mut idt = InterruptDescriptorTable::new(); + set_general_handler!(&mut idt, general_handler, 0); + for i in 0..256 { + if i == 0 { + assert!(entry_present(&idt, i)); + } else { + assert!(!entry_present(&idt, i)); + } + } + set_general_handler!(&mut idt, general_handler, 14); + for i in 0..256 { + if i == 0 || i == 14 { + assert!(entry_present(&idt, i)); + } else { + assert!(!entry_present(&idt, i)); + } + } + set_general_handler!(&mut idt, general_handler, 32..64); + for i in 1..256 { + if i == 0 || i == 14 || (i >= 32 && i < 64) { + assert!(entry_present(&idt, i), "{}", i); + } else { + assert!(!entry_present(&idt, i)); + } + } + set_general_handler!(&mut idt, general_handler); + for i in 0..256 { + if i == 15 || i == 31 || (i >= 21 && i <= 29) { + // reserved entries should not be set + assert!(!entry_present(&idt, i)); + } else { + assert!(entry_present(&idt, i)); + } + } + } + #[test] fn entry_derive_test() { fn foo(_: impl Clone + Copy + PartialEq + fmt::Debug) {} From 8695b39fb9254c515a0daa236041b9a72db2600b Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 7 Nov 2021 13:19:37 +0100 Subject: [PATCH 2/9] add `GeneralHandlerFunc` for `set_general_handler` --- src/structures/idt.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index c7efcefdb..b99089bab 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -685,6 +685,9 @@ pub type DivergingHandlerFuncWithErrCode = #[derive(Copy, Clone, Debug)] pub struct DivergingHandlerFuncWithErrCode(()); +/// A general handler function for an interrupt or an exception with the interrupt/exceptions's index and an optional error code. +pub type GeneralHandlerFunc = fn(InterruptStackFrame, index: u8, error_code: Option); + impl Entry { /// Creates a non-present IDT entry (but sets the must-be-one bits). #[inline] @@ -1153,13 +1156,22 @@ macro_rules! set_general_handler { $crate::set_general_handler!($idt, $handler, $idx..=$idx); }; ($idt:expr, $handler:ident, $range:expr) => {{ - fn set_general_handler( - idt: &mut $crate::structures::idt::InterruptDescriptorTable, - range: impl core::ops::RangeBounds, - ) { - $crate::set_general_handler_recursive_bits!(idt, $handler, range); + /// This constant is used to avoid spamming the same compilation error ~200 times + /// when the handler's signature is wrong. + /// If we just passed `$handler` to `set_general_handler_recursive_bits` + /// an error would be reported for every interrupt handler that tried to call it. + /// With `GENERAL_HANDLER` the error is only reported once for this constant. + const GENERAL_HANDLER: $crate::structures::idt::GeneralHandlerFunc = $handler; + + { + fn set_general_handler( + idt: &mut $crate::structures::idt::InterruptDescriptorTable, + range: impl ::core::ops::RangeBounds, + ) { + $crate::set_general_handler_recursive_bits!(idt, GENERAL_HANDLER, range); + } + set_general_handler($idt, $range); } - set_general_handler($idt, $range); }}; } From d41756a0a2a767f22180c8179db6d90fb5a7dd21 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 7 Nov 2021 13:25:55 +0100 Subject: [PATCH 3/9] use `u8` instead of `usize` --- src/structures/idt.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index b99089bab..90516016a 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -1166,7 +1166,7 @@ macro_rules! set_general_handler { { fn set_general_handler( idt: &mut $crate::structures::idt::InterruptDescriptorTable, - range: impl ::core::ops::RangeBounds, + range: impl ::core::ops::RangeBounds, ) { $crate::set_general_handler_recursive_bits!(idt, GENERAL_HANDLER, range); } @@ -1183,10 +1183,9 @@ macro_rules! set_general_handler_recursive_bits { // if we have 8 all bits, construct the index from the bits, check if the entry is in range and invoke the macro that sets the handler ($idt:expr, $handler:ident, $range:expr, $bit7:tt, $bit6:tt, $bit5:tt, $bit4:tt, $bit3:tt, $bit2:tt, $bit1:tt, $bit0:tt) => {{ const IDX: u8 = $bit0 | ($bit1 << 1) | ($bit2 << 2) | ($bit3 << 3) | ($bit4 << 4) | ($bit5 << 5) | ($bit6 << 6) | ($bit7 << 7); - let idx = IDX as usize; #[allow(unreachable_code)] - if $range.contains(&idx) { + if $range.contains(&IDX) { $crate::set_general_handler_entry!($idt, $handler, IDX, $bit7, $bit6, $bit5, $bit4, $bit3, $bit2, $bit1, $bit0); } }}; From 43233b6fb34b2907b09bb356967079a5654bcf92 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 7 Nov 2021 13:53:57 +0100 Subject: [PATCH 4/9] fix tests --- src/structures/idt.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index 90516016a..abf3b87ee 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -1321,12 +1321,13 @@ mod test { 15 => &idt.reserved_1.options, 17 => &idt.alignment_check.options, 18 => &idt.machine_check.options, - i @ 21..=29 => &idt.reserved_2[i - 21].options, + i @ 21..=28 => &idt.reserved_2[i - 21].options, + 29 => &idt.vmm_communication_exception.options, 30 => &idt.security_exception.options, 31 => &idt.reserved_3.options, other => &idt[other].options, }; - options.bits.get_bit(15) + options.0.get_bit(15) } #[test] From 6ba1dd681742e119195f906b862de83e710895e0 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 7 Nov 2021 14:03:27 +0100 Subject: [PATCH 5/9] put `set_general_handler` behind feature gate --- src/structures/idt.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index abf3b87ee..5be218569 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -1123,6 +1123,7 @@ pub enum ExceptionVector { Security = 0x1E, } +#[cfg(all(feature = "instructions", feature = "abi_x86_interrupt"))] #[macro_export] /// Set a general handler in an [`InterruptDescriptorTable`]. /// ``` @@ -1175,6 +1176,7 @@ macro_rules! set_general_handler { }}; } +#[cfg(all(feature = "instructions", feature = "abi_x86_interrupt"))] #[macro_export] #[doc(hidden)] /// We can't loop in macros, but we can use recursion. @@ -1196,6 +1198,7 @@ macro_rules! set_general_handler_recursive_bits { }; } +#[cfg(all(feature = "instructions", feature = "abi_x86_interrupt"))] #[macro_export] #[doc(hidden)] macro_rules! set_general_handler_entry { @@ -1337,6 +1340,7 @@ mod test { assert_eq!(size_of::(), 256 * 16); } + #[cfg(all(feature = "instructions", feature = "abi_x86_interrupt"))] #[test] fn default_handlers() { fn general_handler( From be14601f56d695986cfd76b78a1b6dad1e8b4d35 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 7 Nov 2021 14:09:33 +0100 Subject: [PATCH 6/9] support `vmm_communication_exception` --- src/structures/idt.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index 5be218569..c5a476e54 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -1276,6 +1276,15 @@ macro_rules! set_general_handler_entry { } $idt.machine_check.set_handler_fn(handler); }}; + ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 1, 0, 1) => { + extern "x86-interrupt" fn handler( + frame: $crate::structures::idt::InterruptStackFrame, + error_code: u64, + ) { + $handler(frame, $idx.into(), Some(error_code)); + } + $idt.vmm_communication_exception.set_handler_fn(handler); + }; ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 1, 1, 0) => {{ extern "x86-interrupt" fn handler( frame: $crate::structures::idt::InterruptStackFrame, @@ -1286,8 +1295,9 @@ macro_rules! set_general_handler_entry { $idt.security_exception.set_handler_fn(handler); }}; - // reserved + // reserved_1 ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 0, 1, 1, 1, 1) => {}; + // reserved_2 ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 0, 1, 0, 1) => {}; ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 0, 1, 1, 0) => {}; ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 0, 1, 1, 1) => {}; @@ -1296,8 +1306,7 @@ macro_rules! set_general_handler_entry { ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 0, 1, 0) => {}; ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 0, 1, 1) => {}; ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 1, 0, 0) => {}; - ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 1, 0, 1) => {}; - ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 1, 1, 0) => {}; + // reserved_3 ($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 1, 1, 1) => {}; // set entries with `HandlerFunc` signature @@ -1377,7 +1386,7 @@ mod test { } set_general_handler!(&mut idt, general_handler); for i in 0..256 { - if i == 15 || i == 31 || (i >= 21 && i <= 29) { + if i == 15 || i == 31 || (i >= 21 && i <= 28) { // reserved entries should not be set assert!(!entry_present(&idt, i)); } else { From e6332936d1e8511f940dca7d4423beb6a3705e84 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 7 Nov 2021 14:10:10 +0100 Subject: [PATCH 7/9] fix clippy warning in test --- src/structures/idt.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index c5a476e54..7fb05f71c 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -1378,7 +1378,7 @@ mod test { } set_general_handler!(&mut idt, general_handler, 32..64); for i in 1..256 { - if i == 0 || i == 14 || (i >= 32 && i < 64) { + if i == 0 || i == 14 || (32..64).contains(&i) { assert!(entry_present(&idt, i), "{}", i); } else { assert!(!entry_present(&idt, i)); @@ -1386,7 +1386,7 @@ mod test { } set_general_handler!(&mut idt, general_handler); for i in 0..256 { - if i == 15 || i == 31 || (i >= 21 && i <= 28) { + if i == 15 || i == 31 || (21..=28).contains(&i) { // reserved entries should not be set assert!(!entry_present(&idt, i)); } else { From 9b0065de1f963e6ec689e5b11285a3275d7521ae Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 7 Nov 2021 18:11:54 +0100 Subject: [PATCH 8/9] don't run `default_handlers` test on windows --- src/structures/idt.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index 7fb05f71c..c6a424ce2 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -1350,6 +1350,9 @@ mod test { } #[cfg(all(feature = "instructions", feature = "abi_x86_interrupt"))] + // there seems to be a bug in LLVM that causes rustc to crash on windows when compiling this test: + // https://github.com/rust-osdev/x86_64/pull/285#issuecomment-962642984 + #[cfg(not(windows))] #[test] fn default_handlers() { fn general_handler( From e447d88793460d87cae2299aca8a17303dea3a79 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 7 Nov 2021 18:35:51 +0100 Subject: [PATCH 9/9] skip `set_general_handler` in doctests on windows --- src/structures/idt.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index c6a424ce2..4a3d27bbb 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -1141,12 +1141,21 @@ pub enum ExceptionVector { /// } /// /// // set only one entry +/// # // there seems to be a bug in LLVM that causes rustc to crash on windows when compiling this test: +/// # // https://github.com/rust-osdev/x86_64/pull/285#issuecomment-962642984 +/// # #[cfg(not(windows))] /// set_general_handler!(&mut idt, my_general_handler, 14); /// /// // set a range of entries +/// # // there seems to be a bug in LLVM that causes rustc to crash on windows when compiling this test: +/// # // https://github.com/rust-osdev/x86_64/pull/285#issuecomment-962642984 +/// # #[cfg(not(windows))] /// set_general_handler!(&mut idt, my_general_handler, 32..64); /// /// // set all entries +/// # // there seems to be a bug in LLVM that causes rustc to crash on windows when compiling this test: +/// # // https://github.com/rust-osdev/x86_64/pull/285#issuecomment-962642984 +/// # #[cfg(not(windows))] /// set_general_handler!(&mut idt, my_general_handler); /// ``` macro_rules! set_general_handler {