ieee1275 0.2.0

Safe and easy-to-use wrapper for building OpenFirmware/IEEE1275 apps
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
// Copyright 2021 Alberto Ruiz <aruiz@redhat.com>
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

#![no_std]

extern crate alloc;

use core::alloc::{GlobalAlloc, Layout};
use core::ptr;
use core::ffi::CStr;

const OF_SIZE_ERR: usize = usize::MAX;

extern "C" fn fallback_entry(_args: *mut Args) -> usize {
    OF_SIZE_ERR
}

pub mod services {
    use core::ffi::c_char;

    use crate::{IHandle, PHandle};
    /// Header for Service Arguments
    #[repr(C)]
    pub struct Args {
        pub service: *const c_char,
        pub nargs: usize,
        pub nret: usize,
    }

    #[repr(C)]
    pub struct WriteArgs {
        pub args: Args,
        pub stdout: *const IHandle,
        pub msg: *const u8,
        pub len: usize,
        pub ret: usize,
    }

    #[repr(C)]
    pub struct FindDeviceArgs {
        pub args: Args,
        pub device: *mut u8,
        pub phandle: *const PHandle,
    }

    #[repr(C)]
    pub struct PropArgs<T> {
        pub args: Args,
        pub phandle: *const PHandle,
        pub prop: *const u8,
        pub buf: *const T,
        pub buflen: usize,
        pub size: usize,
    }

    #[repr(C)]
    pub struct ClaimArgs {
        pub args: Args,
        pub virt: *mut u8,
        pub size: usize,
        pub align: usize,
        pub ret: *mut u8,
    }

    #[repr(C)]
    pub struct ReleaseArgs {
        pub args: Args,
        pub virt: *mut u8,
        pub size: usize,
    }

    #[repr(C)]
    pub struct OpenArgs {
        pub args: Args,
        pub dev: *const c_char,
        pub handle: *const IHandle,
    }

    #[repr(C)]
    pub struct ReadArgs {
        pub args: Args,
        pub handle: *const IHandle,
        pub buffer: *const u8,
        pub size: usize,
        pub actual_size: usize,
    }

    #[repr(C)]
    pub struct CloseArgs {
        pub args: Args,
        pub handle: *const IHandle,
    }

    #[repr(C)]
    pub struct SeekArgs {
        pub args: Args,
        pub handle: *const IHandle,
        pub pos_hi: isize,
        pub pos_low: isize,
        pub status: isize,
    }

    #[repr(C)]
    pub struct CallMethodArgs {
        pub args: Args,
        pub method: *const c_char,
        pub handle: *const IHandle,
    }

    #[repr(C)]
    pub struct BlockSizeArgs {
        pub args: CallMethodArgs,
        pub result: isize,
        pub block_size: isize,
    }

    #[repr(C)]
    pub struct ReadBlocks {
        pub args: CallMethodArgs,
        pub buf: *mut u8,
        pub block_index: usize,
        pub nblocks: usize,
        pub result: usize,
        pub blocks_read: usize,
    }
}

use services::{Args, CallMethodArgs};

#[cfg_attr(not(feature = "no_global_allocator"), global_allocator)]
static mut GLOBAL_PROM: PROM = PROM {
    entry_fn: fallback_entry,
    stdout: ptr::null_mut(),
    chosen: ptr::null_mut(),
};

#[cfg(not(feature = "no_panic_handler"))]
use core::panic::PanicInfo;

#[cfg(not(feature = "no_panic_handler"))]
#[panic_handler]
fn panic(_panic: &PanicInfo<'_>) -> ! {
    unsafe {
        GLOBAL_PROM.exit();
    }
}

/// Opaque type to represent a package handle
#[repr(C)]
pub struct PHandle {}

/// Opaque type to represent a package instance handle
#[repr(C)]
pub struct IHandle {}

/// OF represents an Open Firmware environment
#[derive(Clone, Copy)]
pub struct PROM {
    /// Entry function into the Open Firmware services
    entry_fn: extern "C" fn(*mut Args) -> usize,
    /// Package handle into '/chosen' which holds parameters chosen at runtime
    pub chosen: *const PHandle,
    /// Instance handle into stdout
    pub stdout: *const IHandle,
}

impl PROM {
    /// Creates a new OF instance from a valid entry point
    ///
    /// # Errors
    ///
    /// If it fails on initalization of ```chosen``` and ```stdout``` it will return an error
    pub fn new(entry: extern "C" fn(*mut Args) -> usize) -> Result<Self, &'static str> {
        let mut ret = PROM {
            entry_fn: entry,
            chosen: ptr::null_mut(),
            stdout: ptr::null_mut(),
        };

        ret.init()?;
        Ok(ret)
    }

    fn init(&mut self) -> Result<(), &'static str> {
        let chosen = self.find_device(c"/chosen")?;
        let mut stdout: *const IHandle = ptr::null_mut();
        let _ = self.get_property(
            chosen,
            c"stdout",
            &mut stdout as *mut *const IHandle,
            core::mem::size_of::<*const IHandle>(),
        )?;

        self.stdout = stdout;
        self.chosen = chosen;
        Ok(())
    }

    /// Exits the client program back into Open Firmware
    pub fn exit(&self) -> ! {
        let mut args = Args {
            service: c"exit".as_ptr(),
            nargs: 1,
            nret: 0,
        };

        (self.entry_fn)(&mut args as *mut Args);
        loop {}
    }

    /// Writes a string into stdout
    pub fn write_stdout(&self, msg: &str) -> Result<(), &'static str> {
        if self.stdout.is_null() {
            return Err("stdout is not present");
        }

        let mut args = services::WriteArgs {
            args: Args {
                service: c"write".as_ptr(),
                nargs: 3,
                nret: 1,
            },
            stdout: self.stdout,
            msg: msg.as_ptr(),
            len: msg.len(),
            ret: 0,
        };

        (self.entry_fn)(&mut args.args as *mut Args);

        match args.ret {
            OF_SIZE_ERR => Err("Error writing stdout"),
            _ => Ok(()),
        }
    }

    /// Writes a str into stdout and ends with a newline
    pub fn write_line(&self, msg: &str) {
        let _ = self.write_stdout(msg);
        let _ = self.write_stdout("\n\r");
    }

    /// Finds a device from a null terminated string
    pub fn find_device(&self, name: &CStr) -> Result<*const PHandle, &'static str> {
        let mut args = services::FindDeviceArgs {
            args: Args {
                service: c"finddevice".as_ptr(),
                nargs: 1,
                nret: 1,
            },
            device: name.as_ptr() as *mut u8,
            phandle: ptr::null_mut(),
        };

        match (self.entry_fn)(&mut args.args as *mut Args) {
            OF_SIZE_ERR => Err("Could not retreive property"),
            _ => Ok(args.phandle),
        }
    }

    /// Get property from package
    ///
    /// # Arguments
    ///
    /// ```phandle```: package handle
    /// ```prop```: null terminated property name
    /// ```buf```: pointer to buffer to store the value of the property, note that it has to match the known size of the property
    /// ```buflen```: length of ```buf```
    ///
    /// # Retuns
    ///
    /// The actual amount of bytes written
    pub fn get_property<T>(
        &self,
        phandle: *const PHandle,
        prop: &CStr,
        buf: *mut T,
        buflen: usize,
    ) -> Result<usize, &'static str> {
        let mut args = services::PropArgs {
            args: Args {
                service: c"getprop".as_ptr(),
                nargs: 4,
                nret: 1,
            },
            phandle: phandle,
            prop: prop.as_ptr() as *mut u8,
            buf: buf,
            buflen: buflen,
            size: 0,
        };

        match (self.entry_fn)(&mut args.args as *mut Args) {
            OF_SIZE_ERR => Err("Could not retreive property"),
            _ => Ok(args.size),
        }
    }

    /// Allocate heap memory
    ///
    /// # Arguments
    ///
    /// ```size```: The amount of bytes to be allocated
    /// ```align```: The byte alignment boundary, must be graeter than 0
    pub fn claim(&self, size: usize, align: usize) -> Result<*mut u8, &'static str> {
        if align == 0 {
            return Err("Could not allocate memory with alignment '0'");
        }

        let mut args = services::ClaimArgs {
            args: Args {
                service: c"claim".as_ptr(),
                nargs: 3,
                nret: 1,
            },
            virt: ptr::null_mut(),
            size,
            align,
            ret: ptr::null_mut(),
        };

        match (self.entry_fn)(&mut args.args as *mut Args) {
            OF_SIZE_ERR => Err("Could not allocate memory"),
            _ => Ok(args.ret),
        }
    }

    /// Release allocated heap memory by the ```claim``` method
    pub fn release(&self, virt: *mut u8, size: usize) {
        let mut args = services::ReleaseArgs {
            args: Args {
                service: c"release".as_ptr(),
                nargs: 2,
                nret: 0,
            },
            virt,
            size,
        };

        let _ = (self.entry_fn)(&mut args.args as *mut Args);
    }

    /// Opens a device from a spec
    ///
    /// # Arguments
    ///
    /// ```dev_spec```: The device specifier, must be a null terminated string
    ///
    /// # Returns
    ///
    /// Pointer to the device's package instance handle on success
    pub fn open(&self, dev_spec: &CStr) -> Result<*const IHandle, &'static str> {
        let mut args = services::OpenArgs {
            args: Args {
                service: c"open".as_ptr(),
                nargs: 1,
                nret: 1,
            },
            dev: dev_spec.as_ptr(),
            handle: ptr::null(),
        };

        let _ = (self.entry_fn)(&mut args.args as *mut Args);

        match args.handle.is_null() {
            true => Err("Could not open device"),
            false => Ok(args.handle),
        }
    }

    /// Read operation
    ///
    /// # Arguments
    ///
    /// ```handle```: Instance handle
    /// ```buffer```: Output buffer to write the read content
    /// ```size```: Size in bytes of the output buffer
    ///
    /// # Returns
    ///
    /// Number of bytes read into ```buffer```
    pub fn read(
        &self,
        handle: *const IHandle,
        buffer: *mut u8,
        size: usize,
    ) -> Result<usize, &'static str> {
        let mut args = services::ReadArgs {
            args: Args {
                service: c"read".as_ptr(),
                nargs: 3,
                nret: 1,
            },
            handle,
            buffer,
            size,
            actual_size: 0,
        };

        let _ = (self.entry_fn)(&mut args.args as *mut Args);

        match args.actual_size {
            OF_SIZE_ERR => Err("Could not read device"),
            _ => Ok(args.actual_size),
        }
    }

    pub fn close(&self, handle: *const IHandle) -> Result<(), &'static str> {
        let mut args = services::CloseArgs {
            args: Args {
                service: c"close".as_ptr(),
                nargs: 1,
                nret: 0,
            },
            handle,
        };

        match (self.entry_fn)(&mut args.args as *mut Args) {
            OF_SIZE_ERR => Err("Could not close device"),
            _ => Ok(()),
        }
    }

    pub fn seek(&self, handle: *const IHandle, pos: isize) -> Result<(), &'static str> {
        let mut args = services::SeekArgs {
            args: Args {
                service: c"seek".as_ptr(),
                nargs: 3,
                nret: 1,
            },
            handle,
            pos_hi: 0,
            pos_low: pos,
            status: 0,
        };

        match (self.entry_fn)(&mut args.args as *mut Args) {
            OF_SIZE_ERR => Err("Could not seek device"),
            _ => {
                if args.status == -1 {
                    Err("seek not implemented for this device")
                } else {
                    Ok(())
                }
            }
        }
    }

    pub fn get_block_size(&self, block_device: *const IHandle) -> Result<isize, &'static str> {
        let mut args = services::BlockSizeArgs {
            args: CallMethodArgs {
                args: Args {
                    service: c"call-method".as_ptr(),
                    nargs: 2,
                    nret: 2,
                },
                method: c"block-size".as_ptr(),
                handle: block_device,
            },
            result: 0,
            block_size: 0,
        };

        match (self.entry_fn)(&mut args.args.args as *mut Args) {
            OF_SIZE_ERR => Err("Could not get block size for volue device"),
            _ => match args.result {
                0 => Ok(args.block_size),
                _ => Err("Error trying to retrieve block size"),
            },
        }
    }

    /*pub fn read_blocks(
        &self,
        handle: *const IHandle,
        buf: &mut [u8],
        block_index: usize,
        nblocks: usize,
    ) -> Result<usize, &'static str> {
        let mut args = services::ReadBlocks {
            args: services::CallMethodArgs {
                args: services::Args {
                    service: b"call-method\0".as_ptr(),
                    nargs: 2 + 3,
                    nret: 1 + 1,
                },
                method: b"read-blocks\0".as_ptr(),
                handle,
            },
            buf: buf.as_mut_ptr(),
            block_index,
            nblocks,
            result: usize::MAX,
            blocks_read: 0,
        };

        match (self.entry_fn)(&mut args.args.args as *mut Args) {
            OF_SIZE_ERR => Err("Error reading blocks from volue device"),
            _ => match args.result {
                0 => Ok(args.blocks_read),
                _ => Err("Could not read block from volue device"),
            },
        }
    }*/
}

unsafe impl GlobalAlloc for PROM {
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        match self.claim(layout.size(), layout.align()) {
            Ok(ret) => ret,
            Err(msg) => {
                panic!("{}", msg);
            }
        }
    }

    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
        self.release(ptr, layout.size());
    }
}

/// This function intializes the Open Firmware environment object globally
/// it has to be called before any other API calls are used. Otherwise the
/// default panic and allocation handlers will fail
pub fn prom_init(entry: extern "C" fn(*mut Args) -> usize) -> PROM {
    let prom = match PROM::new(entry) {
        Ok(prom) => prom,
        Err(_) => {
            let mut args = Args {
                service: c"exit".as_ptr(),
                nargs: 0,
                nret: 0,
            };
            let _ = entry(&mut args as *mut Args);
            loop {}
        }
    };

    // WARNING: DO NOT USE alloc:: before this point
    unsafe {
        GLOBAL_PROM = prom;
    };

    prom
}