Skip to content

Commit 25a7caa

Browse files
committed
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.
1 parent f60c519 commit 25a7caa

File tree

8 files changed

+90
-107
lines changed

8 files changed

+90
-107
lines changed

multiboot2/src/boot_loader_name.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
33
use crate::tag::TagHeader;
44
use crate::{new_boxed, parse_slice_as_string, StringError, TagTrait, TagType};
5+
#[cfg(feature = "builder")]
6+
use alloc::boxed::Box;
57
use core::fmt::{Debug, Formatter};
68
use core::mem;
7-
#[cfg(feature = "builder")]
8-
use {alloc::boxed::Box, alloc::vec::Vec};
99

1010
const METADATA_SIZE: usize = mem::size_of::<TagHeader>();
1111

@@ -23,12 +23,12 @@ impl BootLoaderNameTag {
2323
#[cfg(feature = "builder")]
2424
#[must_use]
2525
pub fn new(name: &str) -> Box<Self> {
26-
let mut bytes: Vec<_> = name.bytes().collect();
27-
if !bytes.ends_with(&[0]) {
28-
// terminating null-byte
29-
bytes.push(0);
26+
let bytes = name.as_bytes();
27+
if bytes.ends_with(&[0]) {
28+
new_boxed(&[bytes])
29+
} else {
30+
new_boxed(&[bytes, &[0]])
3031
}
31-
new_boxed(&bytes)
3232
}
3333

3434
/// Returns the underlying [`TagType`].

multiboot2/src/command_line.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ impl CommandLineTag {
2727
#[cfg(feature = "builder")]
2828
#[must_use]
2929
pub fn new(command_line: &str) -> Box<Self> {
30-
let mut bytes: Vec<_> = command_line.bytes().collect();
31-
if !bytes.ends_with(&[0]) {
32-
// terminating null-byte
33-
bytes.push(0);
30+
let bytes = command_line.as_bytes();
31+
if bytes.ends_with(&[0]) {
32+
new_boxed(&[bytes])
33+
} else {
34+
new_boxed(&[bytes, &[0]])
3435
}
35-
new_boxed(&bytes)
3636
}
3737

3838
/// Reads the command line of the kernel as Rust string slice without

multiboot2/src/elf_sections.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,10 @@ impl ElfSectionsTag {
2727
#[cfg(feature = "builder")]
2828
#[must_use]
2929
pub fn new(number_of_sections: u32, entry_size: u32, shndx: u32, sections: &[u8]) -> Box<Self> {
30-
let mut bytes = [
31-
number_of_sections.to_le_bytes(),
32-
entry_size.to_le_bytes(),
33-
shndx.to_le_bytes(),
34-
]
35-
.concat();
36-
bytes.extend_from_slice(sections);
37-
new_boxed(&bytes)
30+
let number_of_sections = number_of_sections.to_ne_bytes();
31+
let entry_size = entry_size.to_ne_bytes();
32+
let shndx = shndx.to_ne_bytes();
33+
new_boxed(&[&number_of_sections, &entry_size, &shndx, sections])
3834
}
3935

4036
/// Get an iterator of loaded ELF sections.

multiboot2/src/framebuffer.rs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,13 @@ impl FramebufferTag {
9595
bpp: u8,
9696
buffer_type: FramebufferType,
9797
) -> Box<Self> {
98-
let mut bytes: Vec<u8> = address.to_le_bytes().into();
99-
bytes.extend(pitch.to_le_bytes());
100-
bytes.extend(width.to_le_bytes());
101-
bytes.extend(height.to_le_bytes());
102-
bytes.extend(bpp.to_le_bytes());
103-
bytes.extend(buffer_type.to_bytes());
104-
new_boxed(&bytes)
98+
let address = address.to_ne_bytes();
99+
let pitch = pitch.to_ne_bytes();
100+
let width = width.to_ne_bytes();
101+
let height = height.to_ne_bytes();
102+
let bpp = bpp.to_ne_bytes();
103+
let buffer_type = buffer_type.to_bytes();
104+
new_boxed(&[&address, &pitch, &width, &height, &bpp, &buffer_type])
105105
}
106106

107107
/// Contains framebuffer physical address.
@@ -145,6 +145,7 @@ impl FramebufferTag {
145145
match typ {
146146
FramebufferTypeId::Indexed => {
147147
let num_colors = reader.read_u32();
148+
// TODO static cast looks like UB?
148149
let palette = unsafe {
149150
slice::from_raw_parts(
150151
reader.current_address() as *const FramebufferColor,
@@ -274,23 +275,23 @@ impl<'a> FramebufferType<'a> {
274275
let mut v = Vec::new();
275276
match self {
276277
FramebufferType::Indexed { palette } => {
277-
v.extend(0u8.to_le_bytes()); // type
278-
v.extend(0u16.to_le_bytes()); // reserved
279-
v.extend((palette.len() as u32).to_le_bytes());
278+
v.extend(0u8.to_ne_bytes()); // type
279+
v.extend(0u16.to_ne_bytes()); // reserved
280+
v.extend((palette.len() as u32).to_ne_bytes());
280281
for color in palette.iter() {
281282
v.extend(color.as_bytes());
282283
}
283284
}
284285
FramebufferType::RGB { red, green, blue } => {
285-
v.extend(1u8.to_le_bytes()); // type
286-
v.extend(0u16.to_le_bytes()); // reserved
286+
v.extend(1u8.to_ne_bytes()); // type
287+
v.extend(0u16.to_ne_bytes()); // reserved
287288
v.extend(red.as_bytes());
288289
v.extend(green.as_bytes());
289290
v.extend(blue.as_bytes());
290291
}
291292
FramebufferType::Text => {
292-
v.extend(2u8.to_le_bytes()); // type
293-
v.extend(0u16.to_le_bytes()); // reserved
293+
v.extend(2u8.to_ne_bytes()); // type
294+
v.extend(0u16.to_ne_bytes()); // reserved
294295
}
295296
}
296297
v

multiboot2/src/memory_map.rs

Lines changed: 20 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use core::fmt::{Debug, Formatter};
1111
use core::marker::PhantomData;
1212
use core::mem;
1313
#[cfg(feature = "builder")]
14-
use {crate::builder::AsBytes, crate::new_boxed, alloc::boxed::Box};
14+
use {crate::new_boxed, alloc::boxed::Box, core::slice};
1515

1616
const METADATA_SIZE: usize = mem::size_of::<TagHeader>() + 2 * mem::size_of::<u32>();
1717

@@ -39,13 +39,14 @@ impl MemoryMapTag {
3939
#[cfg(feature = "builder")]
4040
#[must_use]
4141
pub fn new(areas: &[MemoryArea]) -> Box<Self> {
42-
let entry_size: u32 = mem::size_of::<MemoryArea>().try_into().unwrap();
43-
let entry_version: u32 = 0;
44-
let mut bytes = [entry_size.to_le_bytes(), entry_version.to_le_bytes()].concat();
45-
for area in areas {
46-
bytes.extend(area.as_bytes());
47-
}
48-
new_boxed(bytes.as_slice())
42+
let entry_size = mem::size_of::<MemoryArea>().to_ne_bytes();
43+
let entry_version = 0_u32.to_ne_bytes();
44+
let areas = {
45+
let ptr = areas.as_ptr().cast::<u8>();
46+
let len = areas.len() * size_of::<MemoryArea>();
47+
unsafe { slice::from_raw_parts(ptr, len) }
48+
};
49+
new_boxed(&[&entry_size, &entry_version, areas])
4950
}
5051

5152
/// Returns the entry size.
@@ -139,9 +140,6 @@ impl Debug for MemoryArea {
139140
}
140141
}
141142

142-
#[cfg(feature = "builder")]
143-
impl AsBytes for MemoryArea {}
144-
145143
/// ABI-friendly version of [`MemoryAreaType`].
146144
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
147145
#[repr(C)]
@@ -292,9 +290,6 @@ impl TagTrait for BasicMemoryInfoTag {
292290

293291
const EFI_METADATA_SIZE: usize = mem::size_of::<TagTypeId>() + 3 * mem::size_of::<u32>();
294292

295-
#[cfg(feature = "builder")]
296-
impl AsBytes for EFIMemoryDesc {}
297-
298293
/// EFI memory map tag. The embedded [`EFIMemoryDesc`]s follows the EFI
299294
/// specification.
300295
#[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -322,54 +317,30 @@ pub struct EFIMemoryMapTag {
322317

323318
impl EFIMemoryMapTag {
324319
/// Create a new EFI memory map tag with the given memory descriptors.
325-
/// Version and size can't be set because you're passing a slice of
326-
/// EFIMemoryDescs, not the ones you might have gotten from the firmware.
327320
#[cfg(feature = "builder")]
328321
#[must_use]
329322
pub fn new_from_descs(descs: &[EFIMemoryDesc]) -> Box<Self> {
330-
// TODO replace this EfiMemorydesc::uefi_desc_size() in the next uefi_raw
331-
// release.
332-
333-
let size_base = mem::size_of::<EFIMemoryDesc>();
334-
// Taken from https://github.com/tianocore/edk2/blob/7142e648416ff5d3eac6c6d607874805f5de0ca8/MdeModulePkg/Core/PiSmmCore/Page.c#L1059
335-
let desc_size_diff = mem::size_of::<u64>() - size_base % mem::size_of::<u64>();
336-
let desc_size = size_base + desc_size_diff;
337-
338-
assert!(desc_size >= size_base);
339-
340-
let mut efi_mmap = alloc::vec::Vec::with_capacity(descs.len() * desc_size);
341-
for desc in descs {
342-
efi_mmap.extend(desc.as_bytes());
343-
// fill with zeroes
344-
efi_mmap.extend([0].repeat(desc_size_diff));
345-
}
323+
let efi_mmap = {
324+
let ptr = descs.as_ptr().cast::<u8>();
325+
let len = descs.len() * size_of::<EFIMemoryDesc>();
326+
unsafe { slice::from_raw_parts(ptr, len) }
327+
};
346328

347329
Self::new_from_map(
348-
desc_size as u32,
330+
mem::size_of::<EFIMemoryDesc>() as u32,
349331
EFIMemoryDesc::VERSION,
350-
efi_mmap.as_slice(),
332+
efi_mmap,
351333
)
352334
}
353335

354336
/// Create a new EFI memory map tag from the given EFI memory map.
355337
#[cfg(feature = "builder")]
356338
#[must_use]
357339
pub fn new_from_map(desc_size: u32, desc_version: u32, efi_mmap: &[u8]) -> Box<Self> {
358-
assert!(desc_size > 0);
359-
assert_eq!(efi_mmap.len() % desc_size as usize, 0);
360-
assert_eq!(
361-
efi_mmap
362-
.as_ptr()
363-
.align_offset(mem::align_of::<EFIMemoryDesc>()),
364-
0
365-
);
366-
let bytes = [
367-
&desc_size.to_le_bytes(),
368-
&desc_version.to_le_bytes(),
369-
efi_mmap,
370-
]
371-
.concat();
372-
new_boxed(&bytes)
340+
assert_ne!(desc_size, 0);
341+
let desc_size = desc_size.to_ne_bytes();
342+
let desc_version = desc_version.to_ne_bytes();
343+
new_boxed(&[&desc_size, &desc_version, efi_mmap])
373344
}
374345

375346
/// Returns an iterator over the provided memory areas.
@@ -491,8 +462,6 @@ mod tests {
491462
];
492463
let efi_mmap_tag = EFIMemoryMapTag::new_from_descs(&descs);
493464

494-
assert_eq!(efi_mmap_tag.desc_size, 48 /* 40 + 8 */);
495-
496465
let mut iter = efi_mmap_tag.memory_areas();
497466

498467
assert_eq!(iter.next(), Some(&descs[0]));

multiboot2/src/module.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,15 @@ impl ModuleTag {
2929
pub fn new(start: u32, end: u32, cmdline: &str) -> Box<Self> {
3030
assert!(end > start, "must have a size");
3131

32-
let mut cmdline_bytes: Vec<_> = cmdline.bytes().collect();
33-
if !cmdline_bytes.ends_with(&[0]) {
34-
// terminating null-byte
35-
cmdline_bytes.push(0);
32+
let start = start.to_ne_bytes();
33+
let end = end.to_ne_bytes();
34+
let cmdline = cmdline.as_bytes();
35+
36+
if cmdline.ends_with(&[0]) {
37+
new_boxed(&[&start, &end, cmdline])
38+
} else {
39+
new_boxed(&[&start, &end, cmdline, &[0]])
3640
}
37-
let start_bytes = start.to_le_bytes();
38-
let end_bytes = end.to_le_bytes();
39-
let mut content_bytes = [start_bytes, end_bytes].concat();
40-
content_bytes.extend_from_slice(&cmdline_bytes);
41-
new_boxed(&content_bytes)
4241
}
4342

4443
/// Reads the command line of the boot module as Rust string slice without

multiboot2/src/smbios.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,8 @@ impl SmbiosTag {
2525
#[cfg(feature = "builder")]
2626
#[must_use]
2727
pub fn new(major: u8, minor: u8, tables: &[u8]) -> Box<Self> {
28-
let mut bytes = [major, minor, 0, 0, 0, 0, 0, 0].to_vec();
29-
bytes.extend(tables);
30-
new_boxed(&bytes)
28+
let reserved = [0, 0, 0, 0, 0, 0];
29+
new_boxed(&[&[major, minor], &reserved, tables])
3130
}
3231

3332
/// Returns the major number.

multiboot2/src/util.rs

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,17 @@ impl core::error::Error for StringError {
4141
/// constructor and box the result.
4242
///
4343
/// # Parameters
44-
/// - `additional_bytes`: All bytes apart from the default [`TagHeader`] that
45-
/// are included into the tag.
44+
/// - `additional_bytes_slices`: Array of byte slices that should be included
45+
/// without additional padding in-between. You don't need to add the bytes
46+
/// for [`TagHeader`], but only additional ones.
4647
#[cfg(feature = "alloc")]
47-
pub fn new_boxed<T: TagTrait + ?Sized>(additional_bytes: &[u8]) -> Box<T> {
48-
let size = size_of::<TagHeader>() + additional_bytes.iter().len();
48+
pub fn new_boxed<T: TagTrait + ?Sized>(additional_bytes_slices: &[&[u8]]) -> Box<T> {
49+
let additional_size = additional_bytes_slices
50+
.iter()
51+
.map(|b| b.len())
52+
.sum::<usize>();
53+
54+
let size = size_of::<TagHeader>() + additional_size;
4955
let alloc_size = increase_to_alignment(size);
5056
let layout = Layout::from_size_align(alloc_size, ALIGNMENT).unwrap();
5157
let heap_ptr = unsafe { alloc::alloc::alloc(layout) };
@@ -55,9 +61,16 @@ pub fn new_boxed<T: TagTrait + ?Sized>(additional_bytes: &[u8]) -> Box<T> {
5561
heap_ptr.cast::<u32>().write(T::ID.val());
5662
heap_ptr.cast::<u32>().add(1).write(size as u32);
5763
}
58-
unsafe {
59-
let ptr = heap_ptr.add(size_of::<TagHeader>());
60-
ptr::copy_nonoverlapping(additional_bytes.as_ptr(), ptr, additional_bytes.len());
64+
65+
let mut write_offset = size_of::<TagHeader>();
66+
for &bytes in additional_bytes_slices {
67+
unsafe {
68+
let len = bytes.len();
69+
let src = bytes.as_ptr();
70+
let dst = heap_ptr.add(write_offset);
71+
ptr::copy_nonoverlapping(src, dst, len);
72+
write_offset += len;
73+
}
6174
}
6275

6376
let header = unsafe { heap_ptr.cast::<TagHeader>().as_ref() }.unwrap();
@@ -128,10 +141,16 @@ mod tests {
128141

129142
#[test]
130143
fn test_new_boxed() {
131-
let tag = new_boxed::<GenericTag>(&[0, 1, 2, 3]);
144+
let tag = new_boxed::<GenericTag>(&[&[0, 1, 2, 3]]);
132145
assert_eq!(tag.header().typ, GenericTag::ID);
133-
{}
134-
let tag = new_boxed::<CommandLineTag>("hello\0".as_bytes());
146+
assert_eq!(tag.payload(), &[0, 1, 2, 3]);
147+
148+
// Test that bytes are added consecutively without gaps.
149+
let tag = new_boxed::<GenericTag>(&[&[0], &[1], &[2, 3]]);
150+
assert_eq!(tag.header().typ, GenericTag::ID);
151+
assert_eq!(tag.payload(), &[0, 1, 2, 3]);
152+
153+
let tag = new_boxed::<CommandLineTag>(&["hello\0".as_bytes()]);
135154
assert_eq!(tag.cmdline(), Ok("hello"));
136155
}
137156
}

0 commit comments

Comments
 (0)