@@ -685,6 +685,9 @@ pub type DivergingHandlerFuncWithErrCode =
685
685
#[ derive( Copy , Clone , Debug ) ]
686
686
pub struct DivergingHandlerFuncWithErrCode ( ( ) ) ;
687
687
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
+
688
691
impl < F > Entry < F > {
689
692
/// Creates a non-present IDT entry (but sets the must-be-one bits).
690
693
#[ inline]
@@ -1120,17 +1123,290 @@ pub enum ExceptionVector {
1120
1123
Security = 0x1E ,
1121
1124
}
1122
1125
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
+
1123
1330
#[ cfg( test) ]
1124
1331
mod test {
1125
1332
use super :: * ;
1126
1333
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
+
1127
1354
#[ test]
1128
1355
fn size_test ( ) {
1129
1356
use core:: mem:: size_of;
1130
1357
assert_eq ! ( size_of:: <Entry <HandlerFunc >>( ) , 16 ) ;
1131
1358
assert_eq ! ( size_of:: <InterruptDescriptorTable >( ) , 256 * 16 ) ;
1132
1359
}
1133
1360
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
+
1134
1410
#[ test]
1135
1411
fn entry_derive_test ( ) {
1136
1412
fn foo ( _: impl Clone + Copy + PartialEq + fmt:: Debug ) { }
0 commit comments