Skip to content

Commit f4c2e26

Browse files
authored
Merge pull request #285 from Freax13/default-irq-handler
add set_general_handler macro
2 parents e8aee52 + e447d88 commit f4c2e26

File tree

1 file changed

+276
-0
lines changed

1 file changed

+276
-0
lines changed

src/structures/idt.rs

+276
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,9 @@ pub type DivergingHandlerFuncWithErrCode =
685685
#[derive(Copy, Clone, Debug)]
686686
pub struct DivergingHandlerFuncWithErrCode(());
687687

688+
/// A general handler function for an interrupt or an exception with the interrupt/exceptions's index and an optional error code.
689+
pub type GeneralHandlerFunc = fn(InterruptStackFrame, index: u8, error_code: Option<u64>);
690+
688691
impl<F> Entry<F> {
689692
/// Creates a non-present IDT entry (but sets the must-be-one bits).
690693
#[inline]
@@ -1120,17 +1123,290 @@ pub enum ExceptionVector {
11201123
Security = 0x1E,
11211124
}
11221125

1126+
#[cfg(all(feature = "instructions", feature = "abi_x86_interrupt"))]
1127+
#[macro_export]
1128+
/// Set a general handler in an [`InterruptDescriptorTable`].
1129+
/// ```
1130+
/// #![feature(abi_x86_interrupt)]
1131+
/// use x86_64::set_general_handler;
1132+
/// use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
1133+
///
1134+
/// let mut idt = InterruptDescriptorTable::new();
1135+
/// fn my_general_handler(
1136+
/// stack_frame: InterruptStackFrame,
1137+
/// index: u8,
1138+
/// error_code: Option<u64>,
1139+
/// ) {
1140+
/// todo!("handle irq {}", index)
1141+
/// }
1142+
///
1143+
/// // set only one entry
1144+
/// # // there seems to be a bug in LLVM that causes rustc to crash on windows when compiling this test:
1145+
/// # // https://github.com/rust-osdev/x86_64/pull/285#issuecomment-962642984
1146+
/// # #[cfg(not(windows))]
1147+
/// set_general_handler!(&mut idt, my_general_handler, 14);
1148+
///
1149+
/// // set a range of entries
1150+
/// # // there seems to be a bug in LLVM that causes rustc to crash on windows when compiling this test:
1151+
/// # // https://github.com/rust-osdev/x86_64/pull/285#issuecomment-962642984
1152+
/// # #[cfg(not(windows))]
1153+
/// set_general_handler!(&mut idt, my_general_handler, 32..64);
1154+
///
1155+
/// // set all entries
1156+
/// # // there seems to be a bug in LLVM that causes rustc to crash on windows when compiling this test:
1157+
/// # // https://github.com/rust-osdev/x86_64/pull/285#issuecomment-962642984
1158+
/// # #[cfg(not(windows))]
1159+
/// set_general_handler!(&mut idt, my_general_handler);
1160+
/// ```
1161+
macro_rules! set_general_handler {
1162+
($idt:expr, $handler:ident) => {
1163+
$crate::set_general_handler!($idt, $handler, 0..=255);
1164+
};
1165+
($idt:expr, $handler:ident, $idx:literal) => {
1166+
$crate::set_general_handler!($idt, $handler, $idx..=$idx);
1167+
};
1168+
($idt:expr, $handler:ident, $range:expr) => {{
1169+
/// This constant is used to avoid spamming the same compilation error ~200 times
1170+
/// when the handler's signature is wrong.
1171+
/// If we just passed `$handler` to `set_general_handler_recursive_bits`
1172+
/// an error would be reported for every interrupt handler that tried to call it.
1173+
/// With `GENERAL_HANDLER` the error is only reported once for this constant.
1174+
const GENERAL_HANDLER: $crate::structures::idt::GeneralHandlerFunc = $handler;
1175+
1176+
{
1177+
fn set_general_handler(
1178+
idt: &mut $crate::structures::idt::InterruptDescriptorTable,
1179+
range: impl ::core::ops::RangeBounds<u8>,
1180+
) {
1181+
$crate::set_general_handler_recursive_bits!(idt, GENERAL_HANDLER, range);
1182+
}
1183+
set_general_handler($idt, $range);
1184+
}
1185+
}};
1186+
}
1187+
1188+
#[cfg(all(feature = "instructions", feature = "abi_x86_interrupt"))]
1189+
#[macro_export]
1190+
#[doc(hidden)]
1191+
/// We can't loop in macros, but we can use recursion.
1192+
/// 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.
1193+
macro_rules! set_general_handler_recursive_bits {
1194+
// 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
1195+
($idt:expr, $handler:ident, $range:expr, $bit7:tt, $bit6:tt, $bit5:tt, $bit4:tt, $bit3:tt, $bit2:tt, $bit1:tt, $bit0:tt) => {{
1196+
const IDX: u8 = $bit0 | ($bit1 << 1) | ($bit2 << 2) | ($bit3 << 3) | ($bit4 << 4) | ($bit5 << 5) | ($bit6 << 6) | ($bit7 << 7);
1197+
1198+
#[allow(unreachable_code)]
1199+
if $range.contains(&IDX) {
1200+
$crate::set_general_handler_entry!($idt, $handler, IDX, $bit7, $bit6, $bit5, $bit4, $bit3, $bit2, $bit1, $bit0);
1201+
}
1202+
}};
1203+
// otherwise recursivly invoke the macro adding one more bit
1204+
($idt:expr, $handler:ident, $range:expr $(, $bits:tt)*) => {
1205+
$crate::set_general_handler_recursive_bits!($idt, $handler, $range $(, $bits)*, 0);
1206+
$crate::set_general_handler_recursive_bits!($idt, $handler, $range $(, $bits)*, 1);
1207+
};
1208+
}
1209+
1210+
#[cfg(all(feature = "instructions", feature = "abi_x86_interrupt"))]
1211+
#[macro_export]
1212+
#[doc(hidden)]
1213+
macro_rules! set_general_handler_entry {
1214+
// special case entries that don't have the `HandlerFunc` signature
1215+
($idt:expr, $handler:ident, $idx:expr, 0, 0, 0, 0, 1, 0, 0, 0) => {{
1216+
extern "x86-interrupt" fn handler(
1217+
frame: $crate::structures::idt::InterruptStackFrame,
1218+
error_code: u64,
1219+
) -> ! {
1220+
$handler(frame, $idx.into(), Some(error_code));
1221+
panic!("General handler returned on double fault");
1222+
}
1223+
$idt.double_fault.set_handler_fn(handler);
1224+
}};
1225+
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 0, 1, 0, 1, 0) => {{
1226+
extern "x86-interrupt" fn handler(
1227+
frame: $crate::structures::idt::InterruptStackFrame,
1228+
error_code: u64,
1229+
) {
1230+
$handler(frame, $idx.into(), Some(error_code));
1231+
}
1232+
$idt.invalid_tss.set_handler_fn(handler);
1233+
}};
1234+
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 0, 1, 0, 1, 1) => {{
1235+
extern "x86-interrupt" fn handler(
1236+
frame: $crate::structures::idt::InterruptStackFrame,
1237+
error_code: u64,
1238+
) {
1239+
$handler(frame, $idx.into(), Some(error_code));
1240+
}
1241+
$idt.segment_not_present.set_handler_fn(handler);
1242+
}};
1243+
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 0, 1, 1, 0, 0) => {{
1244+
extern "x86-interrupt" fn handler(
1245+
frame: $crate::structures::idt::InterruptStackFrame,
1246+
error_code: u64,
1247+
) {
1248+
$handler(frame, $idx.into(), Some(error_code));
1249+
}
1250+
$idt.stack_segment_fault.set_handler_fn(handler);
1251+
}};
1252+
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 0, 1, 1, 0, 1) => {{
1253+
extern "x86-interrupt" fn handler(
1254+
frame: $crate::structures::idt::InterruptStackFrame,
1255+
error_code: u64,
1256+
) {
1257+
$handler(frame, $idx.into(), Some(error_code));
1258+
}
1259+
$idt.general_protection_fault.set_handler_fn(handler);
1260+
}};
1261+
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 0, 1, 1, 1, 0) => {{
1262+
extern "x86-interrupt" fn handler(
1263+
frame: $crate::structures::idt::InterruptStackFrame,
1264+
error_code: $crate::structures::idt::PageFaultErrorCode,
1265+
) {
1266+
$handler(frame, IDX.into(), Some(error_code.bits()));
1267+
}
1268+
$idt.page_fault.set_handler_fn(handler);
1269+
}};
1270+
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 0, 0, 0, 1) => {{
1271+
extern "x86-interrupt" fn handler(
1272+
frame: $crate::structures::idt::InterruptStackFrame,
1273+
error_code: u64,
1274+
) {
1275+
$handler(frame, $idx.into(), Some(error_code));
1276+
}
1277+
$idt.alignment_check.set_handler_fn(handler);
1278+
}};
1279+
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 0, 0, 1, 0) => {{
1280+
extern "x86-interrupt" fn handler(
1281+
frame: $crate::structures::idt::InterruptStackFrame,
1282+
) -> ! {
1283+
$handler(frame, $idx.into(), None);
1284+
panic!("General handler returned on machine check exception");
1285+
}
1286+
$idt.machine_check.set_handler_fn(handler);
1287+
}};
1288+
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 1, 0, 1) => {
1289+
extern "x86-interrupt" fn handler(
1290+
frame: $crate::structures::idt::InterruptStackFrame,
1291+
error_code: u64,
1292+
) {
1293+
$handler(frame, $idx.into(), Some(error_code));
1294+
}
1295+
$idt.vmm_communication_exception.set_handler_fn(handler);
1296+
};
1297+
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 1, 1, 0) => {{
1298+
extern "x86-interrupt" fn handler(
1299+
frame: $crate::structures::idt::InterruptStackFrame,
1300+
error_code: u64,
1301+
) {
1302+
$handler(frame, $idx.into(), Some(error_code));
1303+
}
1304+
$idt.security_exception.set_handler_fn(handler);
1305+
}};
1306+
1307+
// reserved_1
1308+
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 0, 1, 1, 1, 1) => {};
1309+
// reserved_2
1310+
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 0, 1, 0, 1) => {};
1311+
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 0, 1, 1, 0) => {};
1312+
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 0, 1, 1, 1) => {};
1313+
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 0, 0, 0) => {};
1314+
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 0, 0, 1) => {};
1315+
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 0, 1, 0) => {};
1316+
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 0, 1, 1) => {};
1317+
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 1, 0, 0) => {};
1318+
// reserved_3
1319+
($idt:expr, $handler:ident, $idx:ident, 0, 0, 0, 1, 1, 1, 1, 1) => {};
1320+
1321+
// set entries with `HandlerFunc` signature
1322+
($idt:expr, $handler:ident, $idx:ident $(, $_bits:tt)*) => {{
1323+
extern "x86-interrupt" fn handler(frame: $crate::structures::idt::InterruptStackFrame) {
1324+
$handler(frame, $idx.into(), None);
1325+
}
1326+
$idt[$idx as usize].set_handler_fn(handler);
1327+
}};
1328+
}
1329+
11231330
#[cfg(test)]
11241331
mod test {
11251332
use super::*;
11261333

1334+
fn entry_present(idt: &InterruptDescriptorTable, index: usize) -> bool {
1335+
let options = match index {
1336+
8 => &idt.double_fault.options,
1337+
10 => &idt.invalid_tss.options,
1338+
11 => &idt.segment_not_present.options,
1339+
12 => &idt.stack_segment_fault.options,
1340+
13 => &idt.general_protection_fault.options,
1341+
14 => &idt.page_fault.options,
1342+
15 => &idt.reserved_1.options,
1343+
17 => &idt.alignment_check.options,
1344+
18 => &idt.machine_check.options,
1345+
i @ 21..=28 => &idt.reserved_2[i - 21].options,
1346+
29 => &idt.vmm_communication_exception.options,
1347+
30 => &idt.security_exception.options,
1348+
31 => &idt.reserved_3.options,
1349+
other => &idt[other].options,
1350+
};
1351+
options.0.get_bit(15)
1352+
}
1353+
11271354
#[test]
11281355
fn size_test() {
11291356
use core::mem::size_of;
11301357
assert_eq!(size_of::<Entry<HandlerFunc>>(), 16);
11311358
assert_eq!(size_of::<InterruptDescriptorTable>(), 256 * 16);
11321359
}
11331360

1361+
#[cfg(all(feature = "instructions", feature = "abi_x86_interrupt"))]
1362+
// there seems to be a bug in LLVM that causes rustc to crash on windows when compiling this test:
1363+
// https://github.com/rust-osdev/x86_64/pull/285#issuecomment-962642984
1364+
#[cfg(not(windows))]
1365+
#[test]
1366+
fn default_handlers() {
1367+
fn general_handler(
1368+
_stack_frame: InterruptStackFrame,
1369+
_index: u8,
1370+
_error_code: Option<u64>,
1371+
) {
1372+
}
1373+
1374+
let mut idt = InterruptDescriptorTable::new();
1375+
set_general_handler!(&mut idt, general_handler, 0);
1376+
for i in 0..256 {
1377+
if i == 0 {
1378+
assert!(entry_present(&idt, i));
1379+
} else {
1380+
assert!(!entry_present(&idt, i));
1381+
}
1382+
}
1383+
set_general_handler!(&mut idt, general_handler, 14);
1384+
for i in 0..256 {
1385+
if i == 0 || i == 14 {
1386+
assert!(entry_present(&idt, i));
1387+
} else {
1388+
assert!(!entry_present(&idt, i));
1389+
}
1390+
}
1391+
set_general_handler!(&mut idt, general_handler, 32..64);
1392+
for i in 1..256 {
1393+
if i == 0 || i == 14 || (32..64).contains(&i) {
1394+
assert!(entry_present(&idt, i), "{}", i);
1395+
} else {
1396+
assert!(!entry_present(&idt, i));
1397+
}
1398+
}
1399+
set_general_handler!(&mut idt, general_handler);
1400+
for i in 0..256 {
1401+
if i == 15 || i == 31 || (21..=28).contains(&i) {
1402+
// reserved entries should not be set
1403+
assert!(!entry_present(&idt, i));
1404+
} else {
1405+
assert!(entry_present(&idt, i));
1406+
}
1407+
}
1408+
}
1409+
11341410
#[test]
11351411
fn entry_derive_test() {
11361412
fn foo(_: impl Clone + Copy + PartialEq + fmt::Debug) {}

0 commit comments

Comments
 (0)