Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,12 @@ virtio-vsock = ["virtio"]
## This is only useful on PCs (x86-64).
vga = []

## Enables the _Bochs Graphics Adapter_ (BGA) driver.
##
## This provides a linear framebuffer for pixel graphics and is primarily useful in QEMU/Bochs environments (x86-64).
## Any rendering has to be done by the application; the kernel itself does not output anything on the framebuffer.
bga = ["pci"]
Comment thread
GloriousAlpaca marked this conversation as resolved.

#! ### Performance Features

## Disables putting the CPU to sleep.
Expand Down
161 changes: 161 additions & 0 deletions src/arch/x86_64/kernel/bga.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
//! This module contains the implementation of the Bochs Graphics Adapter (BGA) driver.
//!
//! The driver uses the Bochs VBE Extensions, which use two I/O ports to communicate with the
//! emulated VGA card instead of relying on a 16-bit VBE BIOS. The driver initializes the BGA
//! device, sets the desired resolution and bits per pixel (BPP), and maps the framebuffer
//! into the virtual address space. It also provides a function to retrieve the physical
//! address of the framebuffer.

use hermit_sync::OnceCell;
use memory_addresses::{PhysAddr, VirtAddr};
use pci_types::{Bar, CommandRegister};
use x86_64::instructions::port::{Port, PortWriteOnly};

use crate::arch::kernel::pci::PciConfigRegion;
use crate::arch::x86_64::mm::paging::{
self, BasePageSize, PageTableEntryFlags, PageTableEntryFlagsExt,
};
use crate::drivers::pci::PciDevice;

#[derive(Debug)]
pub struct BgaInfo {
pub framebuffer: usize,
pub width: u16,
pub height: u16,
pub bpp: u16,
}

static BGA_INFO: OnceCell<BgaInfo> = OnceCell::new();

const VBE_DISPI_IOPORT_INDEX: u16 = 0x01ce;
const VBE_DISPI_IOPORT_DATA: u16 = 0x01cf;

#[allow(dead_code)]
#[repr(u16)]
pub enum VbeDispiIndex {
#[doc(alias = "VBE_DISPI_INDEX_ID")]
Id = 0,
#[doc(alias = "VBE_DISPI_INDEX_XRES")]
Xres = 1,
#[doc(alias = "VBE_DISPI_INDEX_YRES")]
Yres = 2,
#[doc(alias = "VBE_DISPI_INDEX_BPP")]
Bpp = 3,
#[doc(alias = "VBE_DISPI_INDEX_ENABLE")]
Enable = 4,
#[doc(alias = "VBE_DISPI_INDEX_BANK")]
Bank = 5,
#[doc(alias = "VBE_DISPI_INDEX_VIRT_WIDTH")]
VirtWidth = 6,
#[doc(alias = "VBE_DISPI_INDEX_VIRT_HEIGHT")]
VirtHeight = 7,
#[doc(alias = "VBE_DISPI_INDEX_X_OFFSET")]
XOffset = 8,
#[doc(alias = "VBE_DISPI_INDEX_Y_OFFSET")]
YOffset = 9,
}

const VBE_DISPI_DISABLED: u16 = 0x00;
const VBE_DISPI_ENABLED: u16 = 0x01;
const VBE_DISPI_LFB_ENABLED: u16 = 0x40;
Comment thread
GloriousAlpaca marked this conversation as resolved.

#[allow(dead_code)]
const VBE_DISPI_NOCLEARMEM: u16 = 0x80;

#[allow(dead_code)]
#[repr(u16)]
pub enum VbeDispiId {
#[doc(alias = "VBE_DISPI_ID0")]
Id0 = 0xb0c0,
#[doc(alias = "VBE_DISPI_ID1")]
Id1 = 0xb0c1,
#[doc(alias = "VBE_DISPI_ID2")]
Id2 = 0xb0c2,
#[doc(alias = "VBE_DISPI_ID3")]
Id3 = 0xb0c3,
#[doc(alias = "VBE_DISPI_ID4")]
Id4 = 0xb0c4,
#[doc(alias = "VBE_DISPI_ID5")]
Id5 = 0xb0c5,
}

struct BgaRegisters;

impl BgaRegisters {
pub fn read(index: VbeDispiIndex) -> u16 {
let mut index_port: PortWriteOnly<u16> = PortWriteOnly::new(VBE_DISPI_IOPORT_INDEX);
let mut data_port: Port<u16> = Port::new(VBE_DISPI_IOPORT_DATA);
unsafe {
index_port.write(index as u16);
data_port.read()
}
}

pub fn write(index: VbeDispiIndex, value: u16) {
let mut index_port: PortWriteOnly<u16> = PortWriteOnly::new(VBE_DISPI_IOPORT_INDEX);
let mut data_port: Port<u16> = Port::new(VBE_DISPI_IOPORT_DATA);
unsafe {
index_port.write(index as u16);
data_port.write(value);
}
}
}

pub fn init_device(adapter: &PciDevice<PciConfigRegion>) {
//To Do: Add support for different resolutions and BPP values
let width: u16 = 640;
let height: u16 = 400;
let bpp: u16 = 32;

let bga_version = BgaRegisters::read(VbeDispiIndex::Id);

if bga_version != VbeDispiId::Id5 as u16 {
error!("Unsupported BGA version: {bga_version:#06x}");
return;
}

BgaRegisters::write(VbeDispiIndex::Enable, VBE_DISPI_DISABLED);
BgaRegisters::write(VbeDispiIndex::Xres, width);
BgaRegisters::write(VbeDispiIndex::Yres, height);
BgaRegisters::write(VbeDispiIndex::Bpp, bpp);
BgaRegisters::write(
VbeDispiIndex::Enable,
VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED,
);

adapter.set_command(CommandRegister::MEMORY_ENABLE);

let (phys_addr, size) = match adapter.get_bar(0) {
Comment thread
jounathaen marked this conversation as resolved.
Some(Bar::Memory32 { address, size, .. }) => (u64::from(address), size as usize),
Some(Bar::Memory64 { address, size, .. }) => (address, size as usize),
_ => return,
};

BGA_INFO
Comment thread
GloriousAlpaca marked this conversation as resolved.
.set(BgaInfo {
framebuffer: phys_addr as usize,
width,
height,
bpp,
})
.unwrap();

assert!(
Comment thread
jounathaen marked this conversation as resolved.
size.is_multiple_of(4096),
"Framebuffer size must be a multiple of 4096 bytes"
);
let page_count = size / 4096;

let mut flags = PageTableEntryFlags::empty();
Comment thread
jounathaen marked this conversation as resolved.
flags.device().writable().execute_disable();
paging::map::<BasePageSize>(
VirtAddr::new(phys_addr),
PhysAddr::new(phys_addr),
page_count,
flags,
);
}

pub fn get_framebuffer_info() -> Option<&'static BgaInfo> {
BGA_INFO.get()
}
2 changes: 2 additions & 0 deletions src/arch/x86_64/kernel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use crate::env;
#[cfg(feature = "acpi")]
pub mod acpi;
pub mod apic;
#[cfg(feature = "bga")]
pub mod bga;
pub mod core_local;
pub mod gdt;
pub mod interrupts;
Expand Down
14 changes: 14 additions & 0 deletions src/drivers/pci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,20 @@ pub(crate) fn init(handlers: &mut InterruptHandlerMap) {
Err(err) => error!("Could not initialize rtl8139 device: {err}"),
}
}

#[cfg(all(target_arch = "x86_64", feature = "bga"))]
for adapter in PCI_DEVICES.finalize().iter().filter(|x| {
let (vendor_id, device_id) = x.id();
vendor_id == 0x1234 && device_id == 0x1111
}) {
info!(
"Found Bochs VGA device with device id {:#x}",
adapter.device_id()
);

crate::arch::kernel::bga::init_device(adapter);
}

PCI_DRIVERS.finalize();
});
}
Expand Down
40 changes: 40 additions & 0 deletions src/syscalls/system.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#[cfg(all(target_arch = "x86_64", feature = "bga"))]
use core::ffi::c_int;

use crate::arch::mm::paging::{BasePageSize, PageSize};

/// Returns the base page size, in bytes, of the current system.
Expand All @@ -6,3 +9,40 @@ use crate::arch::mm::paging::{BasePageSize, PageSize};
pub extern "C" fn sys_getpagesize() -> i32 {
BasePageSize::SIZE.try_into().unwrap()
}

#[cfg(all(target_arch = "x86_64", feature = "bga"))]
#[repr(C)]
pub struct FramebufferInfo {
pub framebuffer: *mut u8,
pub width: u32,
pub height: u32,
pub bpp: u32,
}

/// Returns the framebuffer information for the BGA device, if it has been initialized. Returns 0
/// on success, or -1 if the BGA device has not been initialized.
#[cfg(all(target_arch = "x86_64", feature = "bga"))]
#[hermit_macro::system]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn sys_get_framebuffer_info(info: *mut FramebufferInfo) -> c_int {
if info.is_null() {
return -1;
};

let bga_info = crate::arch::kernel::bga::get_framebuffer_info();
match bga_info {
Some(bga_info) => {
let info_c = FramebufferInfo {
framebuffer: core::ptr::with_exposed_provenance_mut(bga_info.framebuffer),
width: u32::from(bga_info.width),
height: u32::from(bga_info.height),
bpp: u32::from(bga_info.bpp),
};
unsafe {
info.write(info_c);
Comment thread
jounathaen marked this conversation as resolved.
}
0
}
None => -1,
}
}
Loading