From 14993ed4974e3be5e8efbda28fc38abab3c01292 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 2 Feb 2022 14:34:56 -0700 Subject: [PATCH] Add allocator and set mode using VBE --- Cargo.lock | 36 ++++++++++++ Cargo.toml | 2 + src/lib.rs | 157 +++++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 183 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aaa8a0ade9..23c113ce70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,42 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "linked_list_allocator" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549ce1740e46b291953c4340adcd74c59bcf4308f4cac050fd33ba91b7168f4a" +dependencies = [ + "spinning_top", +] + +[[package]] +name = "lock_api" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +dependencies = [ + "scopeguard", +] + [[package]] name = "redox_bootloader" version = "0.1.0" +dependencies = [ + "linked_list_allocator", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "spinning_top" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75adad84ee84b521fb2cca2d4fd0f1dab1d8d026bda3c5bea4ca63b5f9f9293c" +dependencies = [ + "lock_api", +] diff --git a/Cargo.toml b/Cargo.toml index e3181840a9..fe997d853e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,6 @@ name = "bootloader" path = "src/lib.rs" crate-type = ["staticlib"] + [dependencies] +linked_list_allocator = "0.9.0" diff --git a/src/lib.rs b/src/lib.rs index 5e298c0b7a..ee25e9c48f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,14 +3,23 @@ #![feature(lang_items)] #![feature(llvm_asm)] +#[macro_use] +extern crate alloc; + +use alloc::vec::Vec; use core::{ + cmp, fmt::{self, Write}, ptr, slice, }; +use linked_list_allocator::LockedHeap; mod panic; +#[global_allocator] +static ALLOCATOR: LockedHeap = LockedHeap::empty(); + #[derive(Clone, Copy)] #[repr(packed)] pub struct ThunkData { @@ -117,6 +126,7 @@ pub struct VgaTextBlock { color: u8, } +#[derive(Clone, Copy)] #[repr(u8)] pub enum VgaTextColor { Black = 0, @@ -143,6 +153,8 @@ pub struct Vga { height: usize, x: usize, y: usize, + bg: VgaTextColor, + fg: VgaTextColor, } impl Vga { @@ -156,6 +168,8 @@ impl Vga { height, x: 0, y: 0, + bg: VgaTextColor::DarkGray, + fg: VgaTextColor::White, } } } @@ -192,6 +206,9 @@ impl fmt::Write for Vga { let i = self.y * self.width + self.x; if let Some(block) = self.blocks.get_mut(i) { block.char = c as u8; + block.color = + ((self.bg as u8) << 4) | + (self.fg as u8); } } } @@ -233,6 +250,15 @@ pub unsafe extern "C" fn kstart( (VgaTextColor::White as u8); } + // Initialize allocator at the end of stage 3 with a meager 1 MiB + extern "C" { + static mut __end: u8; + } + let heap_start = &__end as *const _ as usize; + let heap_size = 1024 * 1024; + ALLOCATOR.lock().init(heap_start, heap_size); + + let mut modes = Vec::new(); { // Get card info let mut data = ThunkData::new(); @@ -242,33 +268,47 @@ pub unsafe extern "C" fn kstart( if data.ax == 0x004F { let card_info = ptr::read(0x1000 as *const VbeCardInfo); - let mut modes = card_info.videomodeptr as *const u16; + let mut mode_ptr = card_info.videomodeptr as *const u16; loop { - let mode = *modes; + // Ask for linear frame buffer with mode + let mode = *mode_ptr | (1 << 14); if mode == 0xFFFF { break; } - modes = modes.add(1); + mode_ptr = mode_ptr.add(1); // Get mode info let mut data = ThunkData::new(); data.ax = 0x4F01; - // Ask for linear frame buffer with mode - data.cx = mode | (1 << 14); + data.cx = mode; data.di = 0x2000; data.with(thunk10); if data.ax == 0x004F { let mode_info = ptr::read(0x2000 as *const VbeModeInfo); // We only support 32-bits per pixel modes - if mode_info.bitsperpixel == 32 { - writeln!( - vga, - "{}x{}", - mode_info.xresolution, - mode_info.yresolution - ); + if mode_info.bitsperpixel != 32 { + continue; } + + let w = mode_info.xresolution as u32; + let h = mode_info.yresolution as u32; + + let mut aspect_w = w; + let mut aspect_h = h; + for i in 2..cmp::min(aspect_w / 2, aspect_h / 2) { + while aspect_w % i == 0 && aspect_h % i == 0 { + aspect_w /= i; + aspect_h /= i; + } + } + + //TODO: support resolutions that are not perfect multiples of 4 + if w % 4 != 0 { + continue; + } + + modes.push((mode, w, h, format!("{:>4}x{:<4} {:>3}:{:<3}", w, h, aspect_w, aspect_h))); } else { writeln!(vga, "Failed to read VBE mode 0x{:04X} info: 0x{:04X}", mode, data.ax); } @@ -278,8 +318,39 @@ pub unsafe extern "C" fn kstart( } } + // Sort modes by pixel area, reversed + modes.sort_by(|a, b| (b.1 * b.2).cmp(&(a.1 * a.2))); + writeln!(vga, "Arrow keys and space select mode, enter to continue"); + + //TODO 0x4F03 VBE function to get current mode + let rows = 12; + let mut selected = modes.get(0).map_or(0, |x| x.0); loop { + let mut row = 0; + let mut col = 0; + for (mode, w, h, text) in modes.iter() { + if row >= rows { + col += 1; + row = 0; + } + + vga.x = 1 + col * 20; + vga.y = 1 + row; + + if *mode == selected { + vga.bg = VgaTextColor::White; + vga.fg = VgaTextColor::Black; + } else { + vga.bg = VgaTextColor::DarkGray; + vga.fg = VgaTextColor::White; + } + + write!(vga, "{}", text); + + row += 1; + } + // Read keypress let mut data = ThunkData::new(); data.with(thunk16); @@ -289,5 +360,67 @@ pub unsafe extern "C" fn kstart( (data.ax as u8) as char, (data.ax >> 8) as u8 ); + + match (data.ax >> 8) as u8 { + 0x4B /* Left */ => { + if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) { + if mode_i < rows { + while mode_i < modes.len() { + mode_i += rows; + } + } + mode_i -= rows; + if let Some(new) = modes.get(mode_i) { + selected = new.0; + } + } + }, + 0x4D /* Right */ => { + if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) { + mode_i += rows; + if mode_i >= modes.len() { + mode_i = mode_i % rows; + } + if let Some(new) = modes.get(mode_i) { + selected = new.0; + } + } + }, + 0x48 /* Up */ => { + if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) { + if mode_i % rows == 0 { + mode_i += rows; + if mode_i > modes.len() { + mode_i = modes.len(); + } + } + mode_i -= 1; + if let Some(new) = modes.get(mode_i) { + selected = new.0; + } + } + }, + 0x50 /* Down */ => { + if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) { + mode_i += 1; + if mode_i % rows == 0 { + mode_i -= rows; + } + if mode_i >= modes.len() { + mode_i = mode_i - mode_i % rows; + } + if let Some(new) = modes.get(mode_i) { + selected = new.0; + } + } + }, + 0x1C /* Enter */ => { + let mut data = ThunkData::new(); + data.ax = 0x4F02; + data.bx = selected; + data.with(thunk10); + }, + _ => (), + } } }