Skip to content

Commit 2d0add1

Browse files
authored
Merge pull request #134 from rust-osdev/dst-tags
multiboot2: properly type DST tags
2 parents 3b9c72f + f452b63 commit 2d0add1

File tree

9 files changed

+353
-106
lines changed

9 files changed

+353
-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-header/src/information_request.rs

+1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ pub struct InformationRequestHeaderTagIter<'a> {
9898

9999
impl<'a> InformationRequestHeaderTagIter<'a> {
100100
fn new(count: u32, base_ptr: *const MbiTagType) -> Self {
101+
#[allow(clippy::default_constructed_unit_structs)]
101102
Self {
102103
i: 0,
103104
count,

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

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

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)]
6+
/// The bootloader name tag.
7+
#[derive(ptr_meta::Pointee)]
98
#[repr(C, packed)] // only repr(C) would add unwanted padding before first_section
109
pub struct BootLoaderNameTag {
1110
typ: TagTypeId,
1211
size: u32,
1312
/// Null-terminated UTF-8 string
14-
string: u8,
13+
name: [u8],
1514
}
1615

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

3956
#[cfg(test)]
4057
mod tests {
41-
use crate::TagType;
58+
use crate::{BootLoaderNameTag, Tag, TagType};
4259

4360
const MSG: &str = "hello";
4461

@@ -63,12 +80,8 @@ mod tests {
6380
#[test]
6481
fn test_parse_str() {
6582
let tag = get_bytes();
66-
let tag = unsafe {
67-
tag.as_ptr()
68-
.cast::<super::BootLoaderNameTag>()
69-
.as_ref()
70-
.unwrap()
71-
};
83+
let tag = unsafe { &*tag.as_ptr().cast::<Tag>() };
84+
let tag = tag.cast_tag::<BootLoaderNameTag>();
7285
assert_eq!({ tag.typ }, TagType::BootLoaderName);
7386
assert_eq!(tag.name().expect("must be valid UTF-8"), MSG);
7487
}

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)