Skip to main content

mediatek_optical/receiver/entry/application/
flags.rs

1//! Four fixed bit-test/store clauses, with explicit skip/set convergence.
2use super::{clear_site, Candidate, Prologue, Window};
3
4/// Complete operand-bound four-bit CDB-to-MMIO flag copy and return.
5/// Opcode ownership and command completion are not inferred from this body.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct FlagCopy {
8    /// First MMIO literal load.
9    pub entry: u32,
10    /// First address after the matched return.
11    pub end: u32,
12}
13
14fn jump(w: &Window<'_>, at: u32) -> Option<u32> {
15    if w.word(at)? & 0xf800 == 0xe000 {
16        w.branch(at)
17    } else {
18        Some(at)
19    }
20}
21
22/// Validate all four tests, OR operands, stores, convergence branches and the
23/// stack-restoring return. Only a table's proven prelude may supply constants
24/// and CDB pointers. This performs no I/O and does not authorize a transfer.
25pub fn inspect_flags(
26    bytes: &[u8],
27    address: u32,
28    candidate: &Candidate,
29    prelude: &Prologue,
30) -> Option<FlagCopy> {
31    // Bind every supplied operand to these exact bytes. A stale or manually
32    // altered evidence record cannot invent a CDB pointer or constant.
33    if super::inspect(bytes, address)? != *candidate
34        || super::inspect_prologue(bytes, address, candidate)? != *prelude
35    {
36        return None;
37    }
38    let w = Window {
39        bytes,
40        base: address,
41    };
42    if clear_site(&w, candidate.entry.checked_add(2)?)? != candidate.entry {
43        return None;
44    }
45    let (base, _) = w.literal(candidate.entry)?;
46    let mut p = candidate.entry.checked_add(10)?;
47    let mut constants = prelude.constants;
48    // Clear-site arithmetic destroys its temporary register.
49    let clear_temp = w.word(candidate.entry + 2)? & 7;
50    constants[clear_temp as usize] = None;
51    constants[base as usize] = None;
52    let mut cdb = prelude.cdb_registers & !(1 << base) & !(1 << clear_temp);
53    if let Some((reg, literal)) = w.literal(p) {
54        if literal != prelude.cdb_address || reg == base {
55            return None;
56        }
57        cdb |= 1 << reg;
58        constants[reg as usize] = None;
59        p = p.checked_add(2)?;
60    }
61    for bit in 0..4u16 {
62        p = jump(&w, p)?;
63        let load = w.word(p)?;
64        let temp = load & 7;
65        let source = (load >> 3) & 7;
66        if load != 0x7800 | (9 << 6) | (source << 3) | temp
67            || cdb & (1 << source) == 0
68            || temp == base
69        {
70            return None;
71        }
72        if w.word(p.checked_add(2)?)? != ((31 - bit) << 6) | (temp << 3) | temp {
73            return None;
74        }
75        constants[temp as usize] = None;
76        cdb &= !(1 << temp);
77        let branch = p.checked_add(4)?;
78        let condition = w.word(branch)? & 0xff00;
79        let skip_condition = if bit == 0 { 0xd000 } else { 0xd500 };
80        let set_condition = if bit == 0 { 0xd100 } else { 0xd400 };
81        let target = w.conditional(branch, condition)?;
82        let fallthrough = branch.checked_add(2)?;
83        let (mut set, skip) = if condition == skip_condition {
84            (fallthrough, target)
85        } else if condition == set_condition {
86            (target, fallthrough)
87        } else {
88            return None;
89        };
90        set = jump(&w, set)?;
91        // The set arm must read precisely the MMIO register just cleared.
92        let read = w.word(set)?;
93        let work = read & 7;
94        if read != 0x6980 | (base << 3) | work || work == base {
95            return None;
96        }
97        set = set.checked_add(2)?;
98        let before_set = constants;
99        let mut after_set = constants;
100        after_set[work as usize] = None;
101        if w.word(set)? & 0xf800 == 0x2000 {
102            let op = w.word(set)?;
103            let register = (op >> 8) & 7;
104            if register == base {
105                return None;
106            }
107            after_set[register as usize] = Some(u32::from(op & 255));
108            cdb &= !(1 << register);
109            set = set.checked_add(2)?;
110        }
111        let or = w.word(set)?;
112        if or & 0xffc0 != 0x4300 {
113            return None;
114        }
115        let destination = or & 7;
116        let operand = (or >> 3) & 7;
117        let constant = if destination == work {
118            operand
119        } else if operand == work {
120            destination
121        } else {
122            return None;
123        };
124        if after_set[constant as usize] != Some(1 << bit) || destination == base {
125            return None;
126        }
127        after_set[destination as usize] = None;
128        if w.word(set.checked_add(2)?)? != 0x6180 | (base << 3) | destination {
129            return None;
130        }
131        let next_set = jump(&w, set.checked_add(4)?)?;
132        let next_skip = jump(&w, skip)?;
133        if next_set != next_skip {
134            return None;
135        }
136        for reg in 0..8 {
137            constants[reg] = if before_set[reg] == after_set[reg] {
138                before_set[reg]
139            } else {
140                None
141            };
142        }
143        cdb &= !(1 << work) & !(1 << destination);
144        p = next_set;
145    }
146    p = jump(&w, p)?;
147    if w.word(p)? != 0xb000 | (prelude.stack_bytes / 4) || w.word(p.checked_add(2)?)? != 0xbdf0 {
148        return None;
149    }
150    Some(FlagCopy {
151        entry: candidate.entry,
152        end: p.checked_add(4)?,
153    })
154}