Skip to content

Commit 64904c1

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 64904c1

File tree

8 files changed

+339
-106
lines changed

8 files changed

+339
-106
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

+34-22
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
1-
use crate::TagTypeId;
1+
use crate::{Tag, TagTypeId};
2+
use core::fmt::{Debug, Formatter};
23
use core::str::Utf8Error;
34

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)]
5+
/// The bootloader name tag.
6+
#[derive(ptr_meta::Pointee)]
97
#[repr(C, packed)] // only repr(C) would add unwanted padding before first_section
108
pub struct BootLoaderNameTag {
119
typ: TagTypeId,
1210
size: u32,
1311
/// Null-terminated UTF-8 string
14-
string: u8,
12+
name: [u8],
1513
}
1614

1715
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.
16+
/// Reads the name of the bootloader that is booting the kernel as Rust
17+
/// string slice without the null-byte.
18+
///
19+
/// For example, this returns `"GRUB 2.02~beta3-5"`.
20+
///
21+
/// If the function returns `Err` then perhaps the memory is invalid.
2122
///
2223
/// # Examples
2324
///
@@ -28,17 +29,32 @@ impl BootLoaderNameTag {
2829
/// }
2930
/// ```
3031
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)
32+
Tag::get_dst_str_slice(&self.name)
33+
}
34+
}
35+
36+
impl Debug for BootLoaderNameTag {
37+
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
38+
f.debug_struct("BootLoaderNameTag")
39+
.field("typ", &{ self.typ })
40+
.field("size", &{ self.size })
41+
.field("name", &self.name())
42+
.finish()
43+
}
44+
}
45+
46+
impl crate::TagTrait for BootLoaderNameTag {
47+
fn dst_size(base_tag: &Tag) -> usize {
48+
// The size of the sized portion of the bootloader name tag.
49+
let tag_base_size = 8;
50+
assert!(base_tag.size >= 8);
51+
base_tag.size as usize - tag_base_size
3652
}
3753
}
3854

3955
#[cfg(test)]
4056
mod tests {
41-
use crate::TagType;
57+
use crate::{BootLoaderNameTag, Tag, TagType};
4258

4359
const MSG: &str = "hello";
4460

@@ -63,12 +79,8 @@ mod tests {
6379
#[test]
6480
fn test_parse_str() {
6581
let tag = get_bytes();
66-
let tag = unsafe {
67-
tag.as_ptr()
68-
.cast::<super::BootLoaderNameTag>()
69-
.as_ref()
70-
.unwrap()
71-
};
82+
let tag = unsafe { &*tag.as_ptr().cast::<Tag>() };
83+
let tag = tag.cast_tag::<BootLoaderNameTag>();
7284
assert_eq!({ tag.typ }, TagType::BootLoaderName);
7385
assert_eq!(tag.name().expect("must be valid UTF-8"), MSG);
7486
}

multiboot2/src/command_line.rs

+34-19
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,30 @@
11
//! Module for [CommandLineTag].
22
3-
use crate::TagTypeId;
4-
use core::mem;
5-
use core::slice;
3+
use crate::{Tag, TagTrait, TagTypeId};
4+
use core::fmt::{Debug, Formatter};
65
use core::str;
76

87
/// This tag contains the command line string.
98
///
109
/// The string is a normal C-style UTF-8 zero-terminated string that can be
1110
/// obtained via the `command_line` method.
12-
#[derive(Clone, Copy, Debug)]
1311
#[repr(C, packed)] // only repr(C) would add unwanted padding before first_section
12+
#[derive(ptr_meta::Pointee)]
1413
pub struct CommandLineTag {
1514
typ: TagTypeId,
1615
size: u32,
1716
/// Null-terminated UTF-8 string
18-
string: u8,
17+
cmdline: [u8],
1918
}
2019

2120
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.
21+
/// Reads the command line of the kernel as Rust string slice without
22+
/// the null-byte.
23+
///
24+
/// For example, this returns `"console=ttyS0"`.if the GRUB config
25+
/// contains `"multiboot2 /mykernel console=ttyS0"`.
26+
///
27+
/// If the function returns `Err` then perhaps the memory is invalid.
2528
///
2629
/// # Examples
2730
///
@@ -33,16 +36,32 @@ impl CommandLineTag {
3336
/// }
3437
/// ```
3538
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)
39+
Tag::get_dst_str_slice(&self.cmdline)
40+
}
41+
}
42+
43+
impl Debug for CommandLineTag {
44+
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
45+
f.debug_struct("CommandLineTag")
46+
.field("typ", &{ self.typ })
47+
.field("size", &{ self.size })
48+
.field("cmdline", &self.command_line())
49+
.finish()
50+
}
51+
}
52+
53+
impl TagTrait for CommandLineTag {
54+
fn dst_size(base_tag: &Tag) -> usize {
55+
// The size of the sized portion of the command line tag.
56+
let tag_base_size = 8;
57+
assert!(base_tag.size >= 8);
58+
base_tag.size as usize - tag_base_size
4059
}
4160
}
4261

4362
#[cfg(test)]
4463
mod tests {
45-
use crate::TagType;
64+
use crate::{CommandLineTag, Tag, TagType};
4665

4766
const MSG: &str = "hello";
4867

@@ -67,12 +86,8 @@ mod tests {
6786
#[test]
6887
fn test_parse_str() {
6988
let tag = get_bytes();
70-
let tag = unsafe {
71-
tag.as_ptr()
72-
.cast::<super::CommandLineTag>()
73-
.as_ref()
74-
.unwrap()
75-
};
89+
let tag = unsafe { &*tag.as_ptr().cast::<Tag>() };
90+
let tag = tag.cast_tag::<CommandLineTag>();
7691
assert_eq!({ tag.typ }, TagType::Cmdline);
7792
assert_eq!(tag.command_line().expect("must be valid UTF-8"), MSG);
7893
}

0 commit comments

Comments
 (0)