1use crate::TagType;
4use crate::tag::TagHeader;
5use core::fmt::Debug;
6use core::mem;
7use core::slice;
8use multiboot2_common::{MaybeDynSized, Tag};
9use thiserror::Error;
10#[cfg(feature = "builder")]
11use {alloc::boxed::Box, multiboot2_common::new_boxed};
12
13struct Reader<'a> {
16 buffer: &'a [u8],
17 off: usize,
18}
19
20impl<'a> Reader<'a> {
21 const fn new(buffer: &'a [u8]) -> Self {
22 Self { buffer, off: 0 }
23 }
24
25 fn read_next_u8(&mut self) -> u8 {
31 let val = self
32 .buffer
33 .get(self.off)
34 .cloned()
35 .expect("Embedded framebuffer info should be properly sized and available");
40 self.off += 1;
41 val
42 }
43
44 fn read_next_u16(&mut self) -> u16 {
50 let u16_lo = self.read_next_u8() as u16;
51 let u16_hi = self.read_next_u8() as u16;
52 (u16_hi << 8) | u16_lo
53 }
54
55 const fn current_ptr(&self) -> *const u8 {
56 unsafe { self.buffer.as_ptr().add(self.off) }
57 }
58}
59
60#[derive(ptr_meta::Pointee, Eq)]
62#[repr(C, align(8))]
63pub struct FramebufferTag {
64 header: TagHeader,
65
66 address: u64,
72
73 pitch: u32,
75
76 width: u32,
78
79 height: u32,
81
82 bpp: u8,
84
85 framebuffer_type: FramebufferTypeId,
91
92 _padding: u16,
93
94 buffer: [u8],
96}
97
98impl FramebufferTag {
99 #[cfg(feature = "builder")]
101 #[must_use]
102 pub fn new(
103 address: u64,
104 pitch: u32,
105 width: u32,
106 height: u32,
107 bpp: u8,
108 buffer_type: FramebufferType,
109 ) -> Box<Self> {
110 let header = TagHeader::new(Self::ID, 0);
111 let address = address.to_ne_bytes();
112 let pitch = pitch.to_ne_bytes();
113 let width = width.to_ne_bytes();
114 let height = height.to_ne_bytes();
115 let buffer_type_id = buffer_type.id();
116 let padding = [0; 2];
117 let optional_buffer = buffer_type.serialize();
118 new_boxed(
119 header,
120 &[
121 &address,
122 &pitch,
123 &width,
124 &height,
125 &[bpp],
126 &[buffer_type_id as u8],
127 &padding,
128 &optional_buffer,
129 ],
130 )
131 }
132
133 #[must_use]
139 pub const fn address(&self) -> u64 {
140 self.address
141 }
142
143 #[must_use]
145 pub const fn pitch(&self) -> u32 {
146 self.pitch
147 }
148
149 #[must_use]
151 pub const fn width(&self) -> u32 {
152 self.width
153 }
154
155 #[must_use]
157 pub const fn height(&self) -> u32 {
158 self.height
159 }
160
161 #[must_use]
163 pub const fn bpp(&self) -> u8 {
164 self.bpp
165 }
166
167 pub fn buffer_type(&self) -> Result<FramebufferType, UnknownFramebufferType> {
169 let mut reader = Reader::new(&self.buffer);
170
171 let fb_type_raw = self.framebuffer_type as u8;
174 let fb_type = FramebufferTypeId::try_from(fb_type_raw)?;
175
176 match fb_type {
177 FramebufferTypeId::Indexed => {
178 let num_colors = reader.read_next_u16();
182
183 let palette = {
184 assert_eq!(mem::size_of::<FramebufferColor>(), 3);
186
187 unsafe {
188 slice::from_raw_parts(
189 reader.current_ptr().cast::<FramebufferColor>(),
190 num_colors as usize,
191 )
192 }
193 };
194 Ok(FramebufferType::Indexed { palette })
195 }
196 FramebufferTypeId::RGB => {
197 let red_pos = reader.read_next_u8(); let red_mask = reader.read_next_u8(); let green_pos = reader.read_next_u8();
200 let green_mask = reader.read_next_u8();
201 let blue_pos = reader.read_next_u8();
202 let blue_mask = reader.read_next_u8();
203 Ok(FramebufferType::RGB {
204 red: FramebufferField {
205 position: red_pos,
206 size: red_mask,
207 },
208 green: FramebufferField {
209 position: green_pos,
210 size: green_mask,
211 },
212 blue: FramebufferField {
213 position: blue_pos,
214 size: blue_mask,
215 },
216 })
217 }
218 FramebufferTypeId::Text => Ok(FramebufferType::Text),
219 }
220 }
221}
222
223impl MaybeDynSized for FramebufferTag {
224 type Header = TagHeader;
225
226 const BASE_SIZE: usize = mem::size_of::<TagHeader>()
227 + mem::size_of::<u64>()
228 + 3 * mem::size_of::<u32>()
229 + 2 * mem::size_of::<u8>()
230 + mem::size_of::<u16>();
231
232 fn dst_len(header: &TagHeader) -> usize {
233 assert!(header.size as usize >= Self::BASE_SIZE);
234 header.size as usize - Self::BASE_SIZE
235 }
236}
237
238impl Tag for FramebufferTag {
239 type IDType = TagType;
240
241 const ID: TagType = TagType::Framebuffer;
242}
243
244impl Debug for FramebufferTag {
245 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
246 f.debug_struct("FramebufferTag")
247 .field("typ", &self.header.typ)
248 .field("size", &self.header.size)
249 .field("buffer_type", &self.buffer_type())
250 .field("address", &self.address)
251 .field("pitch", &self.pitch)
252 .field("width", &self.width)
253 .field("height", &self.height)
254 .field("bpp", &self.bpp)
255 .finish()
256 }
257}
258
259impl PartialEq for FramebufferTag {
260 fn eq(&self, other: &Self) -> bool {
261 self.header == other.header
262 && self.address == { other.address }
263 && self.pitch == { other.pitch }
264 && self.width == { other.width }
265 && self.height == { other.height }
266 && self.bpp == { other.bpp }
267 && self.framebuffer_type == { other.framebuffer_type }
268 && self.buffer == other.buffer
269 }
270}
271
272#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
274#[repr(u8)]
275#[allow(clippy::upper_case_acronyms)]
276pub enum FramebufferTypeId {
277 Indexed = 0,
278 RGB = 1,
279 Text = 2,
280 }
282
283impl TryFrom<u8> for FramebufferTypeId {
284 type Error = UnknownFramebufferType;
285
286 fn try_from(value: u8) -> Result<Self, Self::Error> {
287 match value {
288 0 => Ok(Self::Indexed),
289 1 => Ok(Self::RGB),
290 2 => Ok(Self::Text),
291 val => Err(UnknownFramebufferType(val)),
292 }
293 }
294}
295
296impl From<FramebufferType<'_>> for FramebufferTypeId {
297 fn from(value: FramebufferType) -> Self {
298 match value {
299 FramebufferType::Indexed { .. } => Self::Indexed,
300 FramebufferType::RGB { .. } => Self::RGB,
301 FramebufferType::Text => Self::Text,
302 }
303 }
304}
305
306#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
309pub enum FramebufferType<'a> {
310 Indexed {
312 #[allow(missing_docs)]
313 palette: &'a [FramebufferColor],
314 },
315
316 #[allow(missing_docs)]
318 #[allow(clippy::upper_case_acronyms)]
319 RGB {
320 red: FramebufferField,
321 green: FramebufferField,
322 blue: FramebufferField,
323 },
324
325 Text,
332}
333
334impl FramebufferType<'_> {
335 #[must_use]
336 #[cfg(feature = "builder")]
337 const fn id(&self) -> FramebufferTypeId {
338 match self {
339 FramebufferType::Indexed { .. } => FramebufferTypeId::Indexed,
340 FramebufferType::RGB { .. } => FramebufferTypeId::RGB,
341 FramebufferType::Text => FramebufferTypeId::Text,
342 }
343 }
344
345 #[must_use]
346 #[cfg(feature = "builder")]
347 fn serialize(&self) -> alloc::vec::Vec<u8> {
348 let mut data = alloc::vec::Vec::new();
349 match self {
350 FramebufferType::Indexed { palette } => {
351 let num_colors = palette.len() as u16;
355 data.extend(&num_colors.to_ne_bytes());
356 for color in *palette {
357 let serialized_color = [color.red, color.green, color.blue];
358 data.extend(&serialized_color);
359 }
360 }
361 FramebufferType::RGB { red, green, blue } => data.extend(&[
362 red.position,
363 red.size,
364 green.position,
365 green.size,
366 blue.position,
367 blue.size,
368 ]),
369 FramebufferType::Text => {}
370 }
371 data
372 }
373}
374
375#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
377#[repr(C)]
378pub struct FramebufferField {
379 pub position: u8,
381
382 pub size: u8,
384}
385
386#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
392#[repr(C)] pub struct FramebufferColor {
394 pub red: u8,
396
397 pub green: u8,
399
400 pub blue: u8,
402}
403
404#[derive(Debug, Copy, Clone, PartialEq, Eq, Error)]
406#[error("Unknown framebuffer type {0}")]
407pub struct UnknownFramebufferType(u8);
408
409#[cfg(test)]
410mod tests {
411 use super::*;
412
413 #[test]
415 fn test_size() {
416 assert_eq!(mem::size_of::<FramebufferColor>(), 3)
417 }
418
419 #[test]
420 #[cfg(feature = "builder")]
421 fn create_new() {
422 let tag = FramebufferTag::new(0x1000, 1, 1024, 1024, 8, FramebufferType::Text);
423 dbg!(tag);
425
426 let tag = FramebufferTag::new(
427 0x1000,
428 1,
429 1024,
430 1024,
431 8,
432 FramebufferType::Indexed {
433 palette: &[
434 FramebufferColor {
435 red: 255,
436 green: 255,
437 blue: 255,
438 },
439 FramebufferColor {
440 red: 127,
441 green: 42,
442 blue: 73,
443 },
444 ],
445 },
446 );
447 dbg!(tag);
449
450 let tag = FramebufferTag::new(
451 0x1000,
452 1,
453 1024,
454 1024,
455 8,
456 FramebufferType::RGB {
457 red: FramebufferField {
458 position: 0,
459 size: 0,
460 },
461 green: FramebufferField {
462 position: 10,
463 size: 20,
464 },
465 blue: FramebufferField {
466 position: 30,
467 size: 40,
468 },
469 },
470 );
471 dbg!(tag);
473 }
474}