information_request.rs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. use crate::HeaderTagType;
  2. #[cfg(feature = "builder")]
  3. use crate::StructAsBytes;
  4. use crate::{HeaderTagFlag, MbiTagType};
  5. #[cfg(feature = "builder")]
  6. use alloc::collections::BTreeSet;
  7. #[cfg(feature = "builder")]
  8. use alloc::vec::Vec;
  9. use core::fmt;
  10. use core::fmt::{Debug, Formatter};
  11. use core::marker::PhantomData;
  12. use core::mem::size_of;
  13. /// Specifies what specific tag types the bootloader should provide
  14. /// inside the mbi.
  15. #[derive(Copy, Clone)]
  16. #[repr(C, packed(8))]
  17. pub struct InformationRequestHeaderTag<const N: usize> {
  18. typ: HeaderTagType,
  19. flags: HeaderTagFlag,
  20. size: u32,
  21. // Length is determined by size.
  22. // Must be parsed during runtime with unsafe pointer magic and the size field.
  23. requests: [MbiTagType; N],
  24. }
  25. impl<const N: usize> InformationRequestHeaderTag<N> {
  26. /// Creates a new object. The size parameter is the value of the size property.
  27. /// It doesn't have to match with `N` necessarily, because during compile time we
  28. /// can't know the size of the tag in all runtime situations.
  29. pub fn new(flags: HeaderTagFlag, requests: [MbiTagType; N], size: Option<u32>) -> Self {
  30. InformationRequestHeaderTag {
  31. typ: HeaderTagType::InformationRequest,
  32. flags,
  33. size: size.unwrap_or(size_of::<Self>() as u32),
  34. requests,
  35. }
  36. }
  37. pub const fn typ(&self) -> HeaderTagType {
  38. self.typ
  39. }
  40. pub const fn flags(&self) -> HeaderTagFlag {
  41. self.flags
  42. }
  43. pub const fn size(&self) -> u32 {
  44. self.size
  45. }
  46. /// Returns the requests as array. Only works if the number of requests
  47. /// is known at compile time. For safety and correctness during runtime,
  48. /// you should use `req_iter()`.
  49. pub const fn requests(&self) -> [MbiTagType; N] {
  50. // cheap to copy, otherwise difficult with lifetime
  51. self.requests
  52. }
  53. /// Returns the number of [`MbiTagType`]-requests derived
  54. /// from the `size`-property. This method is useful
  55. /// because this struct uses a const generic, but during runtime
  56. /// we don't know the value in almost any case.
  57. pub const fn dynamic_requests_size(&self) -> u32 {
  58. let base_struct_size = size_of::<InformationRequestHeaderTag<0>>();
  59. let size_diff = self.size - base_struct_size as u32;
  60. if size_diff > 0 {
  61. size_diff / size_of::<u32>() as u32
  62. } else {
  63. 0
  64. }
  65. }
  66. /// Returns an [`InformationRequestHeaderTagIter`].
  67. pub fn req_iter(&self) -> InformationRequestHeaderTagIter {
  68. let base_struct_size = size_of::<InformationRequestHeaderTag<0>>();
  69. let count = self.dynamic_requests_size();
  70. let base_ptr = self as *const InformationRequestHeaderTag<N>;
  71. let base_ptr = base_ptr as *const u8;
  72. let base_ptr = unsafe { base_ptr.add(base_struct_size) };
  73. let base_ptr = base_ptr as *const MbiTagType;
  74. InformationRequestHeaderTagIter::new(count, base_ptr)
  75. }
  76. }
  77. impl<const N: usize> Debug for InformationRequestHeaderTag<N> {
  78. fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
  79. f.debug_struct("InformationRequestHeaderTag")
  80. .field("type", &{ self.typ })
  81. .field("flags", &{ self.flags })
  82. .field("size", &{ self.size })
  83. .field("requests", &{ self.req_iter() })
  84. .finish()
  85. }
  86. }
  87. #[cfg(feature = "builder")]
  88. impl<const N: usize> crate::StructAsBytes for InformationRequestHeaderTag<N> {}
  89. /// Helper to build the dynamically sized [`InformationRequestHeaderTag`]
  90. /// at runtime.
  91. #[derive(Debug)]
  92. #[cfg(feature = "builder")]
  93. pub struct InformationRequestHeaderTagBuilder {
  94. flag: HeaderTagFlag,
  95. irs: BTreeSet<MbiTagType>,
  96. }
  97. #[cfg(feature = "builder")]
  98. impl InformationRequestHeaderTagBuilder {
  99. /// New builder.
  100. pub fn new(flag: HeaderTagFlag) -> Self {
  101. Self {
  102. irs: BTreeSet::new(),
  103. flag,
  104. }
  105. }
  106. /// Returns the expected length of the information request tag,
  107. /// when the `build`-method gets called.
  108. pub fn expected_len(&self) -> usize {
  109. let basic_header_size = size_of::<InformationRequestHeaderTag<0>>();
  110. let req_tags_size = self.irs.len() * size_of::<MbiTagType>();
  111. basic_header_size + req_tags_size
  112. }
  113. /// Adds an [`MbiTagType`] to the information request.
  114. pub fn add_ir(mut self, tag: MbiTagType) -> Self {
  115. self.irs.insert(tag);
  116. self
  117. }
  118. /// Adds multiple [`MbiTagType`] to the information request.
  119. pub fn add_irs(mut self, tags: &[MbiTagType]) -> Self {
  120. self.irs.extend(tags);
  121. self
  122. }
  123. /// Builds the bytes of the dynamically sized information request header.
  124. pub fn build(self) -> Vec<u8> {
  125. let expected_len = self.expected_len();
  126. let mut data = Vec::with_capacity(expected_len);
  127. let basic_tag = InformationRequestHeaderTag::<0>::new(
  128. self.flag,
  129. [],
  130. // we put the expected length here already, because in the next step we write
  131. // all the tags into the byte array. We can't know this during compile time,
  132. // therefore N is 0.
  133. Some(expected_len as u32),
  134. );
  135. data.extend(basic_tag.struct_as_bytes());
  136. #[cfg(debug_assertions)]
  137. {
  138. let basic_tag_size = size_of::<InformationRequestHeaderTag<0>>();
  139. assert_eq!(
  140. data.len(),
  141. basic_tag_size,
  142. "the vector must be as long as the basic tag!"
  143. );
  144. }
  145. for tag in &self.irs {
  146. let bytes: [u8; 4] = (*tag as u32).to_ne_bytes();
  147. data.extend(&bytes);
  148. }
  149. debug_assert_eq!(
  150. data.len(),
  151. expected_len,
  152. "the byte vector must be as long as the expected size of the struct"
  153. );
  154. data
  155. }
  156. }
  157. /// Iterates the dynamically sized information request structure and finds all MBI tags
  158. /// that are requested.
  159. #[derive(Copy, Clone)]
  160. pub struct InformationRequestHeaderTagIter<'a> {
  161. base_ptr: *const MbiTagType,
  162. i: u32,
  163. count: u32,
  164. _marker: PhantomData<&'a ()>,
  165. }
  166. impl<'a> InformationRequestHeaderTagIter<'a> {
  167. fn new(count: u32, base_ptr: *const MbiTagType) -> Self {
  168. Self {
  169. i: 0,
  170. count,
  171. base_ptr,
  172. _marker: PhantomData::default(),
  173. }
  174. }
  175. }
  176. impl<'a> Iterator for InformationRequestHeaderTagIter<'a> {
  177. type Item = &'a MbiTagType;
  178. fn next(&mut self) -> Option<Self::Item> {
  179. if self.i < self.count {
  180. let ptr = unsafe { self.base_ptr.offset(self.i as isize) };
  181. self.i += 1;
  182. Some(unsafe { &*ptr })
  183. } else {
  184. None
  185. }
  186. }
  187. }
  188. impl<'a> Debug for InformationRequestHeaderTagIter<'a> {
  189. fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
  190. let mut debug = f.debug_list();
  191. (*self).for_each(|e| {
  192. debug.entry(e);
  193. });
  194. debug.finish()
  195. }
  196. }
  197. #[cfg(test)]
  198. mod tests {
  199. use crate::{
  200. HeaderTagFlag, InformationRequestHeaderTag, InformationRequestHeaderTagBuilder, MbiTagType,
  201. };
  202. #[test]
  203. fn test_builder() {
  204. let builder = InformationRequestHeaderTagBuilder::new(HeaderTagFlag::Required)
  205. .add_ir(MbiTagType::EfiMmap)
  206. .add_ir(MbiTagType::BootLoaderName)
  207. .add_ir(MbiTagType::Cmdline);
  208. // type(u16) + flags(u16) + size(u32) + 3 tags (u32)
  209. assert_eq!(builder.expected_len(), 2 + 2 + 4 + 3 * 4);
  210. let tag = builder.build();
  211. let tag = unsafe {
  212. (tag.as_ptr() as *const InformationRequestHeaderTag<3>)
  213. .as_ref()
  214. .unwrap()
  215. };
  216. assert_eq!(tag.flags, HeaderTagFlag::Required);
  217. // type(u16) + flags(u16) + size(u32) + 3 tags (u32)
  218. assert_eq!(tag.size, 2 + 2 + 4 + 3 * 4);
  219. assert_eq!(tag.dynamic_requests_size(), 3);
  220. assert!(tag.requests.contains(&MbiTagType::EfiMmap));
  221. assert!(tag.requests.contains(&MbiTagType::BootLoaderName));
  222. assert!(tag.requests.contains(&MbiTagType::Cmdline));
  223. assert_eq!(tag.requests.len(), 3);
  224. assert!(!tag.requests.contains(&MbiTagType::AcpiV1));
  225. println!("{:#?}", tag);
  226. }
  227. }