1use memf_core::object_reader::ObjectReader;
7use memf_format::PhysicalMemoryProvider;
8
9use crate::{Error, ModuleInfo, ModuleState, Result};
10
11pub fn walk_modules<P: PhysicalMemoryProvider>(
13 reader: &ObjectReader<P>,
14) -> Result<Vec<ModuleInfo>> {
15 let modules_addr = reader.required_symbol("modules")?;
16
17 let module_addrs = reader.walk_list(modules_addr, "module", "list")?;
18
19 let mut modules = Vec::new();
20 for &mod_addr in &module_addrs {
21 if let Ok(info) = read_module_info(reader, mod_addr) {
22 modules.push(info);
23 }
24 }
25
26 Ok(modules)
27}
28
29fn read_module_info<P: PhysicalMemoryProvider>(
30 reader: &ObjectReader<P>,
31 mod_addr: u64,
32) -> Result<ModuleInfo> {
33 let name = reader.read_field_string(mod_addr, "module", "name", 56)?;
34 let state: u32 = reader.read_field(mod_addr, "module", "state")?;
35 let (base_addr, size) = read_core_layout(reader, mod_addr)?;
36
37 Ok(ModuleInfo {
38 name,
39 base_addr,
40 size,
41 state: ModuleState::from_raw(state),
42 })
43}
44
45fn read_core_layout<P: PhysicalMemoryProvider>(
46 reader: &ObjectReader<P>,
47 mod_addr: u64,
48) -> Result<(u64, u64)> {
49 if let (Some(layout_off), Some(_base_off), Some(_size_off)) = (
51 reader.symbols().field_offset("module", "core_layout"),
52 reader.symbols().field_offset("module_layout", "base"),
53 reader.symbols().field_offset("module_layout", "size"),
54 ) {
55 let layout_addr = mod_addr + layout_off;
56 let base: u64 = reader.read_field(layout_addr, "module_layout", "base")?;
57 let size: u32 = reader.read_field(layout_addr, "module_layout", "size")?;
58 return Ok((base, u64::from(size)));
59 }
60
61 if reader
63 .symbols()
64 .field_offset("module", "module_core")
65 .is_some()
66 && reader
67 .symbols()
68 .field_offset("module", "core_size")
69 .is_some()
70 {
71 let base: u64 = reader.read_field(mod_addr, "module", "module_core")?;
72 let size: u32 = reader.read_field(mod_addr, "module", "core_size")?;
73 return Ok((base, u64::from(size)));
74 }
75
76 Err(Error::WalkFailed {
77 walker: "get_module_core_layout",
78 reason: "cannot determine module core layout: no core_layout or module_core field".into(),
79 })
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use crate::testing::make_reader;
86 use memf_core::object_reader::ObjectReader;
87 use memf_core::test_builders::{flags, PageTableBuilder, SyntheticPhysMem};
88 use memf_core::vas::{TranslationMode, VirtualAddressSpace};
89 use memf_symbols::isf::IsfResolver;
90 use memf_symbols::test_builders::IsfBuilder;
91
92 fn make_module_reader(data: &[u8], vaddr: u64, paddr: u64) -> ObjectReader<SyntheticPhysMem> {
93 let isf = IsfBuilder::new()
94 .add_struct("module", 256)
95 .add_field("module", "list", 0, "list_head")
96 .add_field("module", "name", 16, "char")
97 .add_field("module", "state", 72, "unsigned int")
98 .add_field("module", "core_layout", 80, "module_layout")
99 .add_struct("module_layout", 32)
100 .add_field("module_layout", "base", 0, "pointer")
101 .add_field("module_layout", "size", 8, "unsigned int")
102 .add_struct("list_head", 16)
103 .add_field("list_head", "next", 0, "pointer")
104 .add_field("list_head", "prev", 8, "pointer")
105 .add_symbol("modules", vaddr);
106 let ptb = PageTableBuilder::new()
107 .map_4k(vaddr, paddr, flags::WRITABLE)
108 .write_phys(paddr, data);
109 make_reader(&isf, ptb)
110 }
111
112 #[test]
113 fn walk_two_modules() {
114 let vaddr: u64 = 0xFFFF_8000_0010_0000;
115 let paddr: u64 = 0x0080_0000;
116 let head = vaddr;
117 let a_list = vaddr + 0x100;
118 let b_list = vaddr + 0x300;
119
120 let mut data = vec![0u8; 4096];
121
122 data[0..8].copy_from_slice(&a_list.to_le_bytes());
124 data[8..16].copy_from_slice(&b_list.to_le_bytes());
125
126 data[0x100..0x108].copy_from_slice(&b_list.to_le_bytes());
128 data[0x108..0x110].copy_from_slice(&head.to_le_bytes());
129 data[0x110..0x118].copy_from_slice(b"ext4\0\0\0\0");
130 data[0x148..0x14C].copy_from_slice(&0u32.to_le_bytes());
131 data[0x150..0x158].copy_from_slice(&0xFFFF_A000u64.to_le_bytes());
132 data[0x158..0x15C].copy_from_slice(&0x2000u32.to_le_bytes());
133
134 data[0x300..0x308].copy_from_slice(&head.to_le_bytes());
136 data[0x308..0x310].copy_from_slice(&a_list.to_le_bytes());
137 data[0x310..0x318].copy_from_slice(b"nf_nat\0\0");
138 data[0x348..0x34C].copy_from_slice(&0u32.to_le_bytes());
139 data[0x350..0x358].copy_from_slice(&0xFFFF_B000u64.to_le_bytes());
140 data[0x358..0x35C].copy_from_slice(&0x1000u32.to_le_bytes());
141
142 let reader = make_module_reader(&data, vaddr, paddr);
143 let mods = walk_modules(&reader).unwrap();
144
145 assert_eq!(mods.len(), 2);
146 assert_eq!(mods[0].name, "ext4");
147 assert_eq!(mods[0].base_addr, 0xFFFF_A000);
148 assert_eq!(mods[0].size, 0x2000);
149 assert_eq!(mods[0].state, ModuleState::Live);
150 assert_eq!(mods[1].name, "nf_nat");
151 assert_eq!(mods[1].base_addr, 0xFFFF_B000);
152 assert_eq!(mods[1].size, 0x1000);
153 }
154
155 #[test]
156 fn walk_modules_with_legacy_module_core_layout() {
157 let vaddr: u64 = 0xFFFF_8000_0020_0000;
160 let paddr: u64 = 0x0090_0000;
161 let mod_vaddr: u64 = 0xFFFF_8000_0021_0000;
162 let mod_paddr: u64 = 0x0091_0000;
163
164 let mut head_page = [0u8; 4096];
165 head_page[0..8].copy_from_slice(&mod_vaddr.to_le_bytes());
166 head_page[8..16].copy_from_slice(&mod_vaddr.to_le_bytes());
167
168 let mut mod_page = [0u8; 4096];
169 mod_page[0..8].copy_from_slice(&vaddr.to_le_bytes());
171 mod_page[8..16].copy_from_slice(&vaddr.to_le_bytes());
172 mod_page[16..23].copy_from_slice(b"virtio\0");
174 mod_page[72..76].copy_from_slice(&0u32.to_le_bytes()); mod_page[80..88].copy_from_slice(&0xFFFF_C000_0000u64.to_le_bytes());
178 mod_page[88..92].copy_from_slice(&0x8000u32.to_le_bytes());
180
181 let isf = IsfBuilder::new()
182 .add_struct("module", 256)
183 .add_field("module", "list", 0x00u64, "list_head")
184 .add_field("module", "name", 0x10u64, "char")
185 .add_field("module", "state", 0x48u64, "unsigned int")
186 .add_field("module", "module_core", 0x50u64, "pointer")
187 .add_field("module", "core_size", 0x58u64, "unsigned int")
188 .add_struct("list_head", 16)
190 .add_field("list_head", "next", 0x00u64, "pointer")
191 .add_field("list_head", "prev", 0x08u64, "pointer")
192 .add_symbol("modules", vaddr)
193 .build_json();
194
195 let resolver = IsfResolver::from_value(&isf).unwrap();
196 let (cr3, mem) = PageTableBuilder::new()
197 .map_4k(vaddr, paddr, flags::WRITABLE)
198 .write_phys(paddr, &head_page)
199 .map_4k(mod_vaddr, mod_paddr, flags::WRITABLE)
200 .write_phys(mod_paddr, &mod_page)
201 .build();
202 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
203 let reader = ObjectReader::new(vas, Box::new(resolver));
204
205 let mods = walk_modules(&reader).unwrap();
206 assert_eq!(mods.len(), 1, "should find one module via legacy layout");
207 assert_eq!(mods[0].name, "virtio");
208 assert_eq!(mods[0].base_addr, 0xFFFF_C000_0000);
209 assert_eq!(mods[0].size, 0x8000);
210 }
211
212 #[test]
213 fn walk_modules_no_layout_fields_skips_module() {
214 let vaddr: u64 = 0xFFFF_8000_0030_0000;
217 let paddr: u64 = 0x0092_0000;
218 let mod_vaddr: u64 = 0xFFFF_8000_0031_0000;
219 let mod_paddr: u64 = 0x0093_0000;
220
221 let mut head_page = [0u8; 4096];
222 head_page[0..8].copy_from_slice(&mod_vaddr.to_le_bytes());
223 head_page[8..16].copy_from_slice(&mod_vaddr.to_le_bytes());
224
225 let mut mod_page = [0u8; 4096];
226 mod_page[0..8].copy_from_slice(&vaddr.to_le_bytes());
227 mod_page[8..16].copy_from_slice(&vaddr.to_le_bytes());
228 mod_page[16..23].copy_from_slice(b"broken\0");
229 mod_page[72..76].copy_from_slice(&0u32.to_le_bytes());
230
231 let isf = IsfBuilder::new()
232 .add_struct("module", 256)
233 .add_field("module", "list", 0x00u64, "list_head")
234 .add_field("module", "name", 0x10u64, "char")
235 .add_field("module", "state", 0x48u64, "unsigned int")
236 .add_struct("list_head", 16)
238 .add_field("list_head", "next", 0x00u64, "pointer")
239 .add_field("list_head", "prev", 0x08u64, "pointer")
240 .add_symbol("modules", vaddr)
241 .build_json();
242
243 let resolver = IsfResolver::from_value(&isf).unwrap();
244 let (cr3, mem) = PageTableBuilder::new()
245 .map_4k(vaddr, paddr, flags::WRITABLE)
246 .write_phys(paddr, &head_page)
247 .map_4k(mod_vaddr, mod_paddr, flags::WRITABLE)
248 .write_phys(mod_paddr, &mod_page)
249 .build();
250 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
251 let reader = ObjectReader::new(vas, Box::new(resolver));
252
253 let mods = walk_modules(&reader).unwrap();
254 assert!(
256 mods.is_empty(),
257 "module with no layout fields must be skipped"
258 );
259 }
260
261 #[test]
262 fn walk_modules_missing_symbol_returns_error() {
263 let isf = IsfBuilder::new()
265 .add_struct("module", 256)
266 .add_struct("list_head", 16)
267 .add_field("list_head", "next", 0x00u64, "pointer")
268 .add_field("list_head", "prev", 0x08u64, "pointer")
269 .build_json();
270
271 let resolver = IsfResolver::from_value(&isf).unwrap();
272 let (cr3, mem) = PageTableBuilder::new().build();
273 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
274 let reader = ObjectReader::new(vas, Box::new(resolver));
275
276 let result = walk_modules(&reader);
277 assert!(result.is_err(), "missing modules symbol must return Err");
278 }
279
280 #[test]
281 fn empty_module_list() {
282 let vaddr: u64 = 0xFFFF_8000_0010_0000;
283 let paddr: u64 = 0x0080_0000;
284 let mut data = vec![0u8; 4096];
285 data[0..8].copy_from_slice(&vaddr.to_le_bytes());
286 data[8..16].copy_from_slice(&vaddr.to_le_bytes());
287
288 let reader = make_module_reader(&data, vaddr, paddr);
289 let mods = walk_modules(&reader).unwrap();
290 assert_eq!(mods.len(), 0);
291 }
292}