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 write!(f, "reason: {:#x}", self.reason)?;
78 if let Some(exit_status) = self.exit_status {
79 write!(f, ", exit_status: {exit_status}")?;
80 }
81 if let Some(subcode) = self.subcode {
82 write!(f, ", subcode: {subcode:#x}")?;
83 }
84 Ok(())
85 }
86}
87
88#[derive(Debug, PartialEq, Eq, Copy, Clone)]
90pub struct UnknownCommandDetails {
91 pub operation: u32,
93
94 pub parameter: u32,
96}
97
98impl UnknownCommandDetails {
99 pub fn get_buffer(&self, core: &mut dyn CoreInterface) -> Result<Buffer, Error> {
101 Buffer::from_block_at(core, self.parameter)
102 }
103
104 pub fn write_status(&self, core: &mut dyn CoreInterface, status: i32) -> Result<(), Error> {
106 write_status(core, status)
107 }
108}
109
110#[derive(Debug, PartialEq, Eq, Copy, Clone)]
112pub struct GetCommandLineRequest(Buffer);
113
114impl GetCommandLineRequest {
115 pub fn write_command_line_to_target(
117 &self,
118 core: &mut dyn CoreInterface,
119 cmdline: &str,
120 ) -> Result<(), Error> {
121 let mut buf = cmdline.to_owned().into_bytes();
122 buf.push(0);
123 self.0.write(core, &buf)?;
124
125 write_status(core, 0)?;
127
128 Ok(())
129 }
130}
131
132#[derive(Debug, PartialEq, Eq, Copy, Clone)]
136pub struct OpenRequest {
137 path: ZeroTerminatedString,
138 mode: &'static str,
139}
140
141impl OpenRequest {
142 pub fn path(&self, core: &mut dyn CoreInterface) -> Result<String, Error> {
144 self.path.read(core)
145 }
146
147 pub fn mode(&self) -> &'static str {
149 self.mode
150 }
151
152 pub fn respond_with_handle(
154 &self,
155 core: &mut dyn CoreInterface,
156 handle: NonZeroU32,
157 ) -> Result<(), Error> {
158 write_status(core, handle.get() as i32)
159 }
160}
161
162#[derive(Debug, PartialEq, Eq, Copy, Clone)]
166pub struct CloseRequest {
167 handle: u32,
168}
169
170impl CloseRequest {
171 pub fn file_handle(&self) -> u32 {
173 self.handle
174 }
175
176 pub fn success(&self, core: &mut dyn CoreInterface) -> Result<(), Error> {
178 write_status(core, 0)
179 }
180}
181
182#[derive(Debug, PartialEq, Eq, Copy, Clone)]
184pub struct WriteConsoleRequest(pub(crate) ZeroTerminatedString);
185impl WriteConsoleRequest {
186 pub fn read(&self, core: &mut crate::Core<'_>) -> Result<String, Error> {
188 self.0.read(core)
189 }
190}
191
192#[derive(Debug, PartialEq, Eq, Copy, Clone)]
194pub struct WriteRequest {
195 handle: u32,
196 bytes: u32,
197 len: u32,
198}
199
200impl WriteRequest {
201 pub fn file_handle(&self) -> u32 {
203 self.handle
204 }
205
206 pub fn read(&self, core: &mut crate::Core<'_>) -> Result<Vec<u8>, Error> {
208 let mut buf = vec![0u8; self.len as usize];
209 core.read(self.bytes as u64, &mut buf)?;
210 Ok(buf)
211 }
212
213 pub fn write_status(&self, core: &mut dyn CoreInterface, status: i32) -> Result<(), Error> {
215 write_status(core, status)
216 }
217}
218
219#[derive(Debug, PartialEq, Eq, Copy, Clone)]
221pub struct ReadRequest {
222 handle: u32,
223 bytes: u32,
224 len: u32,
225}
226
227impl ReadRequest {
228 pub fn file_handle(&self) -> u32 {
230 self.handle
231 }
232
233 pub fn bytes_to_read(&self) -> u32 {
235 self.len
236 }
237
238 pub fn write_buffer_to_target(
240 &self,
241 core: &mut crate::Core<'_>,
242 buf: &[u8],
243 ) -> Result<(), Error> {
244 assert!(buf.len() <= self.len as usize);
245
246 if !buf.is_empty() {
247 core.write(self.bytes as u64, buf)?;
248 }
249
250 let status = match buf.len() {
251 0 => self.len as i32,
252 len if len == self.len as usize => 0,
253 len => len as i32,
254 };
255
256 write_status(core, status)
257 }
258}
259
260#[derive(Debug, PartialEq, Eq, Copy, Clone)]
262pub struct SeekRequest {
263 handle: u32,
264 pos: u32,
265}
266
267impl SeekRequest {
268 pub fn file_handle(&self) -> u32 {
270 self.handle
271 }
272
273 pub fn position(&self) -> u32 {
275 self.pos
276 }
277
278 pub fn success(&self, core: &mut dyn CoreInterface) -> Result<(), Error> {
280 write_status(core, 0)
281 }
282}
283
284#[derive(Debug, PartialEq, Eq, Copy, Clone)]
286pub struct FileLengthRequest {
287 handle: u32,
288}
289
290impl FileLengthRequest {
291 pub fn file_handle(&self) -> u32 {
293 self.handle
294 }
295
296 pub fn write_length(&self, core: &mut dyn CoreInterface, len: i32) -> Result<(), Error> {
298 write_status(core, len)
299 }
300}
301
302#[derive(Debug, PartialEq, Eq, Copy, Clone)]
304pub struct RemoveRequest {
305 path: ZeroTerminatedString,
306}
307
308impl RemoveRequest {
309 pub fn path(&self, core: &mut dyn CoreInterface) -> Result<String, Error> {
311 self.path.read(core)
312 }
313
314 pub fn success(&self, core: &mut dyn CoreInterface) -> Result<(), Error> {
316 write_status(core, 0)
317 }
318}
319
320#[derive(Debug, PartialEq, Eq, Copy, Clone)]
322pub struct RenameRequest {
323 from_path: ZeroTerminatedString,
324 to_path: ZeroTerminatedString,
325}
326
327impl RenameRequest {
328 pub fn from_path(&self, core: &mut dyn CoreInterface) -> Result<String, Error> {
330 self.from_path.read(core)
331 }
332
333 pub fn to_path(&self, core: &mut dyn CoreInterface) -> Result<String, Error> {
335 self.to_path.read(core)
336 }
337
338 pub fn success(&self, core: &mut dyn CoreInterface) -> Result<(), Error> {
340 write_status(core, 0)
341 }
342}
343
344#[derive(Debug, PartialEq, Eq, Copy, Clone)]
346pub struct TimeRequest {}
347impl TimeRequest {
348 pub fn write_time(&self, core: &mut dyn CoreInterface, value: u32) -> Result<(), Error> {
350 write_status(core, value as i32)
351 }
352
353 pub fn write_current_time(&self, core: &mut dyn CoreInterface) -> Result<(), Error> {
355 let duration = SystemTime::now()
356 .duration_since(SystemTime::UNIX_EPOCH)
357 .expect("Failed to get system time");
358 self.write_time(core, duration.as_secs() as u32)
359 }
360}
361
362#[derive(Debug, PartialEq, Eq, Copy, Clone)]
364pub struct ErrnoRequest {}
365impl ErrnoRequest {
366 pub fn write_errno(&self, core: &mut dyn CoreInterface, errno: i32) -> Result<(), Error> {
368 write_status(core, errno)
370 }
371}
372
373fn write_status(core: &mut dyn CoreInterface, value: i32) -> Result<(), crate::Error> {
374 let reg = core.registers().get_argument_register(0).unwrap();
375 core.write_core_reg(reg.into(), RegisterValue::U32(value as u32))?;
376
377 Ok(())
378}
379
380#[derive(Debug, PartialEq, Eq, Copy, Clone)]
384pub struct Buffer {
385 buffer_location: u32, address: u32, len: u32, }
389
390impl Buffer {
391 pub fn from_block_at(
393 core: &mut dyn CoreInterface,
394 block_addr: u32,
395 ) -> Result<Self, crate::Error> {
396 let mut block: [u32; 2] = [0, 0];
397 core.read_32(block_addr as u64, &mut block)?;
398 Ok(Self {
399 buffer_location: block_addr,
400 address: block[0],
401 len: block[1],
402 })
403 }
404
405 pub fn read(&self, core: &mut dyn CoreInterface) -> Result<Vec<u8>, Error> {
407 let mut buf = vec![0u8; self.len as usize];
408 core.read(self.address as u64, &mut buf[..])?;
409 Ok(buf)
410 }
411
412 pub fn write(&self, core: &mut dyn CoreInterface, buf: &[u8]) -> Result<(), Error> {
415 if buf.len() > self.len as usize {
416 return Err(Error::Other("buffer not large enough".to_string()));
417 }
418 if buf.last() != Some(&0) {
419 return Err(Error::Other("last byte of buffer must be 0".to_string()));
420 }
421 core.write_8(self.address as u64, buf)?;
422 let block: [u32; 2] = [self.address, (buf.len() - 1) as u32];
423 core.write_32(self.buffer_location as u64, &block)?;
424 Ok(())
425 }
426}
427
428#[derive(Debug, PartialEq, Eq, Copy, Clone)]
429pub(crate) struct ZeroTerminatedString {
430 pub address: u32,
431 pub length: Option<u32>,
432}
433
434impl ZeroTerminatedString {
435 pub fn read(&self, core: &mut dyn CoreInterface) -> Result<String, Error> {
437 let mut bytes = Vec::new();
438
439 if let Some(len) = self.length {
440 bytes = vec![0; len as usize];
441 core.read(self.address as u64, &mut bytes)?;
442 } else {
443 let mut buf = [0; 128];
444 let mut from = self.address as u64;
445
446 loop {
447 core.read(from, &mut buf)?;
448 if let Some(end) = buf.iter().position(|&x| x == 0) {
449 bytes.extend_from_slice(&buf[..end]);
450 break;
451 }
452
453 bytes.extend_from_slice(&buf);
454 from += buf.len() as u64;
455 }
456 }
457
458 Ok(String::from_utf8_lossy(&bytes).to_string())
459 }
460}
461
462pub fn decode_semihosting_syscall(
465 core: &mut dyn CoreInterface,
466) -> Result<SemihostingCommand, Error> {
467 let operation: u32 = core
468 .read_core_reg(core.registers().get_argument_register(0).unwrap().id())?
469 .try_into()?;
470 let parameter: u32 = core
471 .read_core_reg(core.registers().get_argument_register(1).unwrap().id())?
472 .try_into()?;
473
474 tracing::debug!("Semihosting found r0={operation:#x} r1={parameter:#x}");
475
476 const SYS_GET_CMDLINE: u32 = 0x15;
480 const SYS_EXIT: u32 = 0x18;
481 const SYS_EXIT_EXTENDED: u32 = 0x20;
482 const SYS_EXIT_ADP_STOPPED_APPLICATIONEXIT: u32 = 0x20026;
483 const SYS_OPEN: u32 = 0x01;
484 const SYS_CLOSE: u32 = 0x02;
485 const SYS_WRITEC: u32 = 0x03;
486 const SYS_WRITE0: u32 = 0x04;
487 const SYS_WRITE: u32 = 0x05;
488 const SYS_READ: u32 = 0x06;
489 const SYS_SEEK: u32 = 0x0a;
490 const SYS_FLEN: u32 = 0x0c;
491 const SYS_REMOVE: u32 = 0x0e;
492 const SYS_RENAME: u32 = 0x0f;
493 const SYS_TIME: u32 = 0x11;
494 const SYS_ERRNO: u32 = 0x13;
495
496 Ok(match (operation, parameter) {
497 (SYS_EXIT, SYS_EXIT_ADP_STOPPED_APPLICATIONEXIT) => SemihostingCommand::ExitSuccess,
498 (SYS_EXIT, reason) => SemihostingCommand::ExitError(ExitErrorDetails {
499 reason,
500 exit_status: None,
501 subcode: None,
502 }),
503
504 (SYS_EXIT_EXTENDED, block_address) => {
505 let mut buf = [0u32; 2];
507 core.read_32(block_address as u64, &mut buf)?;
508 let reason = buf[0];
509 let subcode = buf[1];
510 match (reason, subcode) {
511 (SYS_EXIT_ADP_STOPPED_APPLICATIONEXIT, 0) => SemihostingCommand::ExitSuccess,
512 (SYS_EXIT_ADP_STOPPED_APPLICATIONEXIT, exit_status) => {
513 SemihostingCommand::ExitError(ExitErrorDetails {
514 reason,
515 exit_status: Some(exit_status),
516 subcode: None,
517 })
518 }
519 (reason, subcode) => SemihostingCommand::ExitError(ExitErrorDetails {
520 reason,
521 exit_status: None,
522 subcode: Some(subcode),
523 }),
524 }
525 }
526
527 (SYS_GET_CMDLINE, block_address) => {
528 write_status(core, -1)?;
531 SemihostingCommand::GetCommandLine(GetCommandLineRequest(Buffer::from_block_at(
532 core,
533 block_address,
534 )?))
535 }
536
537 (SYS_OPEN, pointer) => {
538 let [string, mode, str_len] = param(core, pointer)?;
539
540 write_status(core, -1)?;
543 SemihostingCommand::Open(OpenRequest {
544 path: ZeroTerminatedString {
545 address: string,
546 length: Some(str_len),
547 },
548 mode: match mode {
549 0 => "r",
550 1 => "rb",
551 2 => "r+",
552 3 => "r+b",
553 4 => "w",
554 5 => "wb",
555 6 => "w+",
556 7 => "w+b",
557 8 => "a",
558 9 => "ab",
559 10 => "a+",
560 11 => "a+b",
561 _ => "unknown",
562 },
563 })
564 }
565
566 (SYS_CLOSE, pointer) => {
567 let [handle] = param(core, pointer)?;
568 write_status(core, -1)?;
571 SemihostingCommand::Close(CloseRequest { handle })
572 }
573
574 (SYS_WRITEC, pointer) => {
575 SemihostingCommand::WriteConsole(WriteConsoleRequest(ZeroTerminatedString {
576 address: pointer,
577 length: Some(1),
578 }))
579 }
581
582 (SYS_WRITE0, pointer) => {
583 SemihostingCommand::WriteConsole(WriteConsoleRequest(ZeroTerminatedString {
584 address: pointer,
585 length: None,
586 }))
587 }
589
590 (SYS_WRITE, pointer) => {
591 let [handle, bytes, len] = param(core, pointer)?;
592 write_status(core, -1)?;
594 SemihostingCommand::Write(WriteRequest { handle, bytes, len })
595 }
596
597 (SYS_READ, pointer) => {
598 let [handle, bytes, len] = param(core, pointer)?;
599 write_status(core, -1)?;
601 SemihostingCommand::Read(ReadRequest { handle, bytes, len })
602 }
603
604 (SYS_SEEK, pointer) => {
605 let [handle, pos] = param(core, pointer)?;
606 write_status(core, -1)?;
608 SemihostingCommand::Seek(SeekRequest { handle, pos })
609 }
610
611 (SYS_FLEN, pointer) => {
612 let [handle] = param(core, pointer)?;
613 write_status(core, -1)?;
615 SemihostingCommand::FileLength(FileLengthRequest { handle })
616 }
617
618 (SYS_REMOVE, pointer) => {
619 let [path, len] = param(core, pointer)?;
620 write_status(core, -1)?;
622 SemihostingCommand::Remove(RemoveRequest {
623 path: ZeroTerminatedString {
624 address: path,
625 length: Some(len),
626 },
627 })
628 }
629
630 (SYS_RENAME, pointer) => {
631 let [from_path, from_len, to_path, to_len] = param(core, pointer)?;
632 write_status(core, -1)?;
634 SemihostingCommand::Rename(RenameRequest {
635 from_path: ZeroTerminatedString {
636 address: from_path,
637 length: Some(from_len),
638 },
639 to_path: ZeroTerminatedString {
640 address: to_path,
641 length: Some(to_len),
642 },
643 })
644 }
645
646 (SYS_TIME, 0) => SemihostingCommand::Time(TimeRequest {}),
647
648 (SYS_ERRNO, 0) => SemihostingCommand::Errno(ErrnoRequest {}),
649
650 _ => {
651 write_status(core, -1)?;
654
655 tracing::debug!(
656 "Unknown semihosting operation={operation:04x} parameter={parameter:04x}"
657 );
658 SemihostingCommand::Unknown(UnknownCommandDetails {
659 operation,
660 parameter,
661 })
662 }
663 })
664}
665
666fn param<const N: usize>(
667 core: &mut dyn CoreInterface,
668 pointer: u32,
669) -> Result<[u32; N], crate::Error> {
670 let mut buf = [0; N];
671 core.read_32(pointer as u64, &mut buf)?;
672 Ok(buf)
673}