drivers: add a mock display driver using SDL2 for libos

This commit is contained in:
Yuekai Jia 2021-10-02 00:13:00 +08:00
parent af1e156dc4
commit dd835e5af7
40 changed files with 283 additions and 58 deletions

View File

@ -111,7 +111,7 @@ jobs:
- name: Prepare rootfs
run: make rootfs
- name: Test
run: arch=x86_64 cargo test --all-features --no-fail-fast --workspace --exclude zircon-loader
run: arch=x86_64 cargo test --no-fail-fast --workspace --exclude zircon-loader
# uses: actions-rs/cargo@v1
# with:
# command: test

View File

@ -8,7 +8,8 @@ description = "Device drivers of zCore"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
mock = ["async-std"]
graphic = []
mock = ["async-std", "sdl2"]
virtio = ["virtio-drivers"]
[dependencies]
@ -20,7 +21,10 @@ lazy_static = "1.4"
device_tree = { git = "https://github.com/rcore-os/device_tree-rs", rev = "4e8144b" }
bitmap-allocator = { git = "https://github.com/rcore-os/bitmap-allocator", rev = "b3f9f51" }
virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "2b3c6cf", optional = true }
[target.'cfg(not(target_os = "none"))'.dependencies]
async-std = { version = "1.10", optional = true }
sdl2 = { version = "0.34", optional = true }
[target.'cfg(target_arch = "x86_64")'.dependencies]
acpi = "4.0"

View File

@ -0,0 +1 @@
pub use crate::scheme::display::{ColorDepth, ColorFormat, DisplayInfo};

View File

@ -15,3 +15,5 @@ cfg_if::cfg_if! {
}
}
}
pub use super::scheme::irq::{IrqHandler, IrqPolarity, IrqTriggerMode};

View File

@ -1,7 +1,8 @@
use riscv::register::sie;
use spin::Mutex;
use crate::scheme::{IrqHandler, IrqScheme, Scheme};
use super::IrqHandler;
use crate::scheme::{IrqScheme, Scheme};
use crate::{DeviceError, DeviceResult};
const S_SOFT: usize = 1;

View File

@ -2,8 +2,9 @@ use core::ops::Range;
use spin::Mutex;
use super::IrqHandler;
use crate::io::{Io, Mmio};
use crate::scheme::{IrqHandler, IrqScheme, Scheme};
use crate::scheme::{IrqScheme, Scheme};
use crate::{utils::IrqManager, DeviceError, DeviceResult};
const IRQ_RANGE: Range<usize> = 1..1024;

View File

@ -9,7 +9,8 @@ use spin::Mutex;
use self::consts::{X86_INT_BASE, X86_INT_LOCAL_APIC_BASE};
use self::ioapic::{IoApic, IoApicList};
use self::lapic::LocalApic;
use crate::scheme::{IrqHandler, IrqPolarity, IrqScheme, IrqTriggerMode, Scheme};
use super::{IrqHandler, IrqPolarity, IrqTriggerMode};
use crate::scheme::{IrqScheme, Scheme};
use crate::{utils::IrqManager, DeviceError, DeviceResult, PhysAddr, VirtAddr};
const IOAPIC_IRQ_RANGE: Range<usize> = X86_INT_BASE..X86_INT_LOCAL_APIC_BASE;

View File

@ -18,6 +18,7 @@ pub mod mock;
pub mod virtio;
pub mod builder;
pub mod display;
pub mod io;
pub mod irq;
pub mod scheme;

View File

@ -0,0 +1,44 @@
pub mod sdl;
use alloc::vec::Vec;
use crate::display::{ColorDepth, ColorFormat, DisplayInfo};
use crate::scheme::{DisplayScheme, Scheme};
pub struct MockDisplay {
info: DisplayInfo,
fb: Vec<u8>,
}
impl MockDisplay {
pub fn new(width: u32, height: u32) -> Self {
let depth = ColorDepth::ColorDepth32;
let format = ColorFormat::RGBA8888;
let fb_size = (width * height * depth.bytes() as u32) as usize;
let info = DisplayInfo {
width,
height,
fb_size,
depth,
format,
};
let fb = vec![0; fb_size];
Self { info, fb }
}
}
impl Scheme for MockDisplay {
fn name(&self) -> &str {
"mock-display"
}
}
impl DisplayScheme for MockDisplay {
fn info(&self) -> DisplayInfo {
self.info
}
unsafe fn raw_fb(&self) -> &mut [u8] {
core::slice::from_raw_parts_mut(self.fb.as_ptr() as _, self.info.fb_size)
}
}

View File

@ -0,0 +1,62 @@
use sdl2::{event::Event, keyboard::Keycode, EventPump};
use sdl2::{pixels::PixelFormatEnum, render::Canvas, video::Window};
use crate::display::{ColorFormat, DisplayInfo};
use crate::scheme::DisplayScheme;
pub struct SdlWindow {
canvas: Canvas<Window>,
event_pump: EventPump,
info: DisplayInfo,
}
impl SdlWindow {
pub fn new(title: &str, info: DisplayInfo) -> Self {
assert_eq!(info.format, ColorFormat::RGBA8888);
let sdl_context = sdl2::init().unwrap();
let video_subsystem = sdl_context.video().unwrap();
let window = video_subsystem
.window(title, info.width, info.height)
.position_centered()
.build()
.unwrap();
let event_pump = sdl_context.event_pump().unwrap();
let mut canvas = window.into_canvas().build().unwrap();
canvas.clear();
canvas.present();
Self {
info,
canvas,
event_pump,
}
}
pub fn flush(&mut self, display: &dyn DisplayScheme) {
let texture_creator = self.canvas.texture_creator();
let mut texture = texture_creator
.create_texture_streaming(PixelFormatEnum::RGBA8888, self.info.width, self.info.height)
.unwrap();
let buf = unsafe { display.raw_fb() };
texture
.update(None, buf, display.info().width as usize * 4)
.unwrap();
self.canvas.copy(&texture, None, None).unwrap();
self.canvas.present();
}
pub fn is_quit(&mut self) -> bool {
for event in self.event_pump.poll_iter() {
match event {
Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => return true,
_ => {}
}
}
false
}
}

View File

@ -1,3 +1,4 @@
mod uart;
pub mod uart;
pub use uart::MockUart;
#[cfg(feature = "graphic")]
pub mod display;

View File

@ -3,7 +3,8 @@ use std::collections::VecDeque;
use async_std::{io, io::prelude::*, task};
use spin::Mutex;
use crate::scheme::{IrqHandler, Scheme, UartScheme};
use crate::irq::IrqHandler;
use crate::scheme::{Scheme, UartScheme};
use crate::{utils::EventListener, DeviceResult};
const UART_BUF_LEN: usize = 256;

View File

@ -1,7 +1,57 @@
use super::Scheme;
pub struct DisplayInfo;
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColorDepth {
ColorDepth8 = 8,
ColorDepth16 = 16,
ColorDepth24 = 24,
ColorDepth32 = 32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColorFormat {
RGB332,
RGB565,
RGBA8888, // QEMU and low version RPi use RGBA
BGRA8888, // RPi3 B+ uses BGRA
VgaPalette,
}
#[derive(Debug, Clone, Copy)]
pub struct DisplayInfo {
/// visible width
pub width: u32,
/// visible height
pub height: u32,
/// frame buffer size
pub fb_size: usize,
/// bits per pixel
pub depth: ColorDepth,
/// color encoding format of RGBA
pub format: ColorFormat,
}
impl ColorDepth {
pub fn try_from(depth: u8) -> Result<Self, &'static str> {
match depth {
8 => Ok(Self::ColorDepth8),
16 => Ok(Self::ColorDepth16),
24 => Ok(Self::ColorDepth24),
32 => Ok(Self::ColorDepth32),
_ => Err("unsupported color depth"),
}
}
pub fn bytes(self) -> u8 {
self as u8 / 8
}
}
pub trait DisplayScheme: Scheme {
fn info(&self) -> DisplayInfo;
#[allow(clippy::mut_from_ref)]
unsafe fn raw_fb(&self) -> &mut [u8];
}

View File

@ -1,16 +1,16 @@
mod block;
mod display;
mod input;
mod irq;
mod net;
mod uart;
pub(super) mod block;
pub(super) mod display;
pub(super) mod input;
pub(super) mod irq;
pub(super) mod net;
pub(super) mod uart;
use alloc::sync::Arc;
pub use block::BlockScheme;
pub use display::DisplayScheme;
pub use input::InputScheme;
pub use irq::{IrqHandler, IrqPolarity, IrqScheme, IrqTriggerMode};
pub use irq::IrqScheme;
pub use net::NetScheme;
pub use uart::UartScheme;

View File

@ -1,5 +1,5 @@
use super::{IrqHandler, Scheme};
use crate::DeviceResult;
use super::Scheme;
use crate::{irq::IrqHandler, DeviceResult};
pub trait UartScheme: Scheme {
fn try_recv(&self) -> DeviceResult<Option<u8>>;

View File

@ -2,7 +2,8 @@ use alloc::{boxed::Box, collections::VecDeque, string::String, sync::Arc};
use spin::Mutex;
use crate::scheme::{IrqHandler, Scheme, UartScheme};
use crate::irq::IrqHandler;
use crate::scheme::{Scheme, UartScheme};
use crate::utils::EventListener;
use crate::DeviceResult;

View File

@ -5,7 +5,8 @@ use bitflags::bitflags;
use spin::Mutex;
use crate::io::{Io, Mmio, ReadOnly};
use crate::scheme::{IrqHandler, Scheme, UartScheme};
use crate::irq::IrqHandler;
use crate::scheme::{Scheme, UartScheme};
use crate::{utils::EventListener, DeviceResult};
bitflags! {

View File

@ -2,7 +2,7 @@ use alloc::vec::Vec;
use spin::Mutex;
use crate::scheme::IrqHandler;
use crate::irq::IrqHandler;
pub struct EventListener {
events: Mutex<Vec<(IrqHandler, bool)>>,

View File

@ -3,7 +3,7 @@
use core::ops::Range;
use super::IdAllocator;
use crate::{scheme::IrqHandler, DeviceError, DeviceResult};
use crate::{irq::IrqHandler, DeviceError, DeviceResult};
pub struct IrqManager<const IRQ_COUNT: usize> {
irq_range: Range<usize>,

View File

@ -3,7 +3,8 @@ use core::fmt::{Result, Write};
use spin::Mutex;
use virtio_drivers::{VirtIOConsole as InnerDriver, VirtIOHeader};
use crate::scheme::{IrqHandler, Scheme, UartScheme};
use crate::irq::IrqHandler;
use crate::scheme::{Scheme, UartScheme};
use crate::{utils::EventListener, DeviceResult};
pub struct VirtIoConsole<'a> {

View File

@ -9,6 +9,7 @@ description = "Kernel HAL interface definations."
[features]
libos = ["async-std", "zcore-drivers/mock"]
graphic = ["zcore-drivers/graphic"]
[dependencies]
log = "0.4"

View File

@ -48,7 +48,7 @@ impl IoMapper for IoMapperImpl {
pub(super) fn init() -> DeviceResult {
let dev_list =
DeviceTreeDriverBuilder::new(phys_to_virt(crate::config::KCONFIG.dtb_paddr), IoMapperImpl)?
DeviceTreeDriverBuilder::new(phys_to_virt(crate::KCONFIG.dtb_paddr), IoMapperImpl)?
.build()?;
for dev in dev_list.into_iter() {
if let Device::Uart(uart) = dev {

View File

@ -1 +1 @@
pub use zcore_drivers::scheme::{IrqHandler, IrqPolarity, IrqTriggerMode};
pub use zcore_drivers::irq::{IrqHandler, IrqPolarity, IrqTriggerMode};

View File

@ -47,7 +47,11 @@ macro_rules! device_fn_def {
}
pub fn try_get(idx: usize) -> Option<Arc<dyn $scheme>> {
DEVICES.$dev.read().get(idx).cloned()
all().get(idx).cloned()
}
pub fn find(name: &str) -> Option<Arc<dyn $scheme>> {
all().iter().find(|d| d.name() == name).cloned()
}
pub fn first() -> Option<Arc<dyn $scheme>> {
@ -55,7 +59,7 @@ macro_rules! device_fn_def {
}
pub fn first_unwrap() -> Arc<dyn $scheme> {
first().expect(concat!(stringify!($dev), " deivce not initialized!"))
first().expect(concat!(stringify!($dev), " device not initialized!"))
}
}
};

View File

@ -124,9 +124,16 @@ hal_fn_def! {
pub mod timer {
/// Get current time.
/// TODO: use `Instant` as return type.
pub fn timer_now() -> Duration;
/// Converting from now-relative durations to absolute deadlines.
pub fn deadline_after(dur: Duration) -> Duration {
timer_now() + dur
}
/// Set a new timer. After `deadline`, the `callback` will be called.
/// TODO: use `Instant` as the type of `deadline`.
pub fn timer_set(deadline: Duration, callback: Box<dyn FnOnce(Duration) + Send + Sync>);
/// Check timers, call when timer interrupt happened.

View File

@ -33,7 +33,10 @@ cfg_if! {
}
}
pub(crate) use config::KCONFIG;
pub(crate) use kernel_handler::KHANDLER;
pub use common::{addr, defs::*, serial, user};
pub use config::*;
pub use config::KernelConfig;
pub use imp::*;
pub use kernel_handler::*;
pub use kernel_handler::KernelHandler;

View File

@ -1,7 +1,7 @@
use alloc::sync::Arc;
use zcore_drivers::scheme::Scheme;
use zcore_drivers::{mock::MockUart, Device};
use zcore_drivers::mock::uart::MockUart;
use zcore_drivers::{scheme::Scheme, Device};
use crate::drivers;
@ -9,4 +9,11 @@ pub(super) fn init() {
let uart = Arc::new(MockUart::new());
drivers::add_device(Device::Uart(uart.clone()));
MockUart::start_irq_serve(move || uart.handle_irq(0));
#[cfg(feature = "graphic")]
{
use zcore_drivers::mock::display::MockDisplay;
let display = Arc::new(MockDisplay::new(800, 600));
drivers::add_device(Device::Display(display));
}
}

View File

@ -1,7 +1,8 @@
use super::mem_common::AVAILABLE_FRAMES;
use crate::{KernelHandler, PhysAddr};
use crate::kernel_handler::{DummyKernelHandler, KernelHandler};
use crate::PhysAddr;
impl KernelHandler for crate::DummyKernelHandler {
impl KernelHandler for DummyKernelHandler {
fn frame_alloc(&self) -> Option<PhysAddr> {
let ret = AVAILABLE_FRAMES.lock().unwrap().pop_front();
trace!("frame alloc: {:?}", ret);

View File

@ -1,8 +1,7 @@
mod drivers;
mod dummy;
mod mem_common;
pub(super) mod dummy;
pub mod config;
pub mod mem;
pub mod thread;
@ -10,6 +9,9 @@ pub mod timer;
pub mod vdso;
pub mod vm;
#[path = "special.rs"]
pub mod libos;
pub use super::hal_fn::{context, cpu, interrupt, rand};
hal_fn_impl_default!(context, cpu, interrupt, rand, super::hal_fn::serial);
@ -30,7 +32,8 @@ include!("macos.rs");
///
/// This function must be called at the beginning.
pub fn init() {
crate::KHANDLER.init_once_by(&crate::DummyKernelHandler);
let _ = crate::KCONFIG;
crate::KHANDLER.init_once_by(&crate::kernel_handler::DummyKernelHandler);
drivers::init();

View File

@ -0,0 +1,11 @@
#[cfg(feature = "graphic")]
pub fn run_display_serve() {
use zcore_drivers::mock::display::sdl::SdlWindow;
let display = crate::drivers::display::first_unwrap();
let mut window = SdlWindow::new("zcore-libos", display.info());
while !window.is_quit() {
window.flush(display.as_ref());
std::thread::sleep(std::time::Duration::from_millis(10));
}
}

View File

@ -1,3 +1,4 @@
use async_std::task;
use std::time::{Duration, SystemTime};
hal_fn_impl! {
@ -9,11 +10,9 @@ hal_fn_impl! {
}
fn timer_set(deadline: Duration, callback: Box<dyn FnOnce(Duration) + Send + Sync>) {
std::thread::spawn(move || {
let now = timer_now();
if deadline > now {
std::thread::sleep(deadline - now);
}
let dur = deadline - timer_now();
task::spawn(async move {
task::sleep(dur).await;
callback(timer_now());
});
}

View File

@ -22,4 +22,4 @@ async-std = { version = "1.9", features = ["attributes"], optional = true }
default = ["std", "libos"]
libos = ["kernel-hal/libos"]
std = ["env_logger", "async-std", "rcore-fs-hostfs", "zircon-object/aspace-separate"]
graphic = ["std", "linux-object/graphic"]
graphic = ["linux-object/graphic", "kernel-hal/graphic"]

View File

@ -16,11 +16,6 @@ async fn main() {
init_logger();
// init HAL implementation on unix
kernel_hal::init();
#[cfg(feature = "graphic")]
{
kernel_hal::dev::fb::init();
kernel_hal::dev::input::init();
}
if let Some(uart) = kernel_hal::drivers::uart::first() {
uart.clone().subscribe(
@ -35,11 +30,23 @@ async fn main() {
// run first process
let args: Vec<_> = std::env::args().skip(1).collect();
let proc_name = args.join(" ");
let envs = vec!["PATH=/usr/sbin:/usr/bin:/sbin:/bin:/usr/x86_64-alpine-linux-musl/bin".into()];
let hostfs = HostFS::new("rootfs");
let proc = run(args, envs, hostfs);
let code = proc.wait_for_exit().await;
let wait_for_exit = async move { proc.wait_for_exit().await };
#[cfg(feature = "graphic")]
let wait_for_exit = {
let handle = async_std::task::spawn(wait_for_exit);
kernel_hal::libos::run_display_serve();
handle
};
let code = wait_for_exit.await;
log::info!("process {:?} exited with {}", proc_name, code);
std::process::exit(code as i32);
}

View File

@ -32,6 +32,7 @@ mod pseudo;
mod random;
mod stdio;
/* TODO
cfg_if::cfg_if! {
if #[cfg(feature = "graphic")] {
mod fbdev;
@ -40,6 +41,7 @@ cfg_if::cfg_if! {
pub use self::fbdev::*;
}
}
*/
#[async_trait]
/// Generic file interface
@ -129,7 +131,8 @@ pub fn create_root_fs(rootfs: Arc<dyn FileSystem>) -> Arc<dyn INode> {
.add("urandom", Arc::new(RandomINode::new(true)))
.expect("failed to mknod /dev/urandom");
#[cfg(feature = "graphic")]
/*
#[cfg(feature = "graphic")] // TODO
{
devfs
.add("fb0", Arc::new(Fbdev::default()))
@ -143,6 +146,7 @@ pub fn create_root_fs(rootfs: Arc<dyn FileSystem>) -> Arc<dyn INode> {
.add("input-mice", Arc::new(InputMiceInode::default()))
.expect("failed to mknod /dev/input-mice");
}
*/
// mount DevFS at /dev
let dev = root.find(true, "dev").unwrap_or_else(|_| {

View File

@ -12,7 +12,7 @@ use core::mem::size_of;
use core::pin::Pin;
use core::task::{Context, Poll};
use core::time::Duration;
use kernel_hal::timer::timer_set;
use kernel_hal::timer;
use linux_object::fs::FileDesc;
use linux_object::time::*;
@ -84,8 +84,8 @@ impl Syscall<'_> {
return Poll::Ready(Ok(0));
} else {
let waker = cx.waker().clone();
timer_set(
Duration::from_millis(self.timeout_msecs as u64),
timer::timer_set(
timer::deadline_after(Duration::from_millis(self.timeout_msecs as u64)),
Box::new(move |_| waker.wake()),
);
}
@ -230,8 +230,8 @@ impl Syscall<'_> {
return Poll::Ready(Ok(0));
} else {
let waker = cx.waker().clone();
timer_set(
Duration::from_millis(self.timeout_msecs as u64),
timer::timer_set(
timer::deadline_after(Duration::from_millis(self.timeout_msecs as u64)),
Box::new(move |_| waker.wake()),
);
}

View File

@ -296,8 +296,9 @@ impl Syscall<'_> {
/// an interval specified with nanosecond precision
pub async fn sys_nanosleep(&self, req: UserInPtr<TimeSpec>) -> SysResult {
info!("nanosleep: deadline={:?}", req);
let req = req.read()?;
kernel_hal::thread::sleep_until(req.into()).await;
let duration = req.read()?.into();
use kernel_hal::{thread, timer};
thread::sleep_until(timer::deadline_after(duration)).await;
Ok(0)
}

View File

@ -5,6 +5,7 @@
/libc-test/src/functional/ipc_shm.exe
/libc-test/src/functional/popen.exe
/libc-test/src/functional/pthread_cancel-points.exe
/libc-test/src/functional/pthread_cond.exe
/libc-test/src/functional/pthread_robust.exe
/libc-test/src/functional/sem_open.exe
/libc-test/src/functional/socket.exe

View File

@ -7,7 +7,7 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
graphic = []
graphic = ["linux-loader?/graphic"]
board_qemu = []
board_d1 = ["link_user_img"]
ramfs = []

View File

@ -11,7 +11,7 @@ hypervisor ?=
smp ?= 1
test_filter ?= *.*
build_args := -Z build-std=core,alloc --target $(arch).json
build_args := -Z weak-dep-features -Z build-std=core,alloc --target $(arch).json
build_path := target/$(arch)/$(mode)
kernel := $(build_path)/zcore
kernel_img := $(build_path)/zcore.img

View File

@ -65,6 +65,7 @@ pub extern "C" fn _start(boot_info: &BootInfo) -> ! {
info!("{:#x?}", config);
kernel_hal::init(config, &ZcoreKernelHandler);
/* TODO
#[cfg(feature = "graphic")]
{
let (width, height) = boot_info.graphic_info.mode.resolution();
@ -72,6 +73,7 @@ pub extern "C" fn _start(boot_info: &BootInfo) -> ! {
let fb_size = boot_info.graphic_info.fb_size as usize;
kernel_hal::dev::fb::init(width as u32, height as u32, fb_addr, fb_size);
}
*/
let ramfs_data = unsafe {
core::slice::from_raw_parts_mut(
@ -126,6 +128,7 @@ pub extern "C" fn rust_main(hartid: usize, device_tree_paddr: usize) -> ! {
};
warn!("cmdline: {:?}", cmdline);
/* TODO
#[cfg(feature = "graphic")]
{
let gpu = GPU_DRIVERS
@ -138,6 +141,7 @@ pub extern "C" fn rust_main(hartid: usize, device_tree_paddr: usize) -> ! {
let (fb_vaddr, fb_size) = gpu.setup_framebuffer();
kernel_hal::deb::fb::init(width, height, fb_vaddr, fb_size);
}
*/
// riscv64在之后使用ramfs或virtio, 而x86_64则由bootloader载入文件系统镜像到内存
main(&mut [], &cmdline);