framebuffer working, only cost me 8 hours!
This commit is contained in:
parent
f238ac7b3b
commit
af901cdfb3
18 changed files with 523 additions and 83 deletions
|
|
@ -8,5 +8,5 @@ rustflags = [
|
||||||
|
|
||||||
[tarcet.riscv32i-unknown-none-elf]
|
[tarcet.riscv32i-unknown-none-elf]
|
||||||
rustflags = [
|
rustflags = [
|
||||||
"-C", "link-args=-nostdlib -ffreestanding -fPIC",
|
"-C", "link-args=-nostdlib -ffreestanding -relocation-model=static",
|
||||||
]
|
]
|
||||||
|
|
@ -24,7 +24,7 @@ panic = "abort"
|
||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
debug-assertions = false
|
debug-assertions = false
|
||||||
overflow-checks = false
|
overflow-checks = false
|
||||||
strip = false
|
strip = true
|
||||||
lto = true
|
lto = true
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
||||||
|
|
@ -34,8 +34,10 @@ cc = "1.0"
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
debug_messages = []
|
debug_messages = []
|
||||||
arch_virt = ["dev_virtio", "fs_fat32", "liblbos/arch_riscv32", "ddi/arch_riscv32"]
|
arch_virt = ["dev_virtio", "dev_qemu_ramfb", "dev_framebuffer", "fs_fat32", "liblbos/arch_riscv32", "ddi/arch_riscv32"]
|
||||||
arch_ofw = []
|
arch_ofw = []
|
||||||
arch_ppc32 = ["compiler_builtins", "arch_ofw", "fs_fat32"]
|
arch_ppc32 = ["compiler_builtins", "arch_ofw", "fs_fat32"]
|
||||||
dev_virtio = []
|
dev_virtio = []
|
||||||
|
dev_framebuffer = []
|
||||||
|
dev_qemu_ramfb = []
|
||||||
fs_fat32 = []
|
fs_fat32 = []
|
||||||
|
|
@ -67,6 +67,19 @@ pub enum SysCall {
|
||||||
WaitForNotifAck = 13,
|
WaitForNotifAck = 13,
|
||||||
/// returns the environment pointer for the given task
|
/// returns the environment pointer for the given task
|
||||||
EnvironmentPointer = 14,
|
EnvironmentPointer = 14,
|
||||||
|
/// disables the kernel framebuffer console, allowing you to use it for other purposes.
|
||||||
|
/// if there is no device providing a gpu, this will not do anything.
|
||||||
|
DisableFramebufferConsole = 15,
|
||||||
|
/// enables the kernel framebuffer console, this will result in an immediate framebuffer console reset.
|
||||||
|
/// if the framebuffer console is already enabled, then it will be reset upon calling this.
|
||||||
|
/// if there is no device providing a gpu, this will not do anything.
|
||||||
|
EnableFramebufferConsole = 16,
|
||||||
|
/// gets a pointer to the framebuffer
|
||||||
|
/// if there is no device providing a gpu, this will return a null pointer
|
||||||
|
FramebufferPointer = 17,
|
||||||
|
/// flushes changes done within the given rectangle to the framebuffer
|
||||||
|
/// if there is no device providing a gpu, this will not do anything
|
||||||
|
FlushFramebufferRect = 18,
|
||||||
|
|
||||||
/// initializes kernel, ONLY CALL ONCE! IN FACT, YOU PROBABLY NEVER NEED TO CALL THIS
|
/// initializes kernel, ONLY CALL ONCE! IN FACT, YOU PROBABLY NEVER NEED TO CALL THIS
|
||||||
InitKernel = 666
|
InitKernel = 666
|
||||||
|
|
@ -93,6 +106,10 @@ pub fn usize2sc(u: usize) -> SysCall {
|
||||||
12 => SysCall::PendingNotifications,
|
12 => SysCall::PendingNotifications,
|
||||||
13 => SysCall::WaitForNotifAck,
|
13 => SysCall::WaitForNotifAck,
|
||||||
14 => SysCall::EnvironmentPointer,
|
14 => SysCall::EnvironmentPointer,
|
||||||
|
15 => SysCall::DisableFramebufferConsole,
|
||||||
|
16 => SysCall::EnableFramebufferConsole,
|
||||||
|
17 => SysCall::FramebufferPointer,
|
||||||
|
18 => SysCall::FlushFramebufferRect,
|
||||||
666 => SysCall::InitKernel,
|
666 => SysCall::InitKernel,
|
||||||
_ => SysCall::NoAction,
|
_ => SysCall::NoAction,
|
||||||
}
|
}
|
||||||
|
|
@ -166,6 +183,22 @@ pub fn environment_pointer() -> usize {
|
||||||
syscall(SysCall::EnvironmentPointer, 0, 0, 0, 0, 0, 0)
|
syscall(SysCall::EnvironmentPointer, 0, 0, 0, 0, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn disable_framebuffer_console() {
|
||||||
|
syscall(SysCall::DisableFramebufferConsole, 0, 0, 0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enable_framebuffer_console() {
|
||||||
|
syscall(SysCall::EnableFramebufferConsole, 0, 0, 0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn framebuffer_pointer() -> usize {
|
||||||
|
syscall(SysCall::FramebufferPointer, 0, 0, 0, 0, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flush_framebuffer_rect(x: usize, y: usize, w: usize, h: usize) {
|
||||||
|
syscall(SysCall::FlushFramebufferRect, x, y, w, h, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init_kernel() {
|
pub fn init_kernel() {
|
||||||
syscall(SysCall::InitKernel, 0, 0, 0, 0, 0, 0);
|
syscall(SysCall::InitKernel, 0, 0, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
@ -2,4 +2,4 @@
|
||||||
|
|
||||||
storage_device="${1}"
|
storage_device="${1}"
|
||||||
|
|
||||||
qemu-system-riscv32 -machine virt -bios none -drive if=none,format=raw,file="${storage_device}",id=disk1 -device virtio-blk-device,drive=disk1 -device virtio-gpu-device -serial mon:stdio -m 5M -kernel target/riscv32imac-unknown-none-elf/release/lbos
|
qemu-system-riscv32 -machine virt -bios none -drive if=none,format=raw,file="${storage_device}",id=disk1 -device virtio-blk-device,drive=disk1 -display sdl -device virtio-gpu-device -serial stdio -m 5M -device loader,cpu-num=0,file=target/riscv32imac-unknown-none-elf/release/lbos -monitor telnet:127.0.0.1:1235,server,nowait -d guest_errors,unimp
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// fixme: port this to the device-agnostic framebuffer console
|
||||||
|
|
||||||
pub mod colors;
|
pub mod colors;
|
||||||
pub mod extrachars;
|
pub mod extrachars;
|
||||||
pub mod terminal;
|
pub mod terminal;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ ENTRY( _start )
|
||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
rom (wxa) : ORIGIN = 0x80000000, LENGTH = 64 * 1024
|
rom (wxa) : ORIGIN = 0x80000000, LENGTH = 64 * 1024
|
||||||
ram (wxa) : ORIGIN = 0x80010000, LENGTH = 1048510
|
ram (wxa) : ORIGIN = ORIGIN(rom) + LENGTH(rom), LENGTH = 64 * 1024
|
||||||
|
virtqueues (wxa) : ORIGIN = ORIGIN(ram) + LENGTH(ram), LENGTH = 16 * 1024 * 2
|
||||||
|
framebuffer (wxa) : ORIGIN = ORIGIN(virtqueues) + LENGTH(virtqueues), LENGTH = 320 * 240 * 3
|
||||||
}
|
}
|
||||||
|
|
||||||
PHDRS
|
PHDRS
|
||||||
|
|
@ -53,4 +55,9 @@ SECTIONS {
|
||||||
PROVIDE(_tstack_end = _tstack_start + 16384);
|
PROVIDE(_tstack_end = _tstack_start + 16384);
|
||||||
PROVIDE(_heap_start = _tstack_end);
|
PROVIDE(_heap_start = _tstack_end);
|
||||||
PROVIDE(_heap_size = _MEM_END - _heap_start);
|
PROVIDE(_heap_size = _MEM_END - _heap_start);
|
||||||
|
PROVIDE(_virtio_queue_1_start = ORIGIN(virtqueues));
|
||||||
|
PROVIDE(_virtio_queue_1_end = _virtio_queue_1_start + 16384);
|
||||||
|
PROVIDE(_virtio_queue_2_start = _virtio_queue_1_end);
|
||||||
|
PROVIDE(_virtio_queue_2_end = _virtio_queue_2_start + 16384);
|
||||||
|
PROVIDE(_framebuffer_start = ORIGIN(framebuffer));
|
||||||
}
|
}
|
||||||
90
src/dev/framebuffer/console.rs
Normal file
90
src/dev/framebuffer/console.rs
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
use crate::dev::{framebuffer_update, FRAMEBUFFER_HEIGHT, FRAMEBUFFER_WIDTH};
|
||||||
|
use crate::dev::framebuffer::{fb_clearscreen, fb_write_char_array};
|
||||||
|
use crate::spinlock::Spinlock;
|
||||||
|
use crate::trafficcontrol::TrafficControl;
|
||||||
|
|
||||||
|
pub struct FramebufferConsole {
|
||||||
|
pub buffer: [[char; 20]; 15], // our font is 16x16, 320x240 is the standard res, so 20x15
|
||||||
|
pub cursor: (usize, usize), // (x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static FBCONSOLE: Spinlock<FramebufferConsole> = Spinlock::new(
|
||||||
|
FramebufferConsole::empty()
|
||||||
|
);
|
||||||
|
|
||||||
|
impl FramebufferConsole {
|
||||||
|
pub const fn empty() -> Self {
|
||||||
|
Self {
|
||||||
|
buffer: [[' '; 20]; 15],
|
||||||
|
cursor: (0, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// does NOT send a framebuffer update! that's up to the caller
|
||||||
|
pub fn scroll_terminal(&mut self) {
|
||||||
|
for line_num in 0..(self.buffer.len() - 1) {
|
||||||
|
let copy_from = self.buffer[line_num + 1];
|
||||||
|
self.buffer[line_num] = copy_from;
|
||||||
|
}
|
||||||
|
self.buffer[self.buffer.len() - 1] = [' '; 20];
|
||||||
|
}
|
||||||
|
|
||||||
|
// DOES send a framebuffer update!
|
||||||
|
pub fn clear_terminal(&mut self, tc: &mut TrafficControl) {
|
||||||
|
self.buffer = [[' '; 20]; 15];
|
||||||
|
self.cursor = (0, 0);
|
||||||
|
self.render(tc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DOES send a framebuffer update!
|
||||||
|
pub fn printstr(&mut self, tc: &mut TrafficControl, str: &str) {
|
||||||
|
for c in str.chars() {
|
||||||
|
let mut was_special_char = false;
|
||||||
|
if c == '\n' || c == '\r' {
|
||||||
|
was_special_char = true;
|
||||||
|
self.cursor.0 = 0;
|
||||||
|
self.cursor.1 += 1;
|
||||||
|
}
|
||||||
|
if self.cursor.1 >= self.buffer.len() {
|
||||||
|
self.cursor.0 = 0;
|
||||||
|
self.cursor.1 = self.buffer.len() - 1;
|
||||||
|
self.scroll_terminal();
|
||||||
|
}
|
||||||
|
if c == '\x08' {
|
||||||
|
was_special_char = true;
|
||||||
|
// we don't clear the character, that's up to the terminal
|
||||||
|
if self.cursor.0 > 0 {
|
||||||
|
self.cursor.0 -= 1;
|
||||||
|
} else {
|
||||||
|
self.cursor.0 = 0;
|
||||||
|
if self.cursor.1 > 0 {
|
||||||
|
self.cursor.1 -= 1;
|
||||||
|
self.cursor.0 = self.buffer[self.cursor.1].len() - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !was_special_char {
|
||||||
|
self.buffer[self.cursor.1][self.cursor.0] = c;
|
||||||
|
self.cursor.0 += 1;
|
||||||
|
if self.cursor.0 >= self.buffer[self.cursor.1].len() {
|
||||||
|
self.cursor.0 = 0;
|
||||||
|
self.cursor.1 += 1;
|
||||||
|
if self.cursor.1 >= self.buffer.len() {
|
||||||
|
self.cursor.1 = self.buffer.len() - 1;
|
||||||
|
self.scroll_terminal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.render(tc);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&self, tc: &mut TrafficControl) {
|
||||||
|
fb_clearscreen(tc);
|
||||||
|
|
||||||
|
for (y, line) in self.buffer.iter().enumerate() {
|
||||||
|
fb_write_char_array(tc, 0, y * 16, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
105
src/dev/framebuffer/mod.rs
Normal file
105
src/dev/framebuffer/mod.rs
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
use crate::dev::{
|
||||||
|
FRAMEBUFFER_ADDR, FRAMEBUFFER_BPP, FRAMEBUFFER_HEIGHT, FRAMEBUFFER_WIDTH, framebuffer_update,
|
||||||
|
};
|
||||||
|
use crate::trafficcontrol::TrafficControl;
|
||||||
|
use core::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
pub mod console;
|
||||||
|
|
||||||
|
pub const VAPFONT: &[u8] = include_bytes!("./vapfont.data");
|
||||||
|
pub const VAPFONT_W: usize = 256;
|
||||||
|
pub const VAPFONT_H: usize = 112;
|
||||||
|
|
||||||
|
pub struct FBColor {
|
||||||
|
pub red: u8,
|
||||||
|
pub green: u8,
|
||||||
|
pub blue: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const FB_BG_COLOR: FBColor = FBColor {
|
||||||
|
red: 216,
|
||||||
|
green: 216,
|
||||||
|
blue: 216,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const FB_FG_COLOR: FBColor = FBColor {
|
||||||
|
red: 0,
|
||||||
|
green: 0,
|
||||||
|
blue: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl FBColor {
|
||||||
|
#[inline]
|
||||||
|
pub const fn to_bytes(&self) -> [u8; 3] {
|
||||||
|
[self.red, self.green, self.blue]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fb_write_char_array(tc: &mut TrafficControl, mut x: usize, mut y: usize, chars: &[char]) {
|
||||||
|
const BYTES: [u8; 3] = FB_FG_COLOR.to_bytes();
|
||||||
|
let ogx = x;
|
||||||
|
let fbaddr = FRAMEBUFFER_ADDR.load(Ordering::Relaxed);
|
||||||
|
if fbaddr == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let fbstride = FRAMEBUFFER_BPP.load(Ordering::Relaxed) * FRAMEBUFFER_WIDTH;
|
||||||
|
const CHAR_SIZE: usize = 16;
|
||||||
|
let mut drew_anything = false;
|
||||||
|
for c in chars {
|
||||||
|
let c = *c;
|
||||||
|
//if c == '\n' {
|
||||||
|
// y += CHAR_SIZE;
|
||||||
|
// x = ogx;
|
||||||
|
// if y * CHAR_SIZE > FRAMEBUFFER_HEIGHT {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
//} else if c == ' ' {
|
||||||
|
if c == ' ' {
|
||||||
|
x += CHAR_SIZE;
|
||||||
|
//if x * CHAR_SIZE > FRAMEBUFFER_WIDTH {
|
||||||
|
// x = ogx;
|
||||||
|
// y += CHAR_SIZE;
|
||||||
|
// if y * CHAR_SIZE > FRAMEBUFFER_HEIGHT {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
} else if c as u8 > 32 {
|
||||||
|
let c = c as u8 - 32;
|
||||||
|
let cx = (c % 16) as usize * CHAR_SIZE;
|
||||||
|
let cy = (c / 16) as usize * CHAR_SIZE;
|
||||||
|
for row in 0..CHAR_SIZE {
|
||||||
|
for col in 0..CHAR_SIZE {
|
||||||
|
let coff = (VAPFONT_W * (cy + row)) + (cx + col);
|
||||||
|
let draw = VAPFONT[coff / 8] & (0x80 >> (coff % 8));
|
||||||
|
if draw != 0 {
|
||||||
|
unsafe {
|
||||||
|
let fb = (fbaddr as *mut u8).add(((y+row)*fbstride) + ((x+col)*4)) as *mut u32;
|
||||||
|
fb.write_volatile(u32::from_ne_bytes([0, BYTES[0], BYTES[1], BYTES[2]]));
|
||||||
|
drew_anything = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x += CHAR_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
framebuffer_update(tc, 0, 0, FRAMEBUFFER_WIDTH as u32, FRAMEBUFFER_HEIGHT as u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fb_clearscreen(tc: &mut TrafficControl) {
|
||||||
|
const BYTES: [u8; 3] = FB_BG_COLOR.to_bytes();
|
||||||
|
let fbaddr = FRAMEBUFFER_ADDR.load(Ordering::Relaxed);
|
||||||
|
if fbaddr == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let fbstride = FRAMEBUFFER_BPP.load(Ordering::Relaxed) * FRAMEBUFFER_WIDTH;
|
||||||
|
for y in 0..FRAMEBUFFER_HEIGHT {
|
||||||
|
for x in 0..FRAMEBUFFER_WIDTH {
|
||||||
|
unsafe {
|
||||||
|
let fb = (fbaddr as *mut u8).add(((y)*fbstride) + ((x)*4)) as *mut u32;
|
||||||
|
fb.write_volatile(u32::from_ne_bytes([0, BYTES[0], BYTES[1], BYTES[2]]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
framebuffer_update(tc, 0, 0, FRAMEBUFFER_WIDTH as u32, FRAMEBUFFER_HEIGHT as u32);
|
||||||
|
}
|
||||||
BIN
src/dev/framebuffer/vapfont.data
Normal file
BIN
src/dev/framebuffer/vapfont.data
Normal file
Binary file not shown.
BIN
src/dev/framebuffer/vapfont.png
Normal file
BIN
src/dev/framebuffer/vapfont.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
|
|
@ -1,10 +1,19 @@
|
||||||
|
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use crate::trafficcontrol::TrafficControl;
|
use crate::trafficcontrol::TrafficControl;
|
||||||
|
|
||||||
pub const FRAMEBUFFER_WIDTH: usize = 320;
|
pub const FRAMEBUFFER_WIDTH: usize = 320;
|
||||||
pub const FRAMEBUFFER_HEIGHT: usize = 240;
|
pub const FRAMEBUFFER_HEIGHT: usize = 240;
|
||||||
|
|
||||||
|
// NOTE:
|
||||||
|
// FRAMEBUFFER_ADDR should always be 0 if no framebuffer exists
|
||||||
|
// if FRAMEBUFFER_ADDR is NOT 0, then FRAMEBUFFER_STRIDE should also be NOT 0
|
||||||
|
pub static FRAMEBUFFER_ADDR: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
pub static FRAMEBUFFER_BPP: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
#[cfg(feature = "dev_virtio")]
|
#[cfg(feature = "dev_virtio")]
|
||||||
pub mod virtio;
|
pub mod virtio;
|
||||||
|
#[cfg(feature = "dev_framebuffer")]
|
||||||
|
pub mod framebuffer;
|
||||||
|
|
||||||
pub fn probe_devices(tc: &mut TrafficControl) {
|
pub fn probe_devices(tc: &mut TrafficControl) {
|
||||||
#[cfg(feature = "dev_virtio")]
|
#[cfg(feature = "dev_virtio")]
|
||||||
|
|
@ -15,4 +24,18 @@ pub fn read_sector(tc: &mut TrafficControl, buffer: usize, size: u32, sector: u6
|
||||||
#[cfg(feature = "dev_virtio")]
|
#[cfg(feature = "dev_virtio")]
|
||||||
return virtio::read_sector(tc, buffer, size, sector);
|
return virtio::read_sector(tc, buffer, size, sector);
|
||||||
false
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn framebuffer_address() -> usize {
|
||||||
|
FRAMEBUFFER_ADDR.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn framebuffer_stride() -> usize {
|
||||||
|
FRAMEBUFFER_BPP.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn framebuffer_update(tc: &mut TrafficControl, x: u32, y: u32, width: u32, height: u32) {
|
||||||
|
#[cfg(feature = "dev_virtio")]
|
||||||
|
virtio::framebuffer_update(tc, x, y, width, height);
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
use crate::dev::virtio::{Descriptor, VIRTIO_DESC_F_NEXT, VIRTIO_DESC_F_WRITE, VIRTIO_MMIO_GUEST_FEATURES, VIRTIO_MMIO_GUEST_PAGE_SIZE, VIRTIO_MMIO_HOST_FEATURES, VIRTIO_MMIO_QUEUE_NOTIFY, VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_PFN, VIRTIO_MMIO_QUEUE_SEL, VIRTIO_MMIO_STATUS, VIRTIO_MMIO_STATUS_ACKNOWLEDGE, VIRTIO_MMIO_STATUS_DRIVER, VIRTIO_MMIO_STATUS_DRIVER_OK, VIRTIO_MMIO_STATUS_FAILED, VIRTIO_MMIO_STATUS_FEATURES_OK, VIRTIO_QUEUE_SIZE, VirtQueue, Used};
|
use crate::dev::virtio::{Descriptor, VIRTIO_DESC_F_NEXT, VIRTIO_DESC_F_WRITE, VIRTIO_MMIO_GUEST_FEATURES, VIRTIO_MMIO_GUEST_PAGE_SIZE, VIRTIO_MMIO_HOST_FEATURES, VIRTIO_MMIO_QUEUE_NOTIFY, VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_PFN, VIRTIO_MMIO_QUEUE_SEL, VIRTIO_MMIO_STATUS, VIRTIO_MMIO_STATUS_ACKNOWLEDGE, VIRTIO_MMIO_STATUS_DRIVER, VIRTIO_MMIO_STATUS_DRIVER_OK, VIRTIO_MMIO_STATUS_FAILED, VIRTIO_MMIO_STATUS_FEATURES_OK, VIRTIO_QUEUE_SIZE, VirtQueue, Used};
|
||||||
use crate::trafficcontrol::{TaskWait, TrafficControl};
|
use crate::trafficcontrol::{TaskWait, TrafficControl};
|
||||||
|
|
||||||
|
unsafe extern "C" {
|
||||||
|
fn _virtio_queue_1_start();
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Status {
|
pub struct Status {
|
||||||
pub status: u8,
|
pub status: u8,
|
||||||
|
|
@ -94,10 +98,17 @@ impl VirtIoBlockDevice {
|
||||||
((addr + VIRTIO_MMIO_QUEUE_SEL) as *mut u32).write_volatile(0);
|
((addr + VIRTIO_MMIO_QUEUE_SEL) as *mut u32).write_volatile(0);
|
||||||
}
|
}
|
||||||
let num_blocks = size_of::<VirtQueue>().div_ceil(512) + 8; // 8 extra blocks will assert that the queue is aligned to 4096 bytes
|
let num_blocks = size_of::<VirtQueue>().div_ceil(512) + 8; // 8 extra blocks will assert that the queue is aligned to 4096 bytes
|
||||||
let queue_block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) };
|
//let queue_block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) };
|
||||||
let queue_ptr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().block_to_addr(queue_block) };
|
//let queue_ptr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().block_to_addr(queue_block) };
|
||||||
// align up to 4096
|
//// align up to 4096
|
||||||
let queue_ptr = queue_ptr.wrapping_add(4095) & !(4095);
|
//let queue_ptr = queue_ptr.wrapping_add(4095) & !(4095);
|
||||||
|
let queue_ptr = _virtio_queue_1_start as usize;
|
||||||
|
// zero out queue
|
||||||
|
for i in 0..size_of::<VirtQueue>() {
|
||||||
|
unsafe {
|
||||||
|
((queue_ptr + i) as *mut u8).write_volatile(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
unsafe { ((addr + VIRTIO_MMIO_GUEST_PAGE_SIZE) as *mut u32).write_volatile(4096) };
|
unsafe { ((addr + VIRTIO_MMIO_GUEST_PAGE_SIZE) as *mut u32).write_volatile(4096) };
|
||||||
unsafe {
|
unsafe {
|
||||||
((addr + VIRTIO_MMIO_QUEUE_PFN) as *mut u32).write_volatile(queue_ptr as u32 / 4096)
|
((addr + VIRTIO_MMIO_QUEUE_PFN) as *mut u32).write_volatile(queue_ptr as u32 / 4096)
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,28 @@
|
||||||
|
use crate::arch::serial_port;
|
||||||
use crate::dev::{FRAMEBUFFER_HEIGHT, FRAMEBUFFER_WIDTH};
|
use crate::dev::{FRAMEBUFFER_HEIGHT, FRAMEBUFFER_WIDTH};
|
||||||
use crate::dev::virtio::{free_type, heap_allocate_type, Descriptor, VirtQueue, VIRTIO_DESC_F_NEXT, VIRTIO_DESC_F_WRITE, VIRTIO_MMIO_GUEST_FEATURES, VIRTIO_MMIO_HOST_FEATURES, VIRTIO_MMIO_QUEUE_NOTIFY, VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_SEL, VIRTIO_MMIO_STATUS, VIRTIO_MMIO_STATUS_ACKNOWLEDGE, VIRTIO_MMIO_STATUS_DRIVER, VIRTIO_MMIO_STATUS_DRIVER_OK, VIRTIO_MMIO_STATUS_FAILED, VIRTIO_MMIO_STATUS_FEATURES_OK, VIRTIO_QUEUE_SIZE};
|
use crate::dev::virtio::{heap_allocate_type, Descriptor, VirtQueue, VIRTIO_DESC_F_NEXT, VIRTIO_DESC_F_WRITE, VIRTIO_MMIO_GUEST_FEATURES, VIRTIO_MMIO_GUEST_PAGE_SIZE, VIRTIO_MMIO_HOST_FEATURES, VIRTIO_MMIO_QUEUE_NOTIFY, VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_PFN, VIRTIO_MMIO_QUEUE_SEL, VIRTIO_MMIO_STATUS, VIRTIO_MMIO_STATUS_ACKNOWLEDGE, VIRTIO_MMIO_STATUS_DRIVER, VIRTIO_MMIO_STATUS_DRIVER_OK, VIRTIO_MMIO_STATUS_FAILED, VIRTIO_MMIO_STATUS_FEATURES_OK, VIRTIO_QUEUE_SIZE};
|
||||||
|
use crate::strprint::u32_hex;
|
||||||
use crate::trafficcontrol::TrafficControl;
|
use crate::trafficcontrol::TrafficControl;
|
||||||
|
|
||||||
|
unsafe extern "C" {
|
||||||
|
fn _virtio_queue_2_start();
|
||||||
|
fn _framebuffer_start();
|
||||||
|
}
|
||||||
|
|
||||||
pub const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x0100;
|
pub const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x0100;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct CtrlHeader {
|
pub struct CtrlHeader {
|
||||||
pub ctrl_type: u32,
|
pub ctrl_type: u32,
|
||||||
pub flags: u32,
|
pub flags: u32,
|
||||||
pub fence_id: u32,
|
pub fence_id: u64,
|
||||||
pub ctx_id: u32,
|
pub ctx_id: u32,
|
||||||
pub padding: u32,
|
pub ring_idx: u8,
|
||||||
|
pub padding: [u8; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: u32 = 0x0100 + 1;
|
pub const VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: u32 = 0x0100 + 1;
|
||||||
pub const VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: u32 = 134;
|
pub const VIRTIO_GPU_FORMAT_X8R8G8B8: u32 = 4;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct ResourceCreate2D {
|
pub struct ResourceCreate2D {
|
||||||
|
|
@ -80,6 +88,7 @@ pub struct ResourceFlush {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct VirtIoGpuDevice {
|
pub struct VirtIoGpuDevice {
|
||||||
|
pub latest_response: usize,
|
||||||
pub addr: usize,
|
pub addr: usize,
|
||||||
pub queue: usize,
|
pub queue: usize,
|
||||||
pub idx: u16,
|
pub idx: u16,
|
||||||
|
|
@ -116,7 +125,7 @@ impl VirtIoGpuDevice {
|
||||||
// read host features
|
// read host features
|
||||||
let host_features =
|
let host_features =
|
||||||
unsafe { ((addr + VIRTIO_MMIO_HOST_FEATURES) as *const u32).read_volatile() };
|
unsafe { ((addr + VIRTIO_MMIO_HOST_FEATURES) as *const u32).read_volatile() };
|
||||||
let guest_features = host_features; // todo: configure properly
|
let guest_features = 0; // todo: configure properly
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
((addr + VIRTIO_MMIO_GUEST_FEATURES) as *mut u32).write_volatile(guest_features);
|
((addr + VIRTIO_MMIO_GUEST_FEATURES) as *mut u32).write_volatile(guest_features);
|
||||||
|
|
@ -152,23 +161,36 @@ impl VirtIoGpuDevice {
|
||||||
((addr + VIRTIO_MMIO_QUEUE_SEL) as *mut u32).write_volatile(0);
|
((addr + VIRTIO_MMIO_QUEUE_SEL) as *mut u32).write_volatile(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let num_blocks = size_of::<VirtQueue>().div_ceil(512) + 8; // 8 extra blocks will assert that the queue is aligned to 4096 bytes
|
//let num_blocks = size_of::<VirtQueue>().div_ceil(512) + 8; // 8 extra blocks will assert that the queue is aligned to 4096 bytes
|
||||||
let queue_block= unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) };
|
//let queue_block= unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) };
|
||||||
let queue_ptr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().block_to_addr(queue_block) };
|
//let queue_ptr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().block_to_addr(queue_block) };
|
||||||
// align up to 4096
|
//// align up to 4096
|
||||||
let queue_ptr = queue_ptr.wrapping_add(4095) & !(4095);
|
//let queue_ptr = queue_ptr.wrapping_add(4095) & !(4095);
|
||||||
|
let queue_ptr = _virtio_queue_2_start as usize;
|
||||||
|
// zero out queue
|
||||||
|
for i in 0..size_of::<VirtQueue>() {
|
||||||
|
unsafe {
|
||||||
|
((queue_ptr + i) as *mut u8).write_volatile(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
((addr + VIRTIO_MMIO_GUEST_PAGE_SIZE) as *mut u32).write_volatile(4096);
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
((addr + VIRTIO_MMIO_QUEUE_PFN) as *mut u32).write_volatile(queue_ptr as u32 / 4096);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// allocate memory for framebuffer
|
// allocate memory for framebuffer
|
||||||
let num_blocks = (FRAMEBUFFER_WIDTH * FRAMEBUFFER_HEIGHT * 4).div_ceil(512); // no alignment requirements afaik
|
let latest_response = heap_allocate_type::<CtrlHeader>(tc) as *const _ as usize;
|
||||||
let framebuffer_block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().alloc_n_blocks(num_blocks) };
|
|
||||||
let framebuffer_ptr = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().block_to_addr(framebuffer_block) };
|
|
||||||
|
|
||||||
let mut gpu = Self {
|
let mut gpu = Self {
|
||||||
|
latest_response,
|
||||||
addr,
|
addr,
|
||||||
queue: queue_ptr,
|
queue: queue_ptr,
|
||||||
idx: 0,
|
idx: 0,
|
||||||
ack_used_idx: 0,
|
ack_used_idx: 0,
|
||||||
framebuffer: framebuffer_ptr,
|
framebuffer: _framebuffer_start as usize,
|
||||||
width: FRAMEBUFFER_WIDTH,
|
width: FRAMEBUFFER_WIDTH,
|
||||||
height: FRAMEBUFFER_HEIGHT,
|
height: FRAMEBUFFER_HEIGHT,
|
||||||
};
|
};
|
||||||
|
|
@ -177,7 +199,7 @@ impl VirtIoGpuDevice {
|
||||||
((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status);
|
((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: we heap allocate a lot of types here, they will be freed by the self.pending(tc) call
|
let mut free_me = [0; 4];
|
||||||
|
|
||||||
// create host resource 1, ResourceCreate2D
|
// create host resource 1, ResourceCreate2D
|
||||||
let cmd = heap_allocate_type::<ResourceCreate2D>(tc);
|
let cmd = heap_allocate_type::<ResourceCreate2D>(tc);
|
||||||
|
|
@ -185,14 +207,14 @@ impl VirtIoGpuDevice {
|
||||||
cmd.header.flags = 0;
|
cmd.header.flags = 0;
|
||||||
cmd.header.fence_id = 0;
|
cmd.header.fence_id = 0;
|
||||||
cmd.header.ctx_id = 0;
|
cmd.header.ctx_id = 0;
|
||||||
cmd.header.padding = 0;
|
cmd.header.ring_idx = 0;
|
||||||
|
cmd.header.padding = [0; 3];
|
||||||
|
|
||||||
cmd.resource_id = 1;
|
cmd.resource_id = 1;
|
||||||
cmd.format = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM;
|
cmd.format = VIRTIO_GPU_FORMAT_X8R8G8B8;
|
||||||
cmd.width = FRAMEBUFFER_WIDTH as u32;
|
cmd.width = FRAMEBUFFER_WIDTH as u32;
|
||||||
cmd.height = FRAMEBUFFER_HEIGHT as u32;
|
cmd.height = FRAMEBUFFER_HEIGHT as u32;
|
||||||
|
free_me[0] = unsafe { &(*cmd) as *const _ as usize };
|
||||||
let resp = heap_allocate_type::<CtrlHeader>(tc);
|
|
||||||
|
|
||||||
let desc_rq = Descriptor {
|
let desc_rq = Descriptor {
|
||||||
addr: unsafe { &(*cmd) as *const _ as u64 },
|
addr: unsafe { &(*cmd) as *const _ as u64 },
|
||||||
|
|
@ -201,14 +223,20 @@ impl VirtIoGpuDevice {
|
||||||
next: 0,
|
next: 0,
|
||||||
};
|
};
|
||||||
let desc_resp = Descriptor {
|
let desc_resp = Descriptor {
|
||||||
addr: unsafe { &(*resp) as *const _ as u64 },
|
addr: gpu.latest_response as u64,
|
||||||
len: size_of::<CtrlHeader>() as u32,
|
len: size_of::<CtrlHeader>() as u32,
|
||||||
flags: VIRTIO_DESC_F_WRITE,
|
flags: VIRTIO_DESC_F_WRITE,
|
||||||
next: 0,
|
next: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
gpu.send_rq_rsp(desc_rq, desc_resp);
|
gpu.send_rq_rsp(desc_rq, desc_resp);
|
||||||
gpu.pending(tc);
|
// awful hack so that we don't overload the queue
|
||||||
|
while {
|
||||||
|
gpu.ack_used_idx == unsafe { (gpu.queue as *const VirtQueue).read_volatile().used.idx }
|
||||||
|
} {
|
||||||
|
|
||||||
|
}
|
||||||
|
//gpu.pending(tc);
|
||||||
|
|
||||||
// attach backing, ResourceAttachBacking
|
// attach backing, ResourceAttachBacking
|
||||||
let cmd = heap_allocate_type::<ResourceAttachBacking>(tc);
|
let cmd = heap_allocate_type::<ResourceAttachBacking>(tc);
|
||||||
|
|
@ -216,14 +244,16 @@ impl VirtIoGpuDevice {
|
||||||
cmd.header.flags = 0;
|
cmd.header.flags = 0;
|
||||||
cmd.header.fence_id = 0;
|
cmd.header.fence_id = 0;
|
||||||
cmd.header.ctx_id = 0;
|
cmd.header.ctx_id = 0;
|
||||||
cmd.header.padding = 0;
|
cmd.header.ring_idx = 0;
|
||||||
|
cmd.header.padding = [0; 3];
|
||||||
cmd.resource_id = 1;
|
cmd.resource_id = 1;
|
||||||
cmd.nr_entries = 1;
|
cmd.nr_entries = 1;
|
||||||
|
free_me[1] = unsafe { &(*cmd) as *const _ as usize };
|
||||||
let mem_entry = heap_allocate_type::<GPUMemEntry>(tc);
|
let mem_entry = heap_allocate_type::<GPUMemEntry>(tc);
|
||||||
mem_entry.addr = gpu.framebuffer as u64;
|
mem_entry.addr = gpu.framebuffer as u64;
|
||||||
mem_entry.length = (gpu.width * gpu.height * 4) as u32;
|
mem_entry.length = (gpu.height * gpu.width * 4) as u32;
|
||||||
mem_entry.padding = 0;
|
mem_entry.padding = 0;
|
||||||
let resp = heap_allocate_type::<CtrlHeader>(tc);
|
free_me[2] = unsafe { &(*mem_entry) as *const _ as usize };
|
||||||
|
|
||||||
let desc_rq = Descriptor {
|
let desc_rq = Descriptor {
|
||||||
addr: unsafe { &(*cmd) as *const _ as u64 },
|
addr: unsafe { &(*cmd) as *const _ as u64 },
|
||||||
|
|
@ -238,14 +268,20 @@ impl VirtIoGpuDevice {
|
||||||
next: 0,
|
next: 0,
|
||||||
};
|
};
|
||||||
let desc_resp = Descriptor {
|
let desc_resp = Descriptor {
|
||||||
addr: unsafe { &(*resp) as *const _ as u64 },
|
addr: gpu.latest_response as u64,
|
||||||
len: size_of::<CtrlHeader>() as u32,
|
len: size_of::<CtrlHeader>() as u32,
|
||||||
flags: VIRTIO_DESC_F_WRITE,
|
flags: VIRTIO_DESC_F_WRITE,
|
||||||
next: 0,
|
next: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
gpu.send_rq_next_rsp([desc_rq, desc_mem_entry], desc_resp);
|
gpu.send_rq_next_rsp([desc_rq, desc_mem_entry], desc_resp);
|
||||||
gpu.pending(tc);
|
// awful hack so that we don't overload the queue
|
||||||
|
while {
|
||||||
|
gpu.ack_used_idx == unsafe { (gpu.queue as *const VirtQueue).read_volatile().used.idx }
|
||||||
|
} {
|
||||||
|
|
||||||
|
}
|
||||||
|
//gpu.pending(tc);
|
||||||
|
|
||||||
// set scanout
|
// set scanout
|
||||||
let cmd = heap_allocate_type::<SetScanout>(tc);
|
let cmd = heap_allocate_type::<SetScanout>(tc);
|
||||||
|
|
@ -253,14 +289,15 @@ impl VirtIoGpuDevice {
|
||||||
cmd.header.flags = 0;
|
cmd.header.flags = 0;
|
||||||
cmd.header.fence_id = 0;
|
cmd.header.fence_id = 0;
|
||||||
cmd.header.ctx_id = 0;
|
cmd.header.ctx_id = 0;
|
||||||
cmd.header.padding = 0;
|
cmd.header.ring_idx = 0;
|
||||||
|
cmd.header.padding = [0; 3];
|
||||||
cmd.rect.x = 0;
|
cmd.rect.x = 0;
|
||||||
cmd.rect.y = 0;
|
cmd.rect.y = 0;
|
||||||
cmd.rect.width = gpu.width as u32;
|
cmd.rect.width = gpu.width as u32;
|
||||||
cmd.rect.height = gpu.height as u32;
|
cmd.rect.height = gpu.height as u32;
|
||||||
cmd.scanout_id = 0;
|
cmd.scanout_id = 0;
|
||||||
cmd.resource_id = 1;
|
cmd.resource_id = 1;
|
||||||
let resp = heap_allocate_type::<CtrlHeader>(tc);
|
free_me[3] = unsafe { &(*cmd) as *const _ as usize };
|
||||||
|
|
||||||
let desc_rq = Descriptor {
|
let desc_rq = Descriptor {
|
||||||
addr: unsafe { &(*cmd) as *const _ as u64 },
|
addr: unsafe { &(*cmd) as *const _ as u64 },
|
||||||
|
|
@ -269,13 +306,28 @@ impl VirtIoGpuDevice {
|
||||||
next: 0,
|
next: 0,
|
||||||
};
|
};
|
||||||
let desc_resp = Descriptor {
|
let desc_resp = Descriptor {
|
||||||
addr: unsafe { &(*resp) as *const _ as u64 },
|
addr: gpu.latest_response as u64,
|
||||||
len: size_of::<CtrlHeader>() as u32,
|
len: size_of::<CtrlHeader>() as u32,
|
||||||
flags: VIRTIO_DESC_F_WRITE,
|
flags: VIRTIO_DESC_F_WRITE,
|
||||||
next: 0,
|
next: 0,
|
||||||
};
|
};
|
||||||
gpu.send_rq_rsp(desc_rq, desc_resp);
|
gpu.send_rq_rsp(desc_rq, desc_resp);
|
||||||
gpu.pending(tc);
|
// awful hack so that we don't overload the queue
|
||||||
|
while {
|
||||||
|
gpu.ack_used_idx == unsafe { (gpu.queue as *const VirtQueue).read_volatile().used.idx }
|
||||||
|
} {
|
||||||
|
|
||||||
|
}
|
||||||
|
//gpu.pending(tc);
|
||||||
|
|
||||||
|
for free in free_me {
|
||||||
|
let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().addr_to_block(free) };
|
||||||
|
unsafe {
|
||||||
|
tc.memory_manager.as_mut().unwrap_unchecked().free_n_blocks(block, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gpu.transfer(tc, 0, 0, gpu.width as u32, gpu.height as u32);
|
||||||
|
|
||||||
Ok(gpu)
|
Ok(gpu)
|
||||||
}
|
}
|
||||||
|
|
@ -287,9 +339,9 @@ impl VirtIoGpuDevice {
|
||||||
(*(self.queue as *mut VirtQueue)).desc[self.idx as usize] = desc_rq;
|
(*(self.queue as *mut VirtQueue)).desc[self.idx as usize] = desc_rq;
|
||||||
}
|
}
|
||||||
|
|
||||||
// assume next is always set, in fact set it if it isn't
|
// assume next is always set
|
||||||
unsafe {
|
unsafe {
|
||||||
(*(self.queue as *mut VirtQueue)).desc[self.idx as usize].flags |= VIRTIO_DESC_F_NEXT;
|
//(*(self.queue as *mut VirtQueue)).desc[self.idx as usize].flags |= VIRTIO_DESC_F_NEXT;
|
||||||
|
|
||||||
(*(self.queue as *mut VirtQueue)).desc[self.idx as usize].next =
|
(*(self.queue as *mut VirtQueue)).desc[self.idx as usize].next =
|
||||||
(self.idx + 1) % VIRTIO_QUEUE_SIZE as u16;
|
(self.idx + 1) % VIRTIO_QUEUE_SIZE as u16;
|
||||||
|
|
@ -319,10 +371,8 @@ impl VirtIoGpuDevice {
|
||||||
(*(self.queue as *mut VirtQueue)).desc[self.idx as usize] = desc;
|
(*(self.queue as *mut VirtQueue)).desc[self.idx as usize] = desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
// assume next is always set, in fact set it if it isn't
|
// assume next is always set
|
||||||
unsafe {
|
unsafe {
|
||||||
(*(self.queue as *mut VirtQueue)).desc[self.idx as usize].flags |= VIRTIO_DESC_F_NEXT;
|
|
||||||
|
|
||||||
(*(self.queue as *mut VirtQueue)).desc[self.idx as usize].next =
|
(*(self.queue as *mut VirtQueue)).desc[self.idx as usize].next =
|
||||||
(self.idx + 1) % VIRTIO_QUEUE_SIZE as u16;
|
(self.idx + 1) % VIRTIO_QUEUE_SIZE as u16;
|
||||||
}
|
}
|
||||||
|
|
@ -344,34 +394,54 @@ impl VirtIoGpuDevice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WARNING: THIS VERSION OF PENDING DOES NOT FREE ANYTHING
|
||||||
pub fn pending(&mut self, tc: &mut TrafficControl) {
|
pub fn pending(&mut self, tc: &mut TrafficControl) {
|
||||||
let queue = unsafe { &(*(self.queue as *mut VirtQueue)) };
|
let queue = unsafe { &(*(self.queue as *mut VirtQueue)) };
|
||||||
|
let mut pended = false;
|
||||||
while self.ack_used_idx != queue.used.idx {
|
while self.ack_used_idx != queue.used.idx {
|
||||||
|
pended = true;
|
||||||
|
let uart = serial_port();
|
||||||
|
if let Some(uart) = uart {
|
||||||
|
//uart.putstr("pended\n");
|
||||||
|
}
|
||||||
let elem = &queue.used.ring[self.ack_used_idx as usize % VIRTIO_QUEUE_SIZE];
|
let elem = &queue.used.ring[self.ack_used_idx as usize % VIRTIO_QUEUE_SIZE];
|
||||||
let desc = &queue.desc[elem.id as usize];
|
let desc = &queue.desc[elem.id as usize];
|
||||||
let addr = desc.addr as usize;
|
let addr = desc.addr as usize;
|
||||||
let num_blocks = (desc.len as usize).div_ceil(512);
|
//let num_blocks = (desc.len as usize).div_ceil(512);
|
||||||
let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().addr_to_block(addr) };
|
//let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().addr_to_block(addr) };
|
||||||
|
|
||||||
// free
|
//// free
|
||||||
unsafe {
|
//unsafe {
|
||||||
tc.memory_manager.as_mut().unwrap_unchecked().free_n_blocks(block, num_blocks);
|
// tc.memory_manager.as_mut().unwrap_unchecked().free_n_blocks(block, num_blocks);
|
||||||
}
|
//}
|
||||||
self.ack_used_idx = self.ack_used_idx.wrapping_add(1);
|
self.ack_used_idx = self.ack_used_idx.wrapping_add(1);
|
||||||
|
if self.ack_used_idx >= VIRTIO_QUEUE_SIZE as u16 {
|
||||||
|
self.ack_used_idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if !pended {
|
||||||
|
let uart = serial_port();
|
||||||
|
if let Some(uart) = uart {
|
||||||
|
uart.putstr("no pended\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transfer(&mut self, tc: &mut TrafficControl, x: u32, y: u32, width: u32, height: u32) {
|
pub fn transfer(&mut self, tc: &mut TrafficControl, x: u32, y: u32, width: u32, height: u32) {
|
||||||
// we have enough queue space to send 2 commands (4 descriptors), so lets send
|
let uart = serial_port();
|
||||||
// both the TransferToHost2D and the ResourceFlush commands in one go
|
if let Some(uart) = uart {
|
||||||
// i.e. don't call pending until the flush
|
//uart.putstr("transfer\n");
|
||||||
|
}
|
||||||
|
let mut free_me = [0; 4];
|
||||||
|
|
||||||
let cmd = heap_allocate_type::<TransferToHost2D>(tc);
|
let cmd = heap_allocate_type::<TransferToHost2D>(tc);
|
||||||
cmd.header.ctrl_type = VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D;
|
cmd.header.ctrl_type = VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D;
|
||||||
cmd.header.flags = 0;
|
cmd.header.flags = 0;
|
||||||
cmd.header.fence_id = 0;
|
cmd.header.fence_id = 0;
|
||||||
cmd.header.ctx_id = 0;
|
cmd.header.ctx_id = 0;
|
||||||
cmd.header.padding = 0;
|
cmd.header.ring_idx = 0;
|
||||||
|
cmd.header.padding = [0; 3];
|
||||||
cmd.rect.x = x;
|
cmd.rect.x = x;
|
||||||
cmd.rect.y = y;
|
cmd.rect.y = y;
|
||||||
cmd.rect.width = width;
|
cmd.rect.width = width;
|
||||||
|
|
@ -379,7 +449,7 @@ impl VirtIoGpuDevice {
|
||||||
cmd.offset = 0;
|
cmd.offset = 0;
|
||||||
cmd.resource_id = 1;
|
cmd.resource_id = 1;
|
||||||
cmd.padding = 0;
|
cmd.padding = 0;
|
||||||
let resp = heap_allocate_type::<CtrlHeader>(tc);
|
free_me[1] = unsafe { &(*cmd) as *const _ as usize };
|
||||||
|
|
||||||
let desc_rq = Descriptor {
|
let desc_rq = Descriptor {
|
||||||
addr: unsafe { &(*cmd) as *const _ as u64 },
|
addr: unsafe { &(*cmd) as *const _ as u64 },
|
||||||
|
|
@ -388,13 +458,14 @@ impl VirtIoGpuDevice {
|
||||||
next: 0,
|
next: 0,
|
||||||
};
|
};
|
||||||
let desc_resp = Descriptor {
|
let desc_resp = Descriptor {
|
||||||
addr: unsafe { &(*resp) as *const _ as u64 },
|
addr: self.latest_response as u64,
|
||||||
len: size_of::<CtrlHeader>() as u32,
|
len: size_of::<CtrlHeader>() as u32,
|
||||||
flags: VIRTIO_DESC_F_WRITE,
|
flags: VIRTIO_DESC_F_WRITE,
|
||||||
next: 0,
|
next: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.send_rq_rsp(desc_rq, desc_resp);
|
self.send_rq_rsp(desc_rq, desc_resp);
|
||||||
|
//self.pending(tc);
|
||||||
|
|
||||||
// resource flush
|
// resource flush
|
||||||
let cmd = heap_allocate_type::<ResourceFlush>(tc);
|
let cmd = heap_allocate_type::<ResourceFlush>(tc);
|
||||||
|
|
@ -402,14 +473,15 @@ impl VirtIoGpuDevice {
|
||||||
cmd.header.flags = 0;
|
cmd.header.flags = 0;
|
||||||
cmd.header.fence_id = 0;
|
cmd.header.fence_id = 0;
|
||||||
cmd.header.ctx_id = 0;
|
cmd.header.ctx_id = 0;
|
||||||
cmd.header.padding = 0;
|
cmd.header.ring_idx = 0;
|
||||||
|
cmd.header.padding = [0; 3];
|
||||||
cmd.rect.x = x;
|
cmd.rect.x = x;
|
||||||
cmd.rect.y = y;
|
cmd.rect.y = y;
|
||||||
cmd.rect.width = width;
|
cmd.rect.width = width;
|
||||||
cmd.rect.height = height;
|
cmd.rect.height = height;
|
||||||
cmd.resource_id = 1;
|
cmd.resource_id = 1;
|
||||||
cmd.padding = 0;
|
cmd.padding = 0;
|
||||||
let resp = heap_allocate_type::<CtrlHeader>(tc);
|
free_me[2] = unsafe { &(*cmd) as *const _ as usize };
|
||||||
|
|
||||||
let desc_rq = Descriptor {
|
let desc_rq = Descriptor {
|
||||||
addr: unsafe { &(*cmd) as *const _ as u64 },
|
addr: unsafe { &(*cmd) as *const _ as u64 },
|
||||||
|
|
@ -418,13 +490,25 @@ impl VirtIoGpuDevice {
|
||||||
next: 0,
|
next: 0,
|
||||||
};
|
};
|
||||||
let desc_resp = Descriptor {
|
let desc_resp = Descriptor {
|
||||||
addr: unsafe { &(*resp) as *const _ as u64 },
|
addr: self.latest_response as u64,
|
||||||
len: size_of::<CtrlHeader>() as u32,
|
len: size_of::<CtrlHeader>() as u32,
|
||||||
flags: VIRTIO_DESC_F_WRITE,
|
flags: VIRTIO_DESC_F_WRITE,
|
||||||
next: 0,
|
next: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.send_rq_rsp(desc_rq, desc_resp);
|
self.send_rq_rsp(desc_rq, desc_resp);
|
||||||
self.pending(tc);
|
// awful hack so that we don't overload the queue
|
||||||
|
while {
|
||||||
|
self.ack_used_idx == unsafe { (self.queue as *const VirtQueue).read_volatile().used.idx }
|
||||||
|
} {
|
||||||
|
|
||||||
|
}
|
||||||
|
//self.pending(tc);
|
||||||
|
for free in free_me.into_iter().skip(1) {
|
||||||
|
let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().addr_to_block(free) };
|
||||||
|
unsafe {
|
||||||
|
tc.memory_manager.as_mut().unwrap_unchecked().free_n_blocks(block, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
//! virtio devices
|
//! virtio devices
|
||||||
//! WARNING: virtio is currently completely broken! don't use it!
|
//! WARNING: virtio is currently completely broken! don't use it!
|
||||||
|
|
||||||
|
use core::sync::atomic::Ordering;
|
||||||
|
use crate::dev::{FRAMEBUFFER_ADDR, FRAMEBUFFER_BPP};
|
||||||
use crate::dev::virtio::block::{VirtIoBlockDevice, VirtIoBlockDeviceError};
|
use crate::dev::virtio::block::{VirtIoBlockDevice, VirtIoBlockDeviceError};
|
||||||
use crate::dev::virtio::gpu::{VirtIoGpuDevice, VirtIoGpuDeviceError};
|
use crate::dev::virtio::gpu::{VirtIoGpuDevice, VirtIoGpuDeviceError};
|
||||||
use crate::spinlock::Spinlock;
|
use crate::spinlock::Spinlock;
|
||||||
|
|
@ -34,7 +36,7 @@ pub const VIRTIO_MMIO_STATUS_FAILED: u32 = 1 << 7;
|
||||||
pub const VIRTIO_DESC_F_NEXT: u16 = 1 << 0;
|
pub const VIRTIO_DESC_F_NEXT: u16 = 1 << 0;
|
||||||
pub const VIRTIO_DESC_F_WRITE: u16 = 1 << 1;
|
pub const VIRTIO_DESC_F_WRITE: u16 = 1 << 1;
|
||||||
|
|
||||||
pub const VIRTIO_QUEUE_SIZE: usize = 4;
|
pub const VIRTIO_QUEUE_SIZE: usize = 128;
|
||||||
|
|
||||||
pub static VIRTIO_DEVICES: Spinlock<[Option<VirtIoDevice>; VIRTIO_MMIO_DEVCOUNT]> = Spinlock::new([const { None }; VIRTIO_MMIO_DEVCOUNT]);
|
pub static VIRTIO_DEVICES: Spinlock<[Option<VirtIoDevice>; VIRTIO_MMIO_DEVCOUNT]> = Spinlock::new([const { None }; VIRTIO_MMIO_DEVCOUNT]);
|
||||||
pub static VIRTIO_HARD_BLOCK_DEVICE: Spinlock<Option<u8>> = Spinlock::new(None);
|
pub static VIRTIO_HARD_BLOCK_DEVICE: Spinlock<Option<u8>> = Spinlock::new(None);
|
||||||
|
|
@ -86,13 +88,6 @@ pub fn heap_allocate_type<T: Sized>(tc: &mut TrafficControl) -> &'static mut T {
|
||||||
unsafe { &mut (*(addr as *mut T)) }
|
unsafe { &mut (*(addr as *mut T)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// WARNING: ptr will be invalid after this function returns!
|
|
||||||
pub fn free_type<T: Sized>(tc: &mut TrafficControl, ptr: &'static mut T) {
|
|
||||||
let num_blocks = size_of::<T>().div_ceil(512);
|
|
||||||
let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().addr_to_block(ptr as *const T as usize) };
|
|
||||||
unsafe { tc.memory_manager.as_mut().unwrap_unchecked().free_n_blocks(block, num_blocks) };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn probe_virtio_devices(tc: &mut TrafficControl) {
|
pub fn probe_virtio_devices(tc: &mut TrafficControl) {
|
||||||
let serial_port = crate::arch::serial_port();
|
let serial_port = crate::arch::serial_port();
|
||||||
let mut devices = VIRTIO_DEVICES.lock();
|
let mut devices = VIRTIO_DEVICES.lock();
|
||||||
|
|
@ -139,6 +134,8 @@ pub fn probe_virtio_devices(tc: &mut TrafficControl) {
|
||||||
// gpu device
|
// gpu device
|
||||||
let gpu_device = VirtIoGpuDevice::new_and_init(tc, addr);
|
let gpu_device = VirtIoGpuDevice::new_and_init(tc, addr);
|
||||||
if let Ok(gpu_device) = gpu_device {
|
if let Ok(gpu_device) = gpu_device {
|
||||||
|
FRAMEBUFFER_ADDR.store(gpu_device.framebuffer, Ordering::Relaxed);
|
||||||
|
FRAMEBUFFER_BPP.store(4, Ordering::Relaxed); // virtio always uses 4 byte stride
|
||||||
devices[i] = Some(VirtIoDevice::GPUDevice(gpu_device));
|
devices[i] = Some(VirtIoDevice::GPUDevice(gpu_device));
|
||||||
*VIRTIO_GPU_DEVICE.lock() = Some(i as u8);
|
*VIRTIO_GPU_DEVICE.lock() = Some(i as u8);
|
||||||
if let Some(serial_port) = &serial_port {
|
if let Some(serial_port) = &serial_port {
|
||||||
|
|
@ -201,16 +198,48 @@ pub fn read_sector(tc: &mut TrafficControl, buffer: usize, size: u32, sector: u6
|
||||||
let mut devices = VIRTIO_DEVICES.lock();
|
let mut devices = VIRTIO_DEVICES.lock();
|
||||||
if let Some(idx) = idx {
|
if let Some(idx) = idx {
|
||||||
if let Some(device) = devices[idx as usize].as_mut() {
|
if let Some(device) = devices[idx as usize].as_mut() {
|
||||||
match device {
|
return match device {
|
||||||
VirtIoDevice::BlockDevice(device) => {
|
VirtIoDevice::BlockDevice(device) => {
|
||||||
device.operation(tc, buffer, size, sector, false);
|
device.operation(tc, buffer, size, sector, false);
|
||||||
return true;
|
true
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if let Some(serial_port) = &crate::arch::serial_port() {
|
if let Some(serial_port) = &crate::arch::serial_port() {
|
||||||
serial_port.putstr("unexpected device type (not block)\n");
|
serial_port.putstr("unexpected device type (not block)\n");
|
||||||
}
|
}
|
||||||
return false;
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let uart = crate::uart::UART::new(0x1000_0000);
|
||||||
|
uart.putstr("no device")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let uart = crate::uart::UART::new(0x1000_0000);
|
||||||
|
uart.putstr("no idx")
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn framebuffer_update(tc: &mut TrafficControl, x: u32, y: u32, width: u32, height: u32) -> bool {
|
||||||
|
let idx = {
|
||||||
|
let lock = VIRTIO_GPU_DEVICE.lock();
|
||||||
|
*lock
|
||||||
|
};
|
||||||
|
let mut devices = VIRTIO_DEVICES.lock();
|
||||||
|
if let Some(idx) = idx {
|
||||||
|
if let Some(device) = devices[idx as usize].as_mut() {
|
||||||
|
return match device {
|
||||||
|
VirtIoDevice::GPUDevice(device) => {
|
||||||
|
device.transfer(tc, x, y, width, height);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if let Some(serial_port) = &crate::arch::serial_port() {
|
||||||
|
serial_port.putstr("unexpected device type (not gpu)\n");
|
||||||
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
11
src/main.rs
11
src/main.rs
|
|
@ -32,16 +32,6 @@ pub extern "C" fn ktask() -> ! {
|
||||||
syscalls::init_kernel();
|
syscalls::init_kernel();
|
||||||
print(b"kernel ready!\n");
|
print(b"kernel ready!\n");
|
||||||
|
|
||||||
//let kinfo = kinfo();
|
|
||||||
//print(b"\nkinfo\n");
|
|
||||||
//print(b"task count ");
|
|
||||||
//print(&u32_hex(kinfo.current_process_count as u32));
|
|
||||||
//print(b"\ntotal mem ");
|
|
||||||
//print(&u32_hex(kinfo.total_mem_blocks as u32));
|
|
||||||
//print(b"\nfree mem ");
|
|
||||||
//print(&u32_hex(kinfo.free_mem_blocks as u32));
|
|
||||||
//print(b"\n");
|
|
||||||
|
|
||||||
// load TURNTBL.DDI
|
// load TURNTBL.DDI
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -50,6 +40,7 @@ pub extern "C" fn ktask() -> ! {
|
||||||
rough_panic(['f', 's', 'e']);
|
rough_panic(['f', 's', 'e']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let mut fs = liblbos::fs::FileSystem::empty();
|
let mut fs = liblbos::fs::FileSystem::empty();
|
||||||
if !fs::open_fs(unsafe { &mut *(&mut fs as *mut _ as *mut _) }) {
|
if !fs::open_fs(unsafe { &mut *(&mut fs as *mut _ as *mut _) }) {
|
||||||
fserr();
|
fserr();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
|
use crate::arch::serial_port;
|
||||||
use crate::rough_panic;
|
use crate::rough_panic;
|
||||||
|
use crate::strprint::u32_hex;
|
||||||
|
|
||||||
#[cfg(feature = "arch_virt")]
|
#[cfg(feature = "arch_virt")]
|
||||||
|
|
||||||
|
|
@ -23,6 +25,14 @@ pub struct MemoryManager {
|
||||||
impl MemoryManager {
|
impl MemoryManager {
|
||||||
#[cfg(feature = "arch_virt")]
|
#[cfg(feature = "arch_virt")]
|
||||||
pub fn init() -> Self {
|
pub fn init() -> Self {
|
||||||
|
{
|
||||||
|
let uart = serial_port();
|
||||||
|
if let Some(uart) = uart {
|
||||||
|
uart.putstr("totalmem: ");
|
||||||
|
uart.put_bytes(&u32_hex(MEM_BLOCKS as u32));
|
||||||
|
uart.putstr("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
Self {
|
Self {
|
||||||
heap_start: _heap_start as _,
|
heap_start: _heap_start as _,
|
||||||
heap_size: _heap_size as _,
|
heap_size: _heap_size as _,
|
||||||
|
|
@ -37,6 +47,13 @@ impl MemoryManager {
|
||||||
blockmap: [0; (MEM_BLOCKS+7) / 8],
|
blockmap: [0; (MEM_BLOCKS+7) / 8],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_block_free(&self, block: usize) -> bool {
|
||||||
|
let block = block % 8;
|
||||||
|
let block = block / 8;
|
||||||
|
let val = self.blockmap[block];
|
||||||
|
(val & (1 << block)) == 0
|
||||||
|
}
|
||||||
|
|
||||||
pub fn block_to_addr(&self, block: usize) -> usize {
|
pub fn block_to_addr(&self, block: usize) -> usize {
|
||||||
block * BLOCK_SIZE + self.heap_start
|
block * BLOCK_SIZE + self.heap_start
|
||||||
|
|
@ -101,8 +118,8 @@ impl MemoryManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
0
|
rough_panic(['o', 'o', 'm'])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn free_n_blocks(&mut self, block: usize, n: usize) {
|
pub fn free_n_blocks(&mut self, block: usize, n: usize) {
|
||||||
|
|
|
||||||
|
|
@ -203,6 +203,14 @@ pub fn handle_syscall(
|
||||||
core::slice::from_raw_parts(addr as *const u8, count)
|
core::slice::from_raw_parts(addr as *const u8, count)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if tc.use_fb_console {
|
||||||
|
let mut fbcons = crate::dev::framebuffer::console::FBCONSOLE.lock();
|
||||||
|
fbcons.printstr(&mut tc, unsafe {
|
||||||
|
core::str::from_utf8_unchecked(unsafe {
|
||||||
|
core::slice::from_raw_parts(addr as *const u8, count)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
SysCall::ReadHBD => {
|
SysCall::ReadHBD => {
|
||||||
|
|
@ -229,6 +237,15 @@ pub fn handle_syscall(
|
||||||
SysCall::InitKernel => {
|
SysCall::InitKernel => {
|
||||||
tc.init_mem_if_not();
|
tc.init_mem_if_not();
|
||||||
crate::dev::probe_devices(&mut tc);
|
crate::dev::probe_devices(&mut tc);
|
||||||
|
if crate::dev::FRAMEBUFFER_ADDR.load(Ordering::Relaxed) != 0 {
|
||||||
|
tc.use_fb_console = true;
|
||||||
|
{
|
||||||
|
let uart = serial_port();
|
||||||
|
if let Some(uart) = uart {
|
||||||
|
uart.putstr("using framebuffer console\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
SysCall::SendNotification => {
|
SysCall::SendNotification => {
|
||||||
|
|
@ -330,6 +347,31 @@ pub fn handle_syscall(
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SysCall::DisableFramebufferConsole => {
|
||||||
|
tc.use_fb_console = false;
|
||||||
|
0
|
||||||
|
}
|
||||||
|
SysCall::EnableFramebufferConsole => {
|
||||||
|
if crate::dev::FRAMEBUFFER_ADDR.load(Ordering::Relaxed) != 0 {
|
||||||
|
tc.use_fb_console = true;
|
||||||
|
// clear the screen
|
||||||
|
let mut fbcons = crate::dev::framebuffer::console::FBCONSOLE.lock();
|
||||||
|
fbcons.clear_terminal(&mut tc);
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
SysCall::FramebufferPointer => {
|
||||||
|
crate::dev::FRAMEBUFFER_ADDR.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
SysCall::FlushFramebufferRect => {
|
||||||
|
let x = a1;
|
||||||
|
let y = a2;
|
||||||
|
let w = a3;
|
||||||
|
let h = a4;
|
||||||
|
crate::dev::framebuffer_update(&mut tc, x as u32, y as u32, w as u32, h as u32);
|
||||||
|
0
|
||||||
|
}
|
||||||
},
|
},
|
||||||
suspend,
|
suspend,
|
||||||
)
|
)
|
||||||
|
|
@ -419,6 +461,7 @@ pub struct TrafficControl {
|
||||||
pub inbuf_read: u8,
|
pub inbuf_read: u8,
|
||||||
pub inbuf_write: u8,
|
pub inbuf_write: u8,
|
||||||
pub hung_system: bool,
|
pub hung_system: bool,
|
||||||
|
pub use_fb_console: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Task {
|
pub struct Task {
|
||||||
|
|
@ -451,6 +494,7 @@ impl TrafficControl {
|
||||||
inbuf_read: 0,
|
inbuf_read: 0,
|
||||||
inbuf_write: 0,
|
inbuf_write: 0,
|
||||||
hung_system: false,
|
hung_system: false,
|
||||||
|
use_fb_console: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
// todo: this should probably be in the dev module
|
||||||
|
|
||||||
pub trait Serial {
|
pub trait Serial {
|
||||||
fn put(&self, c: u8);
|
fn put(&self, c: u8);
|
||||||
fn putstr(&self, s: &str);
|
fn putstr(&self, s: &str);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue