limine_protocol/
lib.rs

1//! Crate containing Rust representations for the Limine Boot Protocol
2//!
3//! # Example
4//! If one wanted to have 64KiB of stack space, they would make a request like such
5//! ```rust
6//! // Note that `#[used]` may be required in case Rust decides the object is unreachable or unused
7//! #[used]
8//! static STACK_REQUEST: Request<StackSizeRequest> = StackSizeRequest {
9//!    stack_size: 1024 * 64,
10//!    ..StackSizeRequest::default()
11//! }
12//! .into();
13//! ```
14//!
15//! One could also decide to link the request into the `limine_reqs` section, which would be done like such
16//! ```rust
17//! pub struct NotSync<T>(pub T);
18//!
19//! impl<T> core::ops::Deref for NotSync<T> {
20//!     type Target = T;
21//!  
22//!     fn deref(&self) -> &Self::Target {
23//!         &self.0
24//!     }
25//! }
26//!
27//! unsafe impl<T> Sync for NotSync<T> {}
28//!
29//! // Note that `#[used]` may be required in case Rust decides the object is unreachable or unused
30//! #[used]
31//! static ARR: [NotSync<*mut ()>; 1] = [NotSync(&STACK_REQUEST as *const _ as *mut ())];
32//! ```
33//! Note that this is very unsafe, as is expected by such a thing as a Bootloader. Rust has no clue we're doing this at all.
34#![no_std]
35#![deny(missing_docs)]
36#![warn(
37    clippy::pedantic,
38    clippy::nursery,
39    clippy::unwrap_used,
40    clippy::expect_used
41)]
42#![allow(clippy::module_name_repetitions)]
43
44use core::{cell::UnsafeCell, fmt::Debug, ops::Deref};
45
46#[repr(transparent)]
47/// A Request type, which wraps the internal request in an unsafe cell,
48/// due to the possibility it may be mutated by things outside rust.
49/// However, it automatically derefences to the internal type for ergonomics.
50pub struct Request<T>(UnsafeCell<T>);
51
52impl<T> Request<T> {
53    /// Create a new request.
54    /// This should not be used outside of the `limine-protocol` crate.
55    pub(crate) const fn new(req: T) -> Self {
56        Self(UnsafeCell::new(req))
57    }
58}
59
60impl<T> Deref for Request<T> {
61    type Target = T;
62
63    fn deref(&self) -> &Self::Target {
64        unsafe { &*self.0.get() }
65    }
66}
67
68impl<T: Debug> Debug for Request<T> {
69    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
70        <T as Debug>::fmt(unsafe { &*self.0.get() }, f)
71    }
72}
73
74unsafe impl<T> Sync for Request<T> {}
75
76/// Responses
77mod responses;
78pub use responses::*;
79
80/// Requests
81mod requests;
82pub use requests::*;
83
84mod default_const;
85pub use default_const::ConstDefault;
86
87/// Structures returned by the responses
88pub mod structures;
89
90#[cfg(test)]
91mod tests {
92    use crate::{
93        responses::{FramebufferResponse, SMPResponse, TerminalResponse},
94        structures::{framebuffer::Framebuffer, smpinfo::SMPInfo, terminal::Terminal},
95    };
96
97    extern "C" fn fake_write(_: *mut Terminal, _: *const u8, _: u64) {}
98
99    #[test]
100    fn terminal_get_slice() {
101        let mut framebuffer = Framebuffer::new_empty();
102        let tarray: [&Terminal; 1] = [&Terminal {
103            columns: 120,
104            rows: 80,
105            framebuffer: &mut framebuffer,
106        }];
107        let terminal = TerminalResponse {
108            revision: 1,
109            terminal_count: 1,
110            terminals: tarray.as_ptr() as *const *const Terminal,
111            write: fake_write,
112        };
113        let tarray_from_terminal = unsafe { terminal.get_terminals() }.unwrap();
114        assert_eq!(tarray.as_slice(), tarray_from_terminal);
115    }
116
117    #[test]
118    fn terminal_none_when_null() {
119        let terminal = TerminalResponse {
120            revision: 1,
121            terminal_count: 0,
122            terminals: core::ptr::null(),
123            write: fake_write,
124        };
125        assert!(unsafe { terminal.get_terminals() }.is_none());
126    }
127
128    #[test]
129    fn framebuffer_get_slice() {
130        let mut framebuffer_a = [&Framebuffer::new_empty(), &Framebuffer::new_empty()];
131        let framebuffer = FramebufferResponse {
132            revision: 1,
133            framebuffer_count: 2,
134            framebuffers: framebuffer_a.as_mut_ptr() as *mut *mut Framebuffer,
135        };
136        let fa_from_resp = unsafe { framebuffer.get_framebuffers() }.unwrap();
137        assert_eq!(framebuffer_a, fa_from_resp);
138    }
139
140    #[test]
141    fn framebuffer_none_when_null() {
142        let framebuffer = FramebufferResponse {
143            revision: 1,
144            framebuffer_count: 2,
145            framebuffers: core::ptr::null_mut(),
146        };
147        assert!(unsafe { framebuffer.get_framebuffers() }.is_none());
148    }
149
150    #[test]
151    fn smpinfo_get_slice() {
152        let c0 = SMPInfo::new_empty();
153        let mut c1 = SMPInfo::new_empty();
154        c1.processor_id = 1;
155        c1.lapic_id = 1;
156        let smps = [&c0, &c1];
157        let resp = SMPResponse {
158            revision: 1,
159            flags: 0,
160            bsp_lapic_id: 0,
161            cpu_count: 2,
162            cpus: smps.as_ptr() as *const *const SMPInfo,
163        };
164        let from_resp = unsafe { resp.get_cpu_info() }.unwrap();
165        assert_eq!(smps, from_resp);
166    }
167
168    #[test]
169    fn smpinfo_none_when_null() {
170        let resp = SMPResponse {
171            revision: 1,
172            flags: 0,
173            bsp_lapic_id: 0,
174            cpu_count: 2,
175            cpus: core::ptr::null(),
176        };
177        assert!(unsafe { resp.get_cpu_info() }.is_none())
178    }
179}