Przeglądaj źródła

multiboot2: new_boxed() can be used with less allocations on callee side

Note that Miri runs significantly longer with this change. More memory accesses
that need to be tracked.
Philipp Schuster 10 miesięcy temu
rodzic
commit
c47b7ac40d

+ 7 - 7
multiboot2/src/boot_loader_name.rs

@@ -2,10 +2,10 @@
 
 use crate::tag::TagHeader;
 use crate::{new_boxed, parse_slice_as_string, StringError, TagTrait, TagType};
+#[cfg(feature = "builder")]
+use alloc::boxed::Box;
 use core::fmt::{Debug, Formatter};
 use core::mem;
-#[cfg(feature = "builder")]
-use {alloc::boxed::Box, alloc::vec::Vec};
 
 const METADATA_SIZE: usize = mem::size_of::<TagHeader>();
 
@@ -23,12 +23,12 @@ impl BootLoaderNameTag {
     #[cfg(feature = "builder")]
     #[must_use]
     pub fn new(name: &str) -> Box<Self> {
-        let mut bytes: Vec<_> = name.bytes().collect();
-        if !bytes.ends_with(&[0]) {
-            // terminating null-byte
-            bytes.push(0);
+        let bytes = name.as_bytes();
+        if bytes.ends_with(&[0]) {
+            new_boxed(&[bytes])
+        } else {
+            new_boxed(&[bytes, &[0]])
         }
-        new_boxed(&bytes)
     }
 
     /// Returns the underlying [`TagType`].

+ 5 - 5
multiboot2/src/command_line.rs

@@ -27,12 +27,12 @@ impl CommandLineTag {
     #[cfg(feature = "builder")]
     #[must_use]
     pub fn new(command_line: &str) -> Box<Self> {
-        let mut bytes: Vec<_> = command_line.bytes().collect();
-        if !bytes.ends_with(&[0]) {
-            // terminating null-byte
-            bytes.push(0);
+        let bytes = command_line.as_bytes();
+        if bytes.ends_with(&[0]) {
+            new_boxed(&[bytes])
+        } else {
+            new_boxed(&[bytes, &[0]])
         }
-        new_boxed(&bytes)
     }
 
     /// Reads the command line of the kernel as Rust string slice without

+ 4 - 8
multiboot2/src/elf_sections.rs

@@ -27,14 +27,10 @@ impl ElfSectionsTag {
     #[cfg(feature = "builder")]
     #[must_use]
     pub fn new(number_of_sections: u32, entry_size: u32, shndx: u32, sections: &[u8]) -> Box<Self> {
-        let mut bytes = [
-            number_of_sections.to_le_bytes(),
-            entry_size.to_le_bytes(),
-            shndx.to_le_bytes(),
-        ]
-        .concat();
-        bytes.extend_from_slice(sections);
-        new_boxed(&bytes)
+        let number_of_sections = number_of_sections.to_ne_bytes();
+        let entry_size = entry_size.to_ne_bytes();
+        let shndx = shndx.to_ne_bytes();
+        new_boxed(&[&number_of_sections, &entry_size, &shndx, sections])
     }
 
     /// Get an iterator of loaded ELF sections.

+ 15 - 14
multiboot2/src/framebuffer.rs

@@ -95,13 +95,13 @@ impl FramebufferTag {
         bpp: u8,
         buffer_type: FramebufferType,
     ) -> Box<Self> {
-        let mut bytes: Vec<u8> = address.to_le_bytes().into();
-        bytes.extend(pitch.to_le_bytes());
-        bytes.extend(width.to_le_bytes());
-        bytes.extend(height.to_le_bytes());
-        bytes.extend(bpp.to_le_bytes());
-        bytes.extend(buffer_type.to_bytes());
-        new_boxed(&bytes)
+        let address = address.to_ne_bytes();
+        let pitch = pitch.to_ne_bytes();
+        let width = width.to_ne_bytes();
+        let height = height.to_ne_bytes();
+        let bpp = bpp.to_ne_bytes();
+        let buffer_type = buffer_type.to_bytes();
+        new_boxed(&[&address, &pitch, &width, &height, &bpp, &buffer_type])
     }
 
     /// Contains framebuffer physical address.
@@ -145,6 +145,7 @@ impl FramebufferTag {
         match typ {
             FramebufferTypeId::Indexed => {
                 let num_colors = reader.read_u32();
+                // TODO static cast looks like UB?
                 let palette = unsafe {
                     slice::from_raw_parts(
                         reader.current_address() as *const FramebufferColor,
@@ -274,23 +275,23 @@ impl<'a> FramebufferType<'a> {
         let mut v = Vec::new();
         match self {
             FramebufferType::Indexed { palette } => {
-                v.extend(0u8.to_le_bytes()); // type
-                v.extend(0u16.to_le_bytes()); // reserved
-                v.extend((palette.len() as u32).to_le_bytes());
+                v.extend(0u8.to_ne_bytes()); // type
+                v.extend(0u16.to_ne_bytes()); // reserved
+                v.extend((palette.len() as u32).to_ne_bytes());
                 for color in palette.iter() {
                     v.extend(color.as_bytes());
                 }
             }
             FramebufferType::RGB { red, green, blue } => {
-                v.extend(1u8.to_le_bytes()); // type
-                v.extend(0u16.to_le_bytes()); // reserved
+                v.extend(1u8.to_ne_bytes()); // type
+                v.extend(0u16.to_ne_bytes()); // reserved
                 v.extend(red.as_bytes());
                 v.extend(green.as_bytes());
                 v.extend(blue.as_bytes());
             }
             FramebufferType::Text => {
-                v.extend(2u8.to_le_bytes()); // type
-                v.extend(0u16.to_le_bytes()); // reserved
+                v.extend(2u8.to_ne_bytes()); // type
+                v.extend(0u16.to_ne_bytes()); // reserved
             }
         }
         v

+ 20 - 51
multiboot2/src/memory_map.rs

@@ -11,7 +11,7 @@ use core::fmt::{Debug, Formatter};
 use core::marker::PhantomData;
 use core::mem;
 #[cfg(feature = "builder")]
-use {crate::builder::AsBytes, crate::new_boxed, alloc::boxed::Box};
+use {crate::new_boxed, alloc::boxed::Box, core::slice};
 
 const METADATA_SIZE: usize = mem::size_of::<TagHeader>() + 2 * mem::size_of::<u32>();
 
@@ -39,13 +39,14 @@ impl MemoryMapTag {
     #[cfg(feature = "builder")]
     #[must_use]
     pub fn new(areas: &[MemoryArea]) -> Box<Self> {
-        let entry_size: u32 = mem::size_of::<MemoryArea>().try_into().unwrap();
-        let entry_version: u32 = 0;
-        let mut bytes = [entry_size.to_le_bytes(), entry_version.to_le_bytes()].concat();
-        for area in areas {
-            bytes.extend(area.as_bytes());
-        }
-        new_boxed(bytes.as_slice())
+        let entry_size = mem::size_of::<MemoryArea>().to_ne_bytes();
+        let entry_version = 0_u32.to_ne_bytes();
+        let areas = {
+            let ptr = areas.as_ptr().cast::<u8>();
+            let len = areas.len() * size_of::<MemoryArea>();
+            unsafe { slice::from_raw_parts(ptr, len) }
+        };
+        new_boxed(&[&entry_size, &entry_version, areas])
     }
 
     /// Returns the entry size.
@@ -139,9 +140,6 @@ impl Debug for MemoryArea {
     }
 }
 
-#[cfg(feature = "builder")]
-impl AsBytes for MemoryArea {}
-
 /// ABI-friendly version of [`MemoryAreaType`].
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
@@ -292,9 +290,6 @@ impl TagTrait for BasicMemoryInfoTag {
 
 const EFI_METADATA_SIZE: usize = mem::size_of::<TagTypeId>() + 3 * mem::size_of::<u32>();
 
-#[cfg(feature = "builder")]
-impl AsBytes for EFIMemoryDesc {}
-
 /// EFI memory map tag. The embedded [`EFIMemoryDesc`]s follows the EFI
 /// specification.
 #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -322,32 +317,19 @@ pub struct EFIMemoryMapTag {
 
 impl EFIMemoryMapTag {
     /// Create a new EFI memory map tag with the given memory descriptors.
-    /// Version and size can't be set because you're passing a slice of
-    /// EFIMemoryDescs, not the ones you might have gotten from the firmware.
     #[cfg(feature = "builder")]
     #[must_use]
     pub fn new_from_descs(descs: &[EFIMemoryDesc]) -> Box<Self> {
-        // TODO replace this EfiMemorydesc::uefi_desc_size() in the next uefi_raw
-        // release.
-
-        let size_base = mem::size_of::<EFIMemoryDesc>();
-        // Taken from https://github.com/tianocore/edk2/blob/7142e648416ff5d3eac6c6d607874805f5de0ca8/MdeModulePkg/Core/PiSmmCore/Page.c#L1059
-        let desc_size_diff = mem::size_of::<u64>() - size_base % mem::size_of::<u64>();
-        let desc_size = size_base + desc_size_diff;
-
-        assert!(desc_size >= size_base);
-
-        let mut efi_mmap = alloc::vec::Vec::with_capacity(descs.len() * desc_size);
-        for desc in descs {
-            efi_mmap.extend(desc.as_bytes());
-            // fill with zeroes
-            efi_mmap.extend([0].repeat(desc_size_diff));
-        }
+        let efi_mmap = {
+            let ptr = descs.as_ptr().cast::<u8>();
+            let len = descs.len() * size_of::<EFIMemoryDesc>();
+            unsafe { slice::from_raw_parts(ptr, len) }
+        };
 
         Self::new_from_map(
-            desc_size as u32,
+            mem::size_of::<EFIMemoryDesc>() as u32,
             EFIMemoryDesc::VERSION,
-            efi_mmap.as_slice(),
+            efi_mmap,
         )
     }
 
@@ -355,21 +337,10 @@ impl EFIMemoryMapTag {
     #[cfg(feature = "builder")]
     #[must_use]
     pub fn new_from_map(desc_size: u32, desc_version: u32, efi_mmap: &[u8]) -> Box<Self> {
-        assert!(desc_size > 0);
-        assert_eq!(efi_mmap.len() % desc_size as usize, 0);
-        assert_eq!(
-            efi_mmap
-                .as_ptr()
-                .align_offset(mem::align_of::<EFIMemoryDesc>()),
-            0
-        );
-        let bytes = [
-            &desc_size.to_le_bytes(),
-            &desc_version.to_le_bytes(),
-            efi_mmap,
-        ]
-        .concat();
-        new_boxed(&bytes)
+        assert_ne!(desc_size, 0);
+        let desc_size = desc_size.to_ne_bytes();
+        let desc_version = desc_version.to_ne_bytes();
+        new_boxed(&[&desc_size, &desc_version, efi_mmap])
     }
 
     /// Returns an iterator over the provided memory areas.
@@ -491,8 +462,6 @@ mod tests {
         ];
         let efi_mmap_tag = EFIMemoryMapTag::new_from_descs(&descs);
 
-        assert_eq!(efi_mmap_tag.desc_size, 48 /* 40 + 8 */);
-
         let mut iter = efi_mmap_tag.memory_areas();
 
         assert_eq!(iter.next(), Some(&descs[0]));

+ 8 - 9
multiboot2/src/module.rs

@@ -29,16 +29,15 @@ impl ModuleTag {
     pub fn new(start: u32, end: u32, cmdline: &str) -> Box<Self> {
         assert!(end > start, "must have a size");
 
-        let mut cmdline_bytes: Vec<_> = cmdline.bytes().collect();
-        if !cmdline_bytes.ends_with(&[0]) {
-            // terminating null-byte
-            cmdline_bytes.push(0);
+        let start = start.to_ne_bytes();
+        let end = end.to_ne_bytes();
+        let cmdline = cmdline.as_bytes();
+
+        if cmdline.ends_with(&[0]) {
+            new_boxed(&[&start, &end, cmdline])
+        } else {
+            new_boxed(&[&start, &end, cmdline, &[0]])
         }
-        let start_bytes = start.to_le_bytes();
-        let end_bytes = end.to_le_bytes();
-        let mut content_bytes = [start_bytes, end_bytes].concat();
-        content_bytes.extend_from_slice(&cmdline_bytes);
-        new_boxed(&content_bytes)
     }
 
     /// Reads the command line of the boot module as Rust string slice without

+ 2 - 3
multiboot2/src/smbios.rs

@@ -25,9 +25,8 @@ impl SmbiosTag {
     #[cfg(feature = "builder")]
     #[must_use]
     pub fn new(major: u8, minor: u8, tables: &[u8]) -> Box<Self> {
-        let mut bytes = [major, minor, 0, 0, 0, 0, 0, 0].to_vec();
-        bytes.extend(tables);
-        new_boxed(&bytes)
+        let reserved = [0, 0, 0, 0, 0, 0];
+        new_boxed(&[&[major, minor], &reserved, tables])
     }
 
     /// Returns the major number.

+ 29 - 10
multiboot2/src/util.rs

@@ -41,11 +41,17 @@ impl core::error::Error for StringError {
 /// constructor and box the result.
 ///
 /// # Parameters
-/// - `additional_bytes`: All bytes apart from the default [`TagHeader`] that
-///                       are included into the tag.
+/// - `additional_bytes_slices`: Array of byte slices that should be included
+///   without additional padding in-between. You don't need to add the bytes
+///   for [`TagHeader`], but only additional ones.
 #[cfg(feature = "alloc")]
-pub fn new_boxed<T: TagTrait + ?Sized>(additional_bytes: &[u8]) -> Box<T> {
-    let size = size_of::<TagHeader>() + additional_bytes.iter().len();
+pub fn new_boxed<T: TagTrait + ?Sized>(additional_bytes_slices: &[&[u8]]) -> Box<T> {
+    let additional_size = additional_bytes_slices
+        .iter()
+        .map(|b| b.len())
+        .sum::<usize>();
+
+    let size = size_of::<TagHeader>() + additional_size;
     let alloc_size = increase_to_alignment(size);
     let layout = Layout::from_size_align(alloc_size, ALIGNMENT).unwrap();
     let heap_ptr = unsafe { alloc::alloc::alloc(layout) };
@@ -55,9 +61,16 @@ pub fn new_boxed<T: TagTrait + ?Sized>(additional_bytes: &[u8]) -> Box<T> {
         heap_ptr.cast::<u32>().write(T::ID.val());
         heap_ptr.cast::<u32>().add(1).write(size as u32);
     }
-    unsafe {
-        let ptr = heap_ptr.add(size_of::<TagHeader>());
-        ptr::copy_nonoverlapping(additional_bytes.as_ptr(), ptr, additional_bytes.len());
+
+    let mut write_offset = size_of::<TagHeader>();
+    for &bytes in additional_bytes_slices {
+        unsafe {
+            let len = bytes.len();
+            let src = bytes.as_ptr();
+            let dst = heap_ptr.add(write_offset);
+            ptr::copy_nonoverlapping(src, dst, len);
+            write_offset += len;
+        }
     }
 
     let header = unsafe { heap_ptr.cast::<TagHeader>().as_ref() }.unwrap();
@@ -128,10 +141,16 @@ mod tests {
 
     #[test]
     fn test_new_boxed() {
-        let tag = new_boxed::<GenericTag>(&[0, 1, 2, 3]);
+        let tag = new_boxed::<GenericTag>(&[&[0, 1, 2, 3]]);
         assert_eq!(tag.header().typ, GenericTag::ID);
-        {}
-        let tag = new_boxed::<CommandLineTag>("hello\0".as_bytes());
+        assert_eq!(tag.payload(), &[0, 1, 2, 3]);
+
+        // Test that bytes are added consecutively without gaps.
+        let tag = new_boxed::<GenericTag>(&[&[0], &[1], &[2, 3]]);
+        assert_eq!(tag.header().typ, GenericTag::ID);
+        assert_eq!(tag.payload(), &[0, 1, 2, 3]);
+
+        let tag = new_boxed::<CommandLineTag>(&["hello\0".as_bytes()]);
         assert_eq!(tag.cmdline(), Ok("hello"));
     }
 }