1use std::sync::{Arc, Mutex};
2
3use machina_core::address::GPA;
4
5use crate::ram::RamBlock;
6use crate::region::{MemoryRegion, MmioOps, RegionType};
7
8pub enum FlatRangeKind {
14 Ram { block: Arc<RamBlock> },
15 Rom { block: Arc<RamBlock> },
16 Io { ops: Arc<Mutex<Box<dyn MmioOps>>> },
17}
18
19pub struct FlatRange {
24 pub addr: GPA,
25 pub size: u64,
26 pub kind: FlatRangeKind,
27 pub offset_in_region: u64,
28}
29
30impl FlatRange {
31 pub fn is_io(&self) -> bool {
32 matches!(self.kind, FlatRangeKind::Io { .. })
33 }
34
35 fn end(&self) -> u64 {
36 self.addr.0 + self.size
37 }
38}
39
40pub struct FlatView {
43 pub ranges: Vec<FlatRange>,
44}
45
46struct RawRange {
49 addr: u64,
50 size: u64,
51 priority: i32,
52 kind: FlatRangeKind,
53 offset_in_region: u64,
54}
55
56impl FlatView {
57 pub fn from_region(root: &MemoryRegion) -> Self {
61 let mut raw: Vec<RawRange> = Vec::new();
62 Self::collect(root, 0, 0, &mut raw);
63
64 raw.sort_by(|a, b| {
66 b.priority.cmp(&a.priority).then(a.addr.cmp(&b.addr))
67 });
68
69 let mut resolved: Vec<FlatRange> = Vec::new();
70 for r in raw {
71 Self::insert_range(&mut resolved, r);
72 }
73
74 resolved.sort_by_key(|r| r.addr.0);
76 Self { ranges: resolved }
77 }
78
79 pub fn lookup(&self, addr: GPA) -> Option<&FlatRange> {
82 let a = addr.0;
83 let idx = self.ranges.partition_point(|r| r.addr.0 <= a);
84 if idx == 0 {
85 return None;
86 }
87 let r = &self.ranges[idx - 1];
88 if a < r.end() {
89 Some(r)
90 } else {
91 None
92 }
93 }
94
95 fn collect(
100 region: &MemoryRegion,
101 base: u64,
102 inherited_prio: i32,
103 out: &mut Vec<RawRange>,
104 ) {
105 if !region.enabled {
106 return;
107 }
108 let prio = region.priority.max(inherited_prio);
109
110 match ®ion.region_type {
111 RegionType::Ram { block } => {
112 out.push(RawRange {
113 addr: base,
114 size: region.size,
115 priority: prio,
116 kind: FlatRangeKind::Ram {
117 block: Arc::clone(block),
118 },
119 offset_in_region: 0,
120 });
121 }
122 RegionType::Rom { block } => {
123 out.push(RawRange {
124 addr: base,
125 size: region.size,
126 priority: prio,
127 kind: FlatRangeKind::Rom {
128 block: Arc::clone(block),
129 },
130 offset_in_region: 0,
131 });
132 }
133 RegionType::Io { ops } => {
134 out.push(RawRange {
135 addr: base,
136 size: region.size,
137 priority: prio,
138 kind: FlatRangeKind::Io {
139 ops: Arc::clone(ops),
140 },
141 offset_in_region: 0,
142 });
143 }
144 RegionType::Alias {
145 target,
146 offset: alias_off,
147 } => {
148 Self::collect_alias(
149 target,
150 base,
151 *alias_off,
152 region.size,
153 prio,
154 out,
155 );
156 }
157 RegionType::Container => {}
158 }
159
160 for sub in ®ion.subregions {
161 Self::collect(&sub.region, base + sub.offset.0, prio, out);
162 }
163 }
164
165 fn collect_alias(
170 target: &MemoryRegion,
171 base: u64,
172 alias_off: u64,
173 alias_size: u64,
174 prio: i32,
175 out: &mut Vec<RawRange>,
176 ) {
177 if !target.enabled {
178 return;
179 }
180 match &target.region_type {
181 RegionType::Ram { block } => {
182 out.push(RawRange {
183 addr: base,
184 size: alias_size,
185 priority: prio,
186 kind: FlatRangeKind::Ram {
187 block: Arc::clone(block),
188 },
189 offset_in_region: alias_off,
190 });
191 }
192 RegionType::Rom { block } => {
193 out.push(RawRange {
194 addr: base,
195 size: alias_size,
196 priority: prio,
197 kind: FlatRangeKind::Rom {
198 block: Arc::clone(block),
199 },
200 offset_in_region: alias_off,
201 });
202 }
203 RegionType::Io { ops } => {
204 out.push(RawRange {
205 addr: base,
206 size: alias_size,
207 priority: prio,
208 kind: FlatRangeKind::Io {
209 ops: Arc::clone(ops),
210 },
211 offset_in_region: alias_off,
212 });
213 }
214 RegionType::Alias {
215 target: inner,
216 offset: inner_off,
217 } => {
218 Self::collect_alias(
220 inner,
221 base,
222 alias_off + inner_off,
223 alias_size,
224 prio,
225 out,
226 );
227 }
228 RegionType::Container => {
229 for sub in &target.subregions {
232 let sub_start = sub.offset.0;
233 let sub_end = sub_start + sub.region.size;
234 let win_start = alias_off;
235 let win_end = alias_off + alias_size;
236 if sub_end <= win_start || sub_start >= win_end {
237 continue;
238 }
239 let clip_start = sub_start.max(win_start);
240 let clip_end = sub_end.min(win_end);
241 let new_base = base + (clip_start - win_start);
242 let new_size = clip_end - clip_start;
243 let off_in_sub = clip_start - sub_start;
244 Self::collect_alias(
245 &sub.region,
246 new_base,
247 off_in_sub,
248 new_size,
249 prio,
250 out,
251 );
252 }
253 }
254 }
255 }
256
257 fn insert_range(resolved: &mut Vec<FlatRange>, raw: RawRange) {
261 let mut cur = raw.addr;
262 let end = raw.addr + raw.size;
263
264 let mut overlaps: Vec<(u64, u64)> = resolved
266 .iter()
267 .filter(|r| r.addr.0 < end && r.end() > cur)
268 .map(|r| (r.addr.0, r.end()))
269 .collect();
270 overlaps.sort_by_key(|&(a, _)| a);
271
272 for (oa, ob) in &overlaps {
273 if cur < *oa {
274 let gap_end = (*oa).min(end);
275 Self::push_fragment(resolved, &raw, cur, gap_end);
276 }
277 cur = cur.max(*ob);
278 if cur >= end {
279 break;
280 }
281 }
282 if cur < end {
283 Self::push_fragment(resolved, &raw, cur, end);
284 }
285 }
286
287 fn push_fragment(
288 resolved: &mut Vec<FlatRange>,
289 raw: &RawRange,
290 start: u64,
291 end: u64,
292 ) {
293 let offset_delta = start - raw.addr;
294 let kind = match &raw.kind {
295 FlatRangeKind::Ram { block } => FlatRangeKind::Ram {
296 block: Arc::clone(block),
297 },
298 FlatRangeKind::Rom { block } => FlatRangeKind::Rom {
299 block: Arc::clone(block),
300 },
301 FlatRangeKind::Io { ops } => FlatRangeKind::Io {
302 ops: Arc::clone(ops),
303 },
304 };
305 resolved.push(FlatRange {
306 addr: GPA::new(start),
307 size: end - start,
308 kind,
309 offset_in_region: raw.offset_in_region + offset_delta,
310 });
311 }
312}