diff --git a/Cargo.toml b/Cargo.toml index e7e116a4b689200bbf6afec9ab449b06c733be45..82e2d60f907351c6af1d597e12beee71218b90e3 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 Binary files /dev/null and b/res/unifont.font differ diff --git a/src/arch/x86_64/debug.rs b/src/arch/x86_64/debug.rs new file mode 100644 index 0000000000000000000000000000000000000000..198ca29aad7d58df885b4a624b0a720e84a89d54 --- /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 0000000000000000000000000000000000000000..338b9a1e188d4ead7a9cf568e5c299dc5bfb57e5 --- /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 0000000000000000000000000000000000000000..b2f79f6fb539a15a505b26e4b6ed1ce690e7d9fb --- /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 0000000000000000000000000000000000000000..a8b2a7014ea7ebe17d47806ddae7571a54b7cfc1 --- /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 0000000000000000000000000000000000000000..7d59af64526523ac3d76d6f2c016473a13d353a0 --- /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 0000000000000000000000000000000000000000..16c2536a24accfc3f3acd7c24652e2f71be27b3f --- /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 1772c3d8e622c89cb6af1dd896e553f9aba2af30..d41ee9338b0c9d0395704274b926f6c0dc792c94 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 ffa90a968d2940e24272c0a34008ad02f9e96099..b7857d29f203f714667a96d434dadb1b39ea02fc 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 1d92afc0bdcfce2f72d5ec1faf5c6ad6a2346df6..afba151a49521a4b4cdced6a749f8d8b2149254a 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)