Skip to content

Commit 34d51b8

Browse files
committed
feat: jump to user space
1 parent 3e0ff8f commit 34d51b8

File tree

13 files changed

+480
-263
lines changed

13 files changed

+480
-263
lines changed

Cargo.lock

Lines changed: 26 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,8 @@ kernel = { path = "kernel", artifact = "bin", target = "x86_64-unknown-none" }
1111
ovmf-prebuilt = "0.1.0-alpha.1"
1212

1313
[workspace]
14-
members = ["kernel"]
14+
members = ["kernel"]
15+
16+
[profile.dev]
17+
debug = true
18+
split-debuginfo = "unpacked"

build.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::path::PathBuf;
22

33
fn main() {
44
let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
5-
let kernel = PathBuf::from(std::env::var_os("CARGO_BIN_FILE_KERNEL_kernel").unwrap());
5+
let kernel = PathBuf::from(std::env::var_os("CARGO_BIN_FILE_KERNEL").unwrap());
66

77
let uefi_path = out_dir.join("uefi.img");
88
bootloader::UefiBoot::new(&kernel)
@@ -11,7 +11,7 @@ fn main() {
1111

1212
let bios_path = out_dir.join("bios.img");
1313
bootloader::BiosBoot::new(&kernel)
14-
.create_disk_image(&uefi_path)
14+
.create_disk_image(&bios_path)
1515
.unwrap();
1616

1717
println!("cargo:rustc-env=UEFI_PATH={}", uefi_path.display());

kernel/Cargo.toml

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,36 @@
22
name = "kernel"
33
edition = "2024"
44

5+
[[bin]]
6+
name = "kernel"
7+
test = false
8+
bench = false
9+
510
[dependencies]
6-
volatile = "0.2.6"
11+
bootloader_api = "0.11.12"
12+
volatile = "0.6.1"
713
spin = "0.5.2"
814
x86_64 = "0.14.2"
915
uart_16550 = "0.2.0"
1016
pic8259 = "0.10.1"
1117
pc-keyboard = "0.7.0"
1218
linked_list_allocator = "0.9.0"
19+
font8x8 = { version = "0.2.5", default-features = false, features = ["unicode"]}
20+
21+
[dependencies.noto-sans-mono-bitmap]
22+
version = "0.2.0"
23+
default-features = false
24+
features = [
25+
"regular",
26+
"size_16",
27+
"unicode-basic-latin",
28+
"unicode-specials",
29+
]
1330

1431
[dependencies.lazy_static]
1532
version = "1.0"
1633
features = ["spin_no_std"]
1734

18-
# [profile.dev]
19-
# panic = "abort"
20-
21-
[profile.release]
22-
panic = "abort"
23-
2435
[package.metadata.bootimage]
2536
test-args = [
2637
"-device",
@@ -40,4 +51,5 @@ harness = false # disable test runner from default and custom test framewo
4051

4152
[[test]]
4253
name = "stack_overflow"
43-
harness = false
54+
harness = false
55+

kernel/src/framebuffer.rs

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
use bootloader_api::info::{FrameBuffer, FrameBufferInfo, PixelFormat};
2+
use core::fmt;
3+
use noto_sans_mono_bitmap::{RasterizedChar, get_raster};
4+
use spin::Mutex;
5+
6+
use crate::{framebuffer::font_constants::BACKUP_CHAR, userspace};
7+
8+
pub static WRITER: Mutex<Option<Writer>> = Mutex::new(None);
9+
10+
const LINE_SPACING: usize = 2;
11+
const LETTER_SPACING: usize = 0;
12+
const BORDER_PADDING: usize = 1;
13+
14+
mod font_constants {
15+
use noto_sans_mono_bitmap::{FontWeight, RasterHeight, get_raster_width};
16+
17+
pub const CHAR_RASTER_HEIGHT: RasterHeight = RasterHeight::Size16;
18+
19+
pub const CHAR_RASTER_WIDTH: usize = get_raster_width(FontWeight::Regular, CHAR_RASTER_HEIGHT);
20+
21+
pub const BACKUP_CHAR: char = '�';
22+
23+
pub const FONT_WEIGHT: FontWeight = FontWeight::Regular;
24+
}
25+
26+
fn get_char_raster(character: char) -> RasterizedChar {
27+
fn get(character: char) -> Option<RasterizedChar> {
28+
get_raster(
29+
character,
30+
font_constants::FONT_WEIGHT,
31+
font_constants::CHAR_RASTER_HEIGHT,
32+
)
33+
}
34+
35+
get(character).unwrap_or_else(|| get(BACKUP_CHAR).expect("Backup char not found"))
36+
}
37+
38+
pub fn init(framebuffer: FrameBuffer) {
39+
let mut writer = Writer {
40+
info: framebuffer.info(),
41+
buffer: framebuffer,
42+
x_position: 0,
43+
y_position: 0,
44+
};
45+
writer.clear();
46+
47+
let mut global_writer = WRITER.try_lock().unwrap();
48+
assert!(global_writer.is_none(), "Global writer must be None");
49+
50+
*global_writer = Some(writer);
51+
}
52+
53+
pub struct Writer {
54+
buffer: FrameBuffer,
55+
info: FrameBufferInfo,
56+
x_position: usize,
57+
y_position: usize,
58+
}
59+
60+
impl Writer {
61+
fn write_string(&mut self, str: &str) {
62+
for character in str.chars() {
63+
self.write_char(character);
64+
}
65+
}
66+
67+
fn write_char(&mut self, character: char) {
68+
match character {
69+
'\n' => self.new_line(),
70+
'\r' => self.carriage_return(),
71+
character => {
72+
let updated_x_position = self.x_position + font_constants::CHAR_RASTER_WIDTH;
73+
74+
if updated_x_position >= self.width() {
75+
self.new_line();
76+
}
77+
78+
let updated_y_position =
79+
self.y_position + font_constants::CHAR_RASTER_HEIGHT.val() + BORDER_PADDING;
80+
81+
while updated_y_position >= self.height() {
82+
self.shift_lines_up();
83+
}
84+
85+
self.write_rendered_char(get_char_raster(character));
86+
}
87+
}
88+
}
89+
90+
fn write_rendered_char(&mut self, rendered_char: RasterizedChar) {
91+
for (y, row) in rendered_char.raster().iter().enumerate() {
92+
for (x, byte) in row.iter().enumerate() {
93+
self.write_pixel(self.x_position + x, self.y_position + y, *byte);
94+
}
95+
}
96+
97+
self.x_position += rendered_char.width() + LETTER_SPACING;
98+
}
99+
100+
fn write_pixel(&mut self, x: usize, y: usize, intensity: u8) {
101+
let pixel_offset = y * self.info.stride + x;
102+
103+
let color = match self.info.pixel_format {
104+
PixelFormat::Rgb => [intensity, intensity, intensity / 2, 0],
105+
PixelFormat::Bgr => [intensity / 2, intensity, intensity, 0],
106+
PixelFormat::U8 => [if intensity > 200 { 0xf } else { 0 }, 0, 0, 0],
107+
other => {
108+
self.info.pixel_format = PixelFormat::Rgb;
109+
panic!("pixel format {:?} not supported", other);
110+
}
111+
};
112+
113+
let bytes_per_pixel = self.info.bytes_per_pixel;
114+
let byte_offset = pixel_offset * bytes_per_pixel;
115+
116+
unsafe {
117+
core::arch::asm!("mov r8, r9", in("r9") byte_offset);
118+
}
119+
120+
self.buffer.buffer_mut()[byte_offset..(byte_offset + bytes_per_pixel)]
121+
.copy_from_slice(&color[..bytes_per_pixel]);
122+
123+
// let _ = unsafe { ptr::read_volatile(&self.buffer.buffer_mut()[byte_offset]) };
124+
}
125+
126+
fn new_line(&mut self) {
127+
self.y_position += font_constants::CHAR_RASTER_HEIGHT.val() + LINE_SPACING;
128+
self.carriage_return();
129+
}
130+
131+
fn carriage_return(&mut self) {
132+
self.x_position = BORDER_PADDING;
133+
}
134+
135+
pub fn clear(&mut self) {
136+
self.x_position = BORDER_PADDING;
137+
self.y_position = BORDER_PADDING;
138+
self.buffer.buffer_mut().fill(0);
139+
}
140+
141+
fn shift_lines_up(&mut self) {
142+
let offset = self.info.stride * self.info.bytes_per_pixel * 8;
143+
144+
self.buffer.buffer_mut().copy_within(offset.., 0);
145+
self.y_position += 8;
146+
}
147+
148+
fn width(&self) -> usize {
149+
self.info.width
150+
}
151+
152+
fn height(&self) -> usize {
153+
self.info.height
154+
}
155+
}
156+
157+
impl fmt::Write for Writer {
158+
fn write_str(&mut self, s: &str) -> fmt::Result {
159+
self.write_string(s);
160+
Ok(())
161+
}
162+
}
163+
164+
#[macro_export]
165+
macro_rules! print {
166+
($($arg:tt)*) => ($crate::framebuffer::_print(format_args!($($arg)*)));
167+
}
168+
169+
#[macro_export]
170+
macro_rules! println {
171+
() => ($crate::print("\n"));
172+
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
173+
}
174+
175+
#[doc(hidden)]
176+
pub fn _print(args: fmt::Arguments) {
177+
use core::fmt::Write;
178+
use x86_64::instructions::interrupts;
179+
180+
let f = || {
181+
WRITER.lock().as_mut().unwrap().write_fmt(args).unwrap();
182+
};
183+
184+
if !userspace::is_user_ring() {
185+
interrupts::without_interrupts(f);
186+
} else {
187+
f();
188+
}
189+
}
190+
191+
#[test_case]
192+
fn test_println_simple() {
193+
println!("test_println_simple output");
194+
}
195+
196+
#[test_case]
197+
fn test_println_many() {
198+
for _ in 0..200 {
199+
println!("test_println_many output");
200+
}
201+
}
202+
203+
#[test_case]
204+
fn test_println_output() {
205+
use core::fmt::Write;
206+
use x86_64::instructions::interrupts;
207+
208+
let s = "tatakae";
209+
210+
interrupts::without_interrupts(|| {
211+
let mut writer = WRITER.lock();
212+
writeln!(writer, "\n{}", s).expect("writeln failed");
213+
for (i, c) in s.chars().enumerate() {
214+
let screen_char = writer.buffer.chars[BUFFER_HEIGHT - 2][i].read();
215+
assert_eq!(char::from(screen_char.ascii_character), c);
216+
}
217+
});
218+
}

0 commit comments

Comments
 (0)