|
4 | 4 | //! |
5 | 5 | //! The main value-add of this crate is to abstract away the parsing and |
6 | 6 | //! construction of Multiboot2 structures. This is more complex as it may sound |
7 | | -//! at first due to the difficulties listed below. |
| 7 | +//! at first due to the difficulties listed below. Further, functionality for |
| 8 | +//! the iteration of tags are provided. |
8 | 9 | //! |
9 | 10 | //! The abstractions provided by this crate serve as the base to work with the |
10 | | -//! following structures: |
| 11 | +//! following structures in interaction: |
11 | 12 | //! - multiboot2: |
12 | | -//! - boot information structure (whole) |
| 13 | +//! - boot information |
| 14 | +//! - boot information header (the fixed sized begin portion of a boot |
| 15 | +//! information) |
13 | 16 | //! - boot information tags |
| 17 | +//! - boot information tag header (the fixed sized begin portion of a tag) |
14 | 18 | //! - multiboot2-header: |
15 | | -//! - header structure (whole) |
| 19 | +//! - header |
| 20 | +//! - header header (the fixed sized begin portion of a header) |
16 | 21 | //! - header tags |
| 22 | +//! - header tag header (the fixed sized begin portion of a tag) |
17 | 23 | //! |
18 | | -//! # Solved Problem & Difficulties Along the Way |
| 24 | +//! # TL;DR: Specific Example |
19 | 25 | //! |
20 | | -//! Firstly, the design choice to have ABI-compatible rusty types influenced the |
21 | | -//! requirements and difficulties along the way. They, on the other side, |
22 | | -//! influenced the design. The outcome is what we perceive as the optimal rusty |
23 | | -//! and convenient solution. |
| 26 | +//! To name a specific example, the `multiboot2` crate just needs the following |
| 27 | +//! types: |
| 28 | +//! |
| 29 | +//! - `BootInformationHeader` implementing [`Header`] |
| 30 | +//! - `BootInformation` wrapping [`DynSizedStructure`] |
| 31 | +//! - `type TagIter<'a> = multiboot2_common::TagIter<'a, TagHeader>` |
| 32 | +//! ([`TagIter`]) |
| 33 | +//! - `TagHeader` implementing [`Header`] |
| 34 | +//! - Structs for each tag, each implementing [`MaybeDynSized`] |
| 35 | +//! |
| 36 | +//! Then, all the magic using the [`TagIter`] and [`DynSizedStructure::cast`] |
| 37 | +//! can easily be utilized. |
| 38 | +//! |
| 39 | +//! The same correspondingly applies to the structures in `multiboot2-header`. |
| 40 | +//! |
| 41 | +//! # Design, Solved Problem, and Difficulties along the Way |
| 42 | +//! |
| 43 | +//! Firstly, the design choice to have ABI-compatible rusty types in |
| 44 | +//! `multiboot2` and `multiboot2-header` mainly influenced the requirements and |
| 45 | +//! difficulties along the way. These obstacles on the other side, influenced |
| 46 | +//! the design. The outcome is what we perceive as the optimal rusty and |
| 47 | +//! convenient solution. |
| 48 | +//! |
| 49 | +//! ## Architecture Diagrams |
| 50 | +//! |
| 51 | +//! The figures in the [README](https://crates.io/crates/multiboot2-common) |
| 52 | +//! (currently not embeddable in lib.rs unfortunately) provides an overview of |
| 53 | +//! the parsing of Multiboot2 structures and how the definitions from this |
| 54 | +//! crate are used. |
| 55 | +//! |
| 56 | +//! Note that although the diagrams seem complex, most logic is in |
| 57 | +//! `multiboot2-common`. For downstream users, the usage is quite simple. |
24 | 58 | //! |
25 | 59 | //! ## Multiboot2 Structures |
26 | 60 | //! |
|
83 | 117 | //! |
84 | 118 | //! The overall common abstractions needed to solve the problems mentioned in |
85 | 119 | //! this section are also mainly influenced by the fact that the `multiboot2` |
86 | | -//! and `multiboot2-header` crates use a **zero-copy** design for parsing |
87 | | -//! the corresponding structures. |
| 120 | +//! and `multiboot2-header` crates use a **zero-copy** design by parsing |
| 121 | +//! the corresponding raw bytes with **ABI-compatible types** owning all their |
| 122 | +//! memory. |
88 | 123 | //! |
89 | | -//! Further, by having **ABI-compatible types** that fully represent the |
90 | | -//! reality, we can use the same type for parsing **and** for construction, |
91 | | -//! as modelled in the following simplified example: |
| 124 | +//! Further, by having ABI-compatible types that fully represent the reality, we |
| 125 | +//! can use the same type for parsing **and** for construction, as modelled in |
| 126 | +//! the following simplified example: |
92 | 127 | //! |
93 | 128 | //! ```rust,ignore |
94 | 129 | //! /// ABI-compatible tag for parsing. |
| 130 | +//! #[repr(C)] |
95 | 131 | //! pub struct MemoryMapTag { |
96 | 132 | //! header: TagHeader, |
97 | 133 | //! entry_size: u32, |
|
112 | 148 | //! |
113 | 149 | //! ## Creating Fat Pointers with [`ptr_meta`] |
114 | 150 | //! |
| 151 | +//! Fat pointers are a language feature and the base for references to |
| 152 | +//! dynamically sized types, such as `&str`, `&[T]`, `dyn T` or |
| 153 | +//! `&DynamicallySizedStruct`. |
| 154 | +//! |
| 155 | +//! Currently, they can't be created using the standard library, but |
| 156 | +//! [`ptr_meta`] can be utilized. |
| 157 | +//! |
115 | 158 | //! To create fat pointers with [`ptr_meta`], each tag needs a `Metadata` type |
116 | 159 | //! which is either `usize` (for DSTs) or `()`. A trait is needed to abstract |
117 | | -//! above sized or unsized types. |
| 160 | +//! above sized or unsized types. This is done by [`MaybeDynSized`]. |
118 | 161 | //! |
119 | 162 | //! ## Multiboot2 Requirements |
120 | 163 | //! |
121 | 164 | //! All tags must be 8-byte aligned. The actual payload of tags may be followed |
122 | 165 | //! by padding zeroes to fill the gap until the next alignment boundary, if |
123 | 166 | //! necessary. These zeroes are not reflected in the tag's size, but for Rust, |
124 | | -//! must be reflected in the memory allocation size. |
| 167 | +//! must be reflected in the type's memory allocation. |
125 | 168 | //! |
126 | 169 | //! ## Rustc Requirements |
127 | 170 | //! |
|
141 | 184 | //! [`Layout`] for the underlying type equals the one we manually used for the |
142 | 185 | //! allocation. |
143 | 186 | //! |
144 | | -//! # Architecture & Provided Abstractions |
145 | | -//! |
146 | | -//! The figures in the [README](https://crates.io/crates/multiboot2-common) |
147 | | -//! (currently not embeddable in lib.rs unfortunately) provides an overview of |
148 | | -//! the parsing of Multiboot2 structures and how the definitions from this |
149 | | -//! crate are used. |
150 | | -//! |
151 | | -//! Note that although the diagrams seem complex, most logic is in |
152 | | -//! `multiboot2-common`. For downstream users, the usage is quite simple. |
153 | | -//! |
154 | 187 | //! ## Parsing and Casting |
155 | 188 | //! |
| 189 | +//! The general idea of parsing is that the lifetime of the original byte slice |
| 190 | +//! propagates through to references of target types. |
| 191 | +//! |
156 | 192 | //! First, we need byte slices which are guaranteed to be aligned and are a |
157 | 193 | //! multiple of the alignment. We have [`BytesRef`] for that. With that, we can |
158 | 194 | //! create a [`DynSizedStructure`]. This is a rusty type that owns all the bytes |
@@ -256,25 +292,30 @@ pub trait Header: Clone + Sized + PartialEq + Eq + Debug { |
256 | 292 | } |
257 | 293 |
|
258 | 294 | /// An C ABI-compatible dynamically sized type with a common sized [`Header`] |
259 | | -/// and a dynamic amount of bytes. |
| 295 | +/// and a dynamic amount of bytes without hidden implicit padding. |
260 | 296 | /// |
261 | | -/// This structures owns all its bytes, unlike [`Header`]. Instances guarantees |
262 | | -/// that the memory requirements promised in the crates description are |
263 | | -/// respected. |
| 297 | +/// This structures combines a [`Header`] with the logically owned data by |
| 298 | +/// that header according to the reported [`Header::payload_len`]. Instances |
| 299 | +/// guarantees that the memory requirements promised in the crates description |
| 300 | +/// are respected. |
264 | 301 | /// |
265 | 302 | /// This can be a Multiboot2 header tag, information tag, boot information, or |
266 | | -/// a Multiboot2 header. Depending on the context, the [`Header`] is different. |
| 303 | +/// a Multiboot2 header. It is the base for **same-size casts** to these |
| 304 | +/// corresponding structures using [`DynSizedStructure::cast`]. Depending on the |
| 305 | +/// context, the [`Header`] is different (header header, boot information |
| 306 | +/// header, header tag header, or boot information tag header). |
267 | 307 | /// |
268 | 308 | /// # ABI |
269 | 309 | /// This type uses the C ABI. The fixed [`Header`] portion is always there. |
270 | 310 | /// Further, there is a variable amount of payload bytes. Thus, this type can |
271 | 311 | /// only exist on the heap or references to it can be made by cast via fat |
272 | | -/// pointers. |
| 312 | +/// pointers. The main constructor is [`DynSizedStructure::ref_from_bytes`]. |
273 | 313 | /// |
274 | | -/// As there might be padding necessary for the proper Rust layout, |
| 314 | +/// As terminating padding might be necessary for the proper Rust type layout, |
275 | 315 | /// `size_of_val(&self)` might report additional padding bytes that are not |
276 | 316 | /// reflected by the actual payload. These additional padding bytes however |
277 | | -/// will be reflected in corresponding [`BytesRef`] instances. |
| 317 | +/// will be reflected in corresponding [`BytesRef`] instances from that this |
| 318 | +/// structure was created. |
278 | 319 | #[derive(Debug, PartialEq, Eq, ptr_meta::Pointee)] |
279 | 320 | #[repr(C, align(8))] |
280 | 321 | pub struct DynSizedStructure<H: Header> { |
@@ -363,13 +404,16 @@ impl<H: Header> DynSizedStructure<H> { |
363 | 404 | /// |
364 | 405 | /// [`size_of_val`]: mem::size_of_val |
365 | 406 | pub fn cast<T: MaybeDynSized<Header = H> + ?Sized>(&self) -> &T { |
| 407 | + // Thin or fat pointer, depending on type. |
| 408 | + // However, only thin ptr is needed. |
366 | 409 | let base_ptr = ptr::addr_of!(*self); |
367 | 410 |
|
368 | 411 | // This should be a compile-time assertion. However, this is the best |
369 | 412 | // location to place it for now. |
370 | 413 | assert!(T::BASE_SIZE >= mem::size_of::<H>()); |
371 | 414 |
|
372 | 415 | let t_dst_size = T::dst_len(self.header()); |
| 416 | + // Creates thin or fat pointer, depending on type. |
373 | 417 | let t_ptr = ptr_meta::from_raw_parts(base_ptr.cast(), t_dst_size); |
374 | 418 | let t_ref = unsafe { &*t_ptr }; |
375 | 419 |
|
|
0 commit comments