ds_decomp/analysis/
secure_area.rs

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