Skip to main content

machina_memory/
flat_view.rs

1use std::sync::{Arc, Mutex};
2
3use machina_core::address::GPA;
4
5use crate::ram::RamBlock;
6use crate::region::{MemoryRegion, MmioOps, RegionType};
7
8// ----- FlatRange: one contiguous span in the flat view -----
9
10/// Discriminant carried by each flat range so the address
11/// space can dispatch reads/writes without revisiting the
12/// region tree.
13pub enum FlatRangeKind {
14    Ram { block: Arc<RamBlock> },
15    Rom { block: Arc<RamBlock> },
16    Io { ops: Arc<Mutex<Box<dyn MmioOps>>> },
17}
18
19/// A single non-overlapping range in the flattened address
20/// map.  `offset_in_region` is the byte offset from the
21/// start of the owning leaf region that corresponds to
22/// `addr`.
23pub 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
40// ----- FlatView -----
41
42pub struct FlatView {
43    pub ranges: Vec<FlatRange>,
44}
45
46/// Intermediate record produced by the tree walk before
47/// overlap resolution.
48struct RawRange {
49    addr: u64,
50    size: u64,
51    priority: i32,
52    kind: FlatRangeKind,
53    offset_in_region: u64,
54}
55
56impl FlatView {
57    /// Flatten a `MemoryRegion` tree into a sorted,
58    /// non-overlapping list of `FlatRange`s.  Higher-priority
59    /// regions win when ranges overlap.
60    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        // Higher priority first; ties broken by address.
65        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        // Final sort by address.
75        resolved.sort_by_key(|r| r.addr.0);
76        Self { ranges: resolved }
77    }
78
79    /// Binary-search lookup.  Returns the range containing
80    /// `addr`, if any.
81    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    // -- private helpers --
96
97    /// Recursively collect leaf regions with their absolute
98    /// addresses and inherited priorities.
99    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 &region.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 &region.subregions {
161            Self::collect(&sub.region, base + sub.offset.0, prio, out);
162        }
163    }
164
165    /// Collect a leaf through an alias indirection.  For leaf
166    /// targets (Ram/Rom/Io) the alias offset is forwarded as
167    /// `offset_in_region`.  Container and nested-alias targets
168    /// are recursed into with clipped bounds.
169    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                // Nested alias: compose offsets.
219                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                // Recurse into subregions, clipping to the
230                // alias window [alias_off, alias_off+size).
231                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    /// Insert `raw` into `resolved`, skipping any portions
258    /// already covered by a previously inserted (i.e. higher-
259    /// priority) range.
260    fn insert_range(resolved: &mut Vec<FlatRange>, raw: RawRange) {
261        let mut cur = raw.addr;
262        let end = raw.addr + raw.size;
263
264        // Collect existing ranges that overlap [cur, end).
265        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}