Move video mode selection to generic code

This commit is contained in:
Jeremy Soller
2022-02-08 16:32:12 -07:00
parent 7358344616
commit aae67a40c1
6 changed files with 459 additions and 240 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
ENTRY(kstart)
ENTRY(start)
OUTPUT_FORMAT(elf32-i386)
SECTIONS {
+131
View File
@@ -16,8 +16,139 @@ extern crate alloc;
#[macro_use]
extern crate uefi_std as std;
use alloc::vec::Vec;
use core::cmp;
use redoxfs::Disk;
use self::os::{Os, OsKey, OsMemoryEntry, OsVideoMode};
#[macro_use]
mod os;
mod arch;
mod logger;
fn main<
D: Disk,
M: Iterator<Item=OsMemoryEntry>,
V: Iterator<Item=OsVideoMode>
>(os: &mut dyn Os<D, M, V>) -> Option<OsVideoMode> {
let mut modes = Vec::new();
for mode in os.video_modes() {
let mut aspect_w = mode.width;
let mut aspect_h = mode.height;
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;
}
}
modes.push((
mode,
format!("{:>4}x{:<4} {:>3}:{:<3}", mode.width, mode.height, aspect_w, aspect_h)
));
}
// Sort modes by pixel area, reversed
modes.sort_by(|a, b| (b.0.width * b.0.height).cmp(&(a.0.width * a.0.height)));
println!();
println!("Arrow keys and enter select mode");
println!();
print!(" ");
let (off_x, off_y) = os.get_text_position();
let rows = 12;
//TODO 0x4F03 VBE function to get current mode
let mut selected = modes.get(0).map_or(0, |x| x.0.id);
while ! modes.is_empty() {
let mut row = 0;
let mut col = 0;
for (mode, text) in modes.iter() {
if row >= rows {
col += 1;
row = 0;
}
os.set_text_position(off_x + col * 20, off_y + row);
os.set_text_highlight(mode.id == selected);
print!("{}", text);
row += 1;
}
// Read keypress
match os.get_key() {
OsKey::Left => {
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == 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.id;
}
}
},
OsKey::Right => {
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == 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.id;
}
}
},
OsKey::Up => {
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == 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.id;
}
}
},
OsKey::Down => {
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == 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.id;
}
}
},
OsKey::Enter => {
break;
},
_ => (),
}
}
os.set_text_position(0, off_y + rows);
os.set_text_highlight(false);
println!();
if let Some(mode_i) = modes.iter().position(|x| x.0.id == selected) {
if let Some((mode, _text)) = modes.get(mode_i) {
return Some(*mode);
}
}
None
}
+54 -3
View File
@@ -1,14 +1,65 @@
use core::{cmp, mem, ptr};
use crate::os::{OsMemoryEntry, OsMemoryKind};
use super::{MEMORY_MAP_ADDR, thunk::ThunkData};
#[repr(packed)]
struct MemoryMapEntry {
pub base: u64,
pub length: u64,
pub size: u64,
pub kind: u32,
}
pub struct MemoryMapIter {
thunk15: extern "C" fn(),
data: ThunkData,
first: bool,
}
impl MemoryMapIter {
pub fn new(thunk15: extern "C" fn()) -> Self {
Self {
thunk15,
data: ThunkData::new(),
first: true,
}
}
}
impl Iterator for MemoryMapIter {
type Item=OsMemoryEntry;
fn next(&mut self) -> Option<Self::Item> {
if self.first {
self.first = false;
} else if self.data.ebx == 0 {
return None;
}
self.data.eax = 0xE820;
self.data.ecx = mem::size_of::<MemoryMapEntry>() as u32;
self.data.edx = 0x534D4150;
self.data.edi = MEMORY_MAP_ADDR as u32;
unsafe { self.data.with(self.thunk15); }
//TODO: return error?
assert_eq!({ self.data.eax }, 0x534D4150);
assert_eq!({ self.data.ecx }, mem::size_of::<MemoryMapEntry>() as u32);
let entry = unsafe { ptr::read(MEMORY_MAP_ADDR as *const MemoryMapEntry) };
Some(Self::Item {
base: entry.base,
size: entry.size,
kind: match entry.kind {
1 => OsMemoryKind::Free,
3 => OsMemoryKind::Reclaim,
_ => OsMemoryKind::Reserved,
},
})
}
}
pub unsafe fn memory_map(thunk15: extern "C" fn()) -> Option<(usize, usize)> {
let mut heap_limits = None;
let mut data = ThunkData::new();
@@ -29,10 +80,10 @@ pub unsafe fn memory_map(thunk15: extern "C" fn()) -> Option<(usize, usize)> {
if
entry.kind == 1 &&
entry.base <= heap_start as u64 &&
(entry.base + entry.length) >= heap_start as u64
(entry.base + entry.size) >= heap_start as u64
{
let heap_end = cmp::min(
entry.base + entry.length,
entry.base + entry.size,
usize::MAX as u64
) as usize;
if heap_end >= heap_start {
+92 -191
View File
@@ -1,25 +1,22 @@
use alloc::{
string::String,
vec::Vec,
};
use core::{
alloc::{GlobalAlloc, Layout},
cmp,
convert::TryFrom,
ptr,
slice,
};
use linked_list_allocator::LockedHeap;
use log::error;
use spin::Mutex;
use crate::logger::LOGGER;
use crate::arch::paging_create;
use crate::logger::LOGGER;
use crate::os::{Os, OsKey};
use self::disk::DiskBios;
use self::memory_map::memory_map;
use self::memory_map::{memory_map, MemoryMapIter};
use self::thunk::ThunkData;
use self::vbe::{VbeCardInfo, VbeModeInfo};
use self::vbe::VideoModeIter;
use self::vga::{VgaTextColor, Vga};
#[macro_use]
@@ -73,8 +70,80 @@ pub struct KernelArgs {
acpi_rsdps_size: u64,
}
pub struct OsBios {
boot_disk: usize,
thunk10: extern "C" fn(),
thunk13: extern "C" fn(),
thunk15: extern "C" fn(),
thunk16: extern "C" fn(),
}
impl Os<
DiskBios,
MemoryMapIter,
VideoModeIter
> for OsBios {
fn disk(&self) -> DiskBios {
DiskBios::new(u8::try_from(self.boot_disk).unwrap(), self.thunk13)
}
fn memory(&self) -> MemoryMapIter {
MemoryMapIter::new(self.thunk15)
}
fn video_modes(&self) -> VideoModeIter {
VideoModeIter::new(self.thunk10)
}
fn set_video_mode(&self, id: u32) {
// Set video mode
let mut data = ThunkData::new();
data.eax = 0x4F02;
data.ebx = id;
unsafe { data.with(self.thunk10); }
//TODO: check result
}
fn get_key(&self) -> OsKey {
// Read keypress
let mut data = ThunkData::new();
unsafe { data.with(self.thunk16); }
match (data.eax >> 8) as u8 {
0x4B => OsKey::Left,
0x4D => OsKey::Right,
0x48 => OsKey::Up,
0x50 => OsKey::Down,
0x1C => OsKey::Enter,
_ => OsKey::Other,
}
}
fn get_text_position(&self) -> (usize, usize) {
let vga = VGA.lock();
(vga.x, vga.y)
}
fn set_text_position(&self, x: usize, y: usize) {
//TODO: ensure this is inside bounds!
let mut vga = VGA.lock();
vga.x = x;
vga.y = y;
}
fn set_text_highlight(&self, highlight: bool) {
let mut vga = VGA.lock();
if highlight {
vga.bg = VgaTextColor::White;
vga.fg = VgaTextColor::Black;
} else {
vga.bg = VgaTextColor::DarkGray;
vga.fg = VgaTextColor::White;
}
}
}
#[no_mangle]
pub unsafe extern "C" fn kstart(
pub unsafe extern "C" fn start(
kernel_entry: extern "C" fn(
page_table: usize,
stack: u64,
@@ -113,8 +182,16 @@ pub unsafe extern "C" fn kstart(
ALLOCATOR.lock().init(heap_start, heap_size);
let mut os = OsBios {
boot_disk,
thunk10,
thunk13,
thunk15,
thunk16,
};
// Locate kernel on RedoxFS
let disk = DiskBios::new(u8::try_from(boot_disk).unwrap(), thunk13);
let disk = os.disk();
//TODO: get block from partition table
let block = 1024 * 1024 / redoxfs::BLOCK_SIZE;
@@ -131,177 +208,7 @@ pub unsafe extern "C" fn kstart(
}
println!(": {} MiB", fs.header.1.size / 1024 / 1024);
let mut modes = Vec::new();
{
// Get card info
let mut data = ThunkData::new();
data.eax = 0x4F00;
data.edi = VBE_CARD_INFO_ADDR as u32;
data.with(thunk10);
if data.eax == 0x004F {
let card_info = ptr::read(VBE_CARD_INFO_ADDR as *const VbeCardInfo);
let mut mode_ptr = card_info.videomodeptr as *const u16;
loop {
// Ask for linear frame buffer with mode
let mode = *mode_ptr | (1 << 14);
if mode == 0xFFFF {
break;
}
mode_ptr = mode_ptr.add(1);
// Get mode info
let mut data = ThunkData::new();
data.eax = 0x4F01;
data.ecx = mode as u32;
data.edi = VBE_MODE_INFO_ADDR as u32;
data.with(thunk10);
if data.eax == 0x004F {
let mode_info = ptr::read(VBE_MODE_INFO_ADDR as *const VbeModeInfo);
// We only support 32-bits per pixel modes
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,
mode_info.physbaseptr,
format!("{:>4}x{:<4} {:>3}:{:<3}", w, h, aspect_w, aspect_h)
));
} else {
error!("Failed to read VBE mode 0x{:04X} info: 0x{:04X}", mode, { data.eax });
}
}
} else {
error!("Failed to read VBE card info: 0x{:04X}", { data.eax });
}
}
// Sort modes by pixel area, reversed
modes.sort_by(|a, b| (b.1 * b.2).cmp(&(a.1 * a.2)));
println!();
println!("Arrow keys and enter select mode");
println!();
print!(" ");
//TODO 0x4F03 VBE function to get current mode
let off_x = VGA.lock().x;
let off_y = VGA.lock().y;
let rows = 12;
let mut selected = modes.get(0).map_or(0, |x| x.0);
while ! modes.is_empty() {
let mut row = 0;
let mut col = 0;
for (mode, _w, _h, _ptr, text) in modes.iter() {
if row >= rows {
col += 1;
row = 0;
}
VGA.lock().x = off_x + col * 20;
VGA.lock().y = off_y + row;
if *mode == selected {
VGA.lock().bg = VgaTextColor::White;
VGA.lock().fg = VgaTextColor::Black;
} else {
VGA.lock().bg = VgaTextColor::DarkGray;
VGA.lock().fg = VgaTextColor::White;
}
print!("{}", text);
row += 1;
}
// Read keypress
let mut data = ThunkData::new();
data.with(thunk16);
match (data.eax >> 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 */ => {
break;
},
_ => (),
}
}
VGA.lock().x = 0;
VGA.lock().y = off_y + rows;
VGA.lock().bg = VgaTextColor::DarkGray;
VGA.lock().fg = VgaTextColor::White;
println!();
let mode_opt = crate::main(&mut os);
let kernel = {
let node = fs.find_node("kernel", fs.header.1.root)
@@ -355,17 +262,11 @@ pub unsafe extern "C" fn kstart(
let mut env = String::with_capacity(4096);
if let Some(mode_i) = modes.iter().position(|x| x.0 == selected) {
if let Some((mode, w, h, ptr, _text)) = modes.get(mode_i) {
let mut data = ThunkData::new();
data.eax = 0x4F02;
data.ebx = *mode as u32;
data.with(thunk10);
env.push_str(&format!("FRAMEBUFFER_ADDR={:016x}\n", ptr));
env.push_str(&format!("FRAMEBUFFER_WIDTH={:016x}\n", w));
env.push_str(&format!("FRAMEBUFFER_HEIGHT={:016x}\n", h));
}
if let Some(mode) = mode_opt {
env.push_str(&format!("FRAMEBUFFER_ADDR={:016x}\n", mode.base));
env.push_str(&format!("FRAMEBUFFER_WIDTH={:016x}\n", mode.width));
env.push_str(&format!("FRAMEBUFFER_HEIGHT={:016x}\n", mode.height));
os.set_video_mode(mode.id);
}
env.push_str(&format!("REDOXFS_BLOCK={:016x}\n", fs.block));
env.push_str("REDOXFS_UUID=");
+128 -45
View File
@@ -1,54 +1,137 @@
use core::ptr;
use log::error;
use crate::os::OsVideoMode;
use super::{ThunkData, VBE_CARD_INFO_ADDR, VBE_MODE_INFO_ADDR};
#[derive(Clone, Copy, Debug)]
#[repr(packed)]
pub struct VbeCardInfo {
pub signature: [u8; 4],
pub version: u16,
pub oemstring: u32,
pub capabilities: u32,
pub videomodeptr: u32,
pub totalmemory: u16,
pub oemsoftwarerev: u16,
pub oemvendornameptr: u32,
pub oemproductnameptr: u32,
pub oemproductrevptr: u32,
pub reserved: [u8; 222],
pub oemdata: [u8; 256],
pub signature: [u8; 4],
pub version: u16,
pub oemstring: u32,
pub capabilities: u32,
pub videomodeptr: u32,
pub totalmemory: u16,
pub oemsoftwarerev: u16,
pub oemvendornameptr: u32,
pub oemproductnameptr: u32,
pub oemproductrevptr: u32,
pub reserved: [u8; 222],
pub oemdata: [u8; 256],
}
#[derive(Clone, Copy, Debug)]
#[repr(packed)]
pub struct VbeModeInfo {
pub attributes: u16,
pub win_a: u8,
pub win_b: u8,
pub granularity: u16,
pub winsize: u16,
pub segment_a: u16,
pub segment_b: u16,
pub winfuncptr: u32,
pub bytesperscanline: u16,
pub xresolution: u16,
pub yresolution: u16,
pub xcharsize: u8,
pub ycharsize: u8,
pub numberofplanes: u8,
pub bitsperpixel: u8,
pub numberofbanks: u8,
pub memorymodel: u8,
pub banksize: u8,
pub numberofimagepages: u8,
pub unused: u8,
pub redmasksize: u8,
pub redfieldposition: u8,
pub greenmasksize: u8,
pub greenfieldposition: u8,
pub bluemasksize: u8,
pub bluefieldposition: u8,
pub rsvdmasksize: u8,
pub rsvdfieldposition: u8,
pub directcolormodeinfo: u8,
pub physbaseptr: u32,
pub offscreenmemoryoffset: u32,
pub offscreenmemsize: u16,
pub reserved: [u8; 206],
pub attributes: u16,
pub win_a: u8,
pub win_b: u8,
pub granularity: u16,
pub winsize: u16,
pub segment_a: u16,
pub segment_b: u16,
pub winfuncptr: u32,
pub bytesperscanline: u16,
pub xresolution: u16,
pub yresolution: u16,
pub xcharsize: u8,
pub ycharsize: u8,
pub numberofplanes: u8,
pub bitsperpixel: u8,
pub numberofbanks: u8,
pub memorymodel: u8,
pub banksize: u8,
pub numberofimagepages: u8,
pub unused: u8,
pub redmasksize: u8,
pub redfieldposition: u8,
pub greenmasksize: u8,
pub greenfieldposition: u8,
pub bluemasksize: u8,
pub bluefieldposition: u8,
pub rsvdmasksize: u8,
pub rsvdfieldposition: u8,
pub directcolormodeinfo: u8,
pub physbaseptr: u32,
pub offscreenmemoryoffset: u32,
pub offscreenmemsize: u16,
pub reserved: [u8; 206],
}
pub struct VideoModeIter {
thunk10: extern "C" fn(),
mode_ptr: *const u16,
}
impl VideoModeIter {
pub fn new(thunk10: extern "C" fn()) -> Self {
// Get card info
let mut data = ThunkData::new();
data.eax = 0x4F00;
data.edi = VBE_CARD_INFO_ADDR as u32;
unsafe { data.with(thunk10); }
let mode_ptr = if data.eax == 0x004F {
let card_info = unsafe { ptr::read(VBE_CARD_INFO_ADDR as *const VbeCardInfo) };
card_info.videomodeptr as *const u16
} else {
error!("Failed to read VBE card info: 0x{:04X}", { data.eax });
ptr::null()
};
Self {
thunk10,
mode_ptr
}
}
}
impl Iterator for VideoModeIter {
type Item = OsVideoMode;
fn next(&mut self) -> Option<Self::Item> {
if self.mode_ptr.is_null() {
return None;
}
loop {
// Set bit 14 to get linear frame buffer
let mode = unsafe { *self.mode_ptr } | (1 << 14);
if mode == 0xFFFF {
return None;
}
self.mode_ptr = unsafe { self.mode_ptr.add(1) };
// Get mode info
let mut data = ThunkData::new();
data.eax = 0x4F01;
data.ecx = mode as u32;
data.edi = VBE_MODE_INFO_ADDR as u32;
unsafe { data.with(self.thunk10); }
if data.eax == 0x004F {
let mode_info = unsafe { ptr::read(VBE_MODE_INFO_ADDR as *const VbeModeInfo) };
// We only support 32-bits per pixel modes
if mode_info.bitsperpixel != 32 {
continue;
}
let width = mode_info.xresolution as u32;
let height = mode_info.yresolution as u32;
//TODO: support resolutions that are not perfect multiples of 4
if width % 4 != 0 {
continue;
}
return Some(OsVideoMode {
id: mode as u32,
width,
height,
base: mode_info.physbaseptr as u64,
});
} else {
error!("Failed to read VBE mode 0x{:04X} info: 0x{:04X}", mode, { data.eax });
}
}
}
}
+53
View File
@@ -1,3 +1,5 @@
use redoxfs::Disk;
#[cfg(all(target_arch = "x86", target_os = "none"))]
pub use self::bios::*;
@@ -11,3 +13,54 @@ pub use self::uefi::*;
#[cfg(target_os = "uefi")]
#[macro_use]
mod uefi;
#[derive(Clone, Copy, Debug)]
pub enum OsKey {
Left,
Right,
Up,
Down,
Enter,
Other,
}
#[derive(Clone, Copy, Debug)]
pub enum OsMemoryKind {
Free,
Reclaim,
Reserved,
}
#[derive(Clone, Copy, Debug)]
pub struct OsMemoryEntry {
pub base: u64,
pub size: u64,
pub kind: OsMemoryKind,
}
#[derive(Clone, Copy, Debug)]
pub struct OsVideoMode {
pub id: u32,
pub width: u32,
pub height: u32,
pub base: u64,
}
pub trait Os<
D: Disk,
M: Iterator<Item=OsMemoryEntry>,
V: Iterator<Item=OsVideoMode>
> {
fn disk(&self) -> D;
fn memory(&self) -> M;
fn video_modes(&self) -> V;
fn set_video_mode(&self, id: u32);
fn get_key(&self) -> OsKey;
fn get_text_position(&self) -> (usize, usize);
fn set_text_position(&self, x: usize, y: usize);
fn set_text_highlight(&self, highlight: bool);
}