Skip to content

Commit e1e04a1

Browse files
committed
multiboot2: use dst for tags where applicable
This commit transforms a few tags where applicable to DSTs. This better fits into the Rust type system and makes a few things easier, such as parsing the cmdline string from the command line tag. Depending on how users used the tags, this change is not even breaking. Additionally, there is now a public trait which allows custom tag users to also benefit from DSTs.
1 parent e89b493 commit e1e04a1

File tree

8 files changed

+244
-108
lines changed

8 files changed

+244
-108
lines changed

Cargo.lock

+21
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

multiboot2/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,4 @@ unstable = []
4040
bitflags = "1"
4141
derive_more = { version = "0.99", default-features = false, features = ["display"] }
4242
log = { version = "0.4", default-features = false }
43+
ptr_meta = { version = "0.2.0", default-features = false }

multiboot2/Changelog.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# CHANGELOG for crate `multiboot2`
22

3+
## unreleased
4+
- Add `TagTrait` trait which enables to use DSTs as multiboot2 tags. This is
5+
mostly relevant for the command line tag, the modules tag, and the bootloader
6+
name tag. However, this might also be relevant for users of custom multiboot2
7+
tags that use DSTs as types. See the example provided in the doc of the
8+
`get_tag` method.
9+
310
## 0.15.1 (2023-03-18)
411
- **BREAKING** `MemoryMapTag::all_memory_areas()` was renamed to `memory_areas`
512
and now returns `MemoryAreaIter` instead of

multiboot2/src/boot_loader_name.rs

+22-22
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1-
use crate::TagTypeId;
1+
use crate::{Tag, TagTypeId};
22
use core::str::Utf8Error;
33

4-
/// This tag contains the name of the bootloader that is booting the kernel.
5-
///
6-
/// The name is a normal C-style UTF-8 zero-terminated string that can be
7-
/// obtained via the `name` method.
8-
#[derive(Clone, Copy, Debug)]
4+
/// The bootloader name tag.
5+
#[derive(ptr_meta::Pointee)]
96
#[repr(C, packed)] // only repr(C) would add unwanted padding before first_section
107
pub struct BootLoaderNameTag {
118
typ: TagTypeId,
129
size: u32,
1310
/// Null-terminated UTF-8 string
14-
string: u8,
11+
string: [u8],
1512
}
1613

1714
impl BootLoaderNameTag {
18-
/// Read the name of the bootloader that is booting the kernel.
19-
/// This is an null-terminated UTF-8 string. If this returns `Err` then perhaps the memory
20-
/// is invalid or the bootloader doesn't follow the spec.
15+
/// Reads the name of the bootloader that is booting the kernel as Rust
16+
/// string slice without the null-byte.
17+
///
18+
/// For example, this returns `"GRUB 2.02~beta3-5"`.
19+
///
20+
/// If the function returns `Err` then perhaps the memory is invalid.
2121
///
2222
/// # Examples
2323
///
@@ -28,17 +28,21 @@ impl BootLoaderNameTag {
2828
/// }
2929
/// ```
3030
pub fn name(&self) -> Result<&str, Utf8Error> {
31-
use core::{mem, slice, str};
32-
// strlen without null byte
33-
let strlen = self.size as usize - mem::size_of::<BootLoaderNameTag>();
34-
let bytes = unsafe { slice::from_raw_parts((&self.string) as *const u8, strlen) };
35-
str::from_utf8(bytes)
31+
Tag::get_dst_str_slice(&self.string)
32+
}
33+
}
34+
35+
impl crate::TagTrait for BootLoaderNameTag {
36+
fn dst_size(base_tag: &Tag) -> usize {
37+
// The size of the sized portion of the bootloader name tag.
38+
let tag_base_size = 8;
39+
base_tag.size as usize - tag_base_size
3640
}
3741
}
3842

3943
#[cfg(test)]
4044
mod tests {
41-
use crate::TagType;
45+
use crate::{BootLoaderNameTag, Tag, TagType};
4246

4347
const MSG: &str = "hello";
4448

@@ -63,12 +67,8 @@ mod tests {
6367
#[test]
6468
fn test_parse_str() {
6569
let tag = get_bytes();
66-
let tag = unsafe {
67-
tag.as_ptr()
68-
.cast::<super::BootLoaderNameTag>()
69-
.as_ref()
70-
.unwrap()
71-
};
70+
let tag = unsafe { &*tag.as_ptr().cast::<Tag>() };
71+
let tag = tag.cast_tag::<BootLoaderNameTag>();
7272
assert_eq!({ tag.typ }, TagType::BootLoaderName);
7373
assert_eq!(tag.name().expect("must be valid UTF-8"), MSG);
7474
}

multiboot2/src/command_line.rs

+22-19
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
11
//! Module for [CommandLineTag].
22
3-
use crate::TagTypeId;
4-
use core::mem;
5-
use core::slice;
3+
use crate::{Tag, TagTrait, TagTypeId};
64
use core::str;
75

86
/// This tag contains the command line string.
97
///
108
/// The string is a normal C-style UTF-8 zero-terminated string that can be
119
/// obtained via the `command_line` method.
12-
#[derive(Clone, Copy, Debug)]
1310
#[repr(C, packed)] // only repr(C) would add unwanted padding before first_section
11+
#[derive(ptr_meta::Pointee)]
1412
pub struct CommandLineTag {
1513
typ: TagTypeId,
1614
size: u32,
1715
/// Null-terminated UTF-8 string
18-
string: u8,
16+
cmdline: [u8],
1917
}
2018

2119
impl CommandLineTag {
22-
/// Read the command line string that is being passed to the booting kernel.
23-
/// This is an null-terminated UTF-8 string. If this returns `Err` then perhaps the memory
24-
/// is invalid or the bootloader doesn't follow the spec.
20+
/// Reads the command line of the kernel as Rust string slice without
21+
/// the null-byte.
22+
///
23+
/// For example, this returns `"console=ttyS0"`.if the GRUB config
24+
/// contains `"multiboot2 /mykernel console=ttyS0"`.
25+
///
26+
/// If the function returns `Err` then perhaps the memory is invalid.
2527
///
2628
/// # Examples
2729
///
@@ -33,16 +35,21 @@ impl CommandLineTag {
3335
/// }
3436
/// ```
3537
pub fn command_line(&self) -> Result<&str, str::Utf8Error> {
36-
// strlen without null byte
37-
let strlen = self.size as usize - mem::size_of::<CommandLineTag>();
38-
let bytes = unsafe { slice::from_raw_parts((&self.string) as *const u8, strlen) };
39-
str::from_utf8(bytes)
38+
Tag::get_dst_str_slice(&self.cmdline)
39+
}
40+
}
41+
42+
impl TagTrait for CommandLineTag {
43+
fn dst_size(base_tag: &Tag) -> usize {
44+
// The size of the sized portion of the command line tag.
45+
let tag_base_size = 8;
46+
base_tag.size as usize - tag_base_size
4047
}
4148
}
4249

4350
#[cfg(test)]
4451
mod tests {
45-
use crate::TagType;
52+
use crate::{CommandLineTag, Tag, TagType};
4653

4754
const MSG: &str = "hello";
4855

@@ -67,12 +74,8 @@ mod tests {
6774
#[test]
6875
fn test_parse_str() {
6976
let tag = get_bytes();
70-
let tag = unsafe {
71-
tag.as_ptr()
72-
.cast::<super::CommandLineTag>()
73-
.as_ref()
74-
.unwrap()
75-
};
77+
let tag = unsafe { &*tag.as_ptr().cast::<Tag>() };
78+
let tag = tag.cast_tag::<CommandLineTag>();
7679
assert_eq!({ tag.typ }, TagType::Cmdline);
7780
assert_eq!(tag.command_line().expect("must be valid UTF-8"), MSG);
7881
}

0 commit comments

Comments
 (0)