1use crate::tag::TagHeader;
4use crate::{StringError, TagIter, TagType, parse_slice_as_string};
5use core::fmt::{Debug, Formatter};
6use core::mem;
7use multiboot2_common::{MaybeDynSized, Tag};
8#[cfg(feature = "builder")]
9use {alloc::boxed::Box, multiboot2_common::new_boxed};
10
11#[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)]
15#[repr(C, align(8))]
16pub struct ModuleTag {
17 header: TagHeader,
18 mod_start: u32,
19 mod_end: u32,
20 cmdline: [u8],
22}
23
24impl ModuleTag {
25 #[cfg(feature = "builder")]
27 #[must_use]
28 pub fn new(start: u32, end: u32, cmdline: &str) -> Box<Self> {
29 let header = TagHeader::new(Self::ID, 0);
30 assert!(end > start, "must have a size");
31
32 let start = start.to_ne_bytes();
33 let end = end.to_ne_bytes();
34 let cmdline = cmdline.as_bytes();
35
36 if cmdline.ends_with(&[0]) {
37 new_boxed(header, &[&start, &end, cmdline])
38 } else {
39 new_boxed(header, &[&start, &end, cmdline, &[0]])
40 }
41 }
42
43 pub fn cmdline(&self) -> Result<&str, StringError> {
53 parse_slice_as_string(&self.cmdline)
54 }
55
56 #[must_use]
58 pub const fn start_address(&self) -> u32 {
59 self.mod_start
60 }
61
62 #[must_use]
64 pub const fn end_address(&self) -> u32 {
65 self.mod_end
66 }
67
68 #[must_use]
70 pub const fn module_size(&self) -> u32 {
71 self.mod_end - self.mod_start
72 }
73}
74
75impl MaybeDynSized for ModuleTag {
76 type Header = TagHeader;
77
78 const BASE_SIZE: usize = mem::size_of::<TagHeader>() + 2 * mem::size_of::<u32>();
79
80 fn dst_len(header: &TagHeader) -> usize {
81 assert!(header.size as usize >= Self::BASE_SIZE);
82 header.size as usize - Self::BASE_SIZE
83 }
84}
85
86impl Tag for ModuleTag {
87 type IDType = TagType;
88
89 const ID: TagType = TagType::Module;
90}
91
92impl Debug for ModuleTag {
93 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
94 f.debug_struct("ModuleTag")
95 .field("type", &self.header.typ)
96 .field("size", &self.header.size)
97 .field("mod_start", &self.mod_start)
99 .field("mod_end", &self.mod_end)
100 .field("mod_size", &self.module_size())
101 .field("cmdline", &self.cmdline())
102 .finish()
103 }
104}
105
106pub const fn module_iter(iter: TagIter) -> ModuleIter {
107 ModuleIter { iter }
108}
109
110#[derive(Clone)]
112pub struct ModuleIter<'a> {
113 iter: TagIter<'a>,
114}
115
116impl<'a> Iterator for ModuleIter<'a> {
117 type Item = &'a ModuleTag;
118
119 fn next(&mut self) -> Option<&'a ModuleTag> {
120 self.iter
121 .find(|tag| tag.header().typ == TagType::Module)
122 .map(|tag| tag.cast())
123 }
124}
125
126impl Debug for ModuleIter<'_> {
127 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
128 let mut list = f.debug_list();
129 self.clone().for_each(|tag| {
130 list.entry(&tag);
131 });
132 list.finish()
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use crate::GenericInfoTag;
140 use core::borrow::Borrow;
141 use multiboot2_common::test_utils::AlignedBytes;
142
143 #[rustfmt::skip]
144 fn get_bytes() -> AlignedBytes<24> {
145 AlignedBytes::new([
146 TagType::Module.val() as u8, 0, 0, 0,
147 22, 0, 0, 0,
148 0x00, 0xff, 0, 0,
150 0xff, 0xff, 0, 0,
152 b'h', b'e', b'l', b'l', b'o', b'\0',
153 0, 0,
155 ])
156 }
157
158 #[test]
160 fn test_parse_str() {
161 let bytes = get_bytes();
162 let tag = GenericInfoTag::ref_from_slice(bytes.borrow()).unwrap();
163 let tag = tag.cast::<ModuleTag>();
164 assert_eq!(tag.header.typ, TagType::Module);
165 assert_eq!(tag.cmdline(), Ok("hello"));
166 }
167
168 #[test]
170 #[cfg(feature = "builder")]
171 fn test_build_str() {
172 let tag = ModuleTag::new(0xff00, 0xffff, "hello");
173 let bytes = tag.as_bytes().as_ref();
174 let bytes = &bytes[..tag.header.size as usize];
175 assert_eq!(bytes, &get_bytes()[..tag.header().size as usize]);
176 assert_eq!(tag.cmdline(), Ok("hello"));
177
178 let tag = ModuleTag::new(0xff00, 0xffff, "hello\0");
180 let bytes = tag.as_bytes().as_ref();
181 let bytes = &bytes[..tag.header.size as usize];
182 assert_eq!(bytes, &get_bytes()[..tag.header().size as usize]);
183 assert_eq!(tag.cmdline(), Ok("hello"));
184
185 let tag = ModuleTag::new(0, 1, "AbCdEfGhUjK YEAH");
187 assert_eq!(tag.cmdline(), Ok("AbCdEfGhUjK YEAH"));
188 let tag = ModuleTag::new(0, 1, "AbCdEfGhUjK YEAH".repeat(42).as_str());
189 assert_eq!(tag.cmdline(), Ok("AbCdEfGhUjK YEAH".repeat(42).as_str()));
190 }
191}