1use std::num::NonZeroU32;
6
7use crate::{CoreInterface, Error, MemoryInterface, RegisterValue};
8
9#[derive(Debug, PartialEq, Eq, Clone, Copy)]
11pub enum SemihostingCommand {
12 ExitSuccess,
15
16 ExitError(ExitErrorDetails),
19
20 GetCommandLine(GetCommandLineRequest),
22
23 Open(OpenRequest),
25
26 Close(CloseRequest),
28
29 WriteConsole(WriteConsoleRequest),
31
32 Write(WriteRequest),
34
35 Errno(ErrnoRequest),
37
38 Unknown(UnknownCommandDetails),
40}
41
42#[derive(Debug, PartialEq, Eq, Copy, Clone)]
44pub struct ExitErrorDetails {
45 pub reason: u32,
48
49 pub exit_status: Option<u32>,
52
53 pub subcode: Option<u32>,
55}
56
57impl std::fmt::Display for ExitErrorDetails {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 write!(f, "reason: {:#x}", self.reason)?;
60 if let Some(exit_status) = self.exit_status {
61 write!(f, ", exit_status: {}", exit_status)?;
62 }
63 if let Some(subcode) = self.subcode {
64 write!(f, ", subcode: {:#x}", subcode)?;
65 }
66 Ok(())
67 }
68}
69
70#[derive(Debug, PartialEq, Eq, Copy, Clone)]
72pub struct UnknownCommandDetails {
73 pub operation: u32,
75
76 pub parameter: u32,
78}
79
80impl UnknownCommandDetails {
81 pub fn get_buffer(&self, core: &mut dyn CoreInterface) -> Result<Buffer, Error> {
83 Buffer::from_block_at(core, self.parameter)
84 }
85
86 pub fn write_status(&self, core: &mut dyn CoreInterface, status: i32) -> Result<(), Error> {
88 write_status(core, status)
89 }
90}
91
92#[derive(Debug, PartialEq, Eq, Copy, Clone)]
94pub struct GetCommandLineRequest(Buffer);
95
96impl GetCommandLineRequest {
97 pub fn write_command_line_to_target(
99 &self,
100 core: &mut dyn CoreInterface,
101 cmdline: &str,
102 ) -> Result<(), Error> {
103 let mut buf = cmdline.to_owned().into_bytes();
104 buf.push(0);
105 self.0.write(core, &buf)?;
106
107 write_status(core, 0)?;
109
110 Ok(())
111 }
112}
113
114#[derive(Debug, PartialEq, Eq, Copy, Clone)]
118pub struct OpenRequest {
119 path: ZeroTerminatedString,
120 mode: &'static str,
121}
122
123impl OpenRequest {
124 pub fn path(&self, core: &mut dyn CoreInterface) -> Result<String, Error> {
126 self.path.read(core)
127 }
128
129 pub fn mode(&self) -> &'static str {
131 self.mode
132 }
133
134 pub fn respond_with_handle(
136 &self,
137 core: &mut dyn CoreInterface,
138 handle: NonZeroU32,
139 ) -> Result<(), Error> {
140 write_status(core, handle.get() as i32)
141 }
142}
143
144#[derive(Debug, PartialEq, Eq, Copy, Clone)]
148pub struct CloseRequest {
149 pointer: u32,
150}
151
152impl CloseRequest {
153 pub fn file_handle(&self, core: &mut dyn CoreInterface) -> Result<u32, Error> {
155 core.read_word_32(self.pointer as u64)
156 }
157
158 pub fn success(&self, core: &mut dyn CoreInterface) -> Result<(), Error> {
160 write_status(core, 0)
161 }
162}
163
164#[derive(Debug, PartialEq, Eq, Copy, Clone)]
166pub struct WriteConsoleRequest(pub(crate) ZeroTerminatedString);
167impl WriteConsoleRequest {
168 pub fn read(&self, core: &mut crate::Core<'_>) -> Result<String, Error> {
170 self.0.read(core)
171 }
172}
173
174#[derive(Debug, PartialEq, Eq, Copy, Clone)]
176pub struct WriteRequest {
177 handle: u32,
178 bytes: u32,
179 len: u32,
180}
181impl WriteRequest {
182 pub fn file_handle(&self) -> u32 {
184 self.handle
185 }
186
187 pub fn read(&self, core: &mut crate::Core<'_>) -> Result<Vec<u8>, Error> {
189 let mut buf = vec![0u8; self.len as usize];
190 core.read(self.bytes as u64, &mut buf)?;
191 Ok(buf)
192 }
193
194 pub fn write_status(&self, core: &mut dyn CoreInterface, status: i32) -> Result<(), Error> {
196 write_status(core, status)
197 }
198}
199
200#[derive(Debug, PartialEq, Eq, Copy, Clone)]
202pub struct ErrnoRequest {}
203impl ErrnoRequest {
204 pub fn write_errno(&self, core: &mut dyn CoreInterface, errno: i32) -> Result<(), Error> {
206 write_status(core, errno)
208 }
209}
210
211fn write_status(core: &mut dyn CoreInterface, value: i32) -> Result<(), crate::Error> {
212 let reg = core.registers().get_argument_register(0).unwrap();
213 core.write_core_reg(reg.into(), RegisterValue::U32(value as u32))?;
214
215 Ok(())
216}
217
218#[derive(Debug, PartialEq, Eq, Copy, Clone)]
222pub struct Buffer {
223 buffer_location: u32, address: u32, len: u32, }
227
228impl Buffer {
229 pub fn from_block_at(
231 core: &mut dyn CoreInterface,
232 block_addr: u32,
233 ) -> Result<Self, crate::Error> {
234 let mut block: [u32; 2] = [0, 0];
235 core.read_32(block_addr as u64, &mut block)?;
236 Ok(Self {
237 buffer_location: block_addr,
238 address: block[0],
239 len: block[1],
240 })
241 }
242
243 pub fn read(&self, core: &mut dyn CoreInterface) -> Result<Vec<u8>, Error> {
245 let mut buf = vec![0u8; self.len as usize];
246 core.read(self.address as u64, &mut buf[..])?;
247 Ok(buf)
248 }
249
250 pub fn write(&self, core: &mut dyn CoreInterface, buf: &[u8]) -> Result<(), Error> {
253 if buf.len() > self.len as usize {
254 return Err(Error::Other("buffer not large enough".to_string()));
255 }
256 if buf.last() != Some(&0) {
257 return Err(Error::Other("last byte of buffer must be 0".to_string()));
258 }
259 core.write_8(self.address as u64, buf)?;
260 let block: [u32; 2] = [self.address, (buf.len() - 1) as u32];
261 core.write_32(self.buffer_location as u64, &block)?;
262 Ok(())
263 }
264}
265
266#[derive(Debug, PartialEq, Eq, Copy, Clone)]
267pub(crate) struct ZeroTerminatedString {
268 pub address: u32,
269 pub length: Option<u32>,
270}
271
272impl ZeroTerminatedString {
273 pub fn read(&self, core: &mut dyn CoreInterface) -> Result<String, Error> {
275 let mut bytes = Vec::new();
276
277 if let Some(len) = self.length {
278 bytes = vec![0; len as usize];
279 core.read(self.address as u64, &mut bytes)?;
280 } else {
281 let mut buf = [0; 128];
282 let mut from = self.address as u64;
283
284 loop {
285 core.read(from, &mut buf)?;
286 if let Some(end) = buf.iter().position(|&x| x == 0) {
287 bytes.extend_from_slice(&buf[..end]);
288 break;
289 }
290
291 bytes.extend_from_slice(&buf);
292 from += buf.len() as u64;
293 }
294 }
295
296 Ok(String::from_utf8_lossy(&bytes).to_string())
297 }
298}
299
300pub fn decode_semihosting_syscall(
303 core: &mut dyn CoreInterface,
304) -> Result<SemihostingCommand, Error> {
305 let operation: u32 = core
306 .read_core_reg(core.registers().get_argument_register(0).unwrap().id())?
307 .try_into()?;
308 let parameter: u32 = core
309 .read_core_reg(core.registers().get_argument_register(1).unwrap().id())?
310 .try_into()?;
311
312 tracing::debug!("Semihosting found r0={operation:#x} r1={parameter:#x}");
313
314 const SYS_GET_CMDLINE: u32 = 0x15;
318 const SYS_EXIT: u32 = 0x18;
319 const SYS_EXIT_EXTENDED: u32 = 0x20;
320 const SYS_EXIT_ADP_STOPPED_APPLICATIONEXIT: u32 = 0x20026;
321 const SYS_OPEN: u32 = 0x01;
322 const SYS_CLOSE: u32 = 0x02;
323 const SYS_WRITEC: u32 = 0x03;
324 const SYS_WRITE0: u32 = 0x04;
325 const SYS_WRITE: u32 = 0x05;
326 const SYS_ERRNO: u32 = 0x13;
327
328 Ok(match (operation, parameter) {
329 (SYS_EXIT, SYS_EXIT_ADP_STOPPED_APPLICATIONEXIT) => SemihostingCommand::ExitSuccess,
330 (SYS_EXIT, reason) => SemihostingCommand::ExitError(ExitErrorDetails {
331 reason,
332 exit_status: None,
333 subcode: None,
334 }),
335
336 (SYS_EXIT_EXTENDED, block_address) => {
337 let mut buf = [0u32; 2];
339 core.read_32(block_address as u64, &mut buf)?;
340 let reason = buf[0];
341 let subcode = buf[1];
342 match (reason, subcode) {
343 (SYS_EXIT_ADP_STOPPED_APPLICATIONEXIT, 0) => SemihostingCommand::ExitSuccess,
344 (SYS_EXIT_ADP_STOPPED_APPLICATIONEXIT, exit_status) => {
345 SemihostingCommand::ExitError(ExitErrorDetails {
346 reason,
347 exit_status: Some(exit_status),
348 subcode: None,
349 })
350 }
351 (reason, subcode) => SemihostingCommand::ExitError(ExitErrorDetails {
352 reason,
353 exit_status: None,
354 subcode: Some(subcode),
355 }),
356 }
357 }
358
359 (SYS_GET_CMDLINE, block_address) => {
360 write_status(core, -1)?;
363 SemihostingCommand::GetCommandLine(GetCommandLineRequest(Buffer::from_block_at(
364 core,
365 block_address,
366 )?))
367 }
368
369 (SYS_OPEN, pointer) => {
370 let [string, mode, str_len] = param3(core, pointer)?;
371
372 write_status(core, -1)?;
375 SemihostingCommand::Open(OpenRequest {
376 path: ZeroTerminatedString {
377 address: string,
378 length: Some(str_len),
379 },
380 mode: match mode {
381 0 => "r",
382 1 => "rb",
383 2 => "r+",
384 3 => "r+b",
385 4 => "w",
386 5 => "wb",
387 6 => "w+",
388 7 => "w+b",
389 8 => "a",
390 9 => "ab",
391 10 => "a+",
392 11 => "a+b",
393 _ => "unknown",
394 },
395 })
396 }
397
398 (SYS_CLOSE, pointer) => {
399 write_status(core, -1)?;
402 SemihostingCommand::Close(CloseRequest { pointer })
403 }
404
405 (SYS_WRITEC, pointer) => {
406 SemihostingCommand::WriteConsole(WriteConsoleRequest(ZeroTerminatedString {
407 address: pointer,
408 length: Some(1),
409 }))
410 }
412
413 (SYS_WRITE0, pointer) => {
414 SemihostingCommand::WriteConsole(WriteConsoleRequest(ZeroTerminatedString {
415 address: pointer,
416 length: None,
417 }))
418 }
420
421 (SYS_WRITE, pointer) => {
422 let [handle, bytes, len] = param3(core, pointer)?;
423 write_status(core, -1)?;
425 SemihostingCommand::Write(WriteRequest { handle, bytes, len })
426 }
427
428 (SYS_ERRNO, 0) => SemihostingCommand::Errno(ErrnoRequest {}),
429
430 _ => {
431 write_status(core, -1)?;
434
435 tracing::debug!(
436 "Unknown semihosting operation={operation:04x} parameter={parameter:04x}"
437 );
438 SemihostingCommand::Unknown(UnknownCommandDetails {
439 operation,
440 parameter,
441 })
442 }
443 })
444}
445
446fn param3(core: &mut dyn CoreInterface, pointer: u32) -> Result<[u32; 3], crate::Error> {
447 let mut buf = [0; 3];
448 core.read_32(pointer as u64, &mut buf)?;
449 Ok(buf)
450}