Sync computers
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = ["crates/bytes-struct","crates/io","crates/os-std", "crates/shared", "user/*"]
|
||||
members = ["crates/bytes-struct","crates/io","crates/std", "crates/shared", "user/*"]
|
||||
|
||||
[package]
|
||||
name = "kernel-rust"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
pub use io::SeekFrom;
|
||||
@@ -1,62 +0,0 @@
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod io;
|
||||
pub mod prelude;
|
||||
|
||||
pub use shared::fs;
|
||||
pub use shared::syscall;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! custom_std_setup {
|
||||
() => {
|
||||
use $crate::prelude::*;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
struct GlobalAllocator;
|
||||
|
||||
#[global_allocator]
|
||||
static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator;
|
||||
|
||||
unsafe impl core::alloc::GlobalAlloc for GlobalAllocator {
|
||||
unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
|
||||
$crate::syscall::alloc(layout)
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
|
||||
$crate::syscall::dealloc(ptr, layout)
|
||||
}
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(_panic_info: &core::panic::PanicInfo) -> ! {
|
||||
// TODO print
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _start() {
|
||||
main()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($args:expr),*) => {
|
||||
$crate::syscall::write_string_temp(&format!($($args),*))
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
() => {
|
||||
$crate::print!("");
|
||||
// $crate::print!("\n\r");
|
||||
};
|
||||
($($args:expr),*) => {
|
||||
$crate::print!($($args),*);
|
||||
// $crate::println!();
|
||||
};
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
pub use crate::print;
|
||||
pub use crate::println;
|
||||
pub use alloc::format;
|
||||
pub use alloc::string::String;
|
||||
pub use alloc::vec;
|
||||
@@ -1,3 +1,7 @@
|
||||
use io::{IoBase, Read, Write};
|
||||
|
||||
use crate::syscall;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct File {
|
||||
fd: u64,
|
||||
@@ -6,7 +10,7 @@ pub struct File {
|
||||
impl File {
|
||||
/// # Safety
|
||||
/// The file descriptor must be valid
|
||||
pub unsafe fn new(fd: u64) -> Self {
|
||||
pub unsafe fn from_raw_fd(fd: u64) -> Self {
|
||||
Self { fd }
|
||||
}
|
||||
|
||||
@@ -14,3 +18,23 @@ impl File {
|
||||
self.fd
|
||||
}
|
||||
}
|
||||
|
||||
impl IoBase for File {
|
||||
type Error = ();
|
||||
}
|
||||
|
||||
impl Read for File {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
Ok(syscall::read(self.as_fd(), buf) as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for File {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
Ok(syscall::write(self.as_fd(), buf) as usize)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,12 @@ pub enum SysCall {
|
||||
Read = 0,
|
||||
Write = 1,
|
||||
Open = 2,
|
||||
Close = 3,
|
||||
Seek = 8,
|
||||
Alloc = 40,
|
||||
Dealloc = 41,
|
||||
Spawn = 58,
|
||||
ExecVE = 59,
|
||||
Exit = 60,
|
||||
NanoSleep = 101,
|
||||
WriteIntTemp = 998,
|
||||
@@ -26,9 +29,12 @@ impl From<u64> for SysCall {
|
||||
0 => SysCall::Read,
|
||||
1 => SysCall::Write,
|
||||
2 => SysCall::Open,
|
||||
3 => SysCall::Close,
|
||||
8 => SysCall::Seek,
|
||||
40 => SysCall::Alloc,
|
||||
41 => SysCall::Dealloc,
|
||||
58 => SysCall::Spawn,
|
||||
59 => SysCall::ExecVE,
|
||||
60 => SysCall::Exit,
|
||||
101 => SysCall::NanoSleep,
|
||||
998 => SysCall::WriteIntTemp,
|
||||
@@ -143,30 +149,53 @@ pub fn open<P: AsRef<Path>>(path: P) -> File {
|
||||
let ptr = path_str.as_ptr();
|
||||
let size = path_str.len();
|
||||
let (fd, ..) = syscall!(SysCall::Open, ptr as u64, size as u64);
|
||||
File::new(fd)
|
||||
File::from_raw_fd(fd)
|
||||
}
|
||||
}
|
||||
pub fn write(file: &mut File, buf: &[u8]) {
|
||||
pub fn close(file_descriptor: u64) {
|
||||
unsafe {
|
||||
syscall!(SysCall::Close, file_descriptor);
|
||||
}
|
||||
}
|
||||
pub fn write(file_descriptor: u64, buf: &[u8]) -> u64 {
|
||||
unsafe {
|
||||
let ptr = buf.as_ptr();
|
||||
let size = buf.len();
|
||||
syscall!(SysCall::Write, file.as_fd(), ptr as u64, size as u64);
|
||||
let (len, ..) = syscall!(SysCall::Write, file_descriptor, ptr as u64, size as u64);
|
||||
len
|
||||
}
|
||||
}
|
||||
pub fn read(file: &mut File, buf: &mut [u8]) {
|
||||
pub fn read(file_descriptor: u64, buf: &mut [u8]) -> u64 {
|
||||
unsafe {
|
||||
let ptr = buf.as_ptr();
|
||||
let size = buf.len();
|
||||
syscall!(SysCall::Read, file.as_fd(), ptr as u64, size as u64);
|
||||
let (len, ..) = syscall!(SysCall::Read, file_descriptor, ptr as u64, size as u64);
|
||||
len
|
||||
}
|
||||
}
|
||||
pub fn seek(file: &mut File, seek: SeekFrom) {
|
||||
pub fn seek(file_descriptor: u64, seek: SeekFrom) {
|
||||
unsafe {
|
||||
let (discriminant, value) = match seek {
|
||||
SeekFrom::Start(v) => (0, v),
|
||||
SeekFrom::End(v) => (1, v as u64),
|
||||
SeekFrom::Current(v) => (2, v as u64),
|
||||
};
|
||||
syscall!(SysCall::Seek, file.as_fd(), discriminant, value);
|
||||
syscall!(SysCall::Seek, file_descriptor, discriminant, value);
|
||||
}
|
||||
}
|
||||
pub fn spawn<P: AsRef<Path>>(path: P) {
|
||||
unsafe {
|
||||
let path_str = path.as_ref().as_str();
|
||||
let ptr = path_str.as_ptr();
|
||||
let size = path_str.len();
|
||||
syscall!(SysCall::Spawn, ptr as u64, size as u64);
|
||||
}
|
||||
}
|
||||
pub fn execve<P: AsRef<Path>>(path: P) {
|
||||
unsafe {
|
||||
let path_str = path.as_ref().as_str();
|
||||
let ptr = path_str.as_ptr();
|
||||
let size = path_str.len();
|
||||
syscall!(SysCall::ExecVE, ptr as u64, size as u64);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "os-std"
|
||||
name = "std"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
7
crates/std/src/ffi/c_str.rs
Normal file
7
crates/std/src/ffi/c_str.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
//! [`CStr`], [`CString`], and related types.
|
||||
pub use alloc::ffi::c_str::FromVecWithNulError;
|
||||
pub use alloc::ffi::c_str::IntoStringError;
|
||||
pub use alloc::ffi::c_str::{CString, NulError};
|
||||
pub use core::ffi::c_str::CStr;
|
||||
pub use core::ffi::c_str::FromBytesUntilNulError;
|
||||
pub use core::ffi::c_str::FromBytesWithNulError;
|
||||
184
crates/std/src/ffi/mod.rs
Normal file
184
crates/std/src/ffi/mod.rs
Normal file
@@ -0,0 +1,184 @@
|
||||
//! Utilities related to FFI bindings.
|
||||
//!
|
||||
//! This module provides utilities to handle data across non-Rust
|
||||
//! interfaces, like other programming languages and the underlying
|
||||
//! operating system. It is mainly of use for FFI (Foreign Function
|
||||
//! Interface) bindings and code that needs to exchange C-like strings
|
||||
//! with other languages.
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! Rust represents owned strings with the [`String`] type, and
|
||||
//! borrowed slices of strings with the [`str`] primitive. Both are
|
||||
//! always in UTF-8 encoding, and may contain nul bytes in the middle,
|
||||
//! i.e., if you look at the bytes that make up the string, there may
|
||||
//! be a `\0` among them. Both `String` and `str` store their length
|
||||
//! explicitly; there are no nul terminators at the end of strings
|
||||
//! like in C.
|
||||
//!
|
||||
//! C strings are different from Rust strings:
|
||||
//!
|
||||
//! * **Encodings** - Rust strings are UTF-8, but C strings may use
|
||||
//! other encodings. If you are using a string from C, you should
|
||||
//! check its encoding explicitly, rather than just assuming that it
|
||||
//! is UTF-8 like you can do in Rust.
|
||||
//!
|
||||
//! * **Character size** - C strings may use `char` or `wchar_t`-sized
|
||||
//! characters; please **note** that C's `char` is different from Rust's.
|
||||
//! The C standard leaves the actual sizes of those types open to
|
||||
//! interpretation, but defines different APIs for strings made up of
|
||||
//! each character type. Rust strings are always UTF-8, so different
|
||||
//! Unicode characters will be encoded in a variable number of bytes
|
||||
//! each. The Rust type [`char`] represents a '[Unicode scalar
|
||||
//! value]', which is similar to, but not the same as, a '[Unicode
|
||||
//! code point]'.
|
||||
//!
|
||||
//! * **Nul terminators and implicit string lengths** - Often, C
|
||||
//! strings are nul-terminated, i.e., they have a `\0` character at the
|
||||
//! end. The length of a string buffer is not stored, but has to be
|
||||
//! calculated; to compute the length of a string, C code must
|
||||
//! manually call a function like `strlen()` for `char`-based strings,
|
||||
//! or `wcslen()` for `wchar_t`-based ones. Those functions return
|
||||
//! the number of characters in the string excluding the nul
|
||||
//! terminator, so the buffer length is really `len+1` characters.
|
||||
//! Rust strings don't have a nul terminator; their length is always
|
||||
//! stored and does not need to be calculated. While in Rust
|
||||
//! accessing a string's length is an *O*(1) operation (because the
|
||||
//! length is stored); in C it is an *O*(*n*) operation because the
|
||||
//! length needs to be computed by scanning the string for the nul
|
||||
//! terminator.
|
||||
//!
|
||||
//! * **Internal nul characters** - When C strings have a nul
|
||||
//! terminator character, this usually means that they cannot have nul
|
||||
//! characters in the middle — a nul character would essentially
|
||||
//! truncate the string. Rust strings *can* have nul characters in
|
||||
//! the middle, because nul does not have to mark the end of the
|
||||
//! string in Rust.
|
||||
//!
|
||||
//! # Representations of non-Rust strings
|
||||
//!
|
||||
//! [`CString`] and [`CStr`] are useful when you need to transfer
|
||||
//! UTF-8 strings to and from languages with a C ABI, like Python.
|
||||
//!
|
||||
//! * **From Rust to C:** [`CString`] represents an owned, C-friendly
|
||||
//! string: it is nul-terminated, and has no internal nul characters.
|
||||
//! Rust code can create a [`CString`] out of a normal string (provided
|
||||
//! that the string doesn't have nul characters in the middle), and
|
||||
//! then use a variety of methods to obtain a raw <code>\*mut [u8]</code> that can
|
||||
//! then be passed as an argument to functions which use the C
|
||||
//! conventions for strings.
|
||||
//!
|
||||
//! * **From C to Rust:** [`CStr`] represents a borrowed C string; it
|
||||
//! is what you would use to wrap a raw <code>\*const [u8]</code> that you got from
|
||||
//! a C function. A [`CStr`] is guaranteed to be a nul-terminated array
|
||||
//! of bytes. Once you have a [`CStr`], you can convert it to a Rust
|
||||
//! <code>&[str]</code> if it's valid UTF-8, or lossily convert it by adding
|
||||
//! replacement characters.
|
||||
//!
|
||||
//! [`OsString`] and [`OsStr`] are useful when you need to transfer
|
||||
//! strings to and from the operating system itself, or when capturing
|
||||
//! the output of external commands. Conversions between [`OsString`],
|
||||
//! [`OsStr`] and Rust strings work similarly to those for [`CString`]
|
||||
//! and [`CStr`].
|
||||
//!
|
||||
//! * [`OsString`] losslessly represents an owned platform string. However, this
|
||||
//! representation is not necessarily in a form native to the platform.
|
||||
//! In the Rust standard library, various APIs that transfer strings to/from the operating
|
||||
//! system use [`OsString`] instead of plain strings. For example,
|
||||
//! [`env::var_os()`] is used to query environment variables; it
|
||||
//! returns an <code>[Option]<[OsString]></code>. If the environment variable
|
||||
//! exists you will get a <code>[Some]\(os_string)</code>, which you can
|
||||
//! *then* try to convert to a Rust string. This yields a [`Result`], so that
|
||||
//! your code can detect errors in case the environment variable did
|
||||
//! not in fact contain valid Unicode data.
|
||||
//!
|
||||
//! * [`OsStr`] losslessly represents a borrowed reference to a platform string.
|
||||
//! However, this representation is not necessarily in a form native to the platform.
|
||||
//! It can be converted into a UTF-8 Rust string slice in a similar way to
|
||||
//! [`OsString`].
|
||||
//!
|
||||
//! # Conversions
|
||||
//!
|
||||
//! ## On Unix
|
||||
//!
|
||||
//! On Unix, [`OsStr`] implements the
|
||||
//! <code>std::os::unix::ffi::[OsStrExt][unix.OsStrExt]</code> trait, which
|
||||
//! augments it with two methods, [`from_bytes`] and [`as_bytes`].
|
||||
//! These do inexpensive conversions from and to byte slices.
|
||||
//!
|
||||
//! Additionally, on Unix [`OsString`] implements the
|
||||
//! <code>std::os::unix::ffi::[OsStringExt][unix.OsStringExt]</code> trait,
|
||||
//! which provides [`from_vec`] and [`into_vec`] methods that consume
|
||||
//! their arguments, and take or produce vectors of [`u8`].
|
||||
//!
|
||||
//! ## On Windows
|
||||
//!
|
||||
//! An [`OsStr`] can be losslessly converted to a native Windows string. And
|
||||
//! a native Windows string can be losslessly converted to an [`OsString`].
|
||||
//!
|
||||
//! On Windows, [`OsStr`] implements the
|
||||
//! <code>std::os::windows::ffi::[OsStrExt][windows.OsStrExt]</code> trait,
|
||||
//! which provides an [`encode_wide`] method. This provides an
|
||||
//! iterator that can be [`collect`]ed into a vector of [`u16`]. After a nul
|
||||
//! characters is appended, this is the same as a native Windows string.
|
||||
//!
|
||||
//! Additionally, on Windows [`OsString`] implements the
|
||||
//! <code>std::os::windows:ffi::[OsStringExt][windows.OsStringExt]</code>
|
||||
//! trait, which provides a [`from_wide`] method to convert a native Windows
|
||||
//! string (without the terminating nul character) to an [`OsString`].
|
||||
//!
|
||||
//! ## Other platforms
|
||||
//!
|
||||
//! Many other platforms provide their own extension traits in a
|
||||
//! `std::os::*::ffi` module.
|
||||
//!
|
||||
//! ## On all platforms
|
||||
//!
|
||||
//! On all platforms, [`OsStr`] consists of a sequence of bytes that is encoded as a superset of
|
||||
//! UTF-8; see [`OsString`] for more details on its encoding on different platforms.
|
||||
//!
|
||||
//! For limited, inexpensive conversions from and to bytes, see [`OsStr::as_encoded_bytes`] and
|
||||
//! [`OsStr::from_encoded_bytes_unchecked`].
|
||||
//!
|
||||
//! For basic string processing, see [`OsStr::slice_encoded_bytes`].
|
||||
//!
|
||||
//! [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value
|
||||
//! [Unicode code point]: https://www.unicode.org/glossary/#code_point
|
||||
//! [`env::set_var()`]: crate::env::set_var "env::set_var"
|
||||
//! [`env::var_os()`]: crate::env::var_os "env::var_os"
|
||||
//! [unix.OsStringExt]: crate::os::unix::ffi::OsStringExt "os::unix::ffi::OsStringExt"
|
||||
//! [`from_vec`]: crate::os::unix::ffi::OsStringExt::from_vec "os::unix::ffi::OsStringExt::from_vec"
|
||||
//! [`into_vec`]: crate::os::unix::ffi::OsStringExt::into_vec "os::unix::ffi::OsStringExt::into_vec"
|
||||
//! [unix.OsStrExt]: crate::os::unix::ffi::OsStrExt "os::unix::ffi::OsStrExt"
|
||||
//! [`from_bytes`]: crate::os::unix::ffi::OsStrExt::from_bytes "os::unix::ffi::OsStrExt::from_bytes"
|
||||
//! [`as_bytes`]: crate::os::unix::ffi::OsStrExt::as_bytes "os::unix::ffi::OsStrExt::as_bytes"
|
||||
//! [`OsStrExt`]: crate::os::unix::ffi::OsStrExt "os::unix::ffi::OsStrExt"
|
||||
//! [windows.OsStrExt]: crate::os::windows::ffi::OsStrExt "os::windows::ffi::OsStrExt"
|
||||
//! [`encode_wide`]: crate::os::windows::ffi::OsStrExt::encode_wide "os::windows::ffi::OsStrExt::encode_wide"
|
||||
//! [`collect`]: crate::iter::Iterator::collect "iter::Iterator::collect"
|
||||
//! [windows.OsStringExt]: crate::os::windows::ffi::OsStringExt "os::windows::ffi::OsStringExt"
|
||||
//! [`from_wide`]: crate::os::windows::ffi::OsStringExt::from_wide "os::windows::ffi::OsStringExt::from_wide"
|
||||
pub mod c_str;
|
||||
pub use core::ffi::c_void;
|
||||
pub use core::ffi::{VaArgSafe, VaList};
|
||||
pub use core::ffi::{
|
||||
c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint,
|
||||
c_ulong, c_ulonglong, c_ushort,
|
||||
};
|
||||
pub use core::ffi::{c_ptrdiff_t, c_size_t, c_ssize_t};
|
||||
|
||||
#[doc(inline)]
|
||||
pub use self::c_str::FromBytesUntilNulError;
|
||||
#[doc(inline)]
|
||||
pub use self::c_str::FromBytesWithNulError;
|
||||
#[doc(inline)]
|
||||
pub use self::c_str::FromVecWithNulError;
|
||||
#[doc(inline)]
|
||||
pub use self::c_str::IntoStringError;
|
||||
#[doc(inline)]
|
||||
pub use self::c_str::NulError;
|
||||
#[doc(inline)]
|
||||
pub use self::c_str::{CStr, CString};
|
||||
// #[doc(inline)]
|
||||
// pub use self::os_str::{OsStr, OsString};
|
||||
pub mod os_str;
|
||||
1661
crates/std/src/ffi/os_str.rs
Normal file
1661
crates/std/src/ffi/os_str.rs
Normal file
File diff suppressed because it is too large
Load Diff
22
crates/std/src/io.rs
Normal file
22
crates/std/src/io.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use crate::fs::File;
|
||||
use io::IoBase;
|
||||
pub use io::Read;
|
||||
pub use io::Seek;
|
||||
pub use io::SeekFrom;
|
||||
pub use io::Write;
|
||||
|
||||
pub struct Stdin;
|
||||
|
||||
impl IoBase for Stdin {
|
||||
type Error = ();
|
||||
}
|
||||
|
||||
impl Read for Stdin {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
unsafe { File::from_raw_fd(0).read(buf) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stdin() -> Stdin {
|
||||
Stdin
|
||||
}
|
||||
242
crates/std/src/lib.rs
Normal file
242
crates/std/src/lib.rs
Normal file
@@ -0,0 +1,242 @@
|
||||
#![no_std]
|
||||
#![allow(internal_features)]
|
||||
//
|
||||
// Language features:
|
||||
// tidy-alphabetical-start
|
||||
#![feature(alloc_error_handler)]
|
||||
#![feature(allocator_internals)]
|
||||
#![feature(allow_internal_unsafe)]
|
||||
#![feature(allow_internal_unstable)]
|
||||
#![feature(asm_experimental_arch)]
|
||||
#![feature(autodiff)]
|
||||
#![feature(cfg_sanitizer_cfi)]
|
||||
#![feature(cfg_target_thread_local)]
|
||||
#![feature(cfi_encoding)]
|
||||
#![feature(const_default)]
|
||||
#![feature(const_trait_impl)]
|
||||
#![feature(core_float_math)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(deprecated_suggestion)]
|
||||
#![feature(doc_cfg)]
|
||||
#![feature(doc_masked)]
|
||||
#![feature(doc_notable_trait)]
|
||||
#![feature(dropck_eyepatch)]
|
||||
#![feature(f16)]
|
||||
#![feature(f128)]
|
||||
#![feature(ffi_const)]
|
||||
#![feature(formatting_options)]
|
||||
#![feature(funnel_shifts)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(intra_doc_pointers)]
|
||||
#![feature(iter_advance_by)]
|
||||
#![feature(iter_next_chunk)]
|
||||
#![feature(lang_items)]
|
||||
#![feature(link_cfg)]
|
||||
#![feature(linkage)]
|
||||
#![feature(macro_metavar_expr_concat)]
|
||||
#![feature(maybe_uninit_fill)]
|
||||
#![feature(min_specialization)]
|
||||
#![feature(must_not_suspend)]
|
||||
#![feature(needs_panic_runtime)]
|
||||
#![feature(negative_impls)]
|
||||
#![feature(never_type)]
|
||||
#![feature(optimize_attribute)]
|
||||
#![feature(prelude_import)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(rustdoc_internals)]
|
||||
// #![feature(staged_api)]
|
||||
#![feature(stmt_expr_attributes)]
|
||||
#![feature(strict_provenance_lints)]
|
||||
#![feature(thread_local)]
|
||||
#![feature(try_blocks)]
|
||||
#![feature(try_trait_v2)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
// tidy-alphabetical-end
|
||||
//
|
||||
// Library features (core):
|
||||
// tidy-alphabetical-start
|
||||
#![feature(bstr)]
|
||||
#![feature(bstr_internals)]
|
||||
#![feature(cast_maybe_uninit)]
|
||||
#![feature(cfg_select)]
|
||||
#![feature(char_internals)]
|
||||
#![feature(clone_to_uninit)]
|
||||
#![feature(const_convert)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(core_io_borrowed_buf)]
|
||||
#![feature(drop_guard)]
|
||||
#![feature(duration_constants)]
|
||||
#![feature(error_generic_member_access)]
|
||||
#![feature(error_iter)]
|
||||
#![feature(exact_size_is_empty)]
|
||||
#![feature(exclusive_wrapper)]
|
||||
#![feature(extend_one)]
|
||||
#![feature(float_algebraic)]
|
||||
// #![feature(float_gamma)]
|
||||
#![feature(float_minimum_maximum)]
|
||||
#![feature(fmt_internals)]
|
||||
#![feature(fn_ptr_trait)]
|
||||
#![feature(generic_atomic)]
|
||||
#![feature(hasher_prefixfree_extras)]
|
||||
#![feature(hashmap_internals)]
|
||||
#![feature(hint_must_use)]
|
||||
#![feature(int_from_ascii)]
|
||||
#![feature(ip)]
|
||||
#![feature(maybe_uninit_array_assume_init)]
|
||||
#![feature(panic_can_unwind)]
|
||||
#![feature(panic_internals)]
|
||||
#![feature(pin_coerce_unsized_trait)]
|
||||
#![feature(pointer_is_aligned_to)]
|
||||
#![feature(portable_simd)]
|
||||
#![feature(ptr_as_uninit)]
|
||||
#![feature(ptr_mask)]
|
||||
#![feature(random)]
|
||||
#![feature(slice_internals)]
|
||||
#![feature(slice_ptr_get)]
|
||||
#![feature(slice_range)]
|
||||
#![feature(slice_split_once)]
|
||||
#![feature(std_internals)]
|
||||
#![feature(str_internals)]
|
||||
#![feature(sync_unsafe_cell)]
|
||||
#![feature(temporary_niche_types)]
|
||||
#![feature(ub_checks)]
|
||||
#![feature(used_with_arg)]
|
||||
// tidy-alphabetical-end
|
||||
//
|
||||
// Library features (alloc):
|
||||
// tidy-alphabetical-start
|
||||
#![feature(alloc_layout_extra)]
|
||||
#![feature(allocator_api)]
|
||||
#![feature(clone_from_ref)]
|
||||
#![feature(get_mut_unchecked)]
|
||||
#![feature(map_try_insert)]
|
||||
#![feature(slice_concat_trait)]
|
||||
#![feature(thin_box)]
|
||||
#![feature(try_reserve_kind)]
|
||||
#![feature(try_with_capacity)]
|
||||
#![feature(unique_rc_arc)]
|
||||
#![feature(wtf8_internals)]
|
||||
// tidy-alphabetical-end
|
||||
//
|
||||
// Library features (unwind):
|
||||
// tidy-alphabetical-start
|
||||
// #![feature(panic_unwind)]
|
||||
// tidy-alphabetical-end
|
||||
//
|
||||
// Library features (std_detect):
|
||||
// tidy-alphabetical-start
|
||||
// #![feature(stdarch_internal)]
|
||||
// tidy-alphabetical-end
|
||||
//
|
||||
// Only for re-exporting:
|
||||
// tidy-alphabetical-start
|
||||
#![feature(assert_matches)]
|
||||
#![feature(async_iterator)]
|
||||
#![feature(c_variadic)]
|
||||
#![feature(cfg_accessible)]
|
||||
#![feature(cfg_eval)]
|
||||
#![feature(concat_bytes)]
|
||||
#![feature(const_format_args)]
|
||||
#![feature(custom_test_frameworks)]
|
||||
#![feature(edition_panic)]
|
||||
#![feature(format_args_nl)]
|
||||
#![feature(log_syntax)]
|
||||
#![feature(test)]
|
||||
#![feature(trace_macros)]
|
||||
// tidy-alphabetical-end
|
||||
//
|
||||
// Only used in tests/benchmarks:
|
||||
//
|
||||
// Only for const-ness:
|
||||
// tidy-alphabetical-start
|
||||
// #![feature(io_const_error)]
|
||||
// tidy-alphabetical-end
|
||||
//
|
||||
#![feature(c_size_t, unsafe_binders)]
|
||||
#![allow(clippy::doc_lazy_continuation, clippy::legacy_numeric_constants)]
|
||||
#![allow(stable_features, incomplete_features)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub use core::any;
|
||||
pub use core::array;
|
||||
pub use core::async_iter;
|
||||
pub use core::cell;
|
||||
pub use core::char;
|
||||
pub use core::clone;
|
||||
pub use core::cmp;
|
||||
pub use core::convert;
|
||||
pub use core::default;
|
||||
pub use core::future;
|
||||
pub use core::hint;
|
||||
#[allow(deprecated, deprecated_in_future)]
|
||||
pub use core::i8;
|
||||
#[allow(deprecated, deprecated_in_future)]
|
||||
pub use core::i16;
|
||||
#[allow(deprecated, deprecated_in_future)]
|
||||
pub use core::i32;
|
||||
#[allow(deprecated, deprecated_in_future)]
|
||||
pub use core::i64;
|
||||
#[allow(deprecated, deprecated_in_future)]
|
||||
pub use core::i128;
|
||||
pub use core::intrinsics;
|
||||
#[allow(deprecated, deprecated_in_future)]
|
||||
pub use core::isize;
|
||||
pub use core::iter;
|
||||
pub use core::marker;
|
||||
pub use core::mem;
|
||||
pub use core::ops;
|
||||
pub use core::option;
|
||||
pub use core::pin;
|
||||
pub use core::ptr;
|
||||
pub use core::range;
|
||||
pub use core::result;
|
||||
#[allow(deprecated, deprecated_in_future)]
|
||||
pub use core::u8;
|
||||
#[allow(deprecated, deprecated_in_future)]
|
||||
pub use core::u16;
|
||||
#[allow(deprecated, deprecated_in_future)]
|
||||
pub use core::u32;
|
||||
#[allow(deprecated, deprecated_in_future)]
|
||||
pub use core::u64;
|
||||
#[allow(deprecated, deprecated_in_future)]
|
||||
pub use core::u128;
|
||||
pub use core::unsafe_binder;
|
||||
#[allow(deprecated, deprecated_in_future)]
|
||||
pub use core::usize;
|
||||
|
||||
pub use alloc::borrow;
|
||||
pub use alloc::boxed;
|
||||
pub use alloc::fmt;
|
||||
pub use alloc::format;
|
||||
pub use alloc::rc;
|
||||
pub use alloc::slice;
|
||||
pub use alloc::str;
|
||||
pub use alloc::string;
|
||||
pub use alloc::vec;
|
||||
|
||||
pub mod ffi;
|
||||
pub mod io;
|
||||
pub mod prelude;
|
||||
pub mod process;
|
||||
|
||||
pub use shared::fs;
|
||||
pub use shared::syscall;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($args:expr),*) => {
|
||||
$crate::syscall::write_string_temp(&format!($($args),*))
|
||||
};
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
() => {
|
||||
$crate::print!("");
|
||||
// $crate::print!("\n\r");
|
||||
};
|
||||
($($args:expr),*) => {
|
||||
$crate::print!($($args),*);
|
||||
// $crate::println!();
|
||||
};
|
||||
}
|
||||
54
crates/std/src/prelude.rs
Normal file
54
crates/std/src/prelude.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
pub mod rust_2024 {
|
||||
pub use crate::print;
|
||||
pub use crate::println;
|
||||
pub use alloc::borrow::ToOwned;
|
||||
pub use alloc::format;
|
||||
pub use alloc::string::String;
|
||||
pub use alloc::vec;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
struct GlobalAllocator;
|
||||
|
||||
#[core::prelude::v1::global_allocator]
|
||||
static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator;
|
||||
|
||||
unsafe impl core::alloc::GlobalAlloc for GlobalAllocator {
|
||||
unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
|
||||
crate::syscall::alloc(layout)
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
|
||||
crate::syscall::dealloc(ptr, layout)
|
||||
}
|
||||
}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(_panic_info: &core::panic::PanicInfo) -> ! {
|
||||
// TODO print
|
||||
loop {}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// `argc` and `argv` are passed by the kernel
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn _start(argc: isize, argv: *const *const u8) -> isize {
|
||||
unsafe extern "Rust" {
|
||||
fn main(argc: isize, argv: *const *const u8) -> isize;
|
||||
}
|
||||
|
||||
unsafe { main(argc, argv) }
|
||||
}
|
||||
|
||||
#[lang = "start"]
|
||||
pub fn lang_start<T: crate::process::Termination + 'static>(
|
||||
main: fn() -> T,
|
||||
argc: isize,
|
||||
argv: *const *const u8,
|
||||
_sigpipe: u8,
|
||||
) -> isize {
|
||||
println!("{}", argc);
|
||||
println!("{:?}", argv);
|
||||
main().report().to_isize()
|
||||
}
|
||||
}
|
||||
29
crates/std/src/process.rs
Normal file
29
crates/std/src/process.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
pub struct ExitCode(isize);
|
||||
|
||||
impl ExitCode {
|
||||
pub const SUCCESS: ExitCode = ExitCode(0);
|
||||
|
||||
pub fn to_isize(self) -> isize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[lang = "termination"]
|
||||
pub trait Termination {
|
||||
/// Is called to get the representation of the value as status code.
|
||||
/// This status code is returned to the operating system.
|
||||
fn report(self) -> ExitCode;
|
||||
}
|
||||
|
||||
impl Termination for () {
|
||||
#[inline]
|
||||
fn report(self) -> ExitCode {
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
}
|
||||
impl Termination for isize {
|
||||
#[inline]
|
||||
fn report(self) -> ExitCode {
|
||||
ExitCode(self)
|
||||
}
|
||||
}
|
||||
2
ilm.ld
2
ilm.ld
@@ -5,7 +5,7 @@ OUTPUT_ARCH(riscv)
|
||||
ENTRY(_start)
|
||||
|
||||
MEMORY {
|
||||
RAM (wxa) : ORIGIN = 0x80000000, LENGTH = 128M
|
||||
RAM (wxa) : ORIGIN = 0x80000000, LENGTH = 512M
|
||||
}
|
||||
|
||||
SECTIONS {
|
||||
|
||||
17
justfile
17
justfile
@@ -2,6 +2,7 @@ release := ""
|
||||
qemu_flags := ""
|
||||
cargo_flags := "" + if release != "" { "--release" } else { "" }
|
||||
bin_path := if release != "" { "target/riscv64/release" } else { "target/riscv64/debug" }
|
||||
rust_src := `rustc --print sysroot` / "lib/rustlib/src/rust/library/std/src"
|
||||
|
||||
default: run
|
||||
|
||||
@@ -12,6 +13,18 @@ mount_filesystem:
|
||||
sync_filesystem:
|
||||
sync
|
||||
|
||||
cp_std path:
|
||||
@echo "Copying {{ path }}"
|
||||
@mkdir {{ "crates/std/src" / parent_directory(path) }} -p
|
||||
@cp {{ rust_src / path }} {{ "crates/std/src" / path }}
|
||||
@perl -i -0777 -pe 's/^\s*#!?\[(un)?stable\(.*?\)\s*\]\n//gsm' {{ "crates/std/src" / path }}
|
||||
@perl -i -0777 -pe 's/^\s*#!?\[rustc_.*?\]\n//gsm' {{ "crates/std/src" / path }}
|
||||
|
||||
update_std:
|
||||
@just cp_std "ffi/c_str.rs"
|
||||
@just cp_std "ffi/mod.rs"
|
||||
@just cp_std "ffi/os_str.rs"
|
||||
|
||||
build_user_prog prog:
|
||||
RUSTFLAGS="-C relocation-model=pic -C link-arg=-Tuser.ld -C link-arg=-pie" cargo b {{ cargo_flags }} --package {{ prog }}
|
||||
riscv64-elf-strip {{ bin_path / prog }}
|
||||
@@ -34,8 +47,8 @@ qemu := f"qemu-system-riscv64 \
|
||||
-device bochs-display \
|
||||
-device virtio-keyboard-pci \
|
||||
-device virtio-mouse-pci \
|
||||
-device loader,file=disk.img,addr=0x90000000 \
|
||||
-bios none -m 512M {{qemu_flags}}"
|
||||
-device loader,file=disk.img,addr=0xA0000000 \
|
||||
-bios none -m 1024M {{qemu_flags}}"
|
||||
|
||||
# -trace \"virtio*\"
|
||||
# -d guest_errors,unimp,int"
|
||||
|
||||
2
src/drivers.rs
Normal file
2
src/drivers.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod keyboard;
|
||||
pub mod mouse;
|
||||
67
src/drivers/keyboard.rs
Normal file
67
src/drivers/keyboard.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use crate::{
|
||||
keymap::{KeyType, ModifierType, map_keycode},
|
||||
tty::TTY0,
|
||||
virtio::{
|
||||
Virtqueue,
|
||||
input::{EventCodeValue, VirtioInputEvent, VirtioPciDriver},
|
||||
},
|
||||
virtual_fs::{FILE_SYSTEM, VirtualFileSystem},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct KeyboardState {
|
||||
// ctrl_modifier: bool,
|
||||
pub shift_modifier: bool,
|
||||
pub alt_gr_modifier: bool,
|
||||
}
|
||||
|
||||
impl KeyboardState {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
// ctrl_modifier: false,
|
||||
shift_modifier: false,
|
||||
alt_gr_modifier: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static mut KBD_QUEUE: Virtqueue = unsafe { core::mem::zeroed() };
|
||||
pub static mut KBD_DRIVER: VirtioPciDriver<KeyboardState> = unsafe {
|
||||
VirtioPciDriver::new(
|
||||
|state, event| {
|
||||
let mut kbd_buffer = FILE_SYSTEM.open("/dev/input/keyboard".as_ref()).unwrap();
|
||||
kbd_buffer
|
||||
.write(core::mem::transmute::<
|
||||
&VirtioInputEvent,
|
||||
&[u8; size_of::<VirtioInputEvent>()],
|
||||
>(event))
|
||||
.unwrap();
|
||||
|
||||
if event.is_key() {
|
||||
let event = event.as_key_event();
|
||||
#[allow(clippy::single_match)]
|
||||
match map_keycode(event.code, state) {
|
||||
KeyType::Ascii(c) if event.value == EventCodeValue::Pressed => {
|
||||
let mut buf = [0; 4];
|
||||
let to_send = c.encode_utf8(&mut buf);
|
||||
for c in to_send.as_bytes() {
|
||||
TTY0.buffer.borrow_mut().push(*c);
|
||||
}
|
||||
}
|
||||
KeyType::Modifier(ModifierType::Shift) => {
|
||||
state.shift_modifier = !state.shift_modifier;
|
||||
}
|
||||
KeyType::Modifier(ModifierType::AltGr) => {
|
||||
state.alt_gr_modifier = !state.alt_gr_modifier;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// println!("event: {:#?}", event);
|
||||
} else {
|
||||
// println!("key pressed, {:#?}", event);
|
||||
}
|
||||
},
|
||||
KeyboardState::new(),
|
||||
&mut KBD_QUEUE,
|
||||
)
|
||||
};
|
||||
38
src/drivers/mouse.rs
Normal file
38
src/drivers/mouse.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use crate::{
|
||||
cursor::{clear_cursor, draw_cursor},
|
||||
vga::Vga,
|
||||
virtio::{self, Virtqueue, input::VirtioPciDriver},
|
||||
};
|
||||
|
||||
pub static mut MOUSE_POSITION: (u16, u16) = (0, 0);
|
||||
|
||||
static mut MOUSE_QUEUE: Virtqueue = unsafe { core::mem::zeroed() };
|
||||
pub static mut MOUSE_DRIVER: VirtioPciDriver<()> = unsafe {
|
||||
VirtioPciDriver::new(
|
||||
|_, event| {
|
||||
if event.is_relative() {
|
||||
let event = event.as_relative_event();
|
||||
|
||||
clear_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1);
|
||||
|
||||
match event.code {
|
||||
virtio::input::EventCodeRelative::X => {
|
||||
MOUSE_POSITION.0 = (MOUSE_POSITION.0 as i32 + event.value) as u16
|
||||
}
|
||||
virtio::input::EventCodeRelative::Y => {
|
||||
MOUSE_POSITION.1 = (MOUSE_POSITION.1 as i32 + event.value) as u16
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
draw_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1);
|
||||
|
||||
// println!("mouse moved relatively, {:#?}", event);
|
||||
} else {
|
||||
// println!("mouse moved, {:#?}", event);
|
||||
}
|
||||
},
|
||||
(),
|
||||
&mut MOUSE_QUEUE,
|
||||
)
|
||||
};
|
||||
@@ -12,7 +12,7 @@ use io::{IoBase, Read, Seek, Write};
|
||||
|
||||
use crate::virtual_fs::{VirtualFileSystem, VirtualNode};
|
||||
|
||||
const DISK_ADDR: *const u8 = 0x9000_0000 as *const _;
|
||||
const DISK_ADDR: *const u8 = 0xA000_0000 as *const _;
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Simple disk backend that reads from a fixed memory region.
|
||||
|
||||
@@ -9,9 +9,9 @@ use log::info;
|
||||
use shared::syscall::SysCall;
|
||||
|
||||
use crate::{
|
||||
KBD_DRIVER, MOUSE_DRIVER,
|
||||
boot::sbi::{ExtensionID, TimerFunctionID},
|
||||
clear_csr,
|
||||
drivers::{keyboard::KBD_DRIVER, mouse::MOUSE_DRIVER},
|
||||
process::{ExecutionContext, exit_process, sleep},
|
||||
read_csr,
|
||||
riscv::{disable_interrupt, dump_cpu},
|
||||
@@ -160,6 +160,15 @@ unsafe extern "C" fn supervisor_trap_handler(
|
||||
|
||||
unsafe { (*interrupt_state).a[0] = fd as u64 };
|
||||
}
|
||||
SysCall::Close => {
|
||||
let fd = a1;
|
||||
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
let current_process = scheduler.get_current_process();
|
||||
let mut vnode = current_process.fd_table[fd as usize].take().unwrap();
|
||||
|
||||
vnode.close();
|
||||
}
|
||||
SysCall::Write => {
|
||||
let fd = a1;
|
||||
let buf =
|
||||
@@ -168,7 +177,8 @@ unsafe extern "C" fn supervisor_trap_handler(
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
let current_process = scheduler.get_current_process();
|
||||
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();
|
||||
vnode.write(buf).unwrap();
|
||||
let res = vnode.write(buf).unwrap();
|
||||
unsafe { (*interrupt_state).a[0] = res as u64 };
|
||||
}
|
||||
SysCall::Read => {
|
||||
let fd = a1;
|
||||
@@ -182,6 +192,8 @@ unsafe extern "C" fn supervisor_trap_handler(
|
||||
if res == 0 && !buf.is_empty() {
|
||||
loop_syscall(interrupt_state);
|
||||
scheduler.schedule(&mut interrupt_state);
|
||||
} else {
|
||||
unsafe { (*interrupt_state).a[0] = res as u64 };
|
||||
}
|
||||
}
|
||||
SysCall::Seek => {
|
||||
@@ -197,6 +209,17 @@ unsafe extern "C" fn supervisor_trap_handler(
|
||||
let vnode = current_process.fd_table[fd as usize].as_mut().unwrap();
|
||||
vnode.seek(seek).unwrap();
|
||||
}
|
||||
SysCall::Spawn => {
|
||||
let path = unsafe { str::from_raw_parts(a1 as *const u8, a2 as usize) };
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
scheduler.create_process_from_file(path, 0, core::ptr::null());
|
||||
}
|
||||
SysCall::ExecVE => {
|
||||
// let path = unsafe { str::from_raw_parts(a1 as *const u8, a2 as usize) };
|
||||
// let mut scheduler = SCHEDULER.lock();
|
||||
// scheduler.create_process_from_file(path, &[]);
|
||||
unimplemented!("ExecVE is not implemented")
|
||||
}
|
||||
SysCall::Alloc => {
|
||||
let layout = Layout::from_size_align(a1 as usize, a2 as usize).unwrap();
|
||||
// Allocate memory and put the pointer in a0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::KeyboardState;
|
||||
use crate::drivers::keyboard::KeyboardState;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive_const(PartialEq, Eq)]
|
||||
|
||||
125
src/main.rs
125
src/main.rs
@@ -12,7 +12,8 @@
|
||||
arbitrary_self_types_pointers,
|
||||
derive_const,
|
||||
const_cmp,
|
||||
const_trait_impl
|
||||
const_trait_impl,
|
||||
trait_alias
|
||||
)]
|
||||
|
||||
use core::sync::atomic::AtomicBool;
|
||||
@@ -22,20 +23,15 @@ use embedded_alloc::LlffHeap as Heap;
|
||||
use log::info;
|
||||
|
||||
use crate::{
|
||||
cursor::{clear_cursor, draw_cursor},
|
||||
drivers::{keyboard::KBD_DRIVER, mouse::MOUSE_DRIVER},
|
||||
io::init_log,
|
||||
keymap::{KeyType, ModifierType, map_keycode},
|
||||
pci::{PciDeviceIterator, scan_virtio_devices},
|
||||
pci::scan_virtio_devices,
|
||||
riscv::enable_supervisor_interrupt,
|
||||
scheduler::{SCHEDULER, idle},
|
||||
tty::TTY0,
|
||||
user::{proc2, test},
|
||||
vga::Vga,
|
||||
virtio::{
|
||||
Virtqueue,
|
||||
input::{EventCodeValue, VirtioInputEvent, VirtioPciDriver, init_plic_pci},
|
||||
},
|
||||
virtual_fs::{FILE_SYSTEM, VirtualFileSystem, init_file_system},
|
||||
virtio::input::init_plic_pci,
|
||||
virtual_fs::init_file_system,
|
||||
};
|
||||
|
||||
extern crate alloc;
|
||||
@@ -44,6 +40,7 @@ mod critical_section;
|
||||
mod cursor;
|
||||
mod data_structures;
|
||||
mod draw;
|
||||
mod drivers;
|
||||
mod fs;
|
||||
mod interrupt;
|
||||
mod io;
|
||||
@@ -75,97 +72,6 @@ const _: () = assert!(core::mem::size_of::<usize>() == core::mem::size_of::<u64>
|
||||
#[cfg(not(target_endian = "little"))]
|
||||
compile_error! {"This kernel implementation assume endianness is little-endian. Some memory access like PCI could not work in big-endian."}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct KeyboardState {
|
||||
// ctrl_modifier: bool,
|
||||
pub shift_modifier: bool,
|
||||
pub alt_gr_modifier: bool,
|
||||
}
|
||||
|
||||
impl KeyboardState {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
// ctrl_modifier: false,
|
||||
shift_modifier: false,
|
||||
alt_gr_modifier: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static mut KBD_QUEUE: Virtqueue = unsafe { core::mem::zeroed() };
|
||||
pub static mut KBD_DRIVER: VirtioPciDriver<KeyboardState> = unsafe {
|
||||
VirtioPciDriver::new(
|
||||
|state, event| {
|
||||
let mut kbd_buffer = FILE_SYSTEM.open("/dev/input/keyboard".as_ref()).unwrap();
|
||||
kbd_buffer
|
||||
.write(core::mem::transmute::<
|
||||
&VirtioInputEvent,
|
||||
&[u8; size_of::<VirtioInputEvent>()],
|
||||
>(event))
|
||||
.unwrap();
|
||||
|
||||
if event.is_key() {
|
||||
let event = event.as_key_event();
|
||||
#[allow(clippy::single_match)]
|
||||
match map_keycode(event.code, state) {
|
||||
KeyType::Ascii(c) if event.value == EventCodeValue::Pressed => {
|
||||
let mut buf = [0; 4];
|
||||
let to_send = c.encode_utf8(&mut buf);
|
||||
for c in to_send.as_bytes() {
|
||||
TTY0.buffer.borrow_mut().push(*c);
|
||||
}
|
||||
}
|
||||
KeyType::Modifier(ModifierType::Shift) => {
|
||||
state.shift_modifier = !state.shift_modifier;
|
||||
}
|
||||
KeyType::Modifier(ModifierType::AltGr) => {
|
||||
state.alt_gr_modifier = !state.alt_gr_modifier;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
println!("event: {:#?}", event);
|
||||
} else {
|
||||
// println!("key pressed, {:#?}", event);
|
||||
}
|
||||
},
|
||||
KeyboardState::new(),
|
||||
&mut KBD_QUEUE,
|
||||
)
|
||||
};
|
||||
|
||||
pub static mut MOUSE_POSITION: (u16, u16) = (0, 0);
|
||||
|
||||
static mut MOUSE_QUEUE: Virtqueue = unsafe { core::mem::zeroed() };
|
||||
pub static mut MOUSE_DRIVER: VirtioPciDriver<()> = unsafe {
|
||||
VirtioPciDriver::new(
|
||||
|_, event| {
|
||||
if event.is_relative() {
|
||||
let event = event.as_relative_event();
|
||||
|
||||
clear_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1);
|
||||
|
||||
match event.code {
|
||||
virtio::input::EventCodeRelative::X => {
|
||||
MOUSE_POSITION.0 = (MOUSE_POSITION.0 as i32 + event.value) as u16
|
||||
}
|
||||
virtio::input::EventCodeRelative::Y => {
|
||||
MOUSE_POSITION.1 = (MOUSE_POSITION.1 as i32 + event.value) as u16
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
draw_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1);
|
||||
|
||||
// println!("mouse moved relatively, {:#?}", event);
|
||||
} else {
|
||||
// println!("mouse moved, {:#?}", event);
|
||||
}
|
||||
},
|
||||
(),
|
||||
&mut MOUSE_QUEUE,
|
||||
)
|
||||
};
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn supervisor_mode_entry() {
|
||||
unsafe {
|
||||
@@ -180,19 +86,20 @@ pub extern "C" fn supervisor_mode_entry() {
|
||||
info!("Hello World !");
|
||||
// unsafe { Vga.draw_string(10, 10, "Hello World !", Color::WHITE, Color::BLACK) };
|
||||
|
||||
SCHEDULER.lock().create_process(Box::new(test), "proc1");
|
||||
SCHEDULER.lock().create_process(Box::new(proc2), "proc2");
|
||||
// let binding = Box::leak(Box::new(["coucou".as_bytes()]));
|
||||
SCHEDULER
|
||||
.lock()
|
||||
.create_process(Box::new(test), "proc1", 0, core::ptr::null());
|
||||
SCHEDULER
|
||||
.lock()
|
||||
.create_process(Box::new(proc2), "proc2", 0, core::ptr::null());
|
||||
|
||||
SCHEDULER
|
||||
.lock()
|
||||
.create_process_from_file("/usr/bin/test_pic");
|
||||
.create_process_from_file("/usr/bin/test_pic", 0, core::ptr::null());
|
||||
|
||||
enable_supervisor_interrupt();
|
||||
|
||||
for pci in PciDeviceIterator::new() {
|
||||
println!("{:x?}", pci.vendor_and_device_id())
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let (pci_keyboard, pci_mouse) = scan_virtio_devices();
|
||||
let (pci_keyboard, pci_mouse) = (pci_keyboard.unwrap(), pci_mouse.unwrap());
|
||||
@@ -214,5 +121,5 @@ pub extern "C" fn supervisor_mode_entry() {
|
||||
init_plic_pci(pci_keyboard.irq);
|
||||
init_plic_pci(pci_mouse.irq);
|
||||
}
|
||||
idle();
|
||||
idle(0, core::ptr::null());
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ use crate::{
|
||||
riscv::SStatus,
|
||||
scheduler::{ACTIVE_PID, SCHEDULER, Scheduler},
|
||||
time::elapsed_time_since_startup,
|
||||
tty::TTY0,
|
||||
virtual_fs::{FILE_SYSTEM, VirtualFileSystem, VirtualNode},
|
||||
};
|
||||
|
||||
@@ -66,6 +65,9 @@ pub struct ExecutionContext {
|
||||
pub mstatus: u64,
|
||||
}
|
||||
|
||||
pub trait ProcessEntryTrait = Fn(isize, *const *const u8);
|
||||
pub type ProcessEntry = dyn ProcessEntryTrait;
|
||||
|
||||
/// Represents a process in the system.
|
||||
///
|
||||
/// Each process has its own execution context, stack,
|
||||
@@ -78,13 +80,13 @@ pub struct Process {
|
||||
/// Current state of the process.
|
||||
pub state: ProcessState,
|
||||
/// Optional entry point for the process code.
|
||||
pub entry: Option<Box<dyn Fn()>>,
|
||||
pub entry: Option<Box<ProcessEntry>>,
|
||||
/// Wake time for sleeping processes.
|
||||
pub wake_time: Duration,
|
||||
/// Saved execution context.
|
||||
pub ctx: ExecutionContext,
|
||||
/// Process stack.
|
||||
pub stack: [u64; STACK_SIZE],
|
||||
pub stack: Box<[u64; STACK_SIZE]>,
|
||||
/// File descriptor table.
|
||||
pub fd_table: Vec<Option<Box<dyn VirtualNode>>>,
|
||||
}
|
||||
@@ -109,7 +111,7 @@ impl Default for Process {
|
||||
mepc: core::ptr::null(),
|
||||
mstatus: 0,
|
||||
},
|
||||
stack: [0; _],
|
||||
stack: unsafe { Box::new_zeroed().assume_init() },
|
||||
entry: None,
|
||||
fd_table: Vec::new(),
|
||||
}
|
||||
@@ -150,7 +152,12 @@ impl Scheduler {
|
||||
/// Attempts to open `path`, load its contents into memory and create a new
|
||||
/// kernel process that will execute the loaded binary. Returns the PID of the
|
||||
/// created process, or -1 on failure.
|
||||
pub fn create_process_from_file<T: AsRef<Path>>(&mut self, path: T) -> i64 {
|
||||
pub fn create_process_from_file<T: AsRef<Path>>(
|
||||
&mut self,
|
||||
path: T,
|
||||
argc: isize,
|
||||
argv: *const *const u8,
|
||||
) -> i64 {
|
||||
let path = path.as_ref();
|
||||
let name = path.as_str();
|
||||
|
||||
@@ -231,25 +238,30 @@ impl Scheduler {
|
||||
// Entry point
|
||||
let entry_va = gelf.entry;
|
||||
let entry_addr = unsafe { base.add((entry_va - min_vaddr) as usize) } as *const u8;
|
||||
let entry_fn =
|
||||
unsafe { core::mem::transmute::<*const u8, extern "C" fn()>(entry_addr) };
|
||||
let wrapper = Box::new(move || {
|
||||
entry_fn();
|
||||
});
|
||||
let entry_fn = unsafe {
|
||||
core::mem::transmute::<*const u8, extern "C" fn(isize, *const *const u8)>(
|
||||
entry_addr,
|
||||
)
|
||||
};
|
||||
let wrapper = move |argc, argv| {
|
||||
entry_fn(argc, argv);
|
||||
};
|
||||
println!("Program loaded at : {:x?}", entry_addr);
|
||||
return self.create_process(wrapper, name);
|
||||
return self.create_process(wrapper, name, argc, argv);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: treat the file as a raw binary blob and execute in-place
|
||||
let entry_point = unsafe {
|
||||
core::mem::transmute::<*const u8, extern "C" fn()>(Vec::leak(content).as_ptr())
|
||||
core::mem::transmute::<*const u8, extern "C" fn(isize, *const *const u8)>(
|
||||
Vec::leak(content).as_ptr(),
|
||||
)
|
||||
};
|
||||
let wrapper = move |argc, argv| {
|
||||
entry_point(argc, argv);
|
||||
};
|
||||
let wrapper = Box::new(move || {
|
||||
entry_point();
|
||||
});
|
||||
|
||||
self.create_process(wrapper, name)
|
||||
self.create_process(wrapper, name, argc, argv)
|
||||
}
|
||||
|
||||
/// Creates a new process with the specified code and name.
|
||||
@@ -271,39 +283,48 @@ impl Scheduler {
|
||||
///
|
||||
/// The provided `code` function will be executed when the process is first
|
||||
/// scheduled. Returns the new PID, or -1 if the process table is full.
|
||||
pub fn create_process<T: Into<String>, F: Fn() + 'static + Send>(
|
||||
pub fn create_process<T: Into<String>, F: ProcessEntryTrait + 'static + Send>(
|
||||
&mut self,
|
||||
code: Box<F>,
|
||||
code: F,
|
||||
name: T,
|
||||
argc: isize,
|
||||
argv: *const *const u8,
|
||||
) -> i64 {
|
||||
// SAFETY: Initializing process in the global table.
|
||||
// Access is safe because we verified bounds and found a Dead slot.
|
||||
unsafe {
|
||||
self.process_table
|
||||
.insert(self.next_pid, Box::new(Process::default()));
|
||||
self.process_table.insert(self.next_pid, Process::default());
|
||||
let process = self.process_table.get_mut(&self.next_pid).unwrap();
|
||||
|
||||
// Configure process metadata
|
||||
process.pid = self.next_pid as i64;
|
||||
process.name = name.into();
|
||||
process.state = ProcessState::Activable;
|
||||
process.entry = Some(code);
|
||||
process.entry = Some(Box::new(code));
|
||||
|
||||
process.fd_table = Vec::new();
|
||||
FILE_SYSTEM.mount(
|
||||
format!("/proc/{}/0", process.pid).into(),
|
||||
Box::new(TTY0.clone()),
|
||||
);
|
||||
// FILE_SYSTEM.mount(
|
||||
// format!("/proc/{}/0", process.pid).into(),
|
||||
// Box::new(TTY0.clone()),
|
||||
// );
|
||||
// FD 0
|
||||
process.fd_table.push(Some(
|
||||
FILE_SYSTEM
|
||||
.open(format!("/proc/{}/0", process.pid).as_ref())
|
||||
.unwrap(),
|
||||
));
|
||||
process
|
||||
.fd_table
|
||||
.push(Some(FILE_SYSTEM.open("/dev/null".as_ref()).unwrap()));
|
||||
// FD 1
|
||||
process
|
||||
.fd_table
|
||||
.push(Some(FILE_SYSTEM.open("/dev/null".as_ref()).unwrap()));
|
||||
// FD 2
|
||||
process
|
||||
.fd_table
|
||||
.push(Some(FILE_SYSTEM.open("/dev/null".as_ref()).unwrap()));
|
||||
|
||||
// Configure execution context
|
||||
// a0 contains the pointer to the function to execute
|
||||
process.ctx.a[0] = &raw const *process.entry.as_ref().unwrap_unchecked() as u64;
|
||||
process.ctx.a[1] = argc as u64;
|
||||
process.ctx.a[2] = argv as u64;
|
||||
|
||||
// mepc points to process_launcher which will call the function
|
||||
process.ctx.mepc = process_launcher as *const _;
|
||||
@@ -338,10 +359,15 @@ impl Scheduler {
|
||||
/// This function is installed into the process `mepc` so that when the new
|
||||
/// process is scheduled it will run this launcher which calls the user
|
||||
/// function and ensures the process exits cleanly.
|
||||
extern "C" fn process_launcher(code: *const Box<dyn Fn()>) {
|
||||
#[allow(improper_ctypes_definitions)]
|
||||
extern "C" fn process_launcher(
|
||||
code: *const Box<ProcessEntry>,
|
||||
argc: isize,
|
||||
argv: *const *const u8,
|
||||
) {
|
||||
// SAFETY: The code pointer was initialized in create_process
|
||||
// and points to a valid function.
|
||||
unsafe { (*code)() };
|
||||
unsafe { (*code)(argc, argv) };
|
||||
|
||||
// If user code didn't exit explicitly, call exit() to clean up the process
|
||||
exit();
|
||||
|
||||
@@ -17,7 +17,7 @@ use crate::{
|
||||
#[derive(Debug)]
|
||||
pub struct Scheduler {
|
||||
pub next_pid: u64,
|
||||
pub process_table: BTreeMap<u64, Box<Process>>,
|
||||
pub process_table: BTreeMap<u64, Process>,
|
||||
}
|
||||
|
||||
pub static ACTIVE_PID: AtomicU64 = AtomicU64::new(0);
|
||||
@@ -30,7 +30,7 @@ pub static SCHEDULER: Mutex<LazyCell<Scheduler>> = Mutex::new(LazyCell::new(|| S
|
||||
/// Idle loop executed when there is no runnable process.
|
||||
///
|
||||
/// Uses the `wfi` instruction to yield the CPU while waiting for interrupts.
|
||||
pub fn idle() {
|
||||
pub fn idle(_argc: isize, _argv: *const *const u8) {
|
||||
loop {
|
||||
// write_string_temp("idle");
|
||||
// info!("idle");
|
||||
@@ -47,7 +47,7 @@ impl Scheduler {
|
||||
/// it as the active process.
|
||||
pub fn init(&mut self) {
|
||||
info!("scheduler init");
|
||||
self.create_process(Box::new(idle), "idle");
|
||||
self.create_process(Box::new(idle), "idle", 0, core::ptr::null());
|
||||
self.process_table.get_mut(&0).unwrap().state = ProcessState::Active;
|
||||
}
|
||||
|
||||
@@ -69,9 +69,7 @@ impl Scheduler {
|
||||
}
|
||||
}
|
||||
|
||||
let mut current_process_iter = self
|
||||
.process_table
|
||||
.range_mut((Bound::Excluded(prev_pid), Bound::Unbounded));
|
||||
let mut current_process_iter = self.process_table.range_mut(prev_pid + 1..);
|
||||
|
||||
ACTIVE_PID.store(
|
||||
loop {
|
||||
|
||||
@@ -6,14 +6,15 @@ use core::time::Duration;
|
||||
|
||||
use shared::syscall::{sleep, write_string_temp};
|
||||
|
||||
pub fn test() {
|
||||
pub fn test(_argc: isize, _argv: *const *const u8) {
|
||||
loop {
|
||||
// write_string_temp(str::from_utf8(_args[0]).unwrap());
|
||||
write_string_temp("test");
|
||||
sleep(Duration::new(2, 0));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn proc2() {
|
||||
pub fn proc2(_argc: isize, _argv: *const *const u8) {
|
||||
loop {
|
||||
write_string_temp("proc2");
|
||||
sleep(Duration::new(3, 0));
|
||||
|
||||
@@ -22,6 +22,7 @@ pub struct VirtioPciDriver<S, F: Fn(&mut S, &VirtioInputEvent) = fn(&mut S, &Vir
|
||||
}
|
||||
|
||||
#[repr(u16)]
|
||||
#[allow(unused)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum EventType {
|
||||
Sync = 0,
|
||||
@@ -31,6 +32,7 @@ pub enum EventType {
|
||||
}
|
||||
|
||||
#[repr(u16)]
|
||||
#[allow(unused)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum EventCodeRelative {
|
||||
X = 0,
|
||||
@@ -39,6 +41,7 @@ pub enum EventCodeRelative {
|
||||
}
|
||||
|
||||
#[repr(u16)]
|
||||
#[allow(unused)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum EventCodeValue {
|
||||
Released = 0,
|
||||
|
||||
@@ -3,7 +3,6 @@ use io::SeekFrom;
|
||||
|
||||
use crate::{
|
||||
draw::{Color, Draw, FONT_HEIGHT, FONT_WIDTH},
|
||||
println,
|
||||
vga::{self, Vga},
|
||||
virtual_fs::{self, FILE_SYSTEM, VirtualFileSystem},
|
||||
};
|
||||
@@ -41,7 +40,6 @@ impl VirtualConsole {
|
||||
}
|
||||
|
||||
pub fn write_char(&mut self, c: char) {
|
||||
println!("char_console: {:?}", c as u64);
|
||||
let mut last_cursor = self.cursor;
|
||||
match c {
|
||||
'\n' => {
|
||||
|
||||
@@ -9,6 +9,7 @@ use hashbrown::HashMap;
|
||||
use io::{IoBase, Read, Seek, Write};
|
||||
|
||||
pub mod keyboard;
|
||||
pub mod null;
|
||||
pub mod stdin;
|
||||
pub mod virtual_stdin;
|
||||
|
||||
@@ -16,10 +17,12 @@ use crate::{
|
||||
fs::Disk,
|
||||
tty::TTY0,
|
||||
vga::Vga,
|
||||
virtual_fs::{keyboard::KeyboardBuffer, virtual_stdin::VirtualStdin},
|
||||
virtual_fs::{keyboard::KeyboardBuffer, null::Null, virtual_stdin::VirtualStdin},
|
||||
};
|
||||
|
||||
pub trait VirtualNode: IoBase<Error = ()> + Read + Write + Seek + Debug {}
|
||||
pub trait VirtualNode: IoBase<Error = ()> + Read + Write + Seek + Debug {
|
||||
fn close(&mut self) {}
|
||||
}
|
||||
|
||||
pub trait VirtualFileSystem: Debug {
|
||||
fn open(&mut self, path: &Path) -> Result<Box<dyn VirtualNode + '_>, ()>;
|
||||
@@ -62,6 +65,7 @@ pub unsafe fn init_file_system() {
|
||||
unsafe {
|
||||
FILE_SYSTEM.mount("/dev/fb0".into(), Box::new(VGAFileSystem));
|
||||
FILE_SYSTEM.mount("/dev/tty0".into(), Box::new(TTY0.clone()));
|
||||
FILE_SYSTEM.mount("/dev/null".into(), Box::new(Null));
|
||||
FILE_SYSTEM.mount(
|
||||
"/dev/input/keyboard".into(),
|
||||
Box::new(KeyboardBuffer::new()),
|
||||
|
||||
51
src/virtual_fs/null.rs
Normal file
51
src/virtual_fs/null.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use alloc::boxed::Box;
|
||||
use io::{IoBase, Read, Seek, Write};
|
||||
|
||||
use crate::virtual_fs::{VirtualFileSystem, VirtualNode};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Null;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NullNode;
|
||||
|
||||
impl VirtualFileSystem for Null {
|
||||
fn open(
|
||||
&mut self,
|
||||
path: &bffs::path::Path,
|
||||
) -> Result<alloc::boxed::Box<dyn crate::virtual_fs::VirtualNode + '_>, ()> {
|
||||
if !path.is_empty() {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(Box::new(NullNode))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IoBase for NullNode {
|
||||
type Error = ();
|
||||
}
|
||||
|
||||
impl Read for NullNode {
|
||||
fn read(&mut self, _buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for NullNode {
|
||||
fn seek(&mut self, _pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for NullNode {
|
||||
fn write(&mut self, _buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtualNode for NullNode {}
|
||||
7
user/shell/Cargo.toml
Normal file
7
user/shell/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "shell"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
std = { path = "../../crates/std" }
|
||||
6
user/shell/src/main.rs
Normal file
6
user/shell/src/main.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
fn main() -> isize {
|
||||
println!(
|
||||
"Hello from PIC program loaded dynamically with custom std and a better justfile, and syscalls ! "
|
||||
);
|
||||
0
|
||||
}
|
||||
@@ -4,4 +4,4 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
os-std = { path = "../../crates/os-std" }
|
||||
std = { path = "../../crates/std" }
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![allow(unused)]
|
||||
|
||||
// use core::time::Duration;
|
||||
|
||||
use os_std::syscall;
|
||||
os_std::custom_std_setup! {}
|
||||
use std::{
|
||||
io::{Read, Write, stdin},
|
||||
syscall,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
// let mut input = String::new();
|
||||
@@ -12,19 +11,13 @@ fn main() {
|
||||
// syscall::seek(&mut file, SeekFrom::End(-3));
|
||||
// syscall::write(&mut file, &[255; 6400 * 50]);
|
||||
// syscall::sleep(Duration::from_secs_f64(2.0));
|
||||
let mut stdin = syscall::open("/dev/tty0");
|
||||
let mut file = syscall::open("/dev/tty0");
|
||||
syscall::close(0);
|
||||
let mut tty = syscall::open("/dev/tty0");
|
||||
syscall::spawn("/usr/bin/shell");
|
||||
loop {
|
||||
let mut test = [0; 2];
|
||||
syscall::read(&mut stdin, &mut test);
|
||||
let len = *test.iter().find(|x| **x == 0).unwrap_or(&1) + 1;
|
||||
syscall::write(
|
||||
&mut file,
|
||||
str::from_utf8(&test[..len as usize]).unwrap().as_bytes(),
|
||||
);
|
||||
let len = stdin().read(&mut test).unwrap();
|
||||
tty.write_all(str::from_utf8(&test[..len as usize]).unwrap().as_bytes())
|
||||
.unwrap();
|
||||
}
|
||||
// println!(
|
||||
// "Hello from PIC program loaded dynamically with custom std and a better justfile, and syscalls ! {:?}",
|
||||
// str::from_utf8(&test)
|
||||
// );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user