Skip to content

Commit a5fdb5e

Browse files
committed
Rename and rewrite macro to make it more flexible
It is now aware of x86_64-specific exceptions and correctly handles reserved exceptions and exceptions without error code. The third argument is now optional and can be an index or a range. If it is omitted, all non-reserved entries of the IDT are set.
1 parent 27b973a commit a5fdb5e

File tree

5 files changed

+251
-109
lines changed

5 files changed

+251
-109
lines changed

Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ edition = "2018"
3131
bit_field = "0.9.0"
3232
bitflags = "1.0.4"
3333
array-init = { version = "0.1.1", optional = true }
34-
x86_64-idt-default-handler = { path = "x86_64-idt-default-handler", optional = true}
34+
x86_64-idt-general-handler = { path = "idt-general-handler", optional = true}
3535

3636
[build-dependencies]
3737
cc = { version = "1.0.37", optional = true }
@@ -44,4 +44,4 @@ nightly = [ "inline_asm", "const_fn", "abi_x86_interrupt" ]
4444
inline_asm = []
4545
abi_x86_interrupt = []
4646
const_fn = []
47-
proc_macros = [ "x86_64-idt-default-handler" ]
47+
proc_macros = [ "x86_64-idt-general-handler" ]

x86_64-idt-default-handler/Cargo.toml renamed to idt-general-handler/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[package]
2-
name = "x86_64-idt-default-handler"
2+
name = "x86_64-idt-general-handler"
33
version = "0.1.0"
44
authors = ["Philipp Oppermann <[email protected]>"]
55
edition = "2018"
@@ -12,3 +12,4 @@ proc-macro = true
1212
[dependencies]
1313
syn = { version = "1.0.33", features = ["full"] }
1414
quote = "1.0.7"
15+
proc-macro2 = "1.0.18"

idt-general-handler/src/lib.rs

+186
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
use proc_macro::TokenStream;
2+
use quote::{quote, quote_spanned};
3+
use std::{fmt::Display, ops::RangeInclusive};
4+
use syn::{
5+
parse::Parser, punctuated::Punctuated, spanned::Spanned, token::Comma, Expr, ExprLit, ExprPath,
6+
ExprRange, ExprReference, Lit, RangeLimits,
7+
};
8+
9+
extern crate proc_macro;
10+
11+
const RESERVED_HANDLERS: &[u8] = &[15, 21, 22, 23, 24, 25, 26, 27, 28, 29, 31];
12+
const HANDLERS_WITH_ERR_CODE: &[u8] = &[8, 10, 11, 12, 13, 14, 17, 30];
13+
14+
#[proc_macro]
15+
pub fn set_general_handler(item: TokenStream) -> TokenStream {
16+
set_general_handler_impl(item).unwrap_or_else(|err| err.to_compile_error().into())
17+
}
18+
19+
fn set_general_handler_impl(item: TokenStream) -> syn::Result<TokenStream> {
20+
let input = Punctuated::<Expr, Comma>::parse_separated_nonempty.parse(item)?;
21+
let (idt, function, range) = parse_input(&input)?;
22+
23+
let mut handlers = Vec::new();
24+
for index in range {
25+
if RESERVED_HANDLERS.contains(&index) {
26+
continue; // skip reserved handlers
27+
}
28+
let function_call = if HANDLERS_WITH_ERR_CODE.contains(&index) {
29+
quote_spanned!(function.span() => {
30+
#function(stack_frame, #index.into(), Some(error_code));
31+
})
32+
} else {
33+
quote_spanned!(function.span() => {
34+
#function(stack_frame, #index.into(), None);
35+
})
36+
};
37+
38+
let stack_frame_arg =
39+
quote! { stack_frame: &mut x86_64::structures::idt::InterruptStackFrame };
40+
41+
let handler_with_err_code = quote! {
42+
extern "x86-interrupt" fn handler(#stack_frame_arg, error_code: u64) {
43+
#function_call
44+
}
45+
};
46+
47+
let set_handler_fn = match index {
48+
8 => quote! {
49+
extern "x86-interrupt" fn handler(#stack_frame_arg, error_code: u64) -> ! {
50+
#function_call
51+
panic!("General handler returned on double fault");
52+
}
53+
(#idt).double_fault.set_handler_fn(handler);
54+
},
55+
10 => quote! {
56+
#handler_with_err_code
57+
(#idt).invalid_tss.set_handler_fn(handler);
58+
},
59+
11 => quote! {
60+
#handler_with_err_code
61+
(#idt).segment_not_present.set_handler_fn(handler);
62+
},
63+
12 => quote! {
64+
#handler_with_err_code
65+
(#idt).stack_segment_fault.set_handler_fn(handler);
66+
},
67+
13 => quote! {
68+
#handler_with_err_code
69+
(#idt).general_protection_fault.set_handler_fn(handler);
70+
},
71+
14 => quote! {
72+
extern "x86-interrupt" fn handler(#stack_frame_arg, error_code: x86_64::structures::idt::PageFaultErrorCode) {
73+
let error_code = error_code.bits();
74+
#function_call
75+
}
76+
(#idt).page_fault.set_handler_fn(handler);
77+
},
78+
17 => quote! {
79+
#handler_with_err_code
80+
(#idt).alignment_check.set_handler_fn(handler);
81+
},
82+
18 => quote! {
83+
extern "x86-interrupt" fn handler(#stack_frame_arg) -> ! {
84+
#function_call
85+
panic!("General handler returned on machine check exception");
86+
}
87+
(#idt).machine_check.set_handler_fn(handler);
88+
},
89+
30 => quote! {
90+
#handler_with_err_code
91+
(#idt).security_exception.set_handler_fn(handler);
92+
},
93+
index => quote! {
94+
extern "x86-interrupt" fn handler(#stack_frame_arg) {
95+
#function_call
96+
}
97+
(#idt)[#index.into()].set_handler_fn(handler);
98+
},
99+
};
100+
101+
// double `{{` to create new scope
102+
handlers.push(quote! {{
103+
#set_handler_fn
104+
}});
105+
}
106+
107+
let ret = quote! {
108+
#(#handlers)*
109+
};
110+
111+
// uncomment to print generated code:
112+
// println!("{}", ret);
113+
114+
Ok(ret.into())
115+
}
116+
117+
fn parse_input(
118+
input: &Punctuated<Expr, Comma>,
119+
) -> syn::Result<(&ExprReference, &ExprPath, RangeInclusive<u8>)> {
120+
if input.len() < 2 || input.len() > 3 {
121+
return Err(err(input, "expected 2 or 3 arguments"));
122+
}
123+
124+
let idt = match &input[0] {
125+
Expr::Reference(r) if r.mutability.is_some() => r,
126+
other => return Err(err(other, "expected a `&mut` reference to an IDT")),
127+
};
128+
129+
let function = match &input[1] {
130+
Expr::Path(path) => path,
131+
other => return Err(err(other, "expected the name of a handler function")),
132+
};
133+
134+
let range = if input.len() == 3 {
135+
match &input[2] {
136+
Expr::Lit(lit) => match &lit.lit {
137+
Lit::Int(int) => {
138+
let index: u8 = int.base10_parse()?;
139+
index..=index
140+
}
141+
other => return Err(err(other, "expected index or range")),
142+
},
143+
Expr::Range(range) => parse_range(&range)?,
144+
other => return Err(err(other, "expected index or range")),
145+
}
146+
} else {
147+
0..=255
148+
};
149+
150+
Ok((idt, function, range))
151+
}
152+
153+
fn parse_range(range: &ExprRange) -> syn::Result<RangeInclusive<u8>> {
154+
let range_start: u8 = match range.from.as_deref() {
155+
Some(&Expr::Lit(ExprLit {
156+
lit: Lit::Int(ref int),
157+
..
158+
})) => int.base10_parse()?,
159+
Some(other) => return Err(err(other, "Invalid range start")),
160+
None => 0,
161+
};
162+
let mut range_end: u8 = match range.to.as_deref() {
163+
Some(&Expr::Lit(ExprLit {
164+
lit: Lit::Int(ref int),
165+
..
166+
})) => int.base10_parse()?,
167+
Some(other) => return Err(err(other, "Invalid range end")),
168+
None => 255,
169+
};
170+
if let ExprRange {
171+
limits: RangeLimits::HalfOpen(_),
172+
to: Some(_),
173+
..
174+
} = range
175+
{
176+
range_end = range_end
177+
.checked_sub(1)
178+
.ok_or_else(|| err(&range.to, "Invalid range"))?;
179+
};
180+
181+
Ok(range_start..=range_end)
182+
}
183+
184+
fn err(tokens: impl quote::ToTokens, message: impl Display) -> syn::Error {
185+
syn::Error::new_spanned(tokens, message)
186+
}

src/structures/idt.rs

+61-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use core::marker::PhantomData;
1717
use core::ops::Bound::{Excluded, Included, Unbounded};
1818
use core::ops::{Deref, Index, IndexMut, RangeBounds};
1919
#[cfg(feature = "proc_macros")]
20-
pub use x86_64_idt_default_handler::set_default_handler;
20+
pub use x86_64_idt_general_handler::set_general_handler;
2121

2222
/// An Interrupt Descriptor Table with 256 entries.
2323
///
@@ -824,6 +824,26 @@ bitflags! {
824824
#[cfg(test)]
825825
mod test {
826826
use super::*;
827+
use crate as x86_64;
828+
829+
fn entry_present(idt: &InterruptDescriptorTable, index: usize) -> bool {
830+
let options = match index {
831+
8 => &idt.double_fault.options,
832+
10 => &idt.invalid_tss.options,
833+
11 => &idt.segment_not_present.options,
834+
12 => &idt.stack_segment_fault.options,
835+
13 => &idt.general_protection_fault.options,
836+
14 => &idt.page_fault.options,
837+
15 => &idt.reserved_1.options,
838+
17 => &idt.alignment_check.options,
839+
18 => &idt.machine_check.options,
840+
i @ 21..=29 => &idt.reserved_2[i - 21].options,
841+
30 => &idt.security_exception.options,
842+
31 => &idt.reserved_3.options,
843+
other => &idt[other].options,
844+
};
845+
options.0.get_bit(15)
846+
}
827847

828848
#[test]
829849
fn size_test() {
@@ -834,10 +854,47 @@ mod test {
834854

835855
#[cfg(feature = "proc_macros")]
836856
#[test]
837-
fn default_handlers() {
838-
fn default_handler(_stack_frame: &mut InterruptStackFrame, _index: u8) {}
857+
fn general_handlers() {
858+
fn general_handler(
859+
_stack_frame: &mut InterruptStackFrame,
860+
_index: u8,
861+
_error_code: Option<u64>,
862+
) {
863+
}
839864

840865
let mut idt = InterruptDescriptorTable::new();
841-
set_default_handler!(&mut idt, default_handler, 32..64);
866+
set_general_handler!(&mut idt, general_handler, 0);
867+
for i in 0..256 {
868+
if i == 0 {
869+
assert!(entry_present(&idt, i));
870+
} else {
871+
assert!(!entry_present(&idt, i));
872+
}
873+
}
874+
set_general_handler!(&mut idt, general_handler, 14);
875+
for i in 0..256 {
876+
if i == 0 || i == 14 {
877+
assert!(entry_present(&idt, i));
878+
} else {
879+
assert!(!entry_present(&idt, i));
880+
}
881+
}
882+
set_general_handler!(&mut idt, general_handler, 32..64);
883+
for i in 1..256 {
884+
if i == 0 || i == 14 || (i >= 32 && i < 64) {
885+
assert!(entry_present(&idt, i));
886+
} else {
887+
assert!(!entry_present(&idt, i));
888+
}
889+
}
890+
set_general_handler!(&mut idt, general_handler);
891+
for i in 0..256 {
892+
if i == 15 || i == 31 || (i >= 21 && i <= 29) {
893+
// reserved entries should not be set
894+
assert!(!entry_present(&idt, i));
895+
} else {
896+
assert!(entry_present(&idt, i));
897+
}
898+
}
842899
}
843900
}

0 commit comments

Comments
 (0)