From 819f77daf3a808b43840f4ffe14aa4b988196619 Mon Sep 17 00:00:00 2001 From: Jeremy Soller <jackpot51@gmail.com> Date: Sun, 11 Mar 2018 11:36:58 -0600 Subject: [PATCH] Add support for graphical debug, to be used during ACPI phase --- Cargo.toml | 1 + res/unifont.font | Bin 0 -> 4096 bytes src/arch/x86_64/debug.rs | 41 +++++ src/arch/x86_64/graphical_debug/debug.rs | 83 ++++++++++ src/arch/x86_64/graphical_debug/display.rs | 150 +++++++++++++++++++ src/arch/x86_64/graphical_debug/mod.rs | 71 +++++++++ src/arch/x86_64/graphical_debug/mode_info.rs | 37 +++++ src/arch/x86_64/graphical_debug/primitive.rs | 47 ++++++ src/arch/x86_64/macros.rs | 2 +- src/arch/x86_64/mod.rs | 7 + src/arch/x86_64/start.rs | 10 ++ 11 files changed, 448 insertions(+), 1 deletion(-) create mode 100644 res/unifont.font create mode 100644 src/arch/x86_64/debug.rs create mode 100644 src/arch/x86_64/graphical_debug/debug.rs create mode 100644 src/arch/x86_64/graphical_debug/display.rs create mode 100644 src/arch/x86_64/graphical_debug/mod.rs create mode 100644 src/arch/x86_64/graphical_debug/mode_info.rs create mode 100644 src/arch/x86_64/graphical_debug/primitive.rs diff --git a/Cargo.toml b/Cargo.toml index e7e116a4..82e2d60f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ default-features = false default = ["acpi"] acpi = [] doc = [] +graphical_debug = [] live = [] multi_core = [] pti = [] diff --git a/res/unifont.font b/res/unifont.font new file mode 100644 index 0000000000000000000000000000000000000000..a00366d5f559bc6fb3c20b9ee9cf8320219013b5 GIT binary patch literal 4096 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 literal 0 HcmV?d00001 diff --git a/src/arch/x86_64/debug.rs b/src/arch/x86_64/debug.rs new file mode 100644 index 00000000..198ca29a --- /dev/null +++ b/src/arch/x86_64/debug.rs @@ -0,0 +1,41 @@ +use core::fmt; +use spin::MutexGuard; + +use devices::uart_16550::SerialPort; +use syscall::io::Pio; + +use super::device::serial::COM1; +#[cfg(feature = "graphical_debug")] +use super::graphical_debug::{DEBUG_DISPLAY, DebugDisplay}; + +pub struct Writer<'a> { + serial: MutexGuard<'a, SerialPort<Pio<u8>>>, + #[cfg(feature = "graphical_debug")] + display: MutexGuard<'a, Option<DebugDisplay>> +} + +impl<'a> Writer<'a> { + pub fn new() -> Writer<'a> { + Writer { + serial: COM1.lock(), + #[cfg(feature = "graphical_debug")] + display: DEBUG_DISPLAY.lock(), + } + } +} + +impl<'a> fmt::Write for Writer<'a> { + #[cfg(not(feature = "graphical_debug"))] + fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { + self.serial.write_str(s) + } + + #[cfg(feature = "graphical_debug")] + fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { + if let Some(ref mut display) = *self.display { + display.write_str(s) + } else { + self.serial.write_str(s) + } + } +} diff --git a/src/arch/x86_64/graphical_debug/debug.rs b/src/arch/x86_64/graphical_debug/debug.rs new file mode 100644 index 00000000..338b9a1e --- /dev/null +++ b/src/arch/x86_64/graphical_debug/debug.rs @@ -0,0 +1,83 @@ +use core::fmt; + +use super::Display; + +pub struct DebugDisplay { + display: Display, + x: usize, + y: usize, + w: usize, + h: usize, +} + +impl DebugDisplay { + pub fn new(display: Display) -> DebugDisplay { + let w = display.width/8; + let h = display.height/16; + DebugDisplay { + display, + x: 0, + y: 0, + w: w, + h: h, + } + } + + pub fn write(&mut self, c: char) { + if self.x >= self.w || c == '\n' { + self.x = 0; + self.y += 1; + } + + if self.y >= self.h { + let new_y = self.h - 1; + let d_y = self.y - new_y; + + self.display.scroll(d_y * 16); + + self.display.rect( + 0, (self.h - d_y) * 16, + self.w * 8, d_y * 16, + 0x000000 + ); + + self.display.sync( + 0, 0, + self.w * 8, self.h * 16 + ); + + self.y = new_y; + } + + if c != '\n' { + self.display.rect( + self.x * 8, self.y * 16, + 8, 16, + 0x000000 + ); + + self.display.char( + self.x * 8, self.y * 16, + c, + 0xFFFFFF + ); + + self.display.sync( + self.x, self.y, + 8, 16 + ); + + self.x += 1; + } + } +} + +impl fmt::Write for DebugDisplay { + fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { + for c in s.chars() { + self.write(c); + } + + Ok(()) + } +} diff --git a/src/arch/x86_64/graphical_debug/display.rs b/src/arch/x86_64/graphical_debug/display.rs new file mode 100644 index 00000000..b2f79f6f --- /dev/null +++ b/src/arch/x86_64/graphical_debug/display.rs @@ -0,0 +1,150 @@ +use alloc::allocator::{Alloc, Layout}; +use alloc::heap::Heap; +use core::{cmp, slice}; + +use super::FONT; +use super::primitive::{fast_set32, fast_set64, fast_copy}; + +/// A display +pub struct Display { + pub width: usize, + pub height: usize, + pub onscreen: &'static mut [u32], + pub offscreen: &'static mut [u32], +} + +impl Display { + pub fn new(width: usize, height: usize, onscreen: usize) -> Display { + let size = width * height; + let offscreen = unsafe { Heap.alloc(Layout::from_size_align_unchecked(size * 4, 4096)).unwrap() }; + unsafe { fast_set64(offscreen as *mut u64, 0, size/2) }; + Display { + width: width, + height: height, + onscreen: unsafe { slice::from_raw_parts_mut(onscreen as *mut u32, size) }, + offscreen: unsafe { slice::from_raw_parts_mut(offscreen as *mut u32, size) } + } + } + + /// Draw a rectangle + pub fn rect(&mut self, x: usize, y: usize, w: usize, h: usize, color: u32) { + let start_y = cmp::min(self.height, y); + let end_y = cmp::min(self.height, y + h); + + let start_x = cmp::min(self.width, x); + let len = cmp::min(self.width, x + w) - start_x; + + let mut offscreen_ptr = self.offscreen.as_mut_ptr() as usize; + + let stride = self.width * 4; + + let offset = y * stride + start_x * 4; + offscreen_ptr += offset; + + let mut rows = end_y - start_y; + while rows > 0 { + unsafe { + fast_set32(offscreen_ptr as *mut u32, color, len); + } + offscreen_ptr += stride; + rows -= 1; + } + } + + /// Invert a rectangle + pub fn invert(&mut self, x: usize, y: usize, w: usize, h: usize) { + let start_y = cmp::min(self.height, y); + let end_y = cmp::min(self.height, y + h); + + let start_x = cmp::min(self.width, x); + let len = cmp::min(self.width, x + w) - start_x; + + let mut offscreen_ptr = self.offscreen.as_mut_ptr() as usize; + + let stride = self.width * 4; + + let offset = y * stride + start_x * 4; + offscreen_ptr += offset; + + let mut rows = end_y - start_y; + while rows > 0 { + let mut row_ptr = offscreen_ptr; + let mut cols = len; + while cols > 0 { + unsafe { + let color = *(row_ptr as *mut u32); + *(row_ptr as *mut u32) = !color; + } + row_ptr += 4; + cols -= 1; + } + offscreen_ptr += stride; + rows -= 1; + } + } + + /// Draw a character + pub fn char(&mut self, x: usize, y: usize, character: char, color: u32) { + if x + 8 <= self.width && y + 16 <= self.height { + let mut dst = self.offscreen.as_mut_ptr() as usize + (y * self.width + x) * 4; + + let font_i = 16 * (character as usize); + if font_i + 16 <= FONT.len() { + for row in 0..16 { + let row_data = FONT[font_i + row]; + for col in 0..8 { + if (row_data >> (7 - col)) & 1 == 1 { + unsafe { *((dst + col * 4) as *mut u32) = color; } + } + } + dst += self.width * 4; + } + } + } + } + + // Scroll the screen + pub fn scroll(&mut self, lines: usize) { + let offset = cmp::min(self.height, lines) * self.width; + let size = self.offscreen.len() - offset; + unsafe { + let to = self.offscreen.as_mut_ptr(); + let from = to.offset(offset as isize); + fast_copy(to as *mut u8, from as *const u8, size * 4); + } + } + + /// Copy from offscreen to onscreen + pub fn sync(&mut self, x: usize, y: usize, w: usize, h: usize) { + let start_y = cmp::min(self.height, y); + let end_y = cmp::min(self.height, y + h); + + let start_x = cmp::min(self.width, x); + let len = (cmp::min(self.width, x + w) - start_x) * 4; + + let mut offscreen_ptr = self.offscreen.as_mut_ptr() as usize; + let mut onscreen_ptr = self.onscreen.as_mut_ptr() as usize; + + let stride = self.width * 4; + + let offset = y * stride + start_x * 4; + offscreen_ptr += offset; + onscreen_ptr += offset; + + let mut rows = end_y - start_y; + while rows > 0 { + unsafe { + fast_copy(onscreen_ptr as *mut u8, offscreen_ptr as *const u8, len); + } + offscreen_ptr += stride; + onscreen_ptr += stride; + rows -= 1; + } + } +} + +impl Drop for Display { + fn drop(&mut self) { + unsafe { Heap.dealloc(self.offscreen.as_mut_ptr() as *mut u8, Layout::from_size_align_unchecked(self.offscreen.len() * 4, 4096)) }; + } +} diff --git a/src/arch/x86_64/graphical_debug/mod.rs b/src/arch/x86_64/graphical_debug/mod.rs new file mode 100644 index 00000000..a8b2a701 --- /dev/null +++ b/src/arch/x86_64/graphical_debug/mod.rs @@ -0,0 +1,71 @@ +use spin::Mutex; + +use memory::Frame; +use paging::{ActivePageTable, Page, PhysicalAddress, VirtualAddress}; +use paging::entry::EntryFlags; + +pub use self::debug::DebugDisplay; +use self::display::Display; +use self::mode_info::VBEModeInfo; +use self::primitive::fast_set64; + +pub mod debug; +pub mod display; +pub mod mode_info; +pub mod primitive; + +pub static FONT: &'static [u8] = include_bytes!("../../../../res/unifont.font"); + +pub static DEBUG_DISPLAY: Mutex<Option<DebugDisplay>> = Mutex::new(None); + +pub fn init(active_table: &mut ActivePageTable) { + //TODO: Unmap mode_info and map physbaseptr in kernel space + + println!("Starting graphical debug"); + + let width; + let height; + let physbaseptr; + + { + let mode_info_addr = 0x5200; + + { + let page = Page::containing_address(VirtualAddress::new(mode_info_addr)); + let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get())); + let result = active_table.map_to(page, frame, EntryFlags::PRESENT | EntryFlags::NO_EXECUTE); + result.flush(active_table); + } + + let mode_info = unsafe { &*(mode_info_addr as *const VBEModeInfo) }; + + width = mode_info.xresolution as usize; + height = mode_info.yresolution as usize; + physbaseptr = mode_info.physbaseptr as usize; + } + + { + let size = width * height; + + { + let start_page = Page::containing_address(VirtualAddress::new(physbaseptr)); + let end_page = Page::containing_address(VirtualAddress::new(physbaseptr + size * 4)); + for page in Page::range_inclusive(start_page, end_page) { + let frame = Frame::containing_address(PhysicalAddress::new(page.start_address().get())); + let result = active_table.map_to(page, frame, EntryFlags::PRESENT | EntryFlags::NO_EXECUTE | EntryFlags::WRITABLE | EntryFlags::HUGE_PAGE); + result.flush(active_table); + } + } + + unsafe { fast_set64(physbaseptr as *mut u64, 0, size/2) }; + + *DEBUG_DISPLAY.lock() = Some(DebugDisplay::new(Display::new(width, height, physbaseptr))); + } +} + +pub fn fini(_active_table: &mut ActivePageTable) { + //TODO: Unmap physbaseptr + *DEBUG_DISPLAY.lock() = None; + + println!("Finished graphical debug"); +} diff --git a/src/arch/x86_64/graphical_debug/mode_info.rs b/src/arch/x86_64/graphical_debug/mode_info.rs new file mode 100644 index 00000000..7d59af64 --- /dev/null +++ b/src/arch/x86_64/graphical_debug/mode_info.rs @@ -0,0 +1,37 @@ +/// The info of the VBE mode +#[derive(Copy, Clone, Default, Debug)] +#[repr(packed)] +pub struct VBEModeInfo { + attributes: u16, + win_a: u8, + win_b: u8, + granularity: u16, + winsize: u16, + segment_a: u16, + segment_b: u16, + winfuncptr: u32, + bytesperscanline: u16, + pub xresolution: u16, + pub yresolution: u16, + xcharsize: u8, + ycharsize: u8, + numberofplanes: u8, + bitsperpixel: u8, + numberofbanks: u8, + memorymodel: u8, + banksize: u8, + numberofimagepages: u8, + unused: u8, + redmasksize: u8, + redfieldposition: u8, + greenmasksize: u8, + greenfieldposition: u8, + bluemasksize: u8, + bluefieldposition: u8, + rsvdmasksize: u8, + rsvdfieldposition: u8, + directcolormodeinfo: u8, + pub physbaseptr: u32, + offscreenmemoryoffset: u32, + offscreenmemsize: u16, +} diff --git a/src/arch/x86_64/graphical_debug/primitive.rs b/src/arch/x86_64/graphical_debug/primitive.rs new file mode 100644 index 00000000..16c2536a --- /dev/null +++ b/src/arch/x86_64/graphical_debug/primitive.rs @@ -0,0 +1,47 @@ +#[cfg(target_arch = "x86_64")] +#[inline(always)] +#[cold] +pub unsafe fn fast_copy(dst: *mut u8, src: *const u8, len: usize) { + asm!("cld + rep movsb" + : + : "{rdi}"(dst as usize), "{rsi}"(src as usize), "{rcx}"(len) + : "cc", "memory", "rdi", "rsi", "rcx" + : "intel", "volatile"); +} + +#[cfg(target_arch = "x86_64")] +#[inline(always)] +#[cold] +pub unsafe fn fast_copy64(dst: *mut u64, src: *const u64, len: usize) { + asm!("cld + rep movsq" + : + : "{rdi}"(dst as usize), "{rsi}"(src as usize), "{rcx}"(len) + : "cc", "memory", "rdi", "rsi", "rcx" + : "intel", "volatile"); +} + +#[cfg(target_arch = "x86_64")] +#[inline(always)] +#[cold] +pub unsafe fn fast_set32(dst: *mut u32, src: u32, len: usize) { + asm!("cld + rep stosd" + : + : "{rdi}"(dst as usize), "{eax}"(src), "{rcx}"(len) + : "cc", "memory", "rdi", "rcx" + : "intel", "volatile"); +} + +#[cfg(target_arch = "x86_64")] +#[inline(always)] +#[cold] +pub unsafe fn fast_set64(dst: *mut u64, src: u64, len: usize) { + asm!("cld + rep stosq" + : + : "{rdi}"(dst as usize), "{rax}"(src), "{rcx}"(len) + : "cc", "memory", "rdi", "rcx" + : "intel", "volatile"); +} diff --git a/src/arch/x86_64/macros.rs b/src/arch/x86_64/macros.rs index 1772c3d8..d41ee933 100644 --- a/src/arch/x86_64/macros.rs +++ b/src/arch/x86_64/macros.rs @@ -3,7 +3,7 @@ macro_rules! print { ($($arg:tt)*) => ({ use core::fmt::Write; - let _ = write!($crate::arch::device::serial::COM1.lock(), $($arg)*); + let _ = write!($crate::arch::debug::Writer::new(), $($arg)*); }); } diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index ffa90a96..b7857d29 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -1,12 +1,19 @@ #[macro_use] pub mod macros; +/// Debugging support +pub mod debug; + /// Devices pub mod device; /// Global descriptor table pub mod gdt; +/// Graphical debug +#[cfg(feature = "graphical_debug")] +mod graphical_debug; + /// Interrupt descriptor table pub mod idt; diff --git a/src/arch/x86_64/start.rs b/src/arch/x86_64/start.rs index 1d92afc0..afba151a 100644 --- a/src/arch/x86_64/start.rs +++ b/src/arch/x86_64/start.rs @@ -9,6 +9,8 @@ use core::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, AtomicUsize, ATOMIC_USIZE use allocator; #[cfg(feature = "acpi")] use acpi; +#[cfg(feature = "graphical_debug")] +use arch::x86_64::graphical_debug; use arch::x86_64::pti; use device; use gdt; @@ -100,6 +102,10 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! { // Setup kernel heap allocator::init(&mut active_table); + // Use graphical debug + #[cfg(feature="graphical_debug")] + graphical_debug::init(&mut active_table); + // Initialize devices device::init(&mut active_table); @@ -113,6 +119,10 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! { // Initialize memory functions after core has loaded memory::init_noncore(); + // Stop graphical debug + #[cfg(feature="graphical_debug")] + graphical_debug::fini(&mut active_table); + BSP_READY.store(true, Ordering::SeqCst); slice::from_raw_parts(env_base as *const u8, env_size) -- GitLab