Quellcode durchsuchen

multiboot2: Implement setting the framebuffer tag

Niklas Sombert vor 2 Jahren
Ursprung
Commit
bb21998686

+ 7 - 1
multiboot2/src/builder/information.rs

@@ -1,6 +1,6 @@
 //! Exports item [`Multiboot2InformationBuilder`].
 use crate::builder::traits::StructAsBytes;
-use crate::{BootLoaderNameTag, CommandLineTag, ElfSectionsTag, ModuleTag};
+use crate::{BootLoaderNameTag, CommandLineTag, ElfSectionsTag, FramebufferTag, ModuleTag};
 
 use alloc::boxed::Box;
 use alloc::vec::Vec;
@@ -13,6 +13,7 @@ pub struct Multiboot2InformationBuilder {
     boot_loader_name_tag: Option<Box<BootLoaderNameTag>>,
     command_line_tag: Option<Box<CommandLineTag>>,
     elf_sections_tag: Option<Box<ElfSectionsTag>>,
+    framebuffer_tag: Option<Box<FramebufferTag>>,
     module_tags: Vec<Box<ModuleTag>>,
 }
 
@@ -22,6 +23,7 @@ impl Multiboot2InformationBuilder {
             boot_loader_name_tag: None,
             command_line_tag: None,
             elf_sections_tag: None,
+            framebuffer_tag: None,
             module_tags: Vec::new(),
         }
     }
@@ -38,6 +40,10 @@ impl Multiboot2InformationBuilder {
         self.elf_sections_tag = Some(elf_sections_tag);
     }
 
+    pub fn framebuffer_tag(&mut self, framebuffer_tag: Box<FramebufferTag>) {
+        self.framebuffer_tag = Some(framebuffer_tag);
+    }
+
     pub fn add_module_tag(&mut self, module_tag: Box<ModuleTag>) {
         self.module_tags.push(module_tag);
     }

+ 1 - 1
multiboot2/src/builder/mod.rs

@@ -1,7 +1,7 @@
 //! Module for the builder-feature.
 
 mod information;
-pub(self) mod traits;
+pub(crate) mod traits;
 
 pub use information::Multiboot2InformationBuilder;
 

+ 162 - 74
multiboot2/src/framebuffer.rs

@@ -1,32 +1,152 @@
-use crate::tag_type::Tag;
-use crate::Reader;
+use crate::{Reader, Tag, TagTrait, TagType, TagTypeId};
+
+use core::mem::size_of;
 use core::slice;
 use derive_more::Display;
 
+#[cfg(feature = "builder")]
+use {
+    crate::builder::boxed_dst_tag, crate::builder::traits::StructAsBytes, alloc::boxed::Box,
+    alloc::vec::Vec,
+};
+
+const METADATA_SIZE: usize = size_of::<TagTypeId>()
+    + 4 * size_of::<u32>()
+    + size_of::<u64>()
+    + size_of::<u16>()
+    + 2 * size_of::<u8>();
+
 /// The VBE Framebuffer information Tag.
-#[derive(Debug, PartialEq, Eq)]
-pub struct FramebufferTag<'a> {
+#[derive(Debug, PartialEq, Eq, ptr_meta::Pointee)]
+#[repr(C, packed)]
+pub struct FramebufferTag {
+    typ: TagTypeId,
+    size: u32,
+
     /// Contains framebuffer physical address.
     ///
     /// This field is 64-bit wide but bootloader should set it under 4GiB if
     /// possible for compatibility with payloads which aren’t aware of PAE or
     /// amd64.
-    pub address: u64,
+    address: u64,
 
     /// Contains the pitch in bytes.
-    pub pitch: u32,
+    pitch: u32,
 
     /// Contains framebuffer width in pixels.
-    pub width: u32,
+    width: u32,
 
     /// Contains framebuffer height in pixels.
-    pub height: u32,
+    height: u32,
 
     /// Contains number of bits per pixel.
-    pub bpp: u8,
+    bpp: u8,
 
     /// The type of framebuffer, one of: `Indexed`, `RGB` or `Text`.
-    pub buffer_type: FramebufferType<'a>,
+    type_no: u8,
+
+    // In the multiboot spec, it has this listed as a u8 _NOT_ a u16.
+    // Reading the GRUB2 source code reveals it is in fact a u16.
+    _reserved: u16,
+
+    buffer: [u8],
+}
+
+impl FramebufferTag {
+    #[cfg(feature = "builder")]
+    pub fn new(
+        address: u64,
+        pitch: u32,
+        width: u32,
+        height: u32,
+        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());
+        boxed_dst_tag(TagType::Framebuffer, &bytes)
+    }
+
+    /// Contains framebuffer physical address.
+    ///
+    /// This field is 64-bit wide but bootloader should set it under 4GiB if
+    /// possible for compatibility with payloads which aren’t aware of PAE or
+    /// amd64.
+    pub fn address(&self) -> u64 {
+        self.address
+    }
+
+    /// Contains the pitch in bytes.
+    pub fn pitch(&self) -> u32 {
+        self.pitch
+    }
+
+    /// Contains framebuffer width in pixels.
+    pub fn width(&self) -> u32 {
+        self.width
+    }
+
+    /// Contains framebuffer height in pixels.
+    pub fn height(&self) -> u32 {
+        self.height
+    }
+
+    /// Contains number of bits per pixel.
+    pub fn bpp(&self) -> u8 {
+        self.bpp
+    }
+
+    /// The type of framebuffer, one of: `Indexed`, `RGB` or `Text`.
+    pub fn buffer_type(&self) -> Result<FramebufferType, UnknownFramebufferType> {
+        let mut reader = Reader::new(self.buffer.as_ptr());
+        match self.type_no {
+            0 => {
+                let num_colors = reader.read_u32();
+                let palette = unsafe {
+                    slice::from_raw_parts(
+                        reader.current_address() as *const FramebufferColor,
+                        num_colors as usize,
+                    )
+                } as &'static [FramebufferColor];
+                Ok(FramebufferType::Indexed { palette })
+            }
+            1 => {
+                let red_pos = reader.read_u8(); // These refer to the bit positions of the LSB of each field
+                let red_mask = reader.read_u8(); // And then the length of the field from LSB to MSB
+                let green_pos = reader.read_u8();
+                let green_mask = reader.read_u8();
+                let blue_pos = reader.read_u8();
+                let blue_mask = reader.read_u8();
+                Ok(FramebufferType::RGB {
+                    red: FramebufferField {
+                        position: red_pos,
+                        size: red_mask,
+                    },
+                    green: FramebufferField {
+                        position: green_pos,
+                        size: green_mask,
+                    },
+                    blue: FramebufferField {
+                        position: blue_pos,
+                        size: blue_mask,
+                    },
+                })
+            }
+            2 => Ok(FramebufferType::Text),
+            no => Err(UnknownFramebufferType(no)),
+        }
+    }
+}
+
+impl TagTrait for FramebufferTag {
+    fn dst_size(base_tag: &Tag) -> usize {
+        assert!(base_tag.size as usize >= METADATA_SIZE);
+        base_tag.size as usize - METADATA_SIZE
+    }
 }
 
 /// Helper struct for [`FramebufferType`].
@@ -67,6 +187,35 @@ pub enum FramebufferType<'a> {
     Text,
 }
 
+impl<'a> FramebufferType<'a> {
+    #[cfg(feature = "builder")]
+    fn to_bytes(&self) -> Vec<u8> {
+        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());
+                for color in palette.iter() {
+                    v.extend(color.struct_as_bytes());
+                }
+            }
+            FramebufferType::RGB { red, green, blue } => {
+                v.extend(1u8.to_le_bytes()); // type
+                v.extend(0u16.to_le_bytes()); // reserved
+                v.extend(red.struct_as_bytes());
+                v.extend(green.struct_as_bytes());
+                v.extend(blue.struct_as_bytes());
+            }
+            FramebufferType::Text => {
+                v.extend(2u8.to_le_bytes()); // type
+                v.extend(0u16.to_le_bytes()); // reserved
+            }
+        }
+        v
+    }
+}
+
 /// An RGB color type field.
 #[derive(Debug, PartialEq, Eq)]
 pub struct FramebufferField {
@@ -77,6 +226,8 @@ pub struct FramebufferField {
     pub size: u8,
 }
 
+impl StructAsBytes for FramebufferField {}
+
 /// A framebuffer color descriptor in the palette.
 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
 #[repr(C, packed)] // only repr(C) would add unwanted padding at the end
@@ -99,67 +250,4 @@ pub struct UnknownFramebufferType(u8);
 #[cfg(feature = "unstable")]
 impl core::error::Error for UnknownFramebufferType {}
 
-/// Transforms a [`Tag`] into a [`FramebufferTag`].
-pub fn framebuffer_tag(tag: &Tag) -> Result<FramebufferTag, UnknownFramebufferType> {
-    let mut reader = Reader::new(tag as *const Tag);
-    reader.skip(8);
-    let address = reader.read_u64();
-    let pitch = reader.read_u32();
-    let width = reader.read_u32();
-    let height = reader.read_u32();
-    let bpp = reader.read_u8();
-    let type_no = reader.read_u8();
-    // In the multiboot spec, it has this listed as a u8 _NOT_ a u16.
-    // Reading the GRUB2 source code reveals it is in fact a u16.
-    reader.skip(2);
-    let buffer_type_id = match type_no {
-        0 => Ok(FramebufferTypeId::Indexed),
-        1 => Ok(FramebufferTypeId::RGB),
-        2 => Ok(FramebufferTypeId::Text),
-        id => Err(UnknownFramebufferType(id)),
-    }?;
-    let buffer_type = match buffer_type_id {
-        FramebufferTypeId::Indexed => {
-            let num_colors = reader.read_u32();
-            let palette = unsafe {
-                slice::from_raw_parts(
-                    reader.current_address() as *const FramebufferColor,
-                    num_colors as usize,
-                )
-            } as &[FramebufferColor];
-            FramebufferType::Indexed { palette }
-        }
-        FramebufferTypeId::RGB => {
-            let red_pos = reader.read_u8(); // These refer to the bit positions of the LSB of each field
-            let red_mask = reader.read_u8(); // And then the length of the field from LSB to MSB
-            let green_pos = reader.read_u8();
-            let green_mask = reader.read_u8();
-            let blue_pos = reader.read_u8();
-            let blue_mask = reader.read_u8();
-            FramebufferType::RGB {
-                red: FramebufferField {
-                    position: red_pos,
-                    size: red_mask,
-                },
-                green: FramebufferField {
-                    position: green_pos,
-                    size: green_mask,
-                },
-                blue: FramebufferField {
-                    position: blue_pos,
-                    size: blue_mask,
-                },
-            }
-        }
-        FramebufferTypeId::Text => FramebufferType::Text,
-    };
-
-    Ok(FramebufferTag {
-        address,
-        pitch,
-        width,
-        height,
-        bpp,
-        buffer_type,
-    })
-}
+impl StructAsBytes for FramebufferColor {}

+ 44 - 48
multiboot2/src/lib.rs

@@ -262,9 +262,12 @@ impl BootInformation {
 
     /// Search for the VBE framebuffer tag. The result is `Some(Err(e))`, if the
     /// framebuffer type is unknown, while the framebuffer tag is present.
-    pub fn framebuffer_tag(&self) -> Option<Result<FramebufferTag, UnknownFramebufferType>> {
-        self.get_tag::<Tag, _>(TagType::Framebuffer)
-            .map(framebuffer::framebuffer_tag)
+    pub fn framebuffer_tag(&self) -> Option<Result<&FramebufferTag, UnknownFramebufferType>> {
+        self.get_tag::<FramebufferTag, _>(TagType::Framebuffer)
+            .map(|tag| match tag.buffer_type() {
+                Ok(_) => Ok(tag),
+                Err(e) => Err(e),
+            })
     }
 
     /// Search for the EFI 32-bit SDT tag.
@@ -700,31 +703,34 @@ mod tests {
         assert_eq!(addr, bi.start_address());
         assert_eq!(addr + bytes.0.len(), bi.end_address());
         assert_eq!(bytes.0.len(), bi.total_size());
-        use framebuffer::{FramebufferField, FramebufferTag, FramebufferType};
+        use framebuffer::{FramebufferField, FramebufferType};
+        assert!(bi.framebuffer_tag().is_some());
+        let fbi = bi
+            .framebuffer_tag()
+            .expect("Framebuffer info should be available")
+            .expect("Framebuffer info type should be valid");
+        assert_eq!(fbi.address(), 4244635648);
+        assert_eq!(fbi.pitch(), 5120);
+        assert_eq!(fbi.width(), 1280);
+        assert_eq!(fbi.height(), 720);
+        assert_eq!(fbi.bpp(), 32);
         assert_eq!(
-            bi.framebuffer_tag(),
-            Some(Ok(FramebufferTag {
-                address: 4244635648,
-                pitch: 5120,
-                width: 1280,
-                height: 720,
-                bpp: 32,
-                buffer_type: FramebufferType::RGB {
-                    red: FramebufferField {
-                        position: 16,
-                        size: 8
-                    },
-                    green: FramebufferField {
-                        position: 8,
-                        size: 8
-                    },
-                    blue: FramebufferField {
-                        position: 0,
-                        size: 8
-                    }
+            fbi.buffer_type().unwrap(),
+            FramebufferType::RGB {
+                red: FramebufferField {
+                    position: 16,
+                    size: 8
+                },
+                green: FramebufferField {
+                    position: 8,
+                    size: 8
+                },
+                blue: FramebufferField {
+                    position: 0,
+                    size: 8
                 }
-            }))
-        )
+            }
+        );
     }
 
     #[test]
@@ -762,12 +768,12 @@ mod tests {
             .framebuffer_tag()
             .expect("Framebuffer info should be available")
             .expect("Framebuffer info type should be valid");
-        assert_eq!(fbi.address, 4244635648);
-        assert_eq!(fbi.pitch, 5120);
-        assert_eq!(fbi.width, 1280);
-        assert_eq!(fbi.height, 720);
-        assert_eq!(fbi.bpp, 32);
-        match fbi.buffer_type {
+        assert_eq!(fbi.address(), 4244635648);
+        assert_eq!(fbi.pitch(), 5120);
+        assert_eq!(fbi.width(), 1280);
+        assert_eq!(fbi.height(), 720);
+        assert_eq!(fbi.bpp(), 32);
+        match fbi.buffer_type().unwrap() {
             FramebufferType::Indexed { palette } => assert_eq!(
                 palette,
                 [
@@ -797,16 +803,6 @@ mod tests {
         }
     }
 
-    #[test]
-    /// Compile time test for `FramebufferTag`.
-    fn framebuffer_tag_size() {
-        use crate::FramebufferTag;
-        unsafe {
-            // 24 for the start + 24 for `FramebufferType`.
-            core::mem::transmute::<[u8; 48], FramebufferTag>([0u8; 48]);
-        }
-    }
-
     #[test]
     fn vbe_info_tag() {
         //Taken from GRUB2 running in QEMU.
@@ -1377,12 +1373,12 @@ mod tests {
             .framebuffer_tag()
             .expect("Framebuffer info should be available")
             .expect("Framebuffer info type should be valid");
-        assert_eq!(fbi.address, 753664);
-        assert_eq!(fbi.pitch, 160);
-        assert_eq!(fbi.width, 80);
-        assert_eq!(fbi.height, 25);
-        assert_eq!(fbi.bpp, 16);
-        assert_eq!(fbi.buffer_type, FramebufferType::Text);
+        assert_eq!(fbi.address(), 753664);
+        assert_eq!(fbi.pitch(), 160);
+        assert_eq!(fbi.width(), 80);
+        assert_eq!(fbi.height(), 25);
+        assert_eq!(fbi.bpp(), 16);
+        assert_eq!(fbi.buffer_type(), Ok(FramebufferType::Text));
     }
 
     #[test]