Hacked together support for PC Screen Fonts and the UEFI GOP FB.

* Added a Rust allocator using UEFI.
* Fixed the logger macros.
* Using the cozette font.
* Other stuff. Idc about listing it honestly.
master
James T. Martin 2020-07-16 01:17:45 -07:00
parent 905fb53985
commit 4acc215cf4
Signed by: james
GPG Key ID: 4B7F3DA9351E577C
11 changed files with 339 additions and 17 deletions

34
Cargo.lock generated
View File

@ -1,5 +1,11 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "autocfg"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
[[package]]
name = "bit_field"
version = "0.9.0"
@ -23,6 +29,7 @@ name = "bootproof"
version = "0.1.0"
dependencies = [
"compiler_builtins",
"num-integer",
"uefi",
"x86_64",
]
@ -40,13 +47,32 @@ source = "git+https://github.com/rust-lang/compiler-builtins#f3846bc05da87b8a71c
[[package]]
name = "log"
version = "0.4.8"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
dependencies = [
"cfg-if",
]
[[package]]
name = "num-integer"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
dependencies = [
"autocfg",
]
[[package]]
name = "proc-macro2"
version = "1.0.18"
@ -87,9 +113,9 @@ dependencies = [
[[package]]
name = "uefi"
version = "0.4.6"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab1f1403ecbad37d25120161acc3db12066febf3446efcc40b7631d30678505d"
checksum = "08fcd8a8b1c2488ea5adb67f3840584557a7089150c6f209148567397f767e12"
dependencies = [
"bitflags",
"log",

View File

@ -7,5 +7,9 @@ license = "GPL-3.0+"
[dependencies]
compiler_builtins = { git = "https://github.com/rust-lang/compiler-builtins" }
uefi = "0.4.6"
uefi = "0.4.7"
x86_64 = "0.11.1"
[dependencies.num-integer]
version = "0.1.36"
default-features = false

2
run.sh
View File

@ -2,4 +2,4 @@
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 -machine "q35,accel=kvm:tcg" -smp 8 -m 128M -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" -serial stdio -display none
qemu-system-x86_64 -nodefaults -cpu host -smp 8 -m 512M -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

43
src/allocator.rs Normal file
View File

@ -0,0 +1,43 @@
use alloc::alloc::GlobalAlloc;
use core::alloc::Layout;
use uefi::table::boot::{AllocateType, MemoryDescriptor, MemoryType};
pub enum Allocator {
None,
Uefi(uefi::prelude::SystemTable<uefi::prelude::Boot>)
}
unsafe impl GlobalAlloc for Allocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
match self {
Allocator::Uefi(st) => {
crate::log!("Allocate {:?}", layout);
st.boot_services().allocate_pages(AllocateType::AnyPages, MemoryType::LOADER_DATA, layout.size())
.expect("Failed to allocate memory!")
.expect("Failed to allocate memory! 2")
as *mut u8
},
Allocator::None => panic!("No allocator available!")
}
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
match self {
Allocator::Uefi(st) => {
crate::log!("Free {:?}", layout);
st.boot_services().free_pages(ptr as u64, layout.size());
},
Allocator::None => {
panic!("No allocator available!");
}
}
}
}
#[global_allocator]
pub static mut ALLOCATOR: Allocator = Allocator::None;
#[alloc_error_handler]
fn handle_error(layout: Layout) -> ! {
panic!("Failed to allocate {:?}", layout);
}

85
src/graphics.rs Normal file
View File

@ -0,0 +1,85 @@
mod font;
use crate::println;
use uefi::proto::console::gop::*;
fn px_index(stride: usize, x: usize, y: usize) -> usize {
4 * (y * stride + x)
}
fn make_px(r: u8, g: u8, b: u8) -> [u8; 4] {
[b, g, r, 0]
}
unsafe fn draw_char(fb: &mut FrameBuffer, stride: usize, cx: usize, cy: usize, c: char) {
let font = font::font();
let glyph = font.lookup(c).expect("Character missing from font.");
let color = make_px(255, 255, 255);
for dx in 0..num_integer::div_ceil(glyph.width(), 8) * 8 {
for dy in 0..glyph.height() {
if glyph.get(dx, dy) {
let scale = 2;
for sdx in 0..scale {
for sdy in 0..scale {
let px = cx + scale * dx as usize + sdx;
let py = cy + scale * dy as usize + sdy;
fb.write_value(px_index(stride, px, py), color);
}
}
}
}
}
}
unsafe fn draw_str(fb: &mut FrameBuffer, stride: usize, mut cx: usize, cy: usize, text: &str) {
let width = font::font().width as usize * 2;
for c in text.chars() {
draw_char(fb, stride, cx, cy, c);
cx += width;
}
}
fn draw(fb: &mut FrameBuffer, stride: usize, width: usize, height: usize) {
for x in 0..width {
for y in 0..height {
let i = px_index(stride, x, y);
let r = (x * 256 / width) as u8;
let g = (y * 256 / height) as u8;
let b = 255 - ((r as u16 + g as u16) / 2) as u8;
let px = make_px(r, g, b);
unsafe {
fb.write_value(i, px);
}
}
}
let c_width = width / 8;
let c_height = height / 16;
unsafe {
draw_str(fb, stride, 8, 8, "✔ Hello, world! ♡");
}
}
pub fn do_graphics(st: &uefi::prelude::SystemTable<uefi::prelude::Boot>) {
let gop = st.boot_services().locate_protocol::<GraphicsOutput>()
.unwrap()
.expect("UEFI Graphics Output Protocol (GOP) is not present.");
let mut gop = unsafe { &mut *gop.get() };
let mut mode = None;
for gop_mode in gop.modes() {
let gop_mode = gop_mode.expect("Warning while accessing GOP mode.");
if let PixelFormat::BGR = gop_mode.info().pixel_format() {
mode = Some(gop_mode);
} else {
println!("Ignoring non-BGR pixel format.");
}
}
let mode = mode.expect("No usable pixel formats found.");
let (width, height) = mode.info().resolution();
let stride = mode.info().stride();
println!("Using mode: {}x{} {:?}", width, height, mode.info().pixel_format());
gop.set_mode(&mode).unwrap().expect("Failed to set UEFI Graphics Output mode.");
let mut fb = gop.frame_buffer();
draw(&mut fb, stride, width, height);
}

79
src/graphics/font.rs Normal file
View File

@ -0,0 +1,79 @@
use alloc::sync::Arc;
use alloc::vec::Vec;
use psf::*;
mod psf;
static mut FONT: Option<Arc<PSF>> = None;
pub fn font() -> Arc<PSF> {
unsafe {
FONT.clone().unwrap_or_else(|| {
let font = Arc::new(parse_font());
FONT = Some(font.clone());
font
})
}
}
fn pad(slice: &[u8]) -> [u8; 4] {
if slice.len() == 1 {
[slice[0], 0, 0, 0]
} else if slice.len() == 2 {
[slice[0], slice[1], 0, 0]
} else if slice.len() == 3 {
[slice[0], slice[1], slice[2], 0]
} else if slice.len() == 4 {
[slice[0], slice[1], slice[2], slice[3]]
} else {
crate::panic!("Bad character length {}", slice.len())
}
}
fn parse_font() -> PSF {
use core::convert::TryInto;
let font = core::include_bytes!("font/cozette.psf");
let length = u32::from_le_bytes(font[16..20].try_into().unwrap());
let charsize = u32::from_le_bytes(font[20..24].try_into().unwrap());
let height = u32::from_le_bytes(font[24..28].try_into().unwrap());
let width = u32::from_le_bytes(font[28..32].try_into().unwrap());
crate::log!("{} {} {}", width, height, charsize);
let glyphs_size = (length * charsize) as usize;
let mut glyphs = Vec::with_capacity(glyphs_size);
glyphs.extend_from_slice(&font[32..glyphs_size + 32]);
let mut unicode_map = Vec::new();
let mut unicode_info = &font[glyphs_size + 32..];
let mut glyph = 0;
let mut i = 0;
while i < unicode_info.len() {
let mut nc = unicode_info[i];
while nc != 0xFE && nc != 0xFF {
let ch_bytes = nc.leading_ones().max(1) as usize;
let st = core::str::from_utf8(&unicode_info[i..i + ch_bytes as usize]).expect("Invalid character");
let ch = st.chars().next().unwrap();
unicode_map.push(UnicodeMap { c: ch, i: glyph });
i += ch_bytes;
nc = unicode_info[i];
}
// Ignore multi-codepoint spellings of characters (for now).
while nc != 0xFF {
i += 1;
nc = unicode_info[i];
}
i += 1;
glyph += 1;
}
PSF {
width: width,
height: height,
length: length,
charsize: charsize,
glyphs: glyphs,
unicode: unicode_map,
}
}

View File

@ -0,0 +1 @@
Cozette is an MIT-licensed font by Slavfox. Its source is available here: https://github.com/slavfox/Cozette

Binary file not shown.

60
src/graphics/font/psf.rs Normal file
View File

@ -0,0 +1,60 @@
use alloc::vec::Vec;
pub struct UnicodeMap {
pub c: char,
pub i: usize,
}
pub struct PSF {
pub width: u32,
pub height: u32,
pub length: u32,
pub charsize: u32,
pub glyphs: Vec<u8>,
pub unicode: Vec<UnicodeMap>,
}
pub struct PSFGlyph<'a> {
width: u32,
height: u32,
bitmap: &'a [u8],
}
impl PSF {
fn index_of(&self, c: char) -> Option<usize> {
for entry in &self.unicode {
if entry.c == c {
return Some(entry.i);
}
}
None
}
fn get_bitmap<'a>(&'a self, index: usize) -> &'a [u8] {
let byte_index = self.charsize as usize * index;
&self.glyphs[byte_index..byte_index + self.charsize as usize]
}
pub fn lookup<'a>(&'a self, c: char) -> Option<PSFGlyph<'a>> {
self.index_of(c).map(|i| PSFGlyph {
width: self.width,
height: self.height,
bitmap: self.get_bitmap(i)
})
}
}
impl PSFGlyph<'_> {
pub fn width(&self) -> u32 { self.width }
pub fn height(&self) -> u32 { self.height }
pub fn get(&self, x: u32, y: u32) -> bool {
let line_size = num_integer::div_ceil(self.width, 8);
let char_size = line_size * self.height;
let (line_byte_index, bit_index) = num_integer::div_rem(x, 8);
let mask = 0b10000000 >> bit_index;
let byte = self.bitmap[(y * line_size + line_byte_index) as usize];
byte & mask > 0
}
}

View File

@ -1,5 +1,3 @@
use crate::misc::halt;
use core::fmt::Write;
pub enum LoggerBackend {
@ -45,7 +43,7 @@ impl Write for Logger<'_> {
};
output.write_str(s)
},
_ => {
LoggerBackend::None => {
// There's pretty much no way to recover from a missing logger.
// What are we supposed to do-- log the error?
Ok(())
@ -61,7 +59,7 @@ macro_rules! log {
($( $arg:expr ),* ) => {
unsafe {
use core::fmt::Write;
core::writeln!(LOGGER_BACKEND.stderr(), $( $arg ),*).unwrap();
core::writeln!(crate::logger::LOGGER_BACKEND.stderr(), $( $arg ),*).unwrap();
}
}
}
@ -71,7 +69,7 @@ macro_rules! print {
($( $arg:expr ),* ) => {
unsafe {
use core::fmt::Write;
core::write!(LOGGER_BACKEND.stdout(), $( $arg ),*).unwrap();
core::write!(crate::logger::LOGGER_BACKEND.stdout(), $( $arg ),*).unwrap();
}
}
}
@ -81,7 +79,7 @@ macro_rules! println {
($( $arg:expr ),* ) => {
unsafe {
use core::fmt::Write;
core::writeln!(LOGGER_BACKEND.stdout(), $( $arg ),*).unwrap();
core::writeln!(crate::logger::LOGGER_BACKEND.stdout(), $( $arg ),*).unwrap();
}
}
}
@ -89,13 +87,15 @@ macro_rules! println {
#[macro_export]
macro_rules! panic {
($( $arg:expr ),* ) => {
log!($( $arg ),*);
halt();
{
use crate::misc::halt;
crate::log!($( $arg ),*);
halt()
}
}
}
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
log!("{}", info);
halt();
panic!("{}", info);
}

View File

@ -1,11 +1,17 @@
#![no_std]
#![no_main]
#![feature(abi_efiapi)]
#![feature(alloc)]
#![feature(alloc_error_handler)]
#![feature(asm)]
extern crate alloc;
mod allocator;
mod graphics;
mod logger;
mod misc;
use crate::allocator::{Allocator, ALLOCATOR};
use crate::logger::{LoggerBackend, LOGGER_BACKEND};
use crate::misc::halt;
@ -18,10 +24,26 @@ fn setup(st: &SystemTable<Boot>, _handle: Handle) {
st.stdout().reset(false).expect_success("Failed to reset UEFI stdout.");
println!("Booting...");
for entry in st.config_table() {
use uefi::table::cfg::*;
if entry.guid == ACPI2_GUID {
print!("ACPI2");
} else if entry.guid == SMBIOS_GUID {
print!("SMBIOS");
} else if entry.guid == SMBIOS3_GUID {
print!("SMBIOS3");
} else {
print!("{}", entry.guid);
}
println!(": 0x{:016X}", entry.address as u64);
}
graphics::do_graphics(st);
}
fn main(_st: SystemTable<uefi::table::Runtime>, _mmap: uefi::table::boot::MemoryMapIter) -> ! {
halt();
halt()
}
#[entry]
@ -30,6 +52,7 @@ fn efi_main(handle: Handle, st_boot: SystemTable<Boot>) -> Status {
unsafe {
LOGGER_BACKEND = LoggerBackend::UefiStdio(st_boot.unsafe_clone());
ALLOCATOR = Allocator::Uefi(st_boot.unsafe_clone());
}
setup(&st_boot, handle);
@ -55,6 +78,7 @@ fn efi_main(handle: Handle, st_boot: SystemTable<Boot>) -> Status {
// I do not currently have an adequate stdout for post-UEFI, but the UEFI one is now invalid.
unsafe {
LOGGER_BACKEND = LoggerBackend::None;
ALLOCATOR = Allocator::None;
}
main(st_runtime, mmap);