1use std::{num::NonZeroU32, time::SystemTime};
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 Read(ReadRequest),
37
38 Seek(SeekRequest),
40
41 FileLength(FileLengthRequest),
43
44 Remove(RemoveRequest),
46
47 Rename(RenameRequest),
49
50 Time(TimeRequest),
52
53 Errno(ErrnoRequest),
55
56 Unknown(UnknownCommandDetails),
58}
59
60#[derive(Debug, PartialEq, Eq, Copy, Clone)]
62pub struct ExitErrorDetails {
63 pub reason: u32,
66
67 pub exit_status: Option<u32>,
70
71 pub subcode: Option<u32>,
73}
74
75impl std::fmt::Display for ExitErrorDetails {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 const REASON_APPLICATION_EXIT: u32 = 0x20026;
79
80 const EXIT_SUCCESS: u32 = 0;
82 const EXIT_ABORTED: u32 = 134;
83
84 match self.reason {
85 REASON_APPLICATION_EXIT => match self.exit_status {
86 Some(EXIT_SUCCESS) => write!(f, "success")?,
87 Some(EXIT_ABORTED) => write!(f, "exit_status: aborted")?,
88 Some(other) => write!(f, "exit_status: {other:#x}")?,
89 None => write!(f, "exit_status: unknown")?,
90 },
91 reason => {
92 write!(f, "reason: {reason:#x}")?;
93 if let Some(exit_status) = self.exit_status {
94 write!(f, ", exit_status: {exit_status}")?;
95 }
96 }
97 }
98
99 if let Some(subcode) = self.subcode {
100 write!(f, ", subcode: {subcode:#x}")?;
101 }
102 Ok(())
103 }
104}
105
106#[derive(Debug, PartialEq, Eq, Copy, Clone)]
108pub struct UnknownCommandDetails {
109 pub operation: u32,
111
112 pub parameter: u32,
114}
115
116impl UnknownCommandDetails {
117 pub fn get_buffer(&self, core: &mut dyn CoreInterface) -> Result<Buffer, Error> {
119 Buffer::from_block_at(core, self.parameter)
120 }
121
122 pub fn write_status(&self, core: &mut dyn CoreInterface, status: i32) -> Result<(), Error> {
124 write_status(core, status)
125 }
126}
127
128#[derive(Debug, PartialEq, Eq, Copy, Clone)]
130pub struct GetCommandLineRequest(Buffer);
131
132impl GetCommandLineRequest {
133 pub fn write_command_line_to_target(
135 &self,
136 core: &mut dyn CoreInterface,
137 cmdline: &str,
138 ) -> Result<(), Error> {
139 let mut buf = cmdline.to_owned().into_bytes();
140 buf.push(0);
141 self.0.write(core, &buf)?;
142
143 write_status(core, 0)?;
145
146 Ok(())
147 }
148}
149
150#[derive(Debug, PartialEq, Eq, Copy, Clone)]
154pub struct OpenRequest {
155 path: ZeroTerminatedString,
156 mode: &'static str,
157}
158
159impl OpenRequest {
160 pub fn path(&self, core: &mut dyn CoreInterface) -> Result<String, Error> {
162 self.path.read(core)
163 }
164
165 pub fn mode(&self) -> &'static str {
167 self.mode
168 }
169
170 pub fn respond_with_handle(
172 &self,
173 core: &mut dyn CoreInterface,
174 handle: NonZeroU32,
175 ) -> Result<(), Error> {
176 write_status(core, handle.get() as i32)
177 }
178}
179
180#[derive(Debug, PartialEq, Eq, Copy, Clone)]
184pub struct CloseRequest {
185 handle: u32,
186}
187
188impl CloseRequest {
189 pub fn file_handle(&self) -> u32 {
191 self.handle
192 }
193
194 pub fn success(&self, core: &mut dyn CoreInterface) -> Result<(), Error> {
196 write_status(core, 0)
197 }
198}
199
200#[derive(Debug, PartialEq, Eq, Copy, Clone)]
202pub struct WriteConsoleRequest(pub(crate) ZeroTerminatedString);
203impl WriteConsoleRequest {
204 pub fn read(&self, core: &mut crate::Core<'_>) -> Result<String, Error> {
206 self.0.read(core)
207 }
208}
209
210#[derive(Debug, PartialEq, Eq, Copy, Clone)]
212pub struct WriteRequest {
213 handle: u32,
214 bytes: u32,
215 len: u32,
216}
217
218impl WriteRequest {
219 pub fn file_handle(&self) -> u32 {
221 self.handle
222 }
223
224 pub fn read(&self, core: &mut crate::Core<'_>) -> Result<Vec<u8>, Error> {
226 let mut buf = vec![0u8; self.len as usize];
227 core.read(self.bytes as u64, &mut buf)?;
228 Ok(buf)
229 }
230
231 pub fn write_status(&self, core: &mut dyn CoreInterface, status: i32) -> Result<(), Error> {
233 write_status(core, status)
234 }
235}
236
237#[derive(Debug, PartialEq, Eq, Copy, Clone)]
239pub struct ReadRequest {
240 handle: u32,
241 bytes: u32,
242 len: u32,
243}
244
245impl ReadRequest {
246 pub fn file_handle(&self) -> u32 {
248 self.handle
249 }
250
251 pub fn bytes_to_read(&self) -> u32 {
253 self.len
254 }
255
256 pub fn write_buffer_to_target(
258 &self,
259 core: &mut crate::Core<'_>,
260 buf: &[u8],
261 ) -> Result<(), Error> {
262 assert!(buf.len() <= self.len as usize);
263
264 if !buf.is_empty() {
265 core.write(self.bytes as u64, buf)?;
266 }
267
268 let status = match buf.len() {
269 0 => self.len as i32,
270 len if len == self.len as usize => 0,
271 len => len as i32,
272 };
273
274 write_status(core, status)
275 }
276}
277
278#[derive(Debug, PartialEq, Eq, Copy, Clone)]
280pub struct SeekRequest {
281 handle: u32,
282 pos: u32,
283}
284
285impl SeekRequest {
286 pub fn file_handle(&self) -> u32 {
288 self.handle
289 }
290
291 pub fn position(&self) -> u32 {
293 self.pos
294 }
295
296 pub fn success(&self, core: &mut dyn CoreInterface) -> Result<(), Error> {
298 write_status(core, 0)
299 }
300}
301
302#[derive(Debug, PartialEq, Eq, Copy, Clone)]
304pub struct FileLengthRequest {
305 handle: u32,
306}
307
308impl FileLengthRequest {
309 pub fn file_handle(&self) -> u32 {
311 self.handle
312 }
313
314 pub fn write_length(&self, core: &mut dyn CoreInterface, len: i32) -> Result<(), Error> {
316 write_status(core, len)
317 }
318}
319
320#[derive(Debug, PartialEq, Eq, Copy, Clone)]
322pub struct RemoveRequest {
323 path: ZeroTerminatedString,
324}
325
326impl RemoveRequest {
327 pub fn path(&self, core: &mut dyn CoreInterface) -> Result<String, Error> {
329 self.path.read(core)
330 }
331
332 pub fn success(&self, core: &mut dyn CoreInterface) -> Result<(), Error> {
334 write_status(core, 0)
335 }
336}
337
338#[derive(Debug, PartialEq, Eq, Copy, Clone)]
340pub struct RenameRequest {
341 from_path: ZeroTerminatedString,
342 to_path: ZeroTerminatedString,
343}
344
345impl RenameRequest {
346 pub fn from_path(&self, core: &mut dyn CoreInterface) -> Result<String, Error> {
348 self.from_path.read(core)
349 }
350
351 pub fn to_path(&self, core: &mut dyn CoreInterface) -> Result<String, Error> {
353 self.to_path.read(core)
354 }
355
356 pub fn success(&self, core: &mut dyn CoreInterface) -> Result<(), Error> {
358 write_status(core, 0)
359 }
360}
361
362#[derive(Debug, PartialEq, Eq, Copy, Clone)]
364pub struct TimeRequest {}
365impl TimeRequest {
366 pub fn write_time(&self, core: &mut dyn CoreInterface, value: u32) -> Result<(), Error> {
368 write_status(core, value as i32)
369 }
370
371 pub fn write_current_time(&self, core: &mut dyn CoreInterface) -> Result<(), Error> {
373 let duration = SystemTime::now()
374 .duration_since(SystemTime::UNIX_EPOCH)
375 .expect("Failed to get system time");
376 self.write_time(core, duration.as_secs() as u32)
377 }
378}
379
380#[derive(Debug, PartialEq, Eq, Copy, Clone)]
382pub struct ErrnoRequest {}
383impl ErrnoRequest {
384 pub fn write_errno(&self, core: &mut dyn CoreInterface, errno: i32) -> Result<(), Error> {
386 write_status(core, errno)
388 }
389}
390
391fn write_status(core: &mut dyn CoreInterface, value: i32) -> Result<(), crate::Error> {
392 let reg = core.registers().get_argument_register(0).unwrap();
393 core.write_core_reg(reg.into(), RegisterValue::U32(value as u32))?;
394
395 Ok(())
396}
397
398#[derive(Debug, PartialEq, Eq, Copy, Clone)]
402pub struct Buffer {
403 buffer_location: u32, address: u32, len: u32, }
407
408impl Buffer {
409 pub fn from_block_at(
411 core: &mut dyn CoreInterface,
412 block_addr: u32,
413 ) -> Result<Self, crate::Error> {
414 let mut block: [u32; 2] = [0, 0];
415 core.read_32(block_addr as u64, &mut block)?;
416 Ok(Self {
417 buffer_location: block_addr,
418 address: block[0],
419 len: block[1],
420 })
421 }
422
423 pub fn read(&self, core: &mut dyn CoreInterface) -> Result<Vec<u8>, Error> {
425 let mut buf = vec![0u8; self.len as usize];
426 core.read(self.address as u64, &mut buf[..])?;
427 Ok(buf)
428 }
429
430 pub fn write(&self, core: &mut dyn CoreInterface, buf: &[u8]) -> Result<(), Error> {
433 if buf.len() > self.len as usize {
434 return Err(Error::Other("buffer not large enough".to_string()));
435 }
436 if buf.last() != Some(&0) {
437 return Err(Error::Other("last byte of buffer must be 0".to_string()));
438 }
439 core.write_8(self.address as u64, buf)?;
440 let block: [u32; 2] = [self.address, (buf.len() - 1) as u32];
441 core.write_32(self.buffer_location as u64, &block)?;
442 Ok(())
443 }
444}
445
446#[derive(Debug, PartialEq, Eq, Copy, Clone)]
447pub(crate) struct ZeroTerminatedString {
448 pub address: u32,
449 pub length: Option<u32>,
450}
451
452impl ZeroTerminatedString {
453 pub fn read(&self, core: &mut dyn CoreInterface) -> Result<String, Error> {
455 let mut bytes = Vec::new();
456
457 if let Some(len) = self.length {
458 bytes = vec![0; len as usize];
459 core.read(self.address as u64, &mut bytes)?;
460 } else {
461 let mut buf = [0; 128];
462 let mut from = self.address as u64;
463
464 loop {
465 core.read(from, &mut buf)?;
466 if let Some(end) = buf.iter().position(|&x| x == 0) {
467 bytes.extend_from_slice(&buf[..end]);
468 break;
469 }
470
471 bytes.extend_from_slice(&buf);
472 from += buf.len() as u64;
473 }
474 }
475
476 Ok(String::from_utf8_lossy(&bytes).to_string())
477 }
478}
479
480pub fn decode_semihosting_syscall(
483 core: &mut dyn CoreInterface,
484) -> Result<SemihostingCommand, Error> {
485 let operation: u32 = core
486 .read_core_reg(core.registers().get_argument_register(0).unwrap().id())?
487 .try_into()?;
488 let parameter: u32 = core
489 .read_core_reg(core.registers().get_argument_register(1).unwrap().id())?
490 .try_into()?;
491
492 tracing::debug!("Semihosting found r0={operation:#x} r1={parameter:#x}");
493
494 const SYS_GET_CMDLINE: u32 = 0x15;
498 const SYS_EXIT: u32 = 0x18;
499 const SYS_EXIT_EXTENDED: u32 = 0x20;
500 const SYS_EXIT_ADP_STOPPED_APPLICATIONEXIT: u32 = 0x20026;
501 const SYS_OPEN: u32 = 0x01;
502 const SYS_CLOSE: u32 = 0x02;
503 const SYS_WRITEC: u32 = 0x03;
504 const SYS_WRITE0: u32 = 0x04;
505 const SYS_WRITE: u32 = 0x05;
506 const SYS_READ: u32 = 0x06;
507 const SYS_SEEK: u32 = 0x0a;
508 const SYS_FLEN: u32 = 0x0c;
509 const SYS_REMOVE: u32 = 0x0e;
510 const SYS_RENAME: u32 = 0x0f;
511 const SYS_TIME: u32 = 0x11;
512 const SYS_ERRNO: u32 = 0x13;
513
514 Ok(match (operation, parameter) {
515 (SYS_EXIT, SYS_EXIT_ADP_STOPPED_APPLICATIONEXIT) => SemihostingCommand::ExitSuccess,
516 (SYS_EXIT, reason) => SemihostingCommand::ExitError(ExitErrorDetails {
517 reason,
518 exit_status: None,
519 subcode: None,
520 }),
521
522 (SYS_EXIT_EXTENDED, block_address) => {
523 let mut buf = [0u32; 2];
525 core.read_32(block_address as u64, &mut buf)?;
526 let reason = buf[0];
527 let subcode = buf[1];
528 match (reason, subcode) {
529 (SYS_EXIT_ADP_STOPPED_APPLICATIONEXIT, 0) => SemihostingCommand::ExitSuccess,
530 (SYS_EXIT_ADP_STOPPED_APPLICATIONEXIT, exit_status) => {
531 SemihostingCommand::ExitError(ExitErrorDetails {
532 reason,
533 exit_status: Some(exit_status),
534 subcode: None,
535 })
536 }
537 (reason, subcode) => SemihostingCommand::ExitError(ExitErrorDetails {
538 reason,
539 exit_status: None,
540 subcode: Some(subcode),
541 }),
542 }
543 }
544
545 (SYS_GET_CMDLINE, block_address) => {
546 write_status(core, -1)?;
549 SemihostingCommand::GetCommandLine(GetCommandLineRequest(Buffer::from_block_at(
550 core,
551 block_address,
552 )?))
553 }
554
555 (SYS_OPEN, pointer) => {
556 let [string, mode, str_len] = param(core, pointer)?;
557
558 write_status(core, -1)?;
561 SemihostingCommand::Open(OpenRequest {
562 path: ZeroTerminatedString {
563 address: string,
564 length: Some(str_len),
565 },
566 mode: match mode {
567 0 => "r",
568 1 => "rb",
569 2 => "r+",
570 3 => "r+b",
571 4 => "w",
572 5 => "wb",
573 6 => "w+",
574 7 => "w+b",
575 8 => "a",
576 9 => "ab",
577 10 => "a+",
578 11 => "a+b",
579 _ => "unknown",
580 },
581 })
582 }
583
584 (SYS_CLOSE, pointer) => {
585 let [handle] = param(core, pointer)?;
586 write_status(core, -1)?;
589 SemihostingCommand::Close(CloseRequest { handle })
590 }
591
592 (SYS_WRITEC, pointer) => {
593 SemihostingCommand::WriteConsole(WriteConsoleRequest(ZeroTerminatedString {
594 address: pointer,
595 length: Some(1),
596 }))
597 }
599
600 (SYS_WRITE0, pointer) => {
601 SemihostingCommand::WriteConsole(WriteConsoleRequest(ZeroTerminatedString {
602 address: pointer,
603 length: None,
604 }))
605 }
607
608 (SYS_WRITE, pointer) => {
609 let [handle, bytes, len] = param(core, pointer)?;
610 write_status(core, -1)?;
612 SemihostingCommand::Write(WriteRequest { handle, bytes, len })
613 }
614
615 (SYS_READ, pointer) => {
616 let [handle, bytes, len] = param(core, pointer)?;
617 write_status(core, -1)?;
619 SemihostingCommand::Read(ReadRequest { handle, bytes, len })
620 }
621
622 (SYS_SEEK, pointer) => {
623 let [handle, pos] = param(core, pointer)?;
624 write_status(core, -1)?;
626 SemihostingCommand::Seek(SeekRequest { handle, pos })
627 }
628
629 (SYS_FLEN, pointer) => {
630 let [handle] = param(core, pointer)?;
631 write_status(core, -1)?;
633 SemihostingCommand::FileLength(FileLengthRequest { handle })
634 }
635
636 (SYS_REMOVE, pointer) => {
637 let [path, len] = param(core, pointer)?;
638 write_status(core, -1)?;
640 SemihostingCommand::Remove(RemoveRequest {
641 path: ZeroTerminatedString {
642 address: path,
643 length: Some(len),
644 },
645 })
646 }
647
648 (SYS_RENAME, pointer) => {
649 let [from_path, from_len, to_path, to_len] = param(core, pointer)?;
650 write_status(core, -1)?;
652 SemihostingCommand::Rename(RenameRequest {
653 from_path: ZeroTerminatedString {
654 address: from_path,
655 length: Some(from_len),
656 },
657 to_path: ZeroTerminatedString {
658 address: to_path,
659 length: Some(to_len),
660 },
661 })
662 }
663
664 (SYS_TIME, 0) => SemihostingCommand::Time(TimeRequest {}),
665
666 (SYS_ERRNO, 0) => SemihostingCommand::Errno(ErrnoRequest {}),
667
668 _ => {
669 write_status(core, -1)?;
672
673 tracing::debug!(
674 "Unknown semihosting operation={operation:04x} parameter={parameter:04x}"
675 );
676 SemihostingCommand::Unknown(UnknownCommandDetails {
677 operation,
678 parameter,
679 })
680 }
681 })
682}
683
684fn param<const N: usize>(
685 core: &mut dyn CoreInterface,
686 pointer: u32,
687) -> Result<[u32; N], crate::Error> {
688 let mut buf = [0; N];
689 core.read_32(pointer as u64, &mut buf)?;
690 Ok(buf)
691}