Skip to content

Commit 94c71d5

Browse files
authored
Merge pull request #314 from asensio-project/improve_logging
Improve Logging
2 parents fffa7ce + 7475960 commit 94c71d5

File tree

10 files changed

+333
-175
lines changed

10 files changed

+333
-175
lines changed

Cargo.lock

+4-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/build.rs

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ fn main() {
2323
(97, 9),
2424
(106, 9),
2525
(115, 1),
26+
(116, 1),
27+
(117, 1),
2628
];
2729

2830
let mut code = String::new();

api/src/config.rs

+64-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ pub struct BootloaderConfig {
3131
/// Configuration for changing the level of the filter of the messages that are shown in the
3232
/// screen when booting. The default is 'Trace'.
3333
pub log_level: LevelFilter,
34+
35+
/// Whether the bootloader should print log messages to the framebuffer when booting.
36+
///
37+
/// Enabled by default.
38+
pub frame_buffer_logger_status: LoggerStatus,
39+
40+
/// Whether the bootloader should print log messages to the serial port when booting.
41+
///
42+
/// Enabled by default.
43+
pub serial_logger_status: LoggerStatus,
3444
}
3545

3646
impl BootloaderConfig {
@@ -39,7 +49,7 @@ impl BootloaderConfig {
3949
0x3D,
4050
];
4151
#[doc(hidden)]
42-
pub const SERIALIZED_LEN: usize = 116;
52+
pub const SERIALIZED_LEN: usize = 118;
4353

4454
/// Creates a new default configuration with the following values:
4555
///
@@ -53,6 +63,8 @@ impl BootloaderConfig {
5363
mappings: Mappings::new_default(),
5464
frame_buffer: FrameBuffer::new_default(),
5565
log_level: LevelFilter::Trace,
66+
frame_buffer_logger_status: LoggerStatus::Enable,
67+
serial_logger_status: LoggerStatus::Enable,
5668
}
5769
}
5870

@@ -67,6 +79,8 @@ impl BootloaderConfig {
6779
kernel_stack_size,
6880
frame_buffer,
6981
log_level,
82+
frame_buffer_logger_status,
83+
serial_logger_status,
7084
} = self;
7185
let ApiVersion {
7286
version_major,
@@ -147,7 +161,15 @@ impl BootloaderConfig {
147161
},
148162
);
149163

150-
concat_115_1(buf, (*log_level as u8).to_le_bytes())
164+
let log_level = concat_115_1(buf, (*log_level as u8).to_le_bytes());
165+
166+
let frame_buffer_logger_status =
167+
concat_116_1(log_level, (*frame_buffer_logger_status as u8).to_le_bytes());
168+
169+
concat_117_1(
170+
frame_buffer_logger_status,
171+
(*serial_logger_status as u8).to_le_bytes(),
172+
)
151173
}
152174

153175
/// Tries to deserialize a config byte array that was created using [`Self::serialize`].
@@ -267,6 +289,21 @@ impl BootloaderConfig {
267289
Option::None => return Err("log_level invalid"),
268290
};
269291

292+
let (&frame_buffer_logger_status, s) = split_array_ref(s);
293+
let frame_buffer_logger_status =
294+
LoggerStatus::from_u8(u8::from_le_bytes(frame_buffer_logger_status));
295+
let frame_buffer_logger_status = match frame_buffer_logger_status {
296+
Option::Some(status) => status,
297+
Option::None => return Err("frame_buffer_logger_status invalid"),
298+
};
299+
300+
let (&serial_logger_status, s) = split_array_ref(s);
301+
let serial_logger_status = LoggerStatus::from_u8(u8::from_le_bytes(serial_logger_status));
302+
let serial_logger_status = match serial_logger_status {
303+
Option::Some(status) => status,
304+
Option::None => return Err("serial_logger_status invalid"),
305+
};
306+
270307
if !s.is_empty() {
271308
return Err("unexpected rest");
272309
}
@@ -277,6 +314,8 @@ impl BootloaderConfig {
277314
mappings,
278315
frame_buffer,
279316
log_level,
317+
frame_buffer_logger_status,
318+
serial_logger_status,
280319
})
281320
}
282321

@@ -288,6 +327,8 @@ impl BootloaderConfig {
288327
kernel_stack_size: rand::random(),
289328
frame_buffer: FrameBuffer::random(),
290329
log_level: LevelFilter::Trace,
330+
frame_buffer_logger_status: LoggerStatus::Enable,
331+
serial_logger_status: LoggerStatus::Enable,
291332
}
292333
}
293334
}
@@ -587,6 +628,27 @@ impl LevelFilter {
587628
}
588629
}
589630

631+
/// An enum for enabling or disabling the different methods for logging.
632+
#[repr(u8)]
633+
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
634+
pub enum LoggerStatus {
635+
/// This method of logging is disabled
636+
Disable,
637+
/// This method of logging is enabled
638+
Enable,
639+
}
640+
641+
impl LoggerStatus {
642+
/// Converts an u8 into a Option<LoggerStatus>
643+
pub fn from_u8(value: u8) -> Option<LoggerStatus> {
644+
match value {
645+
0 => Some(Self::Disable),
646+
1 => Some(Self::Enable),
647+
_ => None,
648+
}
649+
}
650+
}
651+
590652
/// Taken from https://github.com/rust-lang/rust/blob/e100ec5bc7cd768ec17d75448b29c9ab4a39272b/library/core/src/slice/mod.rs#L1673-L1677
591653
///
592654
/// TODO replace with `split_array` feature in stdlib as soon as it's stabilized,

bios/stage-4/src/main.rs

+20-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
use crate::memory_descriptor::MemoryRegion;
55
use bootloader_api::{
6-
config::LevelFilter,
6+
config::{LevelFilter, LoggerStatus},
77
info::{FrameBufferInfo, PixelFormat},
88
};
99
use bootloader_x86_64_bios_common::{BiosFramebufferInfo, BiosInfo, E820MemoryRegion};
@@ -109,7 +109,12 @@ pub extern "C" fn _start(info: &mut BiosInfo) -> ! {
109109
};
110110
let kernel = Kernel::parse(kernel_slice);
111111

112-
let framebuffer_info = init_logger(info.framebuffer, kernel.config.log_level);
112+
let framebuffer_info = init_logger(
113+
info.framebuffer,
114+
kernel.config.log_level,
115+
kernel.config.frame_buffer_logger_status,
116+
kernel.config.serial_logger_status,
117+
);
113118

114119
log::info!("4th Stage");
115120
log::info!("{info:x?}");
@@ -126,7 +131,12 @@ pub extern "C" fn _start(info: &mut BiosInfo) -> ! {
126131
load_and_switch_to_kernel(kernel, frame_allocator, page_tables, system_info);
127132
}
128133

129-
fn init_logger(info: BiosFramebufferInfo, log_level: LevelFilter) -> FrameBufferInfo {
134+
fn init_logger(
135+
info: BiosFramebufferInfo,
136+
log_level: LevelFilter,
137+
frame_buffer_logger_status: LoggerStatus,
138+
serial_logger_status: LoggerStatus,
139+
) -> FrameBufferInfo {
130140
let framebuffer_info = FrameBufferInfo {
131141
byte_len: info.region.len.try_into().unwrap(),
132142
width: info.width.into(),
@@ -155,7 +165,13 @@ fn init_logger(info: BiosFramebufferInfo, log_level: LevelFilter) -> FrameBuffer
155165
)
156166
};
157167

158-
bootloader_x86_64_common::init_logger(framebuffer, framebuffer_info, log_level);
168+
bootloader_x86_64_common::init_logger(
169+
framebuffer,
170+
framebuffer_info,
171+
log_level,
172+
frame_buffer_logger_status,
173+
serial_logger_status,
174+
);
159175

160176
framebuffer_info
161177
}

common/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ xmas-elf = "0.8.0"
1919
raw-cpuid = "10.2.0"
2020
rand = { version = "0.8.4", default-features = false }
2121
rand_hc = "0.3.1"
22+
uart_16550 = "0.2.18"
2223

2324
[dependencies.noto-sans-mono-bitmap]
2425
version = "0.2.0"

common/src/framebuffer.rs

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
use bootloader_api::info::{FrameBufferInfo, PixelFormat};
2+
use core::{fmt, ptr};
3+
use font_constants::BACKUP_CHAR;
4+
use noto_sans_mono_bitmap::{
5+
get_raster, get_raster_width, FontWeight, RasterHeight, RasterizedChar,
6+
};
7+
8+
/// Additional vertical space between lines
9+
const LINE_SPACING: usize = 2;
10+
/// Additional horizontal space between characters.
11+
const LETTER_SPACING: usize = 0;
12+
13+
/// Padding from the border. Prevent that font is too close to border.
14+
const BORDER_PADDING: usize = 1;
15+
16+
/// Constants for the usage of the [`noto_sans_mono_bitmap`] crate.
17+
mod font_constants {
18+
use super::*;
19+
20+
/// Height of each char raster. The font size is ~0.84% of this. Thus, this is the line height that
21+
/// enables multiple characters to be side-by-side and appear optically in one line in a natural way.
22+
pub const CHAR_RASTER_HEIGHT: RasterHeight = RasterHeight::Size16;
23+
24+
/// The width of each single symbol of the mono space font.
25+
pub const CHAR_RASTER_WIDTH: usize = get_raster_width(FontWeight::Regular, CHAR_RASTER_HEIGHT);
26+
27+
/// Backup character if a desired symbol is not available by the font.
28+
/// The '�' character requires the feature "unicode-specials".
29+
pub const BACKUP_CHAR: char = '�';
30+
31+
pub const FONT_WEIGHT: FontWeight = FontWeight::Regular;
32+
}
33+
34+
/// Returns the raster of the given char or the raster of [`font_constants::BACKUP_CHAR`].
35+
fn get_char_raster(c: char) -> RasterizedChar {
36+
fn get(c: char) -> Option<RasterizedChar> {
37+
get_raster(
38+
c,
39+
font_constants::FONT_WEIGHT,
40+
font_constants::CHAR_RASTER_HEIGHT,
41+
)
42+
}
43+
get(c).unwrap_or_else(|| get(BACKUP_CHAR).expect("Should get raster of backup char."))
44+
}
45+
46+
/// Allows logging text to a pixel-based framebuffer.
47+
pub struct FrameBufferWriter {
48+
framebuffer: &'static mut [u8],
49+
info: FrameBufferInfo,
50+
x_pos: usize,
51+
y_pos: usize,
52+
}
53+
54+
impl FrameBufferWriter {
55+
/// Creates a new logger that uses the given framebuffer.
56+
pub fn new(framebuffer: &'static mut [u8], info: FrameBufferInfo) -> Self {
57+
let mut logger = Self {
58+
framebuffer,
59+
info,
60+
x_pos: 0,
61+
y_pos: 0,
62+
};
63+
logger.clear();
64+
logger
65+
}
66+
67+
fn newline(&mut self) {
68+
self.y_pos += font_constants::CHAR_RASTER_HEIGHT.val() + LINE_SPACING;
69+
self.carriage_return()
70+
}
71+
72+
fn carriage_return(&mut self) {
73+
self.x_pos = BORDER_PADDING;
74+
}
75+
76+
/// Erases all text on the screen. Resets `self.x_pos` and `self.y_pos`.
77+
pub fn clear(&mut self) {
78+
self.x_pos = BORDER_PADDING;
79+
self.y_pos = BORDER_PADDING;
80+
self.framebuffer.fill(0);
81+
}
82+
83+
fn width(&self) -> usize {
84+
self.info.width
85+
}
86+
87+
fn height(&self) -> usize {
88+
self.info.height
89+
}
90+
91+
/// Writes a single char to the framebuffer. Takes care of special control characters, such as
92+
/// newlines and carriage returns.
93+
fn write_char(&mut self, c: char) {
94+
match c {
95+
'\n' => self.newline(),
96+
'\r' => self.carriage_return(),
97+
c => {
98+
let new_xpos = self.x_pos + font_constants::CHAR_RASTER_WIDTH;
99+
if new_xpos >= self.width() {
100+
self.newline();
101+
}
102+
let new_ypos =
103+
self.y_pos + font_constants::CHAR_RASTER_HEIGHT.val() + BORDER_PADDING;
104+
if new_ypos >= self.height() {
105+
self.clear();
106+
}
107+
self.write_rendered_char(get_char_raster(c));
108+
}
109+
}
110+
}
111+
112+
/// Prints a rendered char into the framebuffer.
113+
/// Updates `self.x_pos`.
114+
fn write_rendered_char(&mut self, rendered_char: RasterizedChar) {
115+
for (y, row) in rendered_char.raster().iter().enumerate() {
116+
for (x, byte) in row.iter().enumerate() {
117+
self.write_pixel(self.x_pos + x, self.y_pos + y, *byte);
118+
}
119+
}
120+
self.x_pos += rendered_char.width() + LETTER_SPACING;
121+
}
122+
123+
fn write_pixel(&mut self, x: usize, y: usize, intensity: u8) {
124+
let pixel_offset = y * self.info.stride + x;
125+
let color = match self.info.pixel_format {
126+
PixelFormat::Rgb => [intensity, intensity, intensity / 2, 0],
127+
PixelFormat::Bgr => [intensity / 2, intensity, intensity, 0],
128+
PixelFormat::U8 => [if intensity > 200 { 0xf } else { 0 }, 0, 0, 0],
129+
other => {
130+
// set a supported (but invalid) pixel format before panicking to avoid a double
131+
// panic; it might not be readable though
132+
self.info.pixel_format = PixelFormat::Rgb;
133+
panic!("pixel format {:?} not supported in logger", other)
134+
}
135+
};
136+
let bytes_per_pixel = self.info.bytes_per_pixel;
137+
let byte_offset = pixel_offset * bytes_per_pixel;
138+
self.framebuffer[byte_offset..(byte_offset + bytes_per_pixel)]
139+
.copy_from_slice(&color[..bytes_per_pixel]);
140+
let _ = unsafe { ptr::read_volatile(&self.framebuffer[byte_offset]) };
141+
}
142+
}
143+
144+
unsafe impl Send for FrameBufferWriter {}
145+
unsafe impl Sync for FrameBufferWriter {}
146+
147+
impl fmt::Write for FrameBufferWriter {
148+
fn write_str(&mut self, s: &str) -> fmt::Result {
149+
for c in s.chars() {
150+
self.write_char(c);
151+
}
152+
Ok(())
153+
}
154+
}

0 commit comments

Comments
 (0)