Add support for graphical debug, to be used during ACPI phase

This commit is contained in:
Jeremy Soller 2018-03-11 11:36:58 -06:00
parent 133c433f60
commit 819f77daf3
11 changed files with 448 additions and 1 deletions

View File

@ -30,6 +30,7 @@ default-features = false
default = ["acpi"]
acpi = []
doc = []
graphical_debug = []
live = []
multi_core = []
pti = []

BIN
res/unifont.font Normal file

Binary file not shown.

41
src/arch/x86_64/debug.rs Normal file
View File

@ -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)
}
}
}

View File

@ -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(())
}
}

View File

@ -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)) };
}
}

View File

@ -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");
}

View File

@ -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,
}

View File

@ -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");
}

View File

@ -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)*);
});
}

View File

@ -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;

View File

@ -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)