forked from rcore-os/zCore
drivers: support graphic console output
This commit is contained in:
parent
dd835e5af7
commit
d8e6aca2f9
|
@ -8,7 +8,7 @@ description = "Device drivers of zCore"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
graphic = []
|
graphic = ["rcore-console"]
|
||||||
mock = ["async-std", "sdl2"]
|
mock = ["async-std", "sdl2"]
|
||||||
virtio = ["virtio-drivers"]
|
virtio = ["virtio-drivers"]
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ lazy_static = "1.4"
|
||||||
device_tree = { git = "https://github.com/rcore-os/device_tree-rs", rev = "4e8144b" }
|
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" }
|
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 }
|
virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "2b3c6cf", optional = true }
|
||||||
|
rcore-console = { git = "https://github.com/rcore-os/rcore-console", default-features = false, rev = "ee95abd", optional = true }
|
||||||
|
|
||||||
[target.'cfg(not(target_os = "none"))'.dependencies]
|
[target.'cfg(not(target_os = "none"))'.dependencies]
|
||||||
async-std = { version = "1.10", optional = true }
|
async-std = { version = "1.10", optional = true }
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
pub use crate::scheme::display::{ColorDepth, ColorFormat, DisplayInfo};
|
pub use crate::scheme::display::{ColorFormat, DisplayInfo, RgbColor};
|
||||||
|
|
|
@ -9,8 +9,6 @@ extern crate log;
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
mod utils;
|
|
||||||
|
|
||||||
#[cfg(feature = "mock")]
|
#[cfg(feature = "mock")]
|
||||||
pub mod mock;
|
pub mod mock;
|
||||||
|
|
||||||
|
@ -23,6 +21,7 @@ pub mod io;
|
||||||
pub mod irq;
|
pub mod irq;
|
||||||
pub mod scheme;
|
pub mod scheme;
|
||||||
pub mod uart;
|
pub mod uart;
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum DeviceError {
|
pub enum DeviceError {
|
||||||
|
|
|
@ -2,7 +2,7 @@ pub mod sdl;
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
use crate::display::{ColorDepth, ColorFormat, DisplayInfo};
|
use crate::display::{ColorFormat, DisplayInfo};
|
||||||
use crate::scheme::{DisplayScheme, Scheme};
|
use crate::scheme::{DisplayScheme, Scheme};
|
||||||
|
|
||||||
pub struct MockDisplay {
|
pub struct MockDisplay {
|
||||||
|
@ -12,15 +12,13 @@ pub struct MockDisplay {
|
||||||
|
|
||||||
impl MockDisplay {
|
impl MockDisplay {
|
||||||
pub fn new(width: u32, height: u32) -> Self {
|
pub fn new(width: u32, height: u32) -> Self {
|
||||||
let depth = ColorDepth::ColorDepth32;
|
let format = ColorFormat::RGB888;
|
||||||
let format = ColorFormat::RGBA8888;
|
let fb_size = (width * height * format.bytes() as u32) as usize;
|
||||||
let fb_size = (width * height * depth.bytes() as u32) as usize;
|
|
||||||
let info = DisplayInfo {
|
let info = DisplayInfo {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
fb_size,
|
|
||||||
depth,
|
|
||||||
format,
|
format,
|
||||||
|
fb_size,
|
||||||
};
|
};
|
||||||
let fb = vec![0; fb_size];
|
let fb = vec![0; fb_size];
|
||||||
Self { info, fb }
|
Self { info, fb }
|
||||||
|
@ -34,10 +32,12 @@ impl Scheme for MockDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DisplayScheme for MockDisplay {
|
impl DisplayScheme for MockDisplay {
|
||||||
|
#[inline]
|
||||||
fn info(&self) -> DisplayInfo {
|
fn info(&self) -> DisplayInfo {
|
||||||
self.info
|
self.info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
unsafe fn raw_fb(&self) -> &mut [u8] {
|
unsafe fn raw_fb(&self) -> &mut [u8] {
|
||||||
core::slice::from_raw_parts_mut(self.fb.as_ptr() as _, self.info.fb_size)
|
core::slice::from_raw_parts_mut(self.fb.as_ptr() as _, self.info.fb_size)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,47 +1,48 @@
|
||||||
|
use alloc::sync::Arc;
|
||||||
|
|
||||||
use sdl2::{event::Event, keyboard::Keycode, EventPump};
|
use sdl2::{event::Event, keyboard::Keycode, EventPump};
|
||||||
use sdl2::{pixels::PixelFormatEnum, render::Canvas, video::Window};
|
use sdl2::{pixels::PixelFormatEnum, render::Canvas, video::Window};
|
||||||
|
|
||||||
use crate::display::{ColorFormat, DisplayInfo};
|
use crate::display::ColorFormat;
|
||||||
use crate::scheme::DisplayScheme;
|
use crate::scheme::DisplayScheme;
|
||||||
|
|
||||||
pub struct SdlWindow {
|
pub struct SdlWindow {
|
||||||
canvas: Canvas<Window>,
|
canvas: Canvas<Window>,
|
||||||
event_pump: EventPump,
|
event_pump: EventPump,
|
||||||
info: DisplayInfo,
|
display: Arc<dyn DisplayScheme>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SdlWindow {
|
impl SdlWindow {
|
||||||
pub fn new(title: &str, info: DisplayInfo) -> Self {
|
pub fn new(title: &str, display: Arc<dyn DisplayScheme>) -> Self {
|
||||||
assert_eq!(info.format, ColorFormat::RGBA8888);
|
|
||||||
let sdl_context = sdl2::init().unwrap();
|
let sdl_context = sdl2::init().unwrap();
|
||||||
let video_subsystem = sdl_context.video().unwrap();
|
let video_subsystem = sdl_context.video().unwrap();
|
||||||
let window = video_subsystem
|
let window = video_subsystem
|
||||||
.window(title, info.width, info.height)
|
.window(title, display.info().width, display.info().height)
|
||||||
.position_centered()
|
.position_centered()
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let event_pump = sdl_context.event_pump().unwrap();
|
let event_pump = sdl_context.event_pump().unwrap();
|
||||||
let mut canvas = window.into_canvas().build().unwrap();
|
let canvas = window.into_canvas().build().unwrap();
|
||||||
canvas.clear();
|
let mut ret = Self {
|
||||||
canvas.present();
|
display,
|
||||||
Self {
|
|
||||||
info,
|
|
||||||
canvas,
|
canvas,
|
||||||
event_pump,
|
event_pump,
|
||||||
}
|
};
|
||||||
|
ret.flush();
|
||||||
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush(&mut self, display: &dyn DisplayScheme) {
|
pub fn flush(&mut self) {
|
||||||
|
let info = self.display.info();
|
||||||
let texture_creator = self.canvas.texture_creator();
|
let texture_creator = self.canvas.texture_creator();
|
||||||
|
let format: PixelFormatEnum = info.format.into();
|
||||||
let mut texture = texture_creator
|
let mut texture = texture_creator
|
||||||
.create_texture_streaming(PixelFormatEnum::RGBA8888, self.info.width, self.info.height)
|
.create_texture_streaming(format, info.width, info.height)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let buf = unsafe { display.raw_fb() };
|
let buf = unsafe { self.display.raw_fb() };
|
||||||
texture
|
texture.update(None, buf, info.pitch() as usize).unwrap();
|
||||||
.update(None, buf, display.info().width as usize * 4)
|
|
||||||
.unwrap();
|
|
||||||
self.canvas.copy(&texture, None, None).unwrap();
|
self.canvas.copy(&texture, None, None).unwrap();
|
||||||
self.canvas.present();
|
self.canvas.present();
|
||||||
}
|
}
|
||||||
|
@ -60,3 +61,15 @@ impl SdlWindow {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl core::convert::From<ColorFormat> for PixelFormatEnum {
|
||||||
|
fn from(format: ColorFormat) -> Self {
|
||||||
|
match format {
|
||||||
|
ColorFormat::RGB332 => Self::RGB332,
|
||||||
|
ColorFormat::RGB565 => Self::RGB565,
|
||||||
|
ColorFormat::RGB888 => Self::BGR24, // notice: BGR24 means R at the highest address, B at the lowest address.
|
||||||
|
ColorFormat::RGBA8888 => Self::RGBA8888,
|
||||||
|
ColorFormat::BGRA8888 => Self::BGRA8888,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,21 +1,65 @@
|
||||||
use super::Scheme;
|
use super::Scheme;
|
||||||
|
use crate::{DeviceError, DeviceResult};
|
||||||
|
|
||||||
#[repr(u8)]
|
#[repr(transparent)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum ColorDepth {
|
pub struct RgbColor(u32);
|
||||||
ColorDepth8 = 8,
|
|
||||||
ColorDepth16 = 16,
|
impl RgbColor {
|
||||||
ColorDepth24 = 24,
|
#[inline]
|
||||||
ColorDepth32 = 32,
|
pub const fn new(r: u8, g: u8, b: u8) -> Self {
|
||||||
|
Self(((r as u32) << 16) | ((g as u32) << 8) | b as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn r(self) -> u8 {
|
||||||
|
(self.0 >> 16) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn g(self) -> u8 {
|
||||||
|
(self.0 >> 8) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn b(self) -> u8 {
|
||||||
|
self.0 as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn raw_value(self) -> u32 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Color format for one pixel. `RGB888` means R in bits 16-23, G in bits 8-15 and B in bits 0-7.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum ColorFormat {
|
pub enum ColorFormat {
|
||||||
RGB332,
|
RGB332,
|
||||||
RGB565,
|
RGB565,
|
||||||
|
RGB888,
|
||||||
RGBA8888, // QEMU and low version RPi use RGBA
|
RGBA8888, // QEMU and low version RPi use RGBA
|
||||||
BGRA8888, // RPi3 B+ uses BGRA
|
BGRA8888, // RPi3 B+ uses BGRA
|
||||||
VgaPalette,
|
}
|
||||||
|
|
||||||
|
impl ColorFormat {
|
||||||
|
/// Number of bits per pixel.
|
||||||
|
#[inline]
|
||||||
|
pub const fn depth(self) -> u8 {
|
||||||
|
match self {
|
||||||
|
Self::RGB332 => 8,
|
||||||
|
Self::RGB565 => 16,
|
||||||
|
Self::RGB888 => 24,
|
||||||
|
Self::RGBA8888 => 32,
|
||||||
|
Self::BGRA8888 => 32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Number of bytes per pixel.
|
||||||
|
#[inline]
|
||||||
|
pub const fn bytes(self) -> u8 {
|
||||||
|
self.depth() / 8
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
@ -24,28 +68,17 @@ pub struct DisplayInfo {
|
||||||
pub width: u32,
|
pub width: u32,
|
||||||
/// visible height
|
/// visible height
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
/// frame buffer size
|
|
||||||
pub fb_size: usize,
|
|
||||||
|
|
||||||
/// bits per pixel
|
|
||||||
pub depth: ColorDepth,
|
|
||||||
/// color encoding format of RGBA
|
/// color encoding format of RGBA
|
||||||
pub format: ColorFormat,
|
pub format: ColorFormat,
|
||||||
|
/// frame buffer size
|
||||||
|
pub fb_size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ColorDepth {
|
impl DisplayInfo {
|
||||||
pub fn try_from(depth: u8) -> Result<Self, &'static str> {
|
/// Number of bytes between each row of the frame buffer.
|
||||||
match depth {
|
#[inline]
|
||||||
8 => Ok(Self::ColorDepth8),
|
pub const fn pitch(self) -> u32 {
|
||||||
16 => Ok(Self::ColorDepth16),
|
self.width * self.format.bytes() as u32
|
||||||
24 => Ok(Self::ColorDepth24),
|
|
||||||
32 => Ok(Self::ColorDepth32),
|
|
||||||
_ => Err("unsupported color depth"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bytes(self) -> u8 {
|
|
||||||
self as u8 / 8
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,5 +86,51 @@ pub trait DisplayScheme: Scheme {
|
||||||
fn info(&self) -> DisplayInfo;
|
fn info(&self) -> DisplayInfo;
|
||||||
|
|
||||||
#[allow(clippy::mut_from_ref)]
|
#[allow(clippy::mut_from_ref)]
|
||||||
|
/// Returns the raw framebuffer.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This function is unsafe because it returns the raw pointer of the framebuffer.
|
||||||
unsafe fn raw_fb(&self) -> &mut [u8];
|
unsafe fn raw_fb(&self) -> &mut [u8];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_pixel(&self, x: u32, y: u32, color: RgbColor) -> DeviceResult {
|
||||||
|
let info = self.info();
|
||||||
|
let fb = unsafe { self.raw_fb() };
|
||||||
|
let offset = (x + y * info.width) as usize * info.format.bytes() as usize;
|
||||||
|
if offset >= info.fb_size {
|
||||||
|
return Err(DeviceError::InvalidParam);
|
||||||
|
}
|
||||||
|
unsafe { write_color(&mut fb[offset as usize] as _, color, info.format) };
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn pack_channel(r_val: u8, _r_bits: u8, g_val: u8, g_bits: u8, b_val: u8, b_bits: u8) -> u32 {
|
||||||
|
((r_val as u32) << (g_bits + b_bits)) | ((g_val as u32) << b_bits) | b_val as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn write_color(ptr: *mut u8, color: RgbColor, format: ColorFormat) {
|
||||||
|
let (r, g, b) = (color.r(), color.g(), color.b());
|
||||||
|
let dst = core::slice::from_raw_parts_mut(ptr, 4);
|
||||||
|
match format {
|
||||||
|
ColorFormat::RGB332 => {
|
||||||
|
*ptr = pack_channel(r >> (8 - 3), 3, g >> (8 - 3), 3, b >> (8 - 2), 2) as u8
|
||||||
|
}
|
||||||
|
ColorFormat::RGB565 => {
|
||||||
|
*(ptr as *mut u16) =
|
||||||
|
pack_channel(r >> (8 - 5), 5, g >> (8 - 6), 6, b >> (8 - 5), 5) as u16
|
||||||
|
}
|
||||||
|
ColorFormat::RGB888 => {
|
||||||
|
dst[2] = r;
|
||||||
|
dst[1] = g;
|
||||||
|
dst[0] = b;
|
||||||
|
}
|
||||||
|
ColorFormat::RGBA8888 => *(ptr as *mut u32) = color.raw_value() << 8,
|
||||||
|
ColorFormat::BGRA8888 => {
|
||||||
|
dst[3] = b;
|
||||||
|
dst[2] = g;
|
||||||
|
dst[1] = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
use alloc::sync::Arc;
|
||||||
|
use core::convert::Infallible;
|
||||||
|
use core::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
use rcore_console::{Console, ConsoleOnGraphic, DrawTarget, OriginDimensions, Pixel, Rgb888, Size};
|
||||||
|
|
||||||
|
use crate::scheme::DisplayScheme;
|
||||||
|
|
||||||
|
pub struct DisplayWrapper(Arc<dyn DisplayScheme>);
|
||||||
|
|
||||||
|
pub struct GraphicConsole {
|
||||||
|
inner: ConsoleOnGraphic<DisplayWrapper>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GraphicConsole {
|
||||||
|
pub fn new(display: Arc<dyn DisplayScheme>) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Console::on_frame_buffer(DisplayWrapper(display)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DrawTarget for DisplayWrapper {
|
||||||
|
type Color = Rgb888;
|
||||||
|
type Error = Infallible;
|
||||||
|
|
||||||
|
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = Pixel<Self::Color>>,
|
||||||
|
{
|
||||||
|
for p in pixels {
|
||||||
|
let color = unsafe { core::mem::transmute(p.1) };
|
||||||
|
self.0
|
||||||
|
.write_pixel(p.0.x as u32, p.0.y as u32, color)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OriginDimensions for DisplayWrapper {
|
||||||
|
fn size(&self) -> Size {
|
||||||
|
let info = self.0.info();
|
||||||
|
Size::new(info.width, info.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for GraphicConsole {
|
||||||
|
type Target = ConsoleOnGraphic<DisplayWrapper>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for GraphicConsole {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.inner
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,13 @@ mod event_listener;
|
||||||
mod id_allocator;
|
mod id_allocator;
|
||||||
mod irq_manager;
|
mod irq_manager;
|
||||||
|
|
||||||
pub(super) use event_listener::EventListener;
|
#[cfg(feature = "graphic")]
|
||||||
|
mod graphic_console;
|
||||||
|
|
||||||
pub(super) use id_allocator::IdAllocator;
|
pub(super) use id_allocator::IdAllocator;
|
||||||
pub(super) use irq_manager::IrqManager;
|
pub(super) use irq_manager::IrqManager;
|
||||||
|
|
||||||
|
pub use event_listener::EventListener;
|
||||||
|
|
||||||
|
#[cfg(feature = "graphic")]
|
||||||
|
pub use graphic_console::GraphicConsole;
|
||||||
|
|
|
@ -33,7 +33,6 @@ async-std = { version = "1.10", optional = true }
|
||||||
executor = { git = "https://github.com/rcore-os/executor.git", rev = "a2d02ee9" }
|
executor = { git = "https://github.com/rcore-os/executor.git", rev = "a2d02ee9" }
|
||||||
naive-timer = "0.2.0"
|
naive-timer = "0.2.0"
|
||||||
lazy_static = { version = "1.4", features = ["spin_no_std"] }
|
lazy_static = { version = "1.4", features = ["spin_no_std"] }
|
||||||
bitmap-allocator = { git = "https://github.com/rcore-os/bitmap-allocator", rev = "b3f9f51" }
|
|
||||||
|
|
||||||
# Bare-metal mode on x86_64
|
# Bare-metal mode on x86_64
|
||||||
[target.'cfg(all(target_os = "none", target_arch = "x86_64"))'.dependencies]
|
[target.'cfg(all(target_os = "none", target_arch = "x86_64"))'.dependencies]
|
||||||
|
|
|
@ -48,8 +48,8 @@ pub fn send_ipi(sipi_value: usize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
hal_fn_impl! {
|
hal_fn_impl! {
|
||||||
impl mod crate::hal_fn::serial {
|
impl mod crate::hal_fn::console {
|
||||||
fn serial_write_early(s: &str) {
|
fn console_write_early(s: &str) {
|
||||||
for c in s.bytes() {
|
for c in s.bytes() {
|
||||||
console_putchar(c as usize);
|
console_putchar(c as usize);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ pub mod special;
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
pub mod vm;
|
pub mod vm;
|
||||||
|
|
||||||
hal_fn_impl_default!(crate::hal_fn::serial);
|
hal_fn_impl_default!(crate::hal_fn::console);
|
||||||
|
|
||||||
use x86_64::registers::control::{Cr4, Cr4Flags};
|
use x86_64::registers::control::{Cr4, Cr4Flags};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
use core::fmt::{Arguments, Result, Write};
|
||||||
|
|
||||||
|
use crate::drivers;
|
||||||
|
|
||||||
|
struct SerialWriter;
|
||||||
|
|
||||||
|
impl Write for SerialWriter {
|
||||||
|
fn write_str(&mut self, s: &str) -> Result {
|
||||||
|
if let Some(uart) = drivers::uart::first() {
|
||||||
|
uart.write_str(s).unwrap();
|
||||||
|
} else {
|
||||||
|
crate::hal_fn::console::console_write_early(s);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(feature = "graphic")] {
|
||||||
|
use crate::utils::init_once::InitOnce;
|
||||||
|
use alloc::sync::Arc;
|
||||||
|
use spin::Mutex;
|
||||||
|
use zcore_drivers::{scheme::DisplayScheme, utils::GraphicConsole};
|
||||||
|
|
||||||
|
static GRAPHIC_CONSOLE: InitOnce<Mutex<GraphicConsole>> = InitOnce::new();
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn init_graphic_console(display: Arc<dyn DisplayScheme>) {
|
||||||
|
GRAPHIC_CONSOLE.init_once_by(Mutex::new(GraphicConsole::new(display)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print format string and its arguments to serial.
|
||||||
|
pub fn serial_write_fmt(fmt: Arguments) {
|
||||||
|
SerialWriter.write_fmt(fmt).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print format string and its arguments to serial.
|
||||||
|
pub fn serial_write(s: &str) {
|
||||||
|
SerialWriter.write_str(s).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print format string and its arguments to graphic console.
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
pub fn graphic_console_write_fmt(fmt: Arguments) {
|
||||||
|
#[cfg(feature = "graphic")]
|
||||||
|
if let Some(cons) = GRAPHIC_CONSOLE.try_get() {
|
||||||
|
cons.lock().write_fmt(fmt).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print format string and its arguments to graphic console.
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
pub fn graphic_console_write(s: &str) {
|
||||||
|
#[cfg(feature = "graphic")]
|
||||||
|
if let Some(cons) = GRAPHIC_CONSOLE.try_get() {
|
||||||
|
cons.lock().write_str(s).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print format string and its arguments to serial and graphic console (if exists).
|
||||||
|
pub fn console_write_fmt(fmt: Arguments) {
|
||||||
|
serial_write_fmt(fmt);
|
||||||
|
graphic_console_write_fmt(fmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print a string to serial and graphic console (if exists).
|
||||||
|
pub fn console_write(s: &str) {
|
||||||
|
serial_write(s);
|
||||||
|
graphic_console_write(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read buffer data from console (serial).
|
||||||
|
pub async fn console_read(buf: &mut [u8]) -> usize {
|
||||||
|
super::future::SerialReadFuture::new(buf).await
|
||||||
|
}
|
|
@ -49,7 +49,7 @@ impl Future for SleepFuture {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use = "`serial_read()` does nothing unless polled/`await`-ed"]
|
#[must_use = "`console_read()` does nothing unless polled/`await`-ed"]
|
||||||
pub(super) struct SerialReadFuture<'a> {
|
pub(super) struct SerialReadFuture<'a> {
|
||||||
buf: &'a mut [u8],
|
buf: &'a mut [u8],
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,5 +10,5 @@ pub(super) mod vdso;
|
||||||
pub(super) mod vm;
|
pub(super) mod vm;
|
||||||
|
|
||||||
pub mod addr;
|
pub mod addr;
|
||||||
pub mod serial;
|
pub mod console;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
use core::fmt::{Arguments, Result, Write};
|
|
||||||
|
|
||||||
use crate::drivers;
|
|
||||||
|
|
||||||
struct SerialWriter;
|
|
||||||
|
|
||||||
impl Write for SerialWriter {
|
|
||||||
fn write_str(&mut self, s: &str) -> Result {
|
|
||||||
if let Some(uart) = drivers::uart::first() {
|
|
||||||
uart.write_str(s).unwrap();
|
|
||||||
} else {
|
|
||||||
crate::hal_fn::serial::serial_write_early(s);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Print format string and its arguments to serial.
|
|
||||||
pub fn serial_write_fmt(fmt: Arguments) {
|
|
||||||
SerialWriter.write_fmt(fmt).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Print a string to serial.
|
|
||||||
pub fn serial_write(s: &str) {
|
|
||||||
serial_write_fmt(format_args!("{}", s));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read buffer data from serial.
|
|
||||||
pub async fn serial_read(buf: &mut [u8]) -> usize {
|
|
||||||
super::future::SerialReadFuture::new(buf).await
|
|
||||||
}
|
|
|
@ -87,8 +87,8 @@ hal_fn_def! {
|
||||||
pub fn msi_register_handler(block: Range<usize>, msi_id: usize, handler: IrqHandler) -> HalResult;
|
pub fn msi_register_handler(block: Range<usize>, msi_id: usize, handler: IrqHandler) -> HalResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) mod serial {
|
pub(crate) mod console {
|
||||||
pub(crate) fn serial_write_early(_s: &str) {}
|
pub(crate) fn console_write_early(_s: &str) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod context: common::context {
|
pub mod context: common::context {
|
||||||
|
|
|
@ -36,7 +36,7 @@ cfg_if! {
|
||||||
pub(crate) use config::KCONFIG;
|
pub(crate) use config::KCONFIG;
|
||||||
pub(crate) use kernel_handler::KHANDLER;
|
pub(crate) use kernel_handler::KHANDLER;
|
||||||
|
|
||||||
pub use common::{addr, defs::*, serial, user};
|
pub use common::{addr, console, defs::*, user};
|
||||||
pub use config::KernelConfig;
|
pub use config::KernelConfig;
|
||||||
pub use imp::*;
|
pub use imp::*;
|
||||||
pub use kernel_handler::KernelHandler;
|
pub use kernel_handler::KernelHandler;
|
||||||
|
|
|
@ -3,17 +3,16 @@ use alloc::sync::Arc;
|
||||||
use zcore_drivers::mock::uart::MockUart;
|
use zcore_drivers::mock::uart::MockUart;
|
||||||
use zcore_drivers::{scheme::Scheme, Device};
|
use zcore_drivers::{scheme::Scheme, Device};
|
||||||
|
|
||||||
use crate::drivers;
|
|
||||||
|
|
||||||
pub(super) fn init() {
|
pub(super) fn init() {
|
||||||
let uart = Arc::new(MockUart::new());
|
let uart = Arc::new(MockUart::new());
|
||||||
drivers::add_device(Device::Uart(uart.clone()));
|
crate::drivers::add_device(Device::Uart(uart.clone()));
|
||||||
MockUart::start_irq_serve(move || uart.handle_irq(0));
|
MockUart::start_irq_serve(move || uart.handle_irq(0));
|
||||||
|
|
||||||
#[cfg(feature = "graphic")]
|
#[cfg(feature = "graphic")]
|
||||||
{
|
{
|
||||||
use zcore_drivers::mock::display::MockDisplay;
|
use zcore_drivers::mock::display::MockDisplay;
|
||||||
let display = Arc::new(MockDisplay::new(800, 600));
|
let display = Arc::new(MockDisplay::new(1280, 720));
|
||||||
drivers::add_device(Device::Display(display));
|
crate::drivers::add_device(Device::Display(display.clone()));
|
||||||
|
crate::console::init_graphic_console(display);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub mod libos;
|
||||||
|
|
||||||
pub use super::hal_fn::{context, cpu, interrupt, rand};
|
pub use super::hal_fn::{context, cpu, interrupt, rand};
|
||||||
|
|
||||||
hal_fn_impl_default!(context, cpu, interrupt, rand, super::hal_fn::serial);
|
hal_fn_impl_default!(context, cpu, interrupt, rand, super::hal_fn::console);
|
||||||
|
|
||||||
cfg_if! {
|
cfg_if! {
|
||||||
if #[cfg(target_os = "linux")] {
|
if #[cfg(target_os = "linux")] {
|
||||||
|
|
|
@ -3,9 +3,9 @@ pub fn run_display_serve() {
|
||||||
use zcore_drivers::mock::display::sdl::SdlWindow;
|
use zcore_drivers::mock::display::sdl::SdlWindow;
|
||||||
|
|
||||||
let display = crate::drivers::display::first_unwrap();
|
let display = crate::drivers::display::first_unwrap();
|
||||||
let mut window = SdlWindow::new("zcore-libos", display.info());
|
let mut window = SdlWindow::new("zcore-libos", display.clone());
|
||||||
while !window.is_quit() {
|
while !window.is_quit() {
|
||||||
window.flush(display.as_ref());
|
window.flush();
|
||||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,24 +28,26 @@ async fn main() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// run first process
|
|
||||||
let args: Vec<_> = std::env::args().skip(1).collect();
|
let args: Vec<_> = std::env::args().skip(1).collect();
|
||||||
let proc_name = args.join(" ");
|
let proc_name = args.join(" ");
|
||||||
let envs = vec!["PATH=/usr/sbin:/usr/bin:/sbin:/bin:/usr/x86_64-alpine-linux-musl/bin".into()];
|
let envs = vec!["PATH=/usr/sbin:/usr/bin:/sbin:/bin:/usr/x86_64-alpine-linux-musl/bin".into()];
|
||||||
|
|
||||||
let hostfs = HostFS::new("rootfs");
|
// Run the first process.
|
||||||
let proc = run(args, envs, hostfs);
|
let run_proc = async move {
|
||||||
|
let hostfs = HostFS::new("rootfs");
|
||||||
let wait_for_exit = async move { proc.wait_for_exit().await };
|
let proc = run(args, envs, hostfs);
|
||||||
|
proc.wait_for_exit().await
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the graphic mode is on, run the process in another thread.
|
||||||
#[cfg(feature = "graphic")]
|
#[cfg(feature = "graphic")]
|
||||||
let wait_for_exit = {
|
let run_proc = {
|
||||||
let handle = async_std::task::spawn(wait_for_exit);
|
let handle = async_std::task::spawn(run_proc);
|
||||||
kernel_hal::libos::run_display_serve();
|
kernel_hal::libos::run_display_serve();
|
||||||
handle
|
handle
|
||||||
};
|
};
|
||||||
|
|
||||||
let code = wait_for_exit.await;
|
let code = run_proc.await;
|
||||||
log::info!("process {:?} exited with {}", proc_name, code);
|
log::info!("process {:?} exited with {}", proc_name, code);
|
||||||
std::process::exit(code as i32);
|
std::process::exit(code as i32);
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,7 +132,7 @@ impl INode for Stdout {
|
||||||
fn write_at(&self, _offset: usize, buf: &[u8]) -> Result<usize> {
|
fn write_at(&self, _offset: usize, buf: &[u8]) -> Result<usize> {
|
||||||
// we do not care the utf-8 things, we just want to print it!
|
// we do not care the utf-8 things, we just want to print it!
|
||||||
let s = unsafe { core::str::from_utf8_unchecked(buf) };
|
let s = unsafe { core::str::from_utf8_unchecked(buf) };
|
||||||
kernel_hal::serial::serial_write(s);
|
kernel_hal::console::console_write(s);
|
||||||
Ok(buf.len())
|
Ok(buf.len())
|
||||||
}
|
}
|
||||||
fn poll(&self) -> Result<PollStatus> {
|
fn poll(&self) -> Result<PollStatus> {
|
||||||
|
|
|
@ -22,7 +22,7 @@ impl Syscall<'_> {
|
||||||
&mut self,
|
&mut self,
|
||||||
mut ufds: UserInOutPtr<PollFd>,
|
mut ufds: UserInOutPtr<PollFd>,
|
||||||
nfds: usize,
|
nfds: usize,
|
||||||
timeout_msecs: usize,
|
timeout_msecs: isize,
|
||||||
) -> SysResult {
|
) -> SysResult {
|
||||||
let mut polls = ufds.read_array(nfds)?;
|
let mut polls = ufds.read_array(nfds)?;
|
||||||
info!(
|
info!(
|
||||||
|
@ -32,7 +32,7 @@ impl Syscall<'_> {
|
||||||
#[must_use = "future does nothing unless polled/`await`-ed"]
|
#[must_use = "future does nothing unless polled/`await`-ed"]
|
||||||
struct PollFuture<'a> {
|
struct PollFuture<'a> {
|
||||||
polls: &'a mut Vec<PollFd>,
|
polls: &'a mut Vec<PollFd>,
|
||||||
timeout_msecs: usize,
|
timeout_msecs: isize,
|
||||||
begin_time_ms: usize,
|
begin_time_ms: usize,
|
||||||
syscall: &'a Syscall<'a>,
|
syscall: &'a Syscall<'a>,
|
||||||
}
|
}
|
||||||
|
@ -79,23 +79,23 @@ impl Syscall<'_> {
|
||||||
return Poll::Ready(Ok(events));
|
return Poll::Ready(Ok(events));
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.timeout_msecs == 0 {
|
match self.timeout_msecs {
|
||||||
// no timeout, return now;
|
// no timeout, return now;
|
||||||
return Poll::Ready(Ok(0));
|
0 => return Poll::Ready(Ok(0)),
|
||||||
} else {
|
1.. => {
|
||||||
let waker = cx.waker().clone();
|
let current_time_ms = TimeVal::now().to_msec();
|
||||||
timer::timer_set(
|
let deadline = self.begin_time_ms + self.timeout_msecs as usize;
|
||||||
timer::deadline_after(Duration::from_millis(self.timeout_msecs as u64)),
|
if current_time_ms >= deadline {
|
||||||
Box::new(move |_| waker.wake()),
|
return Poll::Ready(Ok(0));
|
||||||
);
|
} else {
|
||||||
}
|
let waker = cx.waker().clone();
|
||||||
|
timer::timer_set(
|
||||||
let current_time_ms = TimeVal::now().to_msec();
|
Duration::from_millis(deadline as u64),
|
||||||
// infinity check
|
Box::new(move |_| waker.wake()),
|
||||||
if self.timeout_msecs < (1 << 31)
|
);
|
||||||
&& current_time_ms - self.begin_time_ms >= self.timeout_msecs as usize
|
}
|
||||||
{
|
}
|
||||||
return Poll::Ready(Ok(0));
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
|
@ -122,13 +122,13 @@ impl Syscall<'_> {
|
||||||
timeout: UserInPtr<TimeSpec>,
|
timeout: UserInPtr<TimeSpec>,
|
||||||
) -> SysResult {
|
) -> SysResult {
|
||||||
let timeout_msecs = if timeout.is_null() {
|
let timeout_msecs = if timeout.is_null() {
|
||||||
1 << 31 // infinity
|
-1
|
||||||
} else {
|
} else {
|
||||||
let timeout = timeout.read().unwrap();
|
let timeout = timeout.read().unwrap();
|
||||||
timeout.to_msec()
|
timeout.to_msec() as isize
|
||||||
};
|
};
|
||||||
|
|
||||||
self.sys_poll(ufds, nfds, timeout_msecs as usize).await
|
self.sys_poll(ufds, nfds, timeout_msecs).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// similar to select, but have sigmask argument
|
/// similar to select, but have sigmask argument
|
||||||
|
@ -169,10 +169,10 @@ impl Syscall<'_> {
|
||||||
|
|
||||||
let timeout_msecs = if !timeout.is_null() {
|
let timeout_msecs = if !timeout.is_null() {
|
||||||
let timeout = timeout.read()?;
|
let timeout = timeout.read()?;
|
||||||
timeout.to_msec()
|
timeout.to_msec() as isize
|
||||||
} else {
|
} else {
|
||||||
// infinity
|
// infinity
|
||||||
1 << 31
|
-1
|
||||||
};
|
};
|
||||||
let begin_time_ms = TimeVal::now().to_msec();
|
let begin_time_ms = TimeVal::now().to_msec();
|
||||||
|
|
||||||
|
@ -181,7 +181,7 @@ impl Syscall<'_> {
|
||||||
read_fds: &'a mut FdSet,
|
read_fds: &'a mut FdSet,
|
||||||
write_fds: &'a mut FdSet,
|
write_fds: &'a mut FdSet,
|
||||||
err_fds: &'a mut FdSet,
|
err_fds: &'a mut FdSet,
|
||||||
timeout_msecs: usize,
|
timeout_msecs: isize,
|
||||||
begin_time_ms: usize,
|
begin_time_ms: usize,
|
||||||
syscall: &'a Syscall<'a>,
|
syscall: &'a Syscall<'a>,
|
||||||
}
|
}
|
||||||
|
@ -225,25 +225,24 @@ impl Syscall<'_> {
|
||||||
return Poll::Ready(Ok(events));
|
return Poll::Ready(Ok(events));
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.timeout_msecs == 0 {
|
match self.timeout_msecs {
|
||||||
// no timeout, return now;
|
// no timeout, return now;
|
||||||
return Poll::Ready(Ok(0));
|
0 => return Poll::Ready(Ok(0)),
|
||||||
} else {
|
1.. => {
|
||||||
let waker = cx.waker().clone();
|
let current_time_ms = TimeVal::now().to_msec();
|
||||||
timer::timer_set(
|
let deadline = self.begin_time_ms + self.timeout_msecs as usize;
|
||||||
timer::deadline_after(Duration::from_millis(self.timeout_msecs as u64)),
|
if current_time_ms >= deadline {
|
||||||
Box::new(move |_| waker.wake()),
|
return Poll::Ready(Ok(0));
|
||||||
);
|
} else {
|
||||||
|
let waker = cx.waker().clone();
|
||||||
|
timer::timer_set(
|
||||||
|
Duration::from_millis(deadline as u64),
|
||||||
|
Box::new(move |_| waker.wake()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let current_time_ms = TimeVal::now().to_msec();
|
|
||||||
// infinity check
|
|
||||||
if self.timeout_msecs < (1 << 31)
|
|
||||||
&& current_time_ms - self.begin_time_ms >= self.timeout_msecs as usize
|
|
||||||
{
|
|
||||||
return Poll::Ready(Ok(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,7 +269,7 @@ impl Syscall<'_> {
|
||||||
Sys::OPEN => self.sys_open(a0.into(), a1, a2),
|
Sys::OPEN => self.sys_open(a0.into(), a1, a2),
|
||||||
Sys::STAT => self.sys_stat(a0.into(), a1.into()),
|
Sys::STAT => self.sys_stat(a0.into(), a1.into()),
|
||||||
Sys::LSTAT => self.sys_lstat(a0.into(), a1.into()),
|
Sys::LSTAT => self.sys_lstat(a0.into(), a1.into()),
|
||||||
Sys::POLL => self.sys_poll(a0.into(), a1, a2).await,
|
Sys::POLL => self.sys_poll(a0.into(), a1, a2 as _).await,
|
||||||
Sys::ACCESS => self.sys_access(a0.into(), a1),
|
Sys::ACCESS => self.sys_access(a0.into(), a1),
|
||||||
Sys::PIPE => self.sys_pipe(a0.into()),
|
Sys::PIPE => self.sys_pipe(a0.into()),
|
||||||
Sys::SELECT => {
|
Sys::SELECT => {
|
||||||
|
|
|
@ -37,12 +37,12 @@ macro_rules! with_color {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_in_color(args: fmt::Arguments, color_code: u8) {
|
fn print_in_color(args: fmt::Arguments, color_code: u8) {
|
||||||
kernel_hal::serial::serial_write_fmt(with_color!(args, color_code));
|
kernel_hal::console::console_write_fmt(with_color!(args, color_code));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn print(args: fmt::Arguments) {
|
pub fn print(args: fmt::Arguments) {
|
||||||
kernel_hal::serial::serial_write_fmt(args);
|
kernel_hal::console::console_write_fmt(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SimpleLogger;
|
struct SimpleLogger;
|
||||||
|
|
|
@ -6,7 +6,7 @@ impl Syscall<'_> {
|
||||||
pub fn sys_debug_write(&self, buf: UserInPtr<u8>, len: usize) -> ZxResult {
|
pub fn sys_debug_write(&self, buf: UserInPtr<u8>, len: usize) -> ZxResult {
|
||||||
info!("debug.write: buf=({:?}; {:#x})", buf, len);
|
info!("debug.write: buf=({:?}; {:#x})", buf, len);
|
||||||
let data = buf.read_array(len)?;
|
let data = buf.read_array(len)?;
|
||||||
kernel_hal::serial::serial_write(core::str::from_utf8(&data).unwrap());
|
kernel_hal::console::console_write(core::str::from_utf8(&data).unwrap());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ impl Syscall<'_> {
|
||||||
proc.get_object::<Resource>(handle)?
|
proc.get_object::<Resource>(handle)?
|
||||||
.validate(ResourceKind::ROOT)?;
|
.validate(ResourceKind::ROOT)?;
|
||||||
let mut vec = vec![0u8; buf_size as usize];
|
let mut vec = vec![0u8; buf_size as usize];
|
||||||
let len = kernel_hal::serial::serial_read(&mut vec).await;
|
let len = kernel_hal::console::console_read(&mut vec).await;
|
||||||
buf.write_array(&vec[..len])?;
|
buf.write_array(&vec[..len])?;
|
||||||
actual.write(len as u32)?;
|
actual.write(len as u32)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -54,9 +54,9 @@ impl Syscall<'_> {
|
||||||
let dlog = proc.get_object_with_rights::<DebugLog>(handle_value, Rights::WRITE)?;
|
let dlog = proc.get_object_with_rights::<DebugLog>(handle_value, Rights::WRITE)?;
|
||||||
dlog.write(Severity::Info, options, self.thread.id(), proc.id(), &data);
|
dlog.write(Severity::Info, options, self.thread.id(), proc.id(), &data);
|
||||||
// print to kernel console
|
// print to kernel console
|
||||||
kernel_hal::serial::serial_write(&data);
|
kernel_hal::console::console_write(&data);
|
||||||
if data.as_bytes().last() != Some(&b'\n') {
|
if data.as_bytes().last() != Some(&b'\n') {
|
||||||
kernel_hal::serial::serial_write("\n");
|
kernel_hal::console::console_write("\n");
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue