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
parent
905fb53985
commit
4acc215cf4
|
@ -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",
|
||||
|
|
|
@ -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
2
run.sh
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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.
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
26
src/main.rs
26
src/main.rs
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue