arm_semihosting/
io.rs

1use super::{call_host, Errno, Operation, PointerArgs};
2
3use cstr_core::CString;
4
5use core::fmt;
6
7#[derive(Debug)]
8pub enum Error {
9    InvalidPath,
10    EndOfFile,
11    WriteError(usize),
12    Errno(Errno),
13}
14
15impl From<Errno> for Error {
16    fn from(errno: Errno) -> Self {
17        Error::Errno(errno)
18    }
19}
20
21pub struct Readable;
22pub struct Writeable;
23pub struct ReadWriteable;
24
25pub enum AccessType {
26    Binary,
27    Text,
28}
29
30pub struct File<MODE> {
31    fd: usize,
32    _pd: core::marker::PhantomData<MODE>,
33}
34
35#[repr(usize)]
36pub enum Mode {
37    Read = 0,
38    Write = 1,
39    Append = 2,
40}
41
42const BINARY_BIT_OFFSET: usize = 0;
43const READ_WRITE_BIT_OFFSET: usize = 1;
44const MODE_BIT_OFFSET: usize = 2;
45
46#[repr(C)]
47pub(crate) struct OpenArgs {
48    file_path: *const u8,
49    mode: usize,
50    length: usize,
51}
52
53impl<'a> PointerArgs for OpenArgs {}
54
55#[repr(C)]
56pub(crate) struct ReadArgs<'a> {
57    fd: usize,
58    buffer: &'a mut u8,
59    length: usize,
60}
61
62impl<'a> PointerArgs for ReadArgs<'a> {}
63
64#[repr(C)]
65pub(crate) struct WriteArgs<'a> {
66    fd: usize,
67    buffer: &'a u8,
68    length: usize,
69}
70
71impl<'a> PointerArgs for WriteArgs<'a> {}
72
73#[repr(C)]
74pub(crate) struct CloseArgs {
75    fd: usize,
76}
77
78impl PointerArgs for CloseArgs {}
79
80#[repr(C)]
81pub(crate) struct SeekArgs {
82    fd: usize,
83    offset: usize,
84}
85
86impl PointerArgs for SeekArgs {}
87
88#[repr(C)]
89pub(crate) struct FlenArgs {
90    fd: usize,
91}
92
93impl PointerArgs for FlenArgs {}
94
95#[repr(C)]
96pub(crate) struct RemoveArgs {
97    file_path: *const u8,
98    length: usize,
99}
100
101impl PointerArgs for RemoveArgs {}
102
103#[repr(C)]
104pub(crate) struct RenameArgs {
105    file_path: *const u8,
106    length: usize,
107    new_file_path: *const u8,
108    new_length: usize,
109}
110
111impl PointerArgs for RenameArgs {}
112
113fn open_with_mode(path: &str, mode: usize) -> Result<File<ReadWriteable>, Error> {
114    let cpath = match CString::new(path) {
115        Ok(path) => path,
116        Err(_) => return Err(Error::InvalidPath),
117    };
118
119    let mut op = Operation::Open(OpenArgs {
120        file_path: cpath.as_c_str() as *const _ as *const _,
121        mode,
122        length: path.len(),
123    });
124
125    let result = call_host(&mut op)?;
126
127    Ok(File {
128        fd: result,
129        _pd: core::marker::PhantomData,
130    })
131}
132
133pub fn open(path: &str, access_type: AccessType) -> Result<File<Readable>, Error> {
134    let binary = matches!(access_type, AccessType::Binary);
135    let mode =
136        ((Mode::Read as usize) << MODE_BIT_OFFSET) | ((binary as usize) << BINARY_BIT_OFFSET);
137
138    open_with_mode(path, mode).map(|file| file.as_readonly())
139}
140
141pub fn open_read_write(path: &str, access_type: AccessType) -> Result<File<ReadWriteable>, Error> {
142    let binary = matches!(access_type, AccessType::Binary);
143    let mode = ((Mode::Write as usize) << MODE_BIT_OFFSET)
144        | ((binary as usize) << BINARY_BIT_OFFSET)
145        | (1 << READ_WRITE_BIT_OFFSET);
146
147    open_with_mode(path, mode)
148}
149
150pub fn create(path: &str, access_type: AccessType) -> Result<File<Writeable>, Error> {
151    let binary = matches!(access_type, AccessType::Binary);
152    let mode =
153        ((Mode::Write as usize) << MODE_BIT_OFFSET) | ((binary as usize) << BINARY_BIT_OFFSET);
154
155    open_with_mode(path, mode).map(|file| file.as_writeonly())
156}
157
158pub fn append(path: &str, access_type: AccessType) -> Result<File<Writeable>, Error> {
159    let binary = matches!(access_type, AccessType::Binary);
160    let mode =
161        ((Mode::Append as usize) << MODE_BIT_OFFSET) | ((binary as usize) << BINARY_BIT_OFFSET);
162
163    open_with_mode(path, mode).map(|file| file.as_writeonly())
164}
165
166pub fn remove(path: &str) -> Result<(), Error> {
167    let cpath = match CString::new(path) {
168        Ok(path) => path,
169        Err(_) => return Err(Error::InvalidPath),
170    };
171
172    let mut op = Operation::Remove(RemoveArgs {
173        file_path: cpath.as_c_str() as *const _ as *const _,
174        length: path.len(),
175    });
176
177    call_host(&mut op)?;
178    Ok(())
179}
180
181pub fn rename(path: &str, new_path: &str) -> Result<(), Error> {
182    let cpath = match CString::new(path) {
183        Ok(path) => path,
184        Err(_) => return Err(Error::InvalidPath),
185    };
186
187    let new_cpath = match CString::new(new_path) {
188        Ok(path) => path,
189        Err(_) => return Err(Error::InvalidPath),
190    };
191
192    let mut op = Operation::Rename(RenameArgs {
193        file_path: cpath.as_c_str() as *const _ as *const _,
194        length: path.len(),
195        new_file_path: new_cpath.as_c_str() as *const _ as *const _,
196        new_length: new_path.len(),
197    });
198
199    call_host(&mut op)?;
200    Ok(())
201}
202
203impl<MODE> File<MODE> {
204    fn write_internal(&mut self, buffer: &[u8]) -> Result<(), Error> {
205        let length = buffer.len();
206        let mut op = Operation::Write(WriteArgs {
207            fd: self.fd,
208            buffer: &buffer[0],
209            length,
210        });
211
212        let result = call_host(&mut op)?;
213
214        if result != 0 {
215            Err(Error::WriteError(result))
216        } else {
217            Ok(())
218        }
219    }
220
221    fn read_internal(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
222        let length = buffer.len();
223        let mut op = Operation::Read(ReadArgs {
224            fd: self.fd,
225            buffer: &mut buffer[0],
226            length,
227        });
228
229        let result = call_host(&mut op)?;
230
231        if result == length {
232            Err(Error::EndOfFile)
233        } else {
234            Ok(length - result)
235        }
236    }
237
238    pub fn seek(&mut self, byte_offset: usize) -> Result<(), Error> {
239        let mut op = Operation::Seek(SeekArgs {
240            fd: self.fd,
241            offset: byte_offset,
242        });
243
244        call_host(&mut op)?;
245        Ok(())
246    }
247
248    pub fn length(&self) -> Result<usize, Error> {
249        let mut op = Operation::Flen(FlenArgs { fd: self.fd });
250
251        let result = call_host(&mut op)?;
252        Ok(result as usize)
253    }
254}
255
256impl File<Readable> {
257    pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
258        self.read_internal(buffer)
259    }
260}
261
262impl File<Writeable> {
263    pub fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
264        self.write_internal(buffer)
265    }
266}
267
268impl File<ReadWriteable> {
269    pub fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
270        self.write_internal(buffer)
271    }
272
273    pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
274        self.read_internal(buffer)
275    }
276
277    pub fn as_readonly(self) -> File<Readable> {
278        let file = core::mem::ManuallyDrop::new(self);
279        File {
280            fd: file.fd,
281            _pd: core::marker::PhantomData,
282        }
283    }
284
285    pub fn as_writeonly(self) -> File<Writeable> {
286        let file = core::mem::ManuallyDrop::new(self);
287        File {
288            fd: file.fd,
289            _pd: core::marker::PhantomData,
290        }
291    }
292}
293
294impl<MODE> Drop for File<MODE> {
295    fn drop(&mut self) {
296        let mut op = Operation::Close(CloseArgs { fd: self.fd });
297        call_host(&mut op).ok();
298    }
299}
300
301impl fmt::Write for File<Writeable> {
302    fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
303        self.write(s.as_bytes()).expect("No error writing files?");
304        Ok(())
305    }
306}
307
308impl fmt::Write for File<ReadWriteable> {
309    fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
310        self.write(s.as_bytes()).expect("No error writing files?");
311        Ok(())
312    }
313}