Skip to content

Commit 51cee49

Browse files
committed
dir/gen2: implement Removables
Signed-off-by: Daniel Maslowski <[email protected]>
1 parent be403a5 commit 51cee49

File tree

1 file changed

+188
-0
lines changed

1 file changed

+188
-0
lines changed

src/dir/gen2.rs

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
use core::fmt::{self, Display};
2+
use core::ops::Range;
23
use core::str::from_utf8;
34

45
use bitfield_struct::bitfield;
56
use serde::{Deserialize, Serialize};
67
use zerocopy::{FromBytes, Ref};
78
use zerocopy_derive::{FromBytes, Immutable, IntoBytes};
89

10+
use crate::Removables;
911
use crate::dir::man::{self, Manifest};
1012

13+
// These must never be removed. They are essential for platform initialization.
14+
pub const ALWAYS_RETAIN: &[&str] = &[
15+
"BUP", // bringup
16+
"ROMP", //
17+
];
18+
1119
const ENTRY_MAGIC: &str = "$MME";
1220
const ENTRY_MAGIC_BYTES: &[u8] = ENTRY_MAGIC.as_bytes();
1321
pub const SIG_LUT: &str = "LLUT";
@@ -285,4 +293,184 @@ impl Directory {
285293
name,
286294
})
287295
}
296+
297+
fn dump_ranges(ranges: &Vec<Range<usize>>) {
298+
let group_size = 4;
299+
for (i, r) in ranges.iter().enumerate() {
300+
if i % group_size == group_size - 1 {
301+
println!("{r:08x?}");
302+
} else {
303+
print!("{r:08x?} ");
304+
}
305+
}
306+
println!();
307+
}
308+
309+
// Get the offset ranges of the chunks.
310+
fn chunks_as_ranges(self: &Self, chunks: &Vec<u32>, stream_end: usize) -> Vec<Range<usize>> {
311+
// NOTE: This is the end of the directory.
312+
// me_cleaner uses the end of the ME region.
313+
let dir_end = self.offset + self.size;
314+
let mut nonzero_offsets = vec![stream_end];
315+
let offsets = chunks
316+
.iter()
317+
.map(|c| {
318+
let o = *c as usize;
319+
// Highest byte contains flags. 0x80 means inactive.
320+
const CHUNK_INACTIVE: usize = 0x80;
321+
if o >> 24 == CHUNK_INACTIVE {
322+
0
323+
} else {
324+
let xo = o & 0x00ff_ffff;
325+
if xo != 0 {
326+
nonzero_offsets.push(xo);
327+
}
328+
xo
329+
}
330+
})
331+
.collect::<Vec<usize>>();
332+
nonzero_offsets.sort();
333+
// Turn offsets into ranges by finding the offset of the next chunk.
334+
offsets
335+
.iter()
336+
.map(|offset| {
337+
let o = *offset;
338+
let e = if o != 0 {
339+
// NOTE: nonzero_offsets are a subset of offsets, so this should never fail.
340+
let p = nonzero_offsets.iter().position(|e| *e == o).unwrap();
341+
let next = p + 1;
342+
// The last entry has no successor.
343+
if next < nonzero_offsets.len() {
344+
nonzero_offsets[next]
345+
} else {
346+
stream_end.min(dir_end)
347+
}
348+
} else {
349+
0
350+
};
351+
o..e
352+
})
353+
.collect::<Vec<Range<usize>>>()
354+
}
355+
}
356+
357+
impl Removables for Directory {
358+
/// Removable ranges relative to the start of the directory
359+
fn removables(self: &Self, retention_list: &Vec<String>) -> Vec<Range<usize>> {
360+
use log::{debug, info, warn};
361+
let debug = false;
362+
let mut removables = vec![];
363+
364+
let mut unremovable_chunks = vec![];
365+
let mut all_chunks = vec![];
366+
let dir_offset = self.offset;
367+
368+
for m in &self.modules {
369+
// Get the full directory entry.
370+
let e = match m {
371+
Module::Uncompressed(m) => m,
372+
Module::Lzma(Ok(m)) => m,
373+
Module::Huffman(Ok((m, h))) => {
374+
let n = m.name();
375+
let o = m.offset as usize;
376+
let s = m.size as usize;
377+
378+
// NOTE: The header is always the same, since multiple
379+
// Huffman-encoded modules point to the same offset.
380+
let cs = h.header.chunk_size;
381+
if all_chunks.len() == 0 {
382+
info!("Huffman chunk size: {cs}");
383+
let stream_end = (h.header.hs0 + h.header.hs1) as usize;
384+
all_chunks = self.chunks_as_ranges(&h.chunks, stream_end);
385+
}
386+
387+
const CHUNK_OFFSET: u32 = 0x10000000;
388+
// Each module occupies its own range of chunks.
389+
let b = m.mod_base - (h.header.chunk_base + CHUNK_OFFSET);
390+
let c = (m.code_size / cs) as usize;
391+
let first_chunk = (b / cs) as usize;
392+
let last_chunk = first_chunk + c;
393+
info!("Huffman compressed {n} @ {o:08x} ({s} bytes)");
394+
let a = if retention_list.contains(&n) {
395+
for o in &all_chunks[first_chunk..last_chunk + 1] {
396+
if o.start != 0 {
397+
unremovable_chunks.push(o.clone());
398+
}
399+
}
400+
"retained"
401+
} else {
402+
"removed"
403+
};
404+
info!(" Chunks {first_chunk}..{last_chunk} will be {a}");
405+
continue;
406+
}
407+
Module::Lzma(Err(e)) | Module::Huffman(Err(e)) => {
408+
warn!("Compressed module could not be parsed: {e}, skipping");
409+
continue;
410+
}
411+
Module::Unknown(m) => {
412+
let n = m.name();
413+
let o = m.offset;
414+
let s = m.size;
415+
info!("Unknown module {n} @ {o:08x} ({s} bytes)");
416+
continue;
417+
}
418+
};
419+
let n = e.name();
420+
let o = e.offset as usize;
421+
let s = e.size as usize;
422+
423+
match &n {
424+
n if retention_list.contains(n) => {
425+
info!("Retain {n} @ {o:08x} ({s} bytes)");
426+
}
427+
n => {
428+
info!("Remove {n} @ {o:08x} ({s} bytes)");
429+
removables.push(o..o + s);
430+
}
431+
}
432+
}
433+
434+
let mut r = 0;
435+
for c in &all_chunks {
436+
let mut remove = true;
437+
// Filter out chunks that must be kept: those in range of some unremovable chunk
438+
// TODO: Simplify when Range.is_overlapping is stabiliized (currently nightly),
439+
// https://doc.rust-lang.org/core/slice/trait.GetDisjointMutIndex.html#tymethod.is_overlapping
440+
for u in &unremovable_chunks {
441+
if (u.contains(&c.start)) || (u.contains(&(c.end - 1))) {
442+
debug!("OVERLAP: {u:06x?} (partially) contains {c:06x?}");
443+
remove = false;
444+
break;
445+
}
446+
}
447+
if remove && c.start < c.end {
448+
// NOTE: Chunks are relative to the start of the ME region (or FPT?).
449+
// We provide the offset relative to the directory.
450+
let o = c.start - dir_offset;
451+
let e = c.end - dir_offset;
452+
removables.push(o..e);
453+
r += 1;
454+
} else if debug && c.start != 0 {
455+
debug!("KEEP {c:06x?}");
456+
}
457+
}
458+
459+
let a = &all_chunks.len();
460+
let u = unremovable_chunks.len();
461+
info!("Total chunks: {a}");
462+
info!(" unremovable: {u}");
463+
info!(" will remove: {r}");
464+
465+
if debug {
466+
debug!("== All chunks");
467+
Self::dump_ranges(&all_chunks);
468+
debug!("== Unremovable chunks");
469+
Self::dump_ranges(&unremovable_chunks);
470+
debug!("== All removables");
471+
Self::dump_ranges(&removables);
472+
}
473+
474+
removables
475+
}
288476
}

0 commit comments

Comments
 (0)