Massive reorganization. Most files moved, lots of stuff renamed.
parent
ae7e7d3a80
commit
bc850fecd6
7
run.sh
7
run.sh
|
@ -2,4 +2,9 @@
|
|||
profile=${1:-"debug"}
|
||||
mkdir -p drive/EFI/Boot
|
||||
cp "target/x86_64-unknown-uefi/$profile/bootproof.efi" drive/EFI/Boot/BootX64.efi
|
||||
qemu-system-x86_64 -nodefaults -cpu host -smp 8 -m 1G -machine "q35,accel=kvm:tcg" -drive "if=pflash,format=raw,file=/usr/share/OVMF/OVMF_CODE.fd,readonly=on" -drive "if=pflash,format=raw,file=/usr/share/OVMF/OVMF_VARS.fd,readonly=on" -drive "format=raw,file=fat:rw:drive" -display gtk,gl=on -vga virtio -serial stdio
|
||||
qemu-system-x86_64 \
|
||||
-drive "if=pflash,format=raw,file=/usr/share/OVMF/OVMF_CODE.fd,readonly=on" \
|
||||
-drive "if=pflash,format=raw,file=/usr/share/OVMF/OVMF_VARS.fd,readonly=on" \
|
||||
-drive "format=raw,file=fat:rw:drive" \
|
||||
-nodefaults -cpu host -smp 8 -m 128M -machine "q35,accel=kvm:tcg" \
|
||||
-display gtk,gl=on -vga virtio -serial stdio
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
use crate::graphics::color::RGB;
|
||||
use crate::graphics::font::Glyph;
|
||||
|
||||
pub trait Display {
|
||||
pub trait GraphicDisplay {
|
||||
fn resolution(&self) -> (usize, usize);
|
||||
fn width(&self) -> usize { self.resolution().0 }
|
||||
fn height(&self) -> usize { self.resolution().1 }
|
||||
|
||||
// HACK: This interface sucks.
|
||||
// These interfaces don't support any color (e.g. `impl Color`)
|
||||
// because then I can't use this as a `dyn` trait because of the generic.
|
||||
// I *could* use `dyn Color`, but... why would you do that??
|
||||
/// Unsafe: it is the responsibility of the caller to ensure
|
||||
/// that the pixel is within the boundaries of the screen.
|
||||
unsafe fn set_pixel(&mut self, color: RGB, x: usize, y: usize);
|
||||
fn set_pixel_ignore_oob(&mut self, color: RGB, x: usize, y: usize) {
|
||||
if x > self.width() || y > self.height() {
|
||||
|
@ -18,8 +23,14 @@ pub trait Display {
|
|||
}
|
||||
}
|
||||
|
||||
/// Set the entire display to the same color, clearing everything previously drawn.
|
||||
fn clear(&mut self, color: RGB);
|
||||
|
||||
/// Display everything that was drawn to the screen.
|
||||
fn refresh(&mut self);
|
||||
|
||||
/// Unsafe: it is the responsibility of the caller to ensure
|
||||
/// that the entire glyph fits within the boundaries of the screen.
|
||||
unsafe fn draw_glyph(&mut self, bounding_box: (usize, usize), x: usize, y: usize, color: RGB, glyph: &dyn Glyph) {
|
||||
// We only assume that space was left for pixels within the bounding box,
|
||||
// and that pixels outside the bounding box may be out-of-bounds.
|
|
@ -0,0 +1,3 @@
|
|||
pub mod graphic_display;
|
||||
pub mod text_display;
|
||||
pub mod tty;
|
|
@ -0,0 +1,66 @@
|
|||
pub mod graphic;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
|
||||
/// A text-mode display. Basically, an array of characters that you can set in any order.
|
||||
pub trait TextDisplay {
|
||||
fn borrow_frame<'a>(&'a self) -> &'a TextDisplayFrame;
|
||||
fn borrow_mut_frame<'a>(&'a mut self) -> &'a mut TextDisplayFrame;
|
||||
/// Display all changes made to the frame.
|
||||
fn refresh(&mut self);
|
||||
}
|
||||
|
||||
/// A frame of a text display; basically a 2d array of characters which you can set how you please.
|
||||
/// However, this frame doesn't know anything about how to display itself;
|
||||
/// that's what the TextDisplay trait is for.
|
||||
pub struct TextDisplayFrame {
|
||||
resolution: (usize, usize),
|
||||
buf: Box<[char]>,
|
||||
}
|
||||
|
||||
impl TextDisplayFrame {
|
||||
pub fn new(resolution: (usize, usize)) -> TextDisplayFrame {
|
||||
use alloc::vec::Vec;
|
||||
|
||||
let (width, height) = resolution;
|
||||
let mut buf = Vec::new();
|
||||
buf.resize(width * height, '\u{0}');
|
||||
|
||||
TextDisplayFrame {
|
||||
resolution: resolution,
|
||||
buf: buf.into_boxed_slice()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolution(&self) -> (usize, usize) {
|
||||
self.resolution
|
||||
}
|
||||
|
||||
pub fn width(&self) -> usize {
|
||||
self.resolution.0
|
||||
}
|
||||
|
||||
pub fn height(&self) -> usize {
|
||||
self.resolution.1
|
||||
}
|
||||
|
||||
/// Set all characters in this frame to null.
|
||||
pub fn clear(&mut self) {
|
||||
for i in 0..self.buf.len() {
|
||||
self.buf[i] = '\u{0}';
|
||||
}
|
||||
}
|
||||
|
||||
fn index(&self, x: usize, y: usize) -> usize {
|
||||
self.width() * y + x
|
||||
}
|
||||
|
||||
pub fn get(&self, x: usize, y: usize) -> char {
|
||||
self.buf[self.index(x, y)]
|
||||
}
|
||||
|
||||
pub fn set(&mut self, x: usize, y: usize, c: char) {
|
||||
let i = self.index(x, y);
|
||||
self.buf[i] = c;
|
||||
}
|
||||
}
|
|
@ -1,43 +1,43 @@
|
|||
use crate::driver::graphic_display::GraphicDisplay;
|
||||
use crate::driver::text_display::{TextDisplayFrame, TextDisplay};
|
||||
use crate::graphics::color::{Color, RGB};
|
||||
use crate::graphics::display::Display;
|
||||
use crate::graphics::font::{Font, Glyph};
|
||||
use crate::graphics::terminal::Terminal;
|
||||
use crate::graphics::terminal::frame::TerminalFrame;
|
||||
|
||||
pub struct DisplayTerminal<'d, 'f, G: Glyph> {
|
||||
display: &'d mut (dyn Display + 'd),
|
||||
/// A virtual text display that renders itself onto a graphic display.
|
||||
pub struct GraphicTextDisplay<'d, 'f, G: Glyph> {
|
||||
display: &'d mut (dyn GraphicDisplay + 'd),
|
||||
font: &'f (dyn Font<Glyph = G> + 'f),
|
||||
frame: TerminalFrame,
|
||||
frame: TextDisplayFrame,
|
||||
bg: RGB,
|
||||
fg: RGB,
|
||||
}
|
||||
|
||||
impl<G: Glyph> DisplayTerminal<'_, '_, G> {
|
||||
impl<G: Glyph> GraphicTextDisplay<'_, '_, G> {
|
||||
pub fn new<'d, 'f>
|
||||
(display: &'d mut (dyn Display + 'd), font: &'f (dyn Font<Glyph = G> + 'f),
|
||||
(display: &'d mut (dyn GraphicDisplay + 'd), font: &'f (dyn Font<Glyph = G> + 'f),
|
||||
bg: impl Color, fg: impl Color)
|
||||
-> DisplayTerminal<'d, 'f, G> {
|
||||
-> GraphicTextDisplay<'d, 'f, G> {
|
||||
let (dp_width, dp_height) = display.resolution();
|
||||
let (ft_width, ft_height) = font.bounding_box();
|
||||
let ch_width = dp_width / ft_width as usize;
|
||||
let ch_height = dp_height / ft_height as usize;
|
||||
|
||||
DisplayTerminal {
|
||||
GraphicTextDisplay {
|
||||
display: display,
|
||||
font: font,
|
||||
frame: TerminalFrame::new((ch_width, ch_height)),
|
||||
frame: TextDisplayFrame::new((ch_width, ch_height)),
|
||||
bg: bg.into_rgb(),
|
||||
fg: fg.into_rgb(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: Glyph> Terminal for DisplayTerminal<'_, '_, G> {
|
||||
fn get_frame<'a>(&'a self) -> &'a TerminalFrame {
|
||||
impl<G: Glyph> TextDisplay for GraphicTextDisplay<'_, '_, G> {
|
||||
fn borrow_frame<'a>(&'a self) -> &'a TextDisplayFrame {
|
||||
&self.frame
|
||||
}
|
||||
|
||||
fn borrow_frame<'a>(&'a mut self) -> &'a mut TerminalFrame {
|
||||
fn borrow_mut_frame<'a>(&'a mut self) -> &'a mut TextDisplayFrame {
|
||||
&mut self.frame
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
pub mod serial;
|
||||
pub mod text_display;
|
||||
|
||||
/// A teletypewriter, or really, because those don't exist anymore,
|
||||
/// a device that behaves like or emulates a teletypewriter.
|
||||
/// Basically, this is a device that lets you output text and not much else.
|
||||
/// Its output may be buffered, so make sure you `flush` the output.
|
||||
pub trait Tty {
|
||||
/// Print a single character to the TTY.
|
||||
fn putc(&mut self, c: char);
|
||||
/// Print an entire string to the TTY.
|
||||
fn puts(&mut self, s: &str);
|
||||
/// Clear all TTY output.
|
||||
fn clear(&mut self);
|
||||
/// Synchronously flush any buffered output.
|
||||
fn flush(&mut self);
|
||||
}
|
|
@ -1,17 +1,30 @@
|
|||
use crate::graphics::tty::Tty;
|
||||
use crate::driver::tty::Tty;
|
||||
|
||||
#[derive(Clone)]
|
||||
/// A TTY attached via a serial port.
|
||||
///
|
||||
/// Serial ports don't commonly exist on physical devices anymore,
|
||||
/// but many emulators support them and can map them to the host's TTY/terminal emulator,
|
||||
/// which makes them useful for debugging in a VM.
|
||||
pub struct SerialTty {
|
||||
port: u16,
|
||||
}
|
||||
|
||||
/// The port used by COM1 on x86 devices.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub const COM1_PORT: u16 = 0x3F8;
|
||||
|
||||
impl SerialTty {
|
||||
/// Creates a new serial TTY which will use the provided port for output.
|
||||
///
|
||||
/// Unsafe because it is up to the caller to make sure
|
||||
/// that the port is actually the port for a TTY device.
|
||||
pub unsafe fn new(port: u16) -> SerialTty {
|
||||
SerialTty {
|
||||
port: port,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn outb(&self, cmd: u8) {
|
||||
unsafe {
|
||||
asm!("out dx, al", in("dx") self.port, in("al") cmd);
|
||||
|
@ -44,6 +57,6 @@ impl Tty for SerialTty {
|
|||
}
|
||||
|
||||
fn flush(&mut self) {
|
||||
// This TTY doesn't support buffering.
|
||||
// This TTY doesn't use buffering.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
use alloc::string::{String, ToString};
|
||||
use alloc::vec::Vec;
|
||||
use crate::driver::text_display::TextDisplay;
|
||||
use crate::driver::tty::Tty;
|
||||
|
||||
/// A buffered virtual TTY implemented over a textual display.
|
||||
pub struct TextDisplayTty<'display> {
|
||||
term: &'display mut (dyn TextDisplay + 'display),
|
||||
history: Vec<String>,
|
||||
}
|
||||
|
||||
impl TextDisplayTty<'_> {
|
||||
pub fn new<'a>(term: &'a mut dyn TextDisplay) -> TextDisplayTty<'a> {
|
||||
TextDisplayTty {
|
||||
term: term,
|
||||
history: {
|
||||
let mut vec = Vec::new();
|
||||
vec.push("".to_string());
|
||||
vec
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tty for TextDisplayTty<'_> {
|
||||
fn putc(&mut self, c: char) {
|
||||
if c == '\n' {
|
||||
self.history.push("".to_string());
|
||||
return;
|
||||
}
|
||||
let i = self.history.len() - 1;
|
||||
self.history[i].push(c);
|
||||
}
|
||||
|
||||
fn puts(&mut self, s: &str) {
|
||||
for c in s.chars() {
|
||||
self.putc(c);
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.history.clear();
|
||||
self.history.push("".to_string());
|
||||
}
|
||||
|
||||
fn flush(&mut self) {
|
||||
// Each line of the history represents a virtual line of output.
|
||||
// However, a line of output may be longer than the physical width of the display,
|
||||
// in which case we may need to wrap the line so that it takes up two physical lines.
|
||||
let mut physical_lines = Vec::new();
|
||||
for line in &self.history {
|
||||
let mut chars = line.chars().collect::<Vec<_>>().into_iter();
|
||||
// We iterate over all of the characters in a virtual line
|
||||
// until every character has been added to a physical line.
|
||||
while chars.len() > 0 {
|
||||
let mut physical_line = String::new();
|
||||
// The width of a physical line may be no more than the width of the frame.
|
||||
let width = chars.len().min(self.term.borrow_frame().width());
|
||||
for _ in 0..width {
|
||||
physical_line.push(chars.next().unwrap());
|
||||
}
|
||||
physical_lines.push(physical_line);
|
||||
}
|
||||
}
|
||||
|
||||
// This is how many lines on the display we'll need for all of our physical lines.
|
||||
// We cannot have more lines than allowed by the display.
|
||||
let mut y = physical_lines.len().min(self.term.borrow_frame().height() - 1);
|
||||
let frame = self.term.borrow_mut_frame();
|
||||
// We start from the lowest line and display each line until we reach the top of the screen.
|
||||
// We cannot run out of physical lines because the lowest line
|
||||
// is at lowest the number of physical lines necessary to display all lines.
|
||||
for line in physical_lines.into_iter().rev() {
|
||||
let mut x = 0;
|
||||
for c in line.chars() {
|
||||
frame.set(x, y, c);
|
||||
x += 1;
|
||||
}
|
||||
|
||||
if y == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
y -= 1;
|
||||
}
|
||||
|
||||
self.term.refresh();
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
pub mod color;
|
||||
pub mod display;
|
||||
pub mod font;
|
||||
pub mod terminal;
|
||||
pub mod tty;
|
|
@ -0,0 +1,2 @@
|
|||
pub mod color;
|
||||
pub mod font;
|
|
@ -1,10 +0,0 @@
|
|||
pub mod display;
|
||||
pub mod frame;
|
||||
|
||||
use crate::graphics::terminal::frame::TerminalFrame;
|
||||
|
||||
pub trait Terminal {
|
||||
fn get_frame<'a>(&'a self) -> &'a TerminalFrame;
|
||||
fn borrow_frame<'a>(&'a mut self) -> &'a mut TerminalFrame;
|
||||
fn refresh(&mut self);
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
use alloc::vec::Vec;
|
||||
|
||||
pub struct TerminalFrame {
|
||||
resolution: (usize, usize),
|
||||
buf: Vec<char>,
|
||||
}
|
||||
|
||||
impl TerminalFrame {
|
||||
pub fn new(resolution: (usize, usize)) -> TerminalFrame {
|
||||
let (width, height) = resolution;
|
||||
let buf_length = width * height;
|
||||
let mut buf = Vec::with_capacity(buf_length);
|
||||
for _ in 0..buf_length {
|
||||
buf.push('\u{0}');
|
||||
}
|
||||
|
||||
TerminalFrame {
|
||||
resolution: resolution,
|
||||
buf: buf
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolution(&self) -> (usize, usize) {
|
||||
self.resolution
|
||||
}
|
||||
|
||||
pub fn width(&self) -> usize {
|
||||
self.resolution.0
|
||||
}
|
||||
|
||||
pub fn height(&self) -> usize {
|
||||
self.resolution.1
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
for i in 0..self.buf.len() {
|
||||
self.buf[i] = '\u{0}';
|
||||
}
|
||||
}
|
||||
|
||||
fn index(&self, x: usize, y: usize) -> usize {
|
||||
self.width() * y + x
|
||||
}
|
||||
|
||||
pub fn get(&self, x: usize, y: usize) -> char {
|
||||
self.buf[self.index(x, y)]
|
||||
}
|
||||
|
||||
pub fn set(&mut self, x: usize, y: usize, c: char) {
|
||||
let i = self.index(x, y);
|
||||
self.buf[i] = c;
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
pub mod serial;
|
||||
pub mod terminal;
|
||||
|
||||
pub trait Tty {
|
||||
fn putc(&mut self, c: char);
|
||||
fn puts(&mut self, s: &str);
|
||||
fn clear(&mut self);
|
||||
fn flush(&mut self);
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
use alloc::string::{String, ToString};
|
||||
use alloc::vec::Vec;
|
||||
use crate::graphics::terminal::Terminal;
|
||||
use crate::graphics::tty::Tty;
|
||||
|
||||
pub struct TerminalTty<'terminal> {
|
||||
term: &'terminal mut dyn Terminal,
|
||||
history: Vec<String>,
|
||||
}
|
||||
|
||||
impl TerminalTty<'_> {
|
||||
pub fn new<'a>(term: &'a mut dyn Terminal) -> TerminalTty<'a> {
|
||||
TerminalTty {
|
||||
term: term,
|
||||
history: {
|
||||
let mut vec = Vec::new();
|
||||
vec.push("".to_string());
|
||||
vec
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tty for TerminalTty<'_> {
|
||||
fn putc(&mut self, c: char) {
|
||||
if c == '\n' {
|
||||
self.history.push("".to_string());
|
||||
return;
|
||||
}
|
||||
let i = self.history.len() - 1;
|
||||
self.history[i].push(c);
|
||||
}
|
||||
|
||||
fn puts(&mut self, s: &str) {
|
||||
for c in s.chars() {
|
||||
self.putc(c);
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.history.clear();
|
||||
self.history.push("".to_string());
|
||||
}
|
||||
|
||||
fn flush(&mut self) {
|
||||
let mut physical_lines = Vec::new();
|
||||
for line in &self.history {
|
||||
let mut chars = line.chars().collect::<Vec<_>>().into_iter();
|
||||
while chars.len() > 0 {
|
||||
let mut physical_line = String::new();
|
||||
let width = chars.len().min(self.term.get_frame().width());
|
||||
for _ in 0..width {
|
||||
physical_line.push(chars.next().unwrap());
|
||||
}
|
||||
physical_lines.push(physical_line);
|
||||
}
|
||||
}
|
||||
|
||||
let mut y = physical_lines.len().min(self.term.get_frame().height() - 1);
|
||||
let frame = self.term.borrow_frame();
|
||||
for line in physical_lines.into_iter().rev() {
|
||||
let mut x = 0;
|
||||
for c in line.chars() {
|
||||
frame.set(x, y, c);
|
||||
x += 1;
|
||||
}
|
||||
|
||||
if y == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
y -= 1;
|
||||
}
|
||||
|
||||
self.term.refresh();
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
use alloc::format;
|
||||
use core::cell::UnsafeCell;
|
||||
use crate::graphics::tty::Tty;
|
||||
use crate::graphics::tty::serial::SerialTty;
|
||||
use crate::driver::tty::Tty;
|
||||
use crate::driver::tty::serial::SerialTty;
|
||||
use log::{Record, LevelFilter, Metadata, SetLoggerError};
|
||||
|
||||
enum GlobalLogger {
|
||||
|
|
14
src/main.rs
14
src/main.rs
|
@ -7,19 +7,18 @@
|
|||
#![feature(generic_associated_types)]
|
||||
extern crate alloc;
|
||||
|
||||
mod allocator;
|
||||
mod arch;
|
||||
mod driver;
|
||||
mod graphics;
|
||||
|
||||
mod memory;
|
||||
mod logger;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use crate::graphics::tty::serial::SerialTty;
|
||||
use uefi::prelude::*;
|
||||
|
||||
#[entry]
|
||||
fn efi_main(handle: Handle, st_boot: SystemTable<Boot>) -> Status {
|
||||
use crate::allocator::{ALLOCATOR, GlobalAllocator};
|
||||
use crate::memory::allocator::{ALLOCATOR, GlobalAllocator};
|
||||
unsafe {
|
||||
// Generally speaking, we want to depend on UEFI as little as possible,
|
||||
// so the need for a UEFI allocator may seem a bit strange.
|
||||
|
@ -29,14 +28,15 @@ fn efi_main(handle: Handle, st_boot: SystemTable<Boot>) -> Status {
|
|||
// In theory there are probably ways to get around it, but why bother?
|
||||
// Just taking advantage of the UEFI allocator briefly is a lot easier.
|
||||
// (This also lets us use `println!` prior to our main allocator being set up.)
|
||||
use crate::allocator::uefi::UefiAllocator;
|
||||
use crate::memory::allocator::uefi::UefiAllocator;
|
||||
// ABSOLUTELY DO NOT FORGET TO DISABLE THIS AFTER LEAVING UEFI BOOT SERVICES.
|
||||
// ALL ALLOCATIONS MUST BE STATIC OR BE FREED BEFORE BOOT SERVICES EXITS.
|
||||
// If the're not, Rust still try to free UEFI-allocated data using the new allocator,
|
||||
// which is undefined behavior.
|
||||
ALLOCATOR = GlobalAllocator::Uefi(UefiAllocator::new(st_boot.unsafe_clone()));
|
||||
|
||||
logger::set_tty(SerialTty::new(0x3F8));
|
||||
use crate::driver::tty::serial::{COM1_PORT, SerialTty};
|
||||
logger::set_tty(SerialTty::new(COM1_PORT));
|
||||
logger::init().unwrap();
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ fn efi_main(handle: Handle, st_boot: SystemTable<Boot>) -> Status {
|
|||
|
||||
// HACK: I hate having to use the UEFI allocator just to set up another allocator!
|
||||
// There's got to be a better way.
|
||||
use crate::allocator::standard::StandardAllocator;
|
||||
use crate::memory::allocator::standard::StandardAllocator;
|
||||
let mut allocator;
|
||||
{
|
||||
let mut mmap = bs.memory_map(mmap_buf.as_mut_slice())
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
pub mod allocator;
|
Loading…
Reference in New Issue