lib.rs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. use proc_macro::TokenStream;
  2. use quote::{quote, ToTokens};
  3. use syn::{parse_macro_input, Data, DeriveInput, Generics, Ident, Member};
  4. #[derive(Clone, Default)]
  5. struct RustSBIImp {
  6. fence: Option<Member>,
  7. hsm: Option<Member>,
  8. ipi: Option<Member>,
  9. reset: Option<Member>,
  10. timer: Option<Member>,
  11. pmu: Option<Member>,
  12. console: Option<Member>,
  13. susp: Option<Member>,
  14. cppc: Option<Member>,
  15. nacl: Option<Member>,
  16. sta: Option<Member>,
  17. env_info: Option<Member>,
  18. }
  19. /// This macro should be used in `rustsbi` crate as `rustsbi::RustSBI`.
  20. #[proc_macro_derive(RustSBI, attributes(rustsbi))]
  21. pub fn derive_rustsbi(input: TokenStream) -> TokenStream {
  22. let input = parse_macro_input!(input as DeriveInput);
  23. let Data::Struct(strukt) = input.data else {
  24. panic!("#[derive(RustSBI)] must be used on structs");
  25. };
  26. let mut imp = RustSBIImp::default();
  27. let mut replace_sbi_extension_ident =
  28. |extension_name: &str, member: Member| match extension_name {
  29. "rfnc" | "fence" => (true, imp.fence.replace(member)),
  30. "hsm" => (true, imp.hsm.replace(member)),
  31. "spi" | "ipi" => (true, imp.ipi.replace(member)),
  32. "srst" | "reset" => (true, imp.reset.replace(member)),
  33. "time" | "timer" => (true, imp.timer.replace(member)),
  34. "pmu" => (true, imp.pmu.replace(member)),
  35. "dbcn" | "console" => (true, imp.console.replace(member)),
  36. "susp" => (true, imp.susp.replace(member)),
  37. "cppc" => (true, imp.cppc.replace(member)),
  38. "nacl" => (true, imp.nacl.replace(member)),
  39. "sta" => (true, imp.sta.replace(member)),
  40. "info" | "env_info" => (true, imp.env_info.replace(member)),
  41. _ => (false, None),
  42. };
  43. let mut ans = TokenStream::new();
  44. let check_already_exists = |field: &syn::Field,
  45. extension_name: &str,
  46. origin: Option<Member>,
  47. ans: &mut TokenStream| {
  48. if let Some(_origin) = origin {
  49. // TODO: provide more detailed proc macro error hinting that previous
  50. // definition of this extension resides in `origin` once RFC 1566
  51. // (Procedural Macro Diagnostics) is stablized.
  52. // Link: https://github.com/rust-lang/rust/issues/54140
  53. let error = syn::Error::new_spanned(
  54. field,
  55. format!(
  56. "more than one field defined SBI extension '{}'. \
  57. At most one fields should define the same SBI extension; consider using \
  58. #[rustsbi(skip)] to ignore fields that shouldn't be treated as an extension.",
  59. extension_name
  60. ),
  61. );
  62. ans.extend(TokenStream::from(error.to_compile_error()));
  63. }
  64. };
  65. for (i, field) in strukt.fields.iter().enumerate() {
  66. let member = match &field.ident {
  67. Some(ident) => Member::Named(ident.clone()),
  68. None => Member::Unnamed(i.into()),
  69. };
  70. let mut field_already_parsed = false;
  71. for attr in &field.attrs {
  72. if !attr.path().is_ident("rustsbi") {
  73. continue;
  74. }
  75. let parsed = attr.parse_nested_meta(|meta| {
  76. let mut current_meta_accepted = false;
  77. if meta.path.is_ident("skip") {
  78. // accept meta but do nothing, effectively skip this field in RustSBI
  79. current_meta_accepted = true;
  80. } else if let Some(meta_path_ident) = meta.path.get_ident() {
  81. let extension_name = &meta_path_ident.to_string();
  82. let (replaced, origin) =
  83. replace_sbi_extension_ident(extension_name, member.clone());
  84. if replaced {
  85. check_already_exists(field, extension_name, origin, &mut ans);
  86. current_meta_accepted = true;
  87. }
  88. }
  89. if current_meta_accepted {
  90. field_already_parsed = true;
  91. Ok(())
  92. } else {
  93. let path = meta.path.to_token_stream().to_string().replace(' ', "");
  94. Err(meta.error(format_args!("unknown RustSBI variant attribute `{}`", path)))
  95. }
  96. });
  97. if let Err(err) = parsed {
  98. ans.extend(TokenStream::from(err.to_compile_error()));
  99. }
  100. }
  101. // Already parsed by inner attribute.
  102. // Could be either skipped using #[rustsbi(skip)], or renamed using #[rustsbi(some_extension)]
  103. if field_already_parsed {
  104. continue;
  105. }
  106. if let Some(field_ident) = &field.ident {
  107. let (_replaced, origin) =
  108. replace_sbi_extension_ident(field_ident.to_string().as_str(), member);
  109. check_already_exists(field, &field_ident.to_string(), origin, &mut ans);
  110. }
  111. }
  112. ans.extend(impl_derive_rustsbi(&input.ident, imp, &input.generics));
  113. ans
  114. }
  115. fn impl_derive_rustsbi(name: &Ident, imp: RustSBIImp, generics: &Generics) -> TokenStream {
  116. let base_probe: usize = 1;
  117. let fence_probe: usize = if imp.fence.is_some() { 1 } else { 0 };
  118. let hsm_probe: usize = if imp.hsm.is_some() { 1 } else { 0 };
  119. let ipi_probe: usize = if imp.ipi.is_some() { 1 } else { 0 };
  120. let reset_probe: usize = if imp.reset.is_some() { 1 } else { 0 };
  121. let timer_probe: usize = if imp.timer.is_some() { 1 } else { 0 };
  122. let pmu_probe: usize = if imp.pmu.is_some() { 1 } else { 0 };
  123. let console_probe: usize = if imp.console.is_some() { 1 } else { 0 };
  124. let susp_probe: usize = if imp.susp.is_some() { 1 } else { 0 };
  125. let cppc_probe: usize = if imp.cppc.is_some() { 1 } else { 0 };
  126. let nacl_probe: usize = if imp.nacl.is_some() { 1 } else { 0 };
  127. let sta_probe: usize = if imp.sta.is_some() { 1 } else { 0 };
  128. let probe = quote! {
  129. ::rustsbi::_StandardExtensionProbe {
  130. base: #base_probe,
  131. fence: #fence_probe,
  132. hsm: #hsm_probe,
  133. ipi: #ipi_probe,
  134. reset: #reset_probe,
  135. timer: #timer_probe,
  136. pmu: #pmu_probe,
  137. console: #console_probe,
  138. susp: #susp_probe,
  139. cppc: #cppc_probe,
  140. nacl: #nacl_probe,
  141. sta: #sta_probe,
  142. }
  143. };
  144. let mut match_arms = quote! {};
  145. let base_procedure = if let Some(env_info) = imp.env_info {
  146. quote! {
  147. ::rustsbi::spec::base::EID_BASE => ::rustsbi::_rustsbi_base_env_info(param, function, &self.#env_info, #probe),
  148. }
  149. } else {
  150. match () {
  151. #[cfg(not(feature = "machine"))]
  152. () => quote! {
  153. ::rustsbi::spec::base::EID_BASE => compile_error!(
  154. "can't derive RustSBI: #[cfg(feature = \"machine\")] is needed to derive RustSBI with no extra `EnvInfo` provided; \
  155. consider adding an `info` parameter to provide machine information implementing `rustsbi::EnvInfo`\
  156. if RustSBI is not run on machine mode."
  157. ),
  158. },
  159. #[cfg(feature = "machine")]
  160. () => quote! {
  161. ::rustsbi::spec::base::EID_BASE => ::rustsbi::_rustsbi_base_bare(param, function, #probe),
  162. },
  163. }
  164. };
  165. match_arms.extend(base_procedure);
  166. if let Some(fence) = &imp.fence {
  167. match_arms.extend(quote! {
  168. ::rustsbi::spec::rfnc::EID_RFNC => ::rustsbi::_rustsbi_fence(&self.#fence, param, function),
  169. })
  170. };
  171. if let Some(timer) = &imp.timer {
  172. match_arms.extend(quote! {
  173. ::rustsbi::spec::time::EID_TIME => ::rustsbi::_rustsbi_timer(&self.#timer, param, function),
  174. })
  175. };
  176. if let Some(ipi) = &imp.ipi {
  177. match_arms.extend(quote! {
  178. ::rustsbi::spec::spi::EID_SPI => ::rustsbi::_rustsbi_ipi(&self.#ipi, param, function),
  179. })
  180. }
  181. if let Some(hsm) = &imp.hsm {
  182. match_arms.extend(quote! {
  183. ::rustsbi::spec::hsm::EID_HSM => ::rustsbi::_rustsbi_hsm(&self.#hsm, param, function),
  184. })
  185. }
  186. if let Some(reset) = &imp.reset {
  187. match_arms.extend(quote! {
  188. ::rustsbi::spec::srst::EID_SRST => ::rustsbi::_rustsbi_reset(&self.#reset, param, function),
  189. })
  190. }
  191. if let Some(pmu) = &imp.pmu {
  192. match_arms.extend(quote! {
  193. ::rustsbi::spec::pmu::EID_PMU => ::rustsbi::_rustsbi_pmu(&self.#pmu, param, function),
  194. })
  195. }
  196. if let Some(console) = &imp.console {
  197. match_arms.extend(quote! {
  198. ::rustsbi::spec::dbcn::EID_DBCN => ::rustsbi::_rustsbi_console(&self.#console, param, function),
  199. })
  200. }
  201. if let Some(susp) = &imp.susp {
  202. match_arms.extend(quote! {
  203. ::rustsbi::spec::susp::EID_SUSP => ::rustsbi::_rustsbi_susp(&self.#susp, param, function),
  204. })
  205. }
  206. if let Some(cppc) = &imp.cppc {
  207. match_arms.extend(quote! {
  208. ::rustsbi::spec::cppc::EID_CPPC => ::rustsbi::_rustsbi_cppc(&self.#cppc, param, function),
  209. })
  210. }
  211. if let Some(nacl) = &imp.nacl {
  212. match_arms.extend(quote! {
  213. ::rustsbi::spec::nacl::EID_NACL => ::rustsbi::_rustsbi_nacl(&self.#nacl, param, function),
  214. })
  215. }
  216. if let Some(sta) = &imp.sta {
  217. match_arms.extend(quote! {
  218. ::rustsbi::spec::sta::EID_STA => ::rustsbi::_rustsbi_sta(&self.#sta, param, function),
  219. })
  220. }
  221. let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
  222. let gen = quote! {
  223. impl #impl_generics ::rustsbi::RustSBI for #name #ty_generics #where_clause {
  224. #[inline]
  225. fn handle_ecall(&self, extension: usize, function: usize, param: [usize; 6]) -> ::rustsbi::SbiRet {
  226. match extension {
  227. #match_arms
  228. _ => ::rustsbi::SbiRet::not_supported(),
  229. }
  230. }
  231. }
  232. };
  233. gen.into()
  234. }