Skip to content

Commit

Permalink
Merge pull request #134 from rust-osdev/dst-tags
Browse files Browse the repository at this point in the history
multiboot2: properly type DST tags
  • Loading branch information
phip1611 authored May 19, 2023
2 parents 3b9c72f + f452b63 commit 2d0add1
Show file tree
Hide file tree
Showing 9 changed files with 353 additions and 106 deletions.
21 changes: 21 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions multiboot2-header/src/information_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ pub struct InformationRequestHeaderTagIter<'a> {

impl<'a> InformationRequestHeaderTagIter<'a> {
fn new(count: u32, base_ptr: *const MbiTagType) -> Self {
#[allow(clippy::default_constructed_unit_structs)]
Self {
i: 0,
count,
Expand Down
1 change: 1 addition & 0 deletions multiboot2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ unstable = []
bitflags = "1"
derive_more = { version = "0.99", default-features = false, features = ["display"] }
log = { version = "0.4", default-features = false }
ptr_meta = { version = "0.2.0", default-features = false }
7 changes: 7 additions & 0 deletions multiboot2/Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# CHANGELOG for crate `multiboot2`

## unreleased
- Add `TagTrait` trait which enables to use DSTs as multiboot2 tags. This is
mostly relevant for the command line tag, the modules tag, and the bootloader
name tag. However, this might also be relevant for users of custom multiboot2
tags that use DSTs as types. See the example provided in the doc of the
`get_tag` method.

## 0.15.1 (2023-03-18)
- **BREAKING** `MemoryMapTag::all_memory_areas()` was renamed to `memory_areas`
and now returns `MemoryAreaIter` instead of
Expand Down
57 changes: 35 additions & 22 deletions multiboot2/src/boot_loader_name.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
use crate::TagTypeId;
use crate::TagTrait;
use crate::{Tag, TagTypeId};
use core::fmt::{Debug, Formatter};
use core::str::Utf8Error;

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

impl BootLoaderNameTag {
/// Read the name of the bootloader that is booting the kernel.
/// This is an null-terminated UTF-8 string. If this returns `Err` then perhaps the memory
/// is invalid or the bootloader doesn't follow the spec.
/// Reads the name of the bootloader that is booting the kernel as Rust
/// string slice without the null-byte.
///
/// For example, this returns `"GRUB 2.02~beta3-5"`.
///
/// If the function returns `Err` then perhaps the memory is invalid.
///
/// # Examples
///
Expand All @@ -28,17 +30,32 @@ impl BootLoaderNameTag {
/// }
/// ```
pub fn name(&self) -> Result<&str, Utf8Error> {
use core::{mem, slice, str};
// strlen without null byte
let strlen = self.size as usize - mem::size_of::<BootLoaderNameTag>();
let bytes = unsafe { slice::from_raw_parts((&self.string) as *const u8, strlen) };
str::from_utf8(bytes)
Tag::get_dst_str_slice(&self.name)
}
}

impl Debug for BootLoaderNameTag {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BootLoaderNameTag")
.field("typ", &{ self.typ })
.field("size", &{ self.size })
.field("name", &self.name())
.finish()
}
}

impl TagTrait for BootLoaderNameTag {
fn dst_size(base_tag: &Tag) -> usize {
// The size of the sized portion of the bootloader name tag.
let tag_base_size = 8;
assert!(base_tag.size >= 8);
base_tag.size as usize - tag_base_size
}
}

#[cfg(test)]
mod tests {
use crate::TagType;
use crate::{BootLoaderNameTag, Tag, TagType};

const MSG: &str = "hello";

Expand All @@ -63,12 +80,8 @@ mod tests {
#[test]
fn test_parse_str() {
let tag = get_bytes();
let tag = unsafe {
tag.as_ptr()
.cast::<super::BootLoaderNameTag>()
.as_ref()
.unwrap()
};
let tag = unsafe { &*tag.as_ptr().cast::<Tag>() };
let tag = tag.cast_tag::<BootLoaderNameTag>();
assert_eq!({ tag.typ }, TagType::BootLoaderName);
assert_eq!(tag.name().expect("must be valid UTF-8"), MSG);
}
Expand Down
53 changes: 34 additions & 19 deletions multiboot2/src/command_line.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
//! Module for [CommandLineTag].
use crate::TagTypeId;
use core::mem;
use core::slice;
use crate::{Tag, TagTrait, TagTypeId};
use core::fmt::{Debug, Formatter};
use core::str;

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

impl CommandLineTag {
/// Read the command line string that is being passed to the booting kernel.
/// This is an null-terminated UTF-8 string. If this returns `Err` then perhaps the memory
/// is invalid or the bootloader doesn't follow the spec.
/// Reads the command line of the kernel as Rust string slice without
/// the null-byte.
///
/// For example, this returns `"console=ttyS0"`.if the GRUB config
/// contains `"multiboot2 /mykernel console=ttyS0"`.
///
/// If the function returns `Err` then perhaps the memory is invalid.
///
/// # Examples
///
Expand All @@ -33,16 +36,32 @@ impl CommandLineTag {
/// }
/// ```
pub fn command_line(&self) -> Result<&str, str::Utf8Error> {
// strlen without null byte
let strlen = self.size as usize - mem::size_of::<CommandLineTag>();
let bytes = unsafe { slice::from_raw_parts((&self.string) as *const u8, strlen) };
str::from_utf8(bytes)
Tag::get_dst_str_slice(&self.cmdline)
}
}

impl Debug for CommandLineTag {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("CommandLineTag")
.field("typ", &{ self.typ })
.field("size", &{ self.size })
.field("cmdline", &self.command_line())
.finish()
}
}

impl TagTrait for CommandLineTag {
fn dst_size(base_tag: &Tag) -> usize {
// The size of the sized portion of the command line tag.
let tag_base_size = 8;
assert!(base_tag.size >= 8);
base_tag.size as usize - tag_base_size
}
}

#[cfg(test)]
mod tests {
use crate::TagType;
use crate::{CommandLineTag, Tag, TagType};

const MSG: &str = "hello";

Expand All @@ -67,12 +86,8 @@ mod tests {
#[test]
fn test_parse_str() {
let tag = get_bytes();
let tag = unsafe {
tag.as_ptr()
.cast::<super::CommandLineTag>()
.as_ref()
.unwrap()
};
let tag = unsafe { &*tag.as_ptr().cast::<Tag>() };
let tag = tag.cast_tag::<CommandLineTag>();
assert_eq!({ tag.typ }, TagType::Cmdline);
assert_eq!(tag.command_line().expect("must be valid UTF-8"), MSG);
}
Expand Down
Loading

0 comments on commit 2d0add1

Please sign in to comment.