1#![feature(proc_macro_diagnostic)]
4extern crate proc_macro;
5
6use proc_macro::TokenStream;
7use proc_macro2::Span;
8
9use syn::punctuated::Punctuated;
10use syn::spanned::Spanned;
11use syn::token::Comma;
12use syn::{AttributeArgs, Ident, ItemFn, Lit, Meta, MetaList, NestedMeta};
13
14use quote::quote;
15
16fn parse_two_ints(args: Punctuated<NestedMeta, Comma>) -> (u64, u64) {
18 if args.len() != 2 {
19 args.span()
20 .unstable()
21 .error("needs two numbers as parameters")
22 .emit();
23 }
24
25 let a = if let NestedMeta::Literal(Lit::Int(first)) = &args[0] {
26 first.value()
27 } else {
28 args[0]
29 .span()
30 .unstable()
31 .error("first parameter not an int literal")
32 .emit();
33 0
34 };
35
36 let b = if let NestedMeta::Literal(Lit::Int(second)) = &args[1] {
37 second.value()
38 } else {
39 args[1]
40 .span()
41 .unstable()
42 .error("second parameter not an int literal")
43 .emit();
44 0
45 };
46
47 (a, b)
48}
49
50fn should_panic(fun: &ItemFn) -> bool {
52 fun.attrs
53 .iter()
54 .find(|&attr| {
55 attr.path
56 .segments
57 .iter()
58 .find(|&path_segment| path_segment.ident == "should_panic")
59 .is_some()
60 })
61 .is_some()
62}
63
64#[proc_macro_attribute]
107pub fn x86test(args: TokenStream, input: TokenStream) -> TokenStream {
108 let args: Vec<NestedMeta> = syn::parse_macro_input!(args as AttributeArgs);
109 let input_fn = syn::parse_macro_input!(input as ItemFn);
110
111 let mut physical_memory: (u64, u64) = (0, 0);
112 let mut ioport_enable: (u64, u64) = (0, 0);
113 let should_panic = should_panic(&input_fn);
114 let mut should_halt = false;
115
116 for arg in args {
120 if let NestedMeta::Meta(Meta::List(MetaList {
122 ident,
123 paren_token: _,
124 nested,
125 })) = arg
126 {
127 match ident.to_string().as_str() {
128 "ram" => {
129 physical_memory = parse_two_ints(nested);
130 }
131 "ioport" => {
132 ioport_enable = parse_two_ints(nested);
133 }
134 x => unreachable!("unsupported attribute: {}", x),
135 }
136 } else if let NestedMeta::Meta(Meta::Word(ident)) = arg {
137 match ident.to_string().as_str() {
138 "should_halt" => should_halt = true,
139 x => unreachable!("unsupported attribute: {}", x),
140 }
141 }
142 }
143
144 let physical_memory_tuple = {
145 let (a, b) = physical_memory;
146 quote! { (#a, #b) }
147 };
148
149 let ioport_enable_tuple = {
150 let (a, b) = ioport_enable;
151 quote! { (#a as u16, #b as u32) }
152 };
153
154 let struct_name = format!("{}_genkvmtest", input_fn.ident);
155 let struct_ident = Ident::new(struct_name.as_str(), Span::call_site());
156 let test_name = format!("{}", input_fn.ident);
157 let fn_ident = input_fn.ident.clone();
158
159 let ast = quote! {
160 #[allow(non_upper_case_globals, unused_attributes)]
161 #[test_case]
162 static #struct_ident: X86TestFn = X86TestFn {
163 name: #test_name,
164 ignore: false,
165 identity_map: true,
166 physical_memory: #physical_memory_tuple,
167 ioport_enable: #ioport_enable_tuple,
168 should_panic: #should_panic,
169 should_halt: #should_halt,
170 testfn: x86test::StaticTestFn(|| {
171 #fn_ident();
172 unsafe { x86test::outw(0xf4, 0x00); }
174 })
175 };
176
177 #[allow(unused_attributes)]
179 #input_fn
180 };
181
182 ast.into()
183}