Skip to main content

ds_decomp/analysis/
secure_area.rs

1use snafu::Snafu;
2use unarm::{
3    ParsedIns,
4    args::{Argument, Reg, Register},
5};
6
7#[derive(Clone, Copy, Default, Debug)]
8pub enum SecureAreaState {
9    #[default]
10    Start,
11    Arg {
12        start: u32,
13    },
14    Return {
15        start: u32,
16        function: SwiFunction,
17        return_reg: Register,
18    },
19    ValidFunction(SecureAreaFunction),
20}
21
22impl SecureAreaState {
23    pub fn handle(self, address: u32, parsed_ins: &ParsedIns) -> Self {
24        let args = &parsed_ins.args;
25        match self {
26            Self::Start => match (parsed_ins.mnemonic, args[0], args[1]) {
27                ("swi", Argument::UImm(interrupt), Argument::None)
28                | ("svc", Argument::UImm(interrupt), Argument::None) => {
29                    if let Ok(function) = interrupt.try_into() {
30                        Self::Return { start: address, function, return_reg: Register::R0 }
31                    } else {
32                        Self::default()
33                    }
34                }
35                ("mov", Argument::Reg(Reg { .. }), Argument::UImm(_)) => {
36                    Self::Arg { start: address }
37                }
38                _ => Self::default(),
39            },
40            Self::Arg { start } => match (parsed_ins.mnemonic, args[0], args[1]) {
41                ("swi", Argument::UImm(interrupt), Argument::None)
42                | ("svc", Argument::UImm(interrupt), Argument::None) => {
43                    if let Ok(function) = SwiFunction::try_from(interrupt) {
44                        if function.allows_arg() {
45                            Self::Return { start, function, return_reg: Register::R0 }
46                        } else {
47                            // Ignore the mov
48                            Self::Return { start: address, function, return_reg: Register::R0 }
49                        }
50                    } else {
51                        Self::default()
52                    }
53                }
54                _ => Self::default(),
55            },
56            Self::Return { start, function, return_reg } => {
57                match (parsed_ins.mnemonic, args[0], args[1], args[2]) {
58                    (
59                        "mov",
60                        Argument::Reg(Reg { reg: dest, .. }),
61                        Argument::Reg(Reg { reg: src, .. }),
62                        Argument::None,
63                    ) if dest == return_reg => Self::Return { start, function, return_reg: src },
64                    (
65                        "bx",
66                        Argument::Reg(Reg { reg: Register::Lr, .. }),
67                        Argument::None,
68                        Argument::None,
69                    ) => Self::ValidFunction(SecureAreaFunction {
70                        function,
71                        return_reg,
72                        start,
73                        end: address + 2,
74                    }),
75                    _ => Self::default(),
76                }
77            }
78            Self::ValidFunction { .. } => Self::default(),
79        }
80    }
81
82    pub fn get_function(self) -> Option<SecureAreaFunction> {
83        let Self::ValidFunction(function) = self else { return None };
84        Some(function)
85    }
86}
87
88#[derive(Clone, Copy, Debug)]
89pub enum SwiFunction {
90    SoftReset,
91    WaitByLoop,
92    IntrWait,
93    VBlankIntrWait,
94    Halt,
95    Div,
96    Mod,
97    CpuSet,
98    CpuFastSet,
99    Sqrt,
100    GetCRC16,
101    IsDebugger,
102    BitUnPack,
103    LZ77UnCompReadNormalWrite8bit,
104    LZ77UnCompReadByCallbackWrite16bit,
105    HuffUnCompReadByCallback,
106    RLUnCompReadNormalWrite8bit,
107    RLUnCompReadByCallbackWrite16bit,
108}
109
110impl SwiFunction {
111    pub fn interrupt_value(self) -> u32 {
112        match self {
113            Self::SoftReset => 0x0,
114            Self::WaitByLoop => 0x3,
115            Self::IntrWait => 0x4,
116            Self::VBlankIntrWait => 0x5,
117            Self::Halt => 0x6,
118            Self::Div | Self::Mod => 0x9,
119            Self::CpuSet => 0xb,
120            Self::CpuFastSet => 0xc,
121            Self::Sqrt => 0xd,
122            Self::GetCRC16 => 0xe,
123            Self::IsDebugger => 0xf,
124            Self::BitUnPack => 0x10,
125            Self::LZ77UnCompReadNormalWrite8bit => 0x11,
126            Self::LZ77UnCompReadByCallbackWrite16bit => 0x12,
127            Self::HuffUnCompReadByCallback => 0x13,
128            Self::RLUnCompReadNormalWrite8bit => 0x14,
129            Self::RLUnCompReadByCallbackWrite16bit => 0x15,
130        }
131    }
132
133    pub fn name(self, return_reg: Register) -> &'static str {
134        match (self, return_reg) {
135            (Self::SoftReset, _) => "SoftReset",
136            (Self::WaitByLoop, _) => "WaitByLoop",
137            (Self::IntrWait, _) => "IntrWait",
138            (Self::VBlankIntrWait, _) => "VBlankIntrWait",
139            (Self::Halt, _) => "Halt",
140            (Self::Div, Register::R1) => "Mod",
141            (Self::Div, _) => "Div",
142            (Self::Mod, _) => "Mod",
143            (Self::CpuSet, _) => "CpuSet",
144            (Self::CpuFastSet, _) => "CpuFastSet",
145            (Self::Sqrt, _) => "Sqrt",
146            (Self::GetCRC16, _) => "GetCRC16",
147            (Self::IsDebugger, _) => "IsDebugger",
148            (Self::BitUnPack, _) => "BitUnPack",
149            (Self::LZ77UnCompReadNormalWrite8bit, _) => "LZ77UnCompReadNormalWrite8bit",
150            (Self::LZ77UnCompReadByCallbackWrite16bit, _) => "LZ77UnCompReadByCallbackWrite16bit",
151            (Self::HuffUnCompReadByCallback, _) => "HuffUnCompReadByCallback",
152            (Self::RLUnCompReadNormalWrite8bit, _) => "RLUnCompReadNormalWrite8bit",
153            (Self::RLUnCompReadByCallbackWrite16bit, _) => "RLUnCompReadByCallbackWrite16bit",
154        }
155    }
156
157    pub fn allows_arg(self) -> bool {
158        matches!(self, Self::VBlankIntrWait)
159    }
160}
161
162#[derive(Debug, Snafu)]
163pub enum IntoSwiFunctionError {
164    #[snafu(display("unknown interrupt value {value:#x}"))]
165    UnknownInterrupt { value: u32 },
166}
167
168impl TryFrom<u32> for SwiFunction {
169    type Error = IntoSwiFunctionError;
170
171    fn try_from(value: u32) -> Result<Self, Self::Error> {
172        match value {
173            0x0 => Ok(Self::SoftReset),
174            0x3 => Ok(Self::WaitByLoop),
175            0x4 => Ok(Self::IntrWait),
176            0x5 => Ok(Self::VBlankIntrWait),
177            0x6 => Ok(Self::Halt),
178            0x9 => Ok(Self::Div),
179            0xb => Ok(Self::CpuSet),
180            0xc => Ok(Self::CpuFastSet),
181            0xd => Ok(Self::Sqrt),
182            0xe => Ok(Self::GetCRC16),
183            0xf => Ok(Self::IsDebugger),
184            0x10 => Ok(Self::BitUnPack),
185            0x11 => Ok(Self::LZ77UnCompReadNormalWrite8bit),
186            0x12 => Ok(Self::LZ77UnCompReadByCallbackWrite16bit),
187            0x13 => Ok(Self::HuffUnCompReadByCallback),
188            0x14 => Ok(Self::RLUnCompReadNormalWrite8bit),
189            0x15 => Ok(Self::RLUnCompReadByCallbackWrite16bit),
190            _ => UnknownInterruptSnafu { value }.fail(),
191        }
192    }
193}
194
195#[derive(Clone, Copy, Debug)]
196pub struct SecureAreaFunction {
197    function: SwiFunction,
198    return_reg: Register,
199    start: u32,
200    end: u32,
201}
202
203impl SecureAreaFunction {
204    pub fn name(&self) -> &'static str {
205        self.function.name(self.return_reg)
206    }
207
208    pub fn start(&self) -> u32 {
209        self.start
210    }
211
212    pub fn end(&self) -> u32 {
213        self.end
214    }
215}