1mod percpu;
2
3use proc_macro::TokenStream;
4use proc_macro2::Span;
5use quote::{format_ident, quote};
6use syn::{parse_macro_input, Error, ItemFn, ItemStatic};
7
8#[proc_macro_attribute]
9pub fn arch_entry(_input: TokenStream, annotated_item: TokenStream) -> TokenStream {
10 let annotated_item = parse_macro_input!(annotated_item as ItemFn);
11 TokenStream::from(quote! {
12 #[export_name = "_main_for_arch"]
13 #annotated_item
14 })
15}
16
17#[proc_macro_attribute]
18pub fn arch_interrupt(_input: TokenStream, annotated_item: TokenStream) -> TokenStream {
19 let annotated_item = parse_macro_input!(annotated_item as ItemFn);
20 TokenStream::from(quote! {
21 #[export_name = "_interrupt_for_arch"]
22 #annotated_item
23 })
24}
25
26#[proc_macro_attribute]
32pub fn def_percpu(attr: TokenStream, item: TokenStream) -> TokenStream {
33 if !attr.is_empty() {
34 return compiler_error(Error::new(
35 Span::call_site(),
36 "expect an empty attribute: `#[def_percpu]`",
37 ));
38 }
39
40 let ast = syn::parse_macro_input!(item as ItemStatic);
41
42 let attrs = &ast.attrs;
43 let vis = &ast.vis;
44 let name = &ast.ident;
45 let ty = &ast.ty;
46 let init_expr = &ast.expr;
47
48 let inner_symbol_name = &format_ident!("__PERCPU_{}", name);
49 let struct_name = &format_ident!("{}_WRAPPER", name);
50
51 let ty_str = quote!(#ty).to_string();
52 let is_primitive_int = ["bool", "u8", "u16", "u32", "u64", "usize"].contains(&ty_str.as_str());
53
54 let read_write_methods = if is_primitive_int {
56 quote! {
57 #[inline]
63 pub unsafe fn read_current_raw(&self) -> #ty {
64 unsafe { *self.current_ptr() }
65 }
66
67 #[inline]
73 pub unsafe fn write_current_raw(&self, val: #ty) {
74 unsafe { *self.current_ref_mut_raw() = val };
75 }
76
77 pub fn read_current(&self) -> #ty {
80 unsafe { self.read_current_raw() }
81 }
82
83 pub fn write_current(&self, val: #ty) {
86 unsafe { self.write_current_raw(val) }
87 }
88 }
89 } else {
90 quote! {}
91 };
92
93 let current_ptr = percpu::gen_current_ptr(inner_symbol_name, ty);
94 quote! {
95 #[cfg_attr(not(target_os = "macos"), link_section = "percpu")] #[used(linker)]
97 #(#attrs)*
98 static mut #inner_symbol_name: #ty = #init_expr;
99
100 #[doc = concat!("Wrapper struct for the per-CPU data [`", stringify!(#name), "`]")]
101 #[allow(non_camel_case_types)]
102 #vis struct #struct_name {}
103
104 #(#attrs)*
105 #vis static #name: #struct_name = #struct_name {};
106
107 impl #struct_name {
108 #[inline]
110 pub fn offset(&self) -> usize {
111 extern "Rust" {
112 fn __start_percpu();
113 }
114 unsafe {
115 &#inner_symbol_name as *const _ as usize - __start_percpu as usize
116 }
117 }
118
119 #[inline]
125 pub unsafe fn current_ptr(&self) -> *const #ty {
126 #current_ptr
127 }
128
129 #[inline]
135 pub unsafe fn current_ref_raw(&self) -> &#ty {
136 &*self.current_ptr()
137 }
138
139 #[inline]
145 #[allow(clippy::mut_from_ref)]
146 pub unsafe fn current_ref_mut_raw(&self) -> &mut #ty {
147 &mut *(self.current_ptr() as *mut #ty)
148 }
149
150 pub fn with_current<F, T>(&self, f: F) -> T
153 where
154 F: FnOnce(&mut #ty) -> T,
155 {
156 f(unsafe { self.current_ref_mut_raw() })
157 }
158
159 #read_write_methods
160 }
161 }
162 .into()
163}
164
165#[proc_macro]
166pub fn define_arch_mods(_input: TokenStream) -> TokenStream {
167 quote! {
168 #[cfg(target_arch = "riscv64")]
169 mod riscv64;
170 #[cfg(target_arch = "riscv64")]
171 #[allow(unused_imports)]
172 pub use riscv64::*;
173 #[cfg(target_arch = "aarch64")]
174 mod aarch64;
175 #[cfg(target_arch = "aarch64")]
176 #[allow(unused_imports)]
177 pub use aarch64::*;
178 #[cfg(target_arch = "x86_64")]
179 mod x86_64;
180 #[cfg(target_arch = "x86_64")]
181 #[allow(unused_imports)]
182 pub use x86_64::*;
183 #[cfg(target_arch = "loongarch64")]
184 mod loongarch64;
185 #[cfg(target_arch = "loongarch64")]
186 #[allow(unused_imports)]
187 pub use loongarch64::*;
188 }
189 .into()
190}
191
192fn compiler_error(err: Error) -> TokenStream {
193 err.to_compile_error().into()
194}