Эх сурвалжийг харах

multiboot2: Implement setting the command line

Niklas Sombert 2 жил өмнө
parent
commit
544e1ca555

+ 11 - 2
multiboot2/src/builder/information.rs

@@ -2,15 +2,24 @@
 use crate::{builder::traits::StructAsBytes, CommandLineTag};
 
 use alloc::boxed::Box;
+use alloc::vec::Vec;
 
 /// Builder to construct a valid Multiboot2 information dynamically at runtime.
 /// The tags will appear in the order of their corresponding enumeration,
 /// except for the END tag.
 #[derive(Debug)]
-pub struct Multiboot2InformationBuilder {}
+pub struct Multiboot2InformationBuilder {
+    command_line_tag: Option<Box<CommandLineTag>>,
+}
 
 impl Multiboot2InformationBuilder {
     pub const fn new() -> Self {
-        Self {}
+        Self {
+            command_line_tag: None,
+        }
+    }
+
+    pub fn command_line_tag(&mut self, command_line_tag: Box<CommandLineTag>) {
+        self.command_line_tag = Some(command_line_tag);
     }
 }

+ 38 - 0
multiboot2/src/builder/mod.rs

@@ -4,3 +4,41 @@ mod information;
 pub(self) mod traits;
 
 pub use information::Multiboot2InformationBuilder;
+
+use alloc::alloc::alloc;
+use alloc::boxed::Box;
+use core::alloc::Layout;
+use core::mem::size_of;
+
+use crate::{TagTrait, TagTypeId};
+
+/// Create a boxed tag with the given content.
+pub(super) fn boxed_dst_tag<T: TagTrait<Metadata = usize> + ?Sized>(
+    typ: impl Into<TagTypeId>,
+    content: &[u8],
+) -> Box<T> {
+    // based on https://stackoverflow.com/a/64121094/2192464
+    let (layout, size_offset) = Layout::new::<TagTypeId>()
+        .extend(Layout::new::<u32>())
+        .unwrap();
+    let (layout, inner_offset) = layout
+        .extend(Layout::array::<usize>(content.len()).unwrap())
+        .unwrap();
+    let ptr = unsafe { alloc(layout) };
+    assert!(!ptr.is_null());
+    unsafe {
+        // initialize the content as good as we can
+        ptr.cast::<TagTypeId>().write(typ.into());
+        ptr.add(size_offset).cast::<u32>().write(
+            (content.len() + size_of::<TagTypeId>() + size_of::<u32>())
+                .try_into()
+                .unwrap(),
+        );
+        // initialize body
+        let content_ptr = ptr.add(inner_offset);
+        for (idx, val) in content.iter().enumerate() {
+            content_ptr.add(idx).write(*val);
+        }
+        Box::from_raw(ptr_meta::from_raw_parts_mut(ptr as *mut (), content.len()))
+    }
+}

+ 17 - 5
multiboot2/src/command_line.rs

@@ -1,9 +1,15 @@
 //! Module for [CommandLineTag].
 
-use crate::{Tag, TagTrait, TagTypeId};
+use crate::{Tag, TagTrait, TagType, TagTypeId};
 use core::fmt::{Debug, Formatter};
+use core::mem;
 use core::str;
 
+#[cfg(feature = "builder")]
+use {crate::builder::boxed_dst_tag, alloc::boxed::Box, alloc::vec::Vec};
+
+pub(crate) const METADATA_SIZE: usize = mem::size_of::<TagTypeId>() + mem::size_of::<u32>();
+
 /// This tag contains the command line string.
 ///
 /// The string is a normal C-style UTF-8 zero-terminated string that can be
@@ -18,6 +24,14 @@ pub struct CommandLineTag {
 }
 
 impl CommandLineTag {
+    /// Create a new command line tag from the given string.
+    #[cfg(feature = "builder")]
+    pub fn new(command_line: &str) -> Box<Self> {
+        let mut bytes: Vec<_> = command_line.bytes().collect();
+        bytes.push(0);
+        boxed_dst_tag(TagType::Cmdline, &bytes)
+    }
+
     /// Reads the command line of the kernel as Rust string slice without
     /// the null-byte.
     ///
@@ -52,10 +66,8 @@ impl Debug for CommandLineTag {
 
 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
+        assert!(base_tag.size as usize >= METADATA_SIZE);
+        base_tag.size as usize - METADATA_SIZE
     }
 }