├── 设计文档.pdf ├── 技术报告 2023-9-23.pptx ├── .gdbinit ├── .gitignore ├── mizu ├── lib │ ├── config │ │ ├── src │ │ │ ├── cv1811h.dtb │ │ │ ├── qemu-virt.rs │ │ │ ├── cv1811h.rs │ │ │ └── lib.rs │ │ └── Cargo.toml │ ├── umifs │ │ ├── src │ │ │ ├── lib.rs │ │ │ ├── traits.rs │ │ │ ├── misc.rs │ │ │ └── types.rs │ │ └── Cargo.toml │ ├── hart-id │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── art │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ ├── ksync-core │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── state.rs │ │ │ └── lib.rs │ ├── devices │ │ ├── src │ │ │ ├── intr.rs │ │ │ ├── lib.rs │ │ │ ├── net.rs │ │ │ ├── net │ │ │ │ ├── socket │ │ │ │ │ └── dns.rs │ │ │ │ ├── time.rs │ │ │ │ ├── socket.rs │ │ │ │ ├── config.rs │ │ │ │ └── driver.rs │ │ │ ├── common.rs │ │ │ └── block.rs │ │ └── Cargo.toml │ ├── ksc │ │ ├── src │ │ │ └── lib.rs │ │ └── Cargo.toml │ ├── ktime-core │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ └── instant.rs │ ├── range-map │ │ └── Cargo.toml │ ├── ksc-core │ │ ├── src │ │ │ ├── lib.rs │ │ │ ├── raw_reg.rs │ │ │ ├── handler.rs │ │ │ └── scn.rs │ │ └── Cargo.toml │ ├── ksc-macros │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── parse.rs │ │ │ ├── bound.rs │ │ │ ├── lib.rs │ │ │ ├── lifetime.rs │ │ │ └── receiver.rs │ ├── paging │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ ├── consts.rs │ │ │ ├── level.rs │ │ │ └── addr.rs │ ├── sygnal │ │ ├── src │ │ │ ├── lib.rs │ │ │ ├── action.rs │ │ │ └── queue.rs │ │ └── Cargo.toml │ ├── rand-riscv │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── kalloc │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ └── imp.rs │ ├── umio │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── event.rs │ │ │ └── lib.rs │ ├── co-trap │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── imp.S │ │ │ ├── lib.rs │ │ │ └── tf.rs │ ├── ktime │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── afat32 │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── ksync │ │ ├── src │ │ │ ├── lib.rs │ │ │ ├── channel.rs │ │ │ ├── atomic.rs │ │ │ ├── epoch.rs │ │ │ └── channel │ │ │ │ └── broadcast.rs │ │ └── Cargo.toml │ └── kmem │ │ ├── Cargo.toml │ │ └── src │ │ ├── insn.rs │ │ ├── lib.rs │ │ └── virt │ │ └── tlb.rs ├── dev │ ├── plic │ │ ├── src │ │ │ ├── lib.rs │ │ │ ├── intr.rs │ │ │ └── dev.rs │ │ └── Cargo.toml │ ├── uart │ │ └── Cargo.toml │ ├── sdmmc │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── imp │ │ │ └── mars.rs │ │ │ └── adma.rs │ └── virtio │ │ ├── Cargo.toml │ │ └── src │ │ └── lib.rs └── kernel │ ├── src │ ├── dev │ │ ├── block.rs │ │ ├── net.rs │ │ ├── intr.rs │ │ ├── sdmmc.rs │ │ └── virtio.rs │ ├── main.rs │ ├── cpu.rs │ ├── mem.rs │ ├── syscall │ │ └── ffi.rs │ ├── mem │ │ └── shm.rs │ ├── dev.rs │ ├── fs │ │ ├── serial.rs │ │ └── debug.rs │ ├── trap.S │ ├── trap.rs │ └── task │ │ └── time.rs │ ├── cargo-config │ └── config.toml │ ├── Makefile │ ├── build.rs │ ├── Cargo.toml │ └── link.ld ├── Cargo.toml ├── third-party └── bin │ ├── opensbi-qemu-virt │ ├── rustsbi-qemu-virt │ └── rustsbi-qemu-virt.bin ├── cargo-config └── config.toml ├── rust-toolchain.toml ├── .vscode └── settings.json ├── rustfmt.toml ├── scripts └── perf.sh ├── README.md ├── LICENSE-MIT ├── Makefile └── .github └── workflows ├── code.yml └── mirror.yml /设计文档.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/js2xxx/umi/HEAD/设计文档.pdf -------------------------------------------------------------------------------- /技术报告 2023-9-23.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/js2xxx/umi/HEAD/技术报告 2023-9-23.pptx -------------------------------------------------------------------------------- /.gdbinit: -------------------------------------------------------------------------------- 1 | symbol-file -readnow debug/mizu.sym 2 | set disassemble-next-line on 3 | target remote :1234 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | /debug 3 | kernel-qemu 4 | sbi-qemu 5 | os.bin 6 | /third-party/img 7 | .cargo -------------------------------------------------------------------------------- /mizu/lib/config/src/cv1811h.dtb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/js2xxx/umi/HEAD/mizu/lib/config/src/cv1811h.dtb -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "mizu/kernel", 4 | "mizu/lib/*", 5 | "mizu/dev/*", 6 | ] 7 | -------------------------------------------------------------------------------- /third-party/bin/opensbi-qemu-virt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/js2xxx/umi/HEAD/third-party/bin/opensbi-qemu-virt -------------------------------------------------------------------------------- /third-party/bin/rustsbi-qemu-virt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/js2xxx/umi/HEAD/third-party/bin/rustsbi-qemu-virt -------------------------------------------------------------------------------- /third-party/bin/rustsbi-qemu-virt.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/js2xxx/umi/HEAD/third-party/bin/rustsbi-qemu-virt.bin -------------------------------------------------------------------------------- /mizu/lib/umifs/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | pub mod misc; 4 | pub mod path; 5 | pub mod traits; 6 | pub mod types; 7 | 8 | extern crate alloc; 9 | -------------------------------------------------------------------------------- /cargo-config/config.toml: -------------------------------------------------------------------------------- 1 | [profile.dev] 2 | incremental = true 3 | 4 | [profile.release] 5 | lto = true 6 | 7 | [build] 8 | rustflags = ["-Zshare-generics=n"] 9 | -------------------------------------------------------------------------------- /mizu/lib/hart-id/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "hart-id" 5 | version = "0.1.0" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /mizu/dev/plic/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(slice_ptr_get)] 3 | 4 | pub mod dev; 5 | mod intr; 6 | 7 | extern crate alloc; 8 | 9 | pub use self::intr::IntrManager; 10 | -------------------------------------------------------------------------------- /mizu/dev/uart/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "uart" 4 | version = "0.1.0" 5 | 6 | [features] 7 | uart-status = [] 8 | 9 | [dependencies] 10 | bitflags = "2" 11 | volatile = "0" 12 | -------------------------------------------------------------------------------- /mizu/lib/art/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Asynchorous RunTime. 2 | #![cfg_attr(not(feature = "test"), no_std)] 3 | 4 | extern crate alloc; 5 | 6 | pub mod queue; 7 | mod sched; 8 | 9 | pub use self::sched::Executor; 10 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2023-03-29" 3 | components = ["rustfmt", "rustc", "rust-std", "rust-src", "cargo", "clippy", "llvm-tools"] 4 | profile = "default" 5 | targets = ["riscv64gc-unknown-none-elf"] 6 | -------------------------------------------------------------------------------- /mizu/lib/ksync-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "ksync-core" 5 | version = "0.1.0" 6 | 7 | [dependencies] 8 | riscv = "0" 9 | 10 | [dev-dependencies] 11 | spin = "0" 12 | -------------------------------------------------------------------------------- /mizu/lib/devices/src/intr.rs: -------------------------------------------------------------------------------- 1 | pub type Completion = dyn Fn() + Send + Sync + 'static; 2 | 3 | pub trait IntrHandler: for<'any> Fn(&'any Completion) -> bool + Send + Sync + 'static {} 4 | impl Fn(&'any Completion) -> bool + Send + Sync + 'static> IntrHandler for T {} 5 | -------------------------------------------------------------------------------- /mizu/lib/ksc/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "test"), no_std)] 2 | #![cfg_attr(feature = "test", feature(once_cell))] 3 | 4 | extern crate alloc; 5 | 6 | mod handler; 7 | 8 | pub use ksc_core::*; 9 | pub use ksc_macros::*; 10 | 11 | pub use self::handler::*; 12 | -------------------------------------------------------------------------------- /mizu/lib/devices/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(if_let_guard)] 3 | #![feature(pointer_byte_offsets)] 4 | #![feature(result_option_inspect)] 5 | 6 | extern crate alloc; 7 | 8 | pub mod block; 9 | mod common; 10 | pub mod intr; 11 | pub mod net; 12 | 13 | pub use self::common::*; 14 | -------------------------------------------------------------------------------- /mizu/lib/config/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "config" 5 | version = "0.1.0" 6 | 7 | [features] 8 | cv1811h = [] 9 | qemu-virt = [] 10 | 11 | [dependencies] 12 | cfg-if = "1" 13 | num-rational = {version = "0", default-features = false} 14 | -------------------------------------------------------------------------------- /mizu/lib/ktime-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "ktime-core" 5 | version = "0.1.0" 6 | 7 | [features] 8 | test = [] 9 | 10 | [dependencies] 11 | # Local crates 12 | config = {path = "../config"} 13 | # External crates 14 | riscv = "0" 15 | -------------------------------------------------------------------------------- /mizu/lib/range-map/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "range-map" 5 | version = "0.1.0" 6 | 7 | [features] 8 | test = ["dep:rand"] 9 | 10 | [dependencies] 11 | rand = {version = "0", optional = true} 12 | rand-riscv = {path = "../rand-riscv"} 13 | -------------------------------------------------------------------------------- /mizu/lib/ksc-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(test), no_std)] 2 | #![feature(macro_metavar_expr)] 3 | 4 | extern crate alloc; 5 | 6 | mod error; 7 | pub mod handler; 8 | mod raw_reg; 9 | mod scn; 10 | 11 | pub use self::{ 12 | error::Error::{self, *}, 13 | raw_reg::*, 14 | scn::*, 15 | }; 16 | -------------------------------------------------------------------------------- /mizu/lib/ksc-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "ksc-macros" 5 | version = "0.1.0" 6 | 7 | [lib] 8 | proc-macro = true 9 | 10 | [dependencies] 11 | proc-macro2 = "1" 12 | quote = "1" 13 | syn = { version = "2.0.9", features = ["full", "visit-mut"] } -------------------------------------------------------------------------------- /mizu/lib/paging/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "rv39-paging" 5 | version = "0.1.0" 6 | 7 | [dependencies] 8 | bitflags = "2" 9 | static_assertions = "1" 10 | cfg-if = "1" 11 | 12 | [features] 13 | cv1811h = [] 14 | qemu-virt = [] 15 | test = [] 16 | -------------------------------------------------------------------------------- /mizu/lib/sygnal/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! [Official manual](https://www.man7.org/linux/man-pages/man7/signal.7.html) 2 | 3 | #![cfg_attr(not(test), no_std)] 4 | #![feature(const_bool_to_option)] 5 | #![feature(const_trait_impl)] 6 | 7 | extern crate alloc; 8 | 9 | mod action; 10 | mod queue; 11 | mod types; 12 | 13 | pub use self::{action::*, queue::*, types::*}; 14 | -------------------------------------------------------------------------------- /mizu/lib/rand-riscv/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "rand-riscv" 5 | version = "0.1.0" 6 | 7 | [features] 8 | test = ["dep:rand"] 9 | 10 | [dependencies] 11 | ahash = {version = "0", default-features = false} 12 | rand = {version = "0", optional = true} 13 | rand_chacha = {version = "0", default-features = false} 14 | -------------------------------------------------------------------------------- /mizu/lib/kalloc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "kalloc" 5 | version = "0.1.0" 6 | 7 | [features] 8 | test = [] 9 | 10 | [dependencies] 11 | # Local crates 12 | ksync-core = {path = "../ksync-core"} 13 | # External crates 14 | buddy_system_allocator = {version = "0", default-features = false} 15 | log = "0" 16 | spin = "0" 17 | -------------------------------------------------------------------------------- /mizu/lib/devices/src/net.rs: -------------------------------------------------------------------------------- 1 | //! Inspired by [`embassy-net`](https://crates.io/crates/embassy-net). 2 | 3 | mod config; 4 | mod driver; 5 | mod socket; 6 | mod stack; 7 | #[allow(dead_code)] 8 | mod time; 9 | 10 | pub use self::{ 11 | config::*, 12 | driver::{Features, Net, NetRx, NetTx}, 13 | socket::*, 14 | stack::{Stack, StackBackground, LOOPBACK_IPV4, LOOPBACK_IPV6}, 15 | }; 16 | -------------------------------------------------------------------------------- /mizu/dev/plic/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "plic" 4 | version = "0.1.0" 5 | 6 | [dependencies] 7 | # Local crates 8 | devices = {path = "../../lib/devices"} 9 | hart-id = {path = "../../lib/hart-id"} 10 | ksc = {path = "../../lib/ksc"} 11 | ksync = {path = "../../lib/ksync"} 12 | # External crates 13 | log = "0" 14 | spin = "0" 15 | static_assertions = "1" 16 | volatile = "0" 17 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.cargo.target": "riscv64gc-unknown-none-elf", 3 | "rust-analyzer.check.targets": [ 4 | "riscv64gc-unknown-none-elf", 5 | "x86_64-unknown-linux-gnu", 6 | ], 7 | "rust-analyzer.check.allTargets": false, 8 | "rust-analyzer.check.command": "clippy", 9 | "rust-analyzer.runnables.extraArgs": [ 10 | "--features", 11 | "test" 12 | ] 13 | } -------------------------------------------------------------------------------- /mizu/lib/paging/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "test"), no_std)] 2 | #![feature(const_convert)] 3 | #![feature(const_mut_refs)] 4 | #![feature(const_option_ext)] 5 | #![feature(const_trait_impl)] 6 | #![cfg_attr(test, feature(allocator_api))] 7 | #[cfg(test)] 8 | extern crate alloc; 9 | 10 | mod addr; 11 | mod consts; 12 | mod entry; 13 | mod level; 14 | 15 | pub use self::{addr::*, consts::*, entry::*, level::*}; 16 | -------------------------------------------------------------------------------- /mizu/lib/umio/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "umio" 5 | version = "0.1.0" 6 | 7 | [dependencies] 8 | # Local crates 9 | ksc-core = {path = "../ksc-core"} 10 | # External crates 11 | arsc-rs = {git = "https://github.com/js2xxx/arsc"} 12 | async-trait = "0" 13 | bitflags = "2" 14 | futures-util = {version = "0", default-features = false, features = ["alloc"]} 15 | log = "0" 16 | -------------------------------------------------------------------------------- /mizu/kernel/src/dev/block.rs: -------------------------------------------------------------------------------- 1 | use alloc::{sync::Arc, vec::Vec}; 2 | 3 | use devices::block::Block; 4 | use spin::Mutex; 5 | 6 | pub(super) static BLOCKS: Mutex>> = Mutex::new(Vec::new()); 7 | 8 | pub fn block(index: usize) -> Option> { 9 | ksync::critical(|| BLOCKS.lock().get(index).cloned()) 10 | } 11 | 12 | pub fn blocks() -> Vec> { 13 | ksync::critical(|| BLOCKS.lock().clone()) 14 | } 15 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | condense_wildcard_suffixes = true 2 | edition = "2021" 3 | format_macro_matchers = true 4 | group_imports = "StdExternalCrate" 5 | hard_tabs = false 6 | imports_granularity = "Crate" 7 | max_width = 100 8 | merge_derives = false 9 | normalize_comments = true 10 | remove_nested_parens = true 11 | reorder_imports = true 12 | reorder_modules = true 13 | use_try_shorthand = true 14 | use_field_init_shorthand = true 15 | wrap_comments = true 16 | -------------------------------------------------------------------------------- /mizu/kernel/src/dev/net.rs: -------------------------------------------------------------------------------- 1 | use alloc::{sync::Arc, vec::Vec}; 2 | 3 | use devices::net::Net; 4 | use spin::{Mutex, RwLock}; 5 | 6 | pub(super) static NETS: Mutex>>> = Mutex::new(Vec::new()); 7 | 8 | #[allow(dead_code)] 9 | pub fn net(index: usize) -> Option>> { 10 | ksync::critical(|| NETS.lock().get(index).cloned()) 11 | } 12 | 13 | pub fn nets() -> Vec>> { 14 | ksync::critical(|| NETS.lock().clone()) 15 | } 16 | -------------------------------------------------------------------------------- /mizu/lib/ksc-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "ksc-core" 5 | version = "0.1.0" 6 | 7 | [features] 8 | test = [] 9 | 10 | [dependencies] 11 | # Local crates 12 | rv39-paging = {path = "../paging"} 13 | # External crates 14 | bevy_utils_proc_macros = "0" 15 | enum-primitive-derive = "0" 16 | log = "0" 17 | nom = {version = "7", default-features = false, features = ["alloc"]} 18 | num-traits = {version = "0", default-features = false} 19 | -------------------------------------------------------------------------------- /mizu/lib/umifs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "umifs" 5 | version = "0.1.0" 6 | 7 | [features] 8 | test = [] 9 | 10 | [dependencies] 11 | # Local crates 12 | ksc-core = {path = "../ksc-core"} 13 | ktime-core = {path = "../ktime-core"} 14 | rv39-paging = {path = "../paging"} 15 | umio = {path = "../umio"} 16 | # External crates 17 | arsc-rs = {git = "https://github.com/js2xxx/arsc"} 18 | async-trait = "0" 19 | bitflags = "2" 20 | log = "0" 21 | -------------------------------------------------------------------------------- /mizu/lib/co-trap/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "co-trap" 5 | version = "0.1.0" 6 | 7 | [features] 8 | test = [] 9 | 10 | [dependencies] 11 | # Local crates 12 | ksc-core = {path = "../ksc-core"} 13 | ksync-core = {path = "../ksync-core"} 14 | # External crates 15 | bevy_utils_proc_macros = "0" 16 | enum-primitive-derive = "0" 17 | log = "0" 18 | num-traits = {version = "0", default-features = false} 19 | riscv = "0" 20 | static_assertions = "1" 21 | -------------------------------------------------------------------------------- /mizu/lib/ksc-macros/src/parse.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::Span; 2 | use syn::{ 3 | parse::{Parse, ParseStream}, 4 | Error, ItemFn, Result, 5 | }; 6 | 7 | pub struct Item(pub ItemFn); 8 | 9 | impl Parse for Item { 10 | fn parse(input: ParseStream) -> Result { 11 | let func = input.parse::()?; 12 | let sig = &func.sig; 13 | if sig.asyncness.is_none() { 14 | return Err(Error::new(Span::call_site(), "expect an async fn")); 15 | } 16 | Ok(Item(func)) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /mizu/lib/ktime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "ktime" 5 | version = "0.1.0" 6 | 7 | [features] 8 | test = ["ktime-core/test", "dep:spin_on"] 9 | 10 | [dependencies] 11 | # Local crates 12 | ksync-core = {path = "../ksync-core"} 13 | ktime-core = {path = "../ktime-core"} 14 | # External crates 15 | futures-lite = {version = "1", default-features = false, features = ["alloc"]} 16 | heapless = "0" 17 | pin-project = "1" 18 | spin_on = {version = "0", optional = true} 19 | spin = "0" 20 | -------------------------------------------------------------------------------- /mizu/lib/devices/src/net/socket/dns.rs: -------------------------------------------------------------------------------- 1 | use smoltcp::socket::dns::{GetQueryResultError, StartQueryError}; 2 | 3 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 4 | pub enum Error { 5 | Start(StartQueryError), 6 | Query(GetQueryResultError), 7 | } 8 | 9 | impl From for Error { 10 | fn from(value: StartQueryError) -> Self { 11 | Error::Start(value) 12 | } 13 | } 14 | 15 | impl From for Error { 16 | fn from(value: GetQueryResultError) -> Self { 17 | Error::Query(value) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /scripts/perf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SAMPLE_COUNT=100 4 | SLEEP_TIME=0.1 5 | 6 | for x in $(seq 1 $SAMPLE_COUNT) 7 | do 8 | riscv64-unknown-elf-gdb \ 9 | -ex "set pagination 0" \ 10 | -ex "thread apply all bt" \ 11 | -ex "detach" \ 12 | --batch 13 | sleep $SLEEP_TIME 14 | done | \ 15 | awk ' 16 | BEGIN { s = ""; } 17 | /^Thread/ { print s; s = ""; } 18 | /^\#/ { if (s != "" ) { s = s "," $4} else { s = $4 } } 19 | END { print s }' | \ 20 | sort | uniq -c | sort -r -n -k 1,1 21 | -------------------------------------------------------------------------------- /mizu/lib/art/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "art" 5 | version = "0.1.0" 6 | 7 | [features] 8 | test = [] 9 | 10 | [dependencies] 11 | # Local crates 12 | rand-riscv = {path = "../rand-riscv"} 13 | # Extenal crates 14 | arsc-rs = {git = "https://github.com/js2xxx/arsc"} 15 | async-task = {version = "4", default-features = false} 16 | crossbeam-queue = {version = "0", default-features = false, features = ["alloc", "nightly"]} 17 | log = "0" 18 | scoped-tls = {git = "https://github.com/js2xxx/scoped-tls", branch = "no_std"} 19 | -------------------------------------------------------------------------------- /mizu/lib/sygnal/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "sygnal" 5 | version = "0.1.0" 6 | 7 | [features] 8 | test = [] 9 | 10 | [dependencies] 11 | # Local crates 12 | ksc-core = {path = "../ksc-core"} 13 | ksync = {path = "../ksync"} 14 | rv39-paging = {path = "../paging"} 15 | # External crates 16 | array-macro = "2" 17 | crossbeam-queue = {version = "0", default-features = false, features = ["alloc", "nightly"]} 18 | futures-util = {version = "0", default-features = false, features = ["alloc"]} 19 | log = "0" 20 | spin = "0" 21 | -------------------------------------------------------------------------------- /mizu/lib/afat32/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "afat32" 5 | version = "0.1.0" 6 | 7 | [dependencies] 8 | # Local crates 9 | ksc-core = {path = "../ksc-core"} 10 | ksync = {path = "../ksync"} 11 | umifs = {path = "../umifs"} 12 | umio = {path = "../umio"} 13 | # External crates 14 | arsc-rs = {git = "https://github.com/js2xxx/arsc"} 15 | async-trait = "0" 16 | bitflags = "2" 17 | concat-arrays = "0" 18 | futures-util = {version = "0", default-features = false, features = ["alloc"]} 19 | log = "0" 20 | nom = {version = "7", default-features = false, features = ["alloc"]} 21 | smallvec = "1" 22 | spin = "0" 23 | -------------------------------------------------------------------------------- /mizu/kernel/cargo-config/config.toml: -------------------------------------------------------------------------------- 1 | [target.riscv64gc-unknown-none-elf] 2 | linker = "rust-lld" 3 | rustflags = [ 4 | "-Crelocation-model=pic", 5 | "-Clink-arg=--pie", 6 | "-Clink-arg=--pic-veneer", 7 | "-Clink-arg=-Bstatic", 8 | "-Clink-arg=--apply-dynamic-relocs", 9 | "-Clink-arg=--ignore-function-address-equality", 10 | "-Clink-arg=--ignore-data-address-equality", 11 | "-Clink-arg=-zseparate-loadable-segments", 12 | "-Clink-arg=--pack-dyn-relocs=relr", 13 | "-Ztls-model=local-exec", 14 | ] 15 | 16 | [profile.dev] 17 | incremental = true 18 | # lto = 'thin' 19 | # opt-level = 1 20 | 21 | [profile.release] 22 | lto = 'fat' 23 | opt-level = 3 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UMI: A RISC-V experimental OS 2 | 3 | ## 初赛文档 4 | 5 | [Gitlab](https://gitlab.eduxiji.net/PLNTRY/OSKernel2023-umi/-/blob/comp1/README.md), 6 | [Github](https://github.com/js2xxx/umi/blob/comp1/README.md) 7 | 互为镜像。 8 | 9 | ## 全国赛第一阶段文档 10 | 11 | [Gitlab](https://gitlab.eduxiji.net/PLNTRY/OSKernel2023-umi/-/blob/comp2/docs/index.md), 12 | [Github](https://github.com/js2xxx/umi/blob/comp2/docs/index.md) 13 | 互为镜像。 14 | 15 | ## 全国赛第二阶段 T3 相关文档 16 | 17 | [Gitlab](https://gitlab.eduxiji.net/PLNTRY/OSKernel2023-umi/-/blob/comp3-coverage/docs/kcov.md), 18 | [Github](https://github.com/js2xxx/umi/blob/comp3-coverage/docs/kcov.md) 19 | 互为镜像。 20 | 21 | ## [最终设计文档](设计文档.pdf) 22 | -------------------------------------------------------------------------------- /mizu/lib/afat32/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Async FAT 32 file system. Mainly inspired from 2 | //! [`rust-fatfs`](https://github.com/rafalh/rust-fatfs). 3 | #![cfg_attr(not(test), no_std)] 4 | #![feature(split_array)] 5 | #![feature(maybe_uninit_as_bytes)] 6 | #![feature(maybe_uninit_slice)] 7 | 8 | mod dir; 9 | mod dirent; 10 | mod file; 11 | mod fs; 12 | mod raw; 13 | mod table; 14 | mod time; 15 | 16 | extern crate alloc; 17 | 18 | pub use self::{ 19 | dir::FatDir, 20 | dirent::{DirEntry, FileAttributes}, 21 | file::FatFile, 22 | fs::{FatFileSystem, FatStats, FsStatusFlags}, 23 | time::{Date, DateTime, DefaultTimeProvider, NullTimeProvider, Time, TimeProvider}, 24 | }; 25 | -------------------------------------------------------------------------------- /mizu/lib/config/src/qemu-virt.rs: -------------------------------------------------------------------------------- 1 | use core::ops::Range; 2 | 3 | use num_rational::Ratio; 4 | 5 | use crate::{RAM_START, VIRT_START}; 6 | 7 | pub const RAM_SIZE: usize = 128 * 1024 * 1024; 8 | 9 | pub const KERNEL_OFFSET: usize = 0x200000; 10 | pub const KERNEL_START_PHYS: usize = RAM_START + KERNEL_OFFSET; 11 | pub const KERNEL_START: usize = VIRT_START + KERNEL_OFFSET; 12 | 13 | pub const TIME_FREQ: u128 = 10_000_000; 14 | pub const TIME_FREQ_M: Ratio = Ratio::new_raw(1, 10); // 10^6 / FREQ 15 | 16 | pub const MAX_HARTS: usize = 2; 17 | pub const HART_RANGE: Range = 0..MAX_HARTS; 18 | 19 | pub fn device_tree(payload: usize) -> *const () { 20 | payload as _ 21 | } 22 | -------------------------------------------------------------------------------- /mizu/lib/umio/src/event.rs: -------------------------------------------------------------------------------- 1 | use alloc::boxed::Box; 2 | 3 | use async_trait::async_trait; 4 | 5 | use crate::IntoAny; 6 | 7 | bitflags::bitflags! { 8 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)] 9 | pub struct Event: u16 { 10 | const READABLE = 1 << 0; 11 | const EXCEPTION = 1 << 1; 12 | const WRITABLE = 1 << 2; 13 | const ERROR = 1 << 3; 14 | const HANG_UP = 1 << 4; 15 | const INVALID = 1 << 5; 16 | } 17 | } 18 | 19 | #[async_trait] 20 | pub trait IoPoll: IntoAny { 21 | async fn event(&self, expected: Event) -> Option { 22 | let _ = expected; 23 | Some(Event::READABLE | Event::WRITABLE) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /mizu/dev/sdmmc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "sdmmc" 4 | version = "0.1.0" 5 | 6 | [features] 7 | mars = ["kmem/cv1811h"] 8 | 9 | [dependencies] 10 | # Local crates 11 | devices = {path = "../../lib/devices"} 12 | kmem = {path = "../../lib/kmem"} 13 | ksc = {path = "../../lib/ksc"} 14 | ksync = {path = "../../lib/ksync"} 15 | ktime = {path = "../../lib/ktime"} 16 | umio = {path = "../../lib/umio"} 17 | # External crates 18 | async-trait = "0" 19 | bit-struct = {version = "0", default-features = false} 20 | bitflags = "2" 21 | futures-util = {version = "0", default-features = false, features = ["alloc"]} 22 | log = "0" 23 | sdio-host = "0" 24 | spin = "0" 25 | static_assertions = "1" 26 | volatile = "0" 27 | -------------------------------------------------------------------------------- /mizu/lib/ksc-macros/src/bound.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, Span, TokenStream}; 2 | use quote::quote_spanned; 3 | 4 | pub enum InferredBound { 5 | Send, 6 | Sync, 7 | } 8 | 9 | impl InferredBound { 10 | fn as_str(&self) -> &str { 11 | match self { 12 | InferredBound::Send => "Send", 13 | InferredBound::Sync => "Sync", 14 | } 15 | } 16 | 17 | pub fn spanned_path(&self, span: Span) -> TokenStream { 18 | let ident = Ident::new(self.as_str(), span); 19 | quote_spanned!(span=> ::core::marker::#ident) 20 | } 21 | } 22 | 23 | impl PartialEq for Ident { 24 | fn eq(&self, bound: &InferredBound) -> bool { 25 | self == bound.as_str() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /mizu/lib/devices/src/net/time.rs: -------------------------------------------------------------------------------- 1 | use ktime::InstantExt; 2 | 3 | pub fn instant_to_smoltcp(src: ktime::Instant) -> smoltcp::time::Instant { 4 | let (s, u) = src.to_su(); 5 | smoltcp::time::Instant::from_micros((u + s * 1_000_000) as i64) 6 | } 7 | 8 | pub fn instant_from_smoltcp(src: smoltcp::time::Instant) -> ktime::Instant { 9 | let u = src.micros() as u64; 10 | ktime::Instant::from_su(u / 1_000_000, u % 1_000_000) 11 | } 12 | 13 | pub fn duration_to_smoltcp(src: core::time::Duration) -> smoltcp::time::Duration { 14 | smoltcp::time::Duration::from_micros(src.as_micros() as u64) 15 | } 16 | 17 | pub fn duration_from_smoltcp(src: smoltcp::time::Duration) -> core::time::Duration { 18 | core::time::Duration::from_micros(src.micros()) 19 | } 20 | -------------------------------------------------------------------------------- /mizu/lib/config/src/cv1811h.rs: -------------------------------------------------------------------------------- 1 | use core::ops::Range; 2 | 3 | use num_rational::Ratio; 4 | 5 | use crate::{RAM_START, VIRT_START}; 6 | 7 | pub const RAM_SIZE: usize = 128 * 1024 * 1024; 8 | 9 | pub const KERNEL_OFFSET: usize = 0x200000; 10 | pub const KERNEL_START_PHYS: usize = RAM_START + KERNEL_OFFSET; 11 | pub const KERNEL_START: usize = VIRT_START + KERNEL_OFFSET; 12 | 13 | pub const TIME_FREQ: u128 = 25_000_000; 14 | pub const TIME_FREQ_M: Ratio = Ratio::new_raw(1, 25); // 10^6 / FREQ 15 | 16 | pub const MAX_HARTS: usize = 1; 17 | pub const HART_RANGE: Range = 0..MAX_HARTS; 18 | 19 | pub fn device_tree(_payload: usize) -> *const () { 20 | static DEVICE_TREE: &[u8] = include_bytes!("cv1811h.dtb"); 21 | 22 | DEVICE_TREE.as_ptr().cast() 23 | } 24 | -------------------------------------------------------------------------------- /mizu/dev/virtio/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "virtio" 4 | version = "0.1.0" 5 | 6 | [dependencies] 7 | # Local crates 8 | devices = {path = "../../lib/devices"} 9 | kmem = {path = "../../lib/kmem"} 10 | ksc = {path = "../../lib/ksc"} 11 | ksync = {path = "../../lib/ksync"} 12 | umio = {path = "../../lib/umio"} 13 | # External crates 14 | arsc-rs = {git = "https://github.com/js2xxx/arsc"} 15 | async-trait = "0" 16 | atomic_refcell = "0" 17 | crossbeam-queue = {version = "0", default-features = false, features = ["alloc", "nightly"]} 18 | futures-util = {version = "0", default-features = false, features = ["alloc"]} 19 | log = "0" 20 | spin = "0" 21 | static_assertions = "1" 22 | virtio-drivers = {git = "https://github.com/rcore-os/virtio-drivers", branch = "new-netdev"} 23 | -------------------------------------------------------------------------------- /mizu/lib/config/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | cfg_if::cfg_if! { 4 | if #[cfg(feature = "cv1811h")] { 5 | #[path = "cv1811h.rs"] 6 | mod imp; 7 | } else { 8 | #[path = "qemu-virt.rs"] 9 | mod imp; 10 | } 11 | } 12 | 13 | use core::time::Duration; 14 | 15 | pub use imp::*; 16 | 17 | pub const RAM_START: usize = 0x8000_0000; 18 | pub const RAM_END: usize = RAM_START + RAM_SIZE; 19 | 20 | pub const VIRT_START: usize = 0xffff_ffc0_0000_0000 + RAM_START; 21 | pub const VIRT_END: usize = VIRT_START + RAM_SIZE; 22 | 23 | pub fn to_duration(raw: u64) -> Duration { 24 | let micros = TIME_FREQ_M.numer() * raw as u128 / TIME_FREQ_M.denom(); 25 | Duration::from_secs((micros / 1_000_000) as u64) 26 | + Duration::from_micros((micros % 1_000_000) as u64) 27 | } 28 | -------------------------------------------------------------------------------- /mizu/lib/ksc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "ksc" 5 | version = "0.1.0" 6 | 7 | [features] 8 | test = [ 9 | "dep:spin_on", 10 | "dep:co-trap", 11 | "ksc-core/test", 12 | "rand-riscv/test", 13 | ] 14 | 15 | [dependencies] 16 | # Local crates 17 | co-trap = {path = "../co-trap", optional = true} 18 | ksc-core = {path = "../ksc-core"} 19 | ksc-macros = {path = "../ksc-macros"} 20 | rand-riscv = {path = "../rand-riscv"} 21 | # External crates 22 | bevy_utils_proc_macros = "0" 23 | futures-util = {version = "0", default-features = false, features = ["alloc"]} 24 | hashbrown = {version = "0", default-features = false, features = ["inline-more"]} 25 | num-traits = {version = "0", default-features = false} 26 | spin_on = {version = "0", optional = true} 27 | -------------------------------------------------------------------------------- /mizu/lib/ksync/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(test), no_std)] 2 | #![feature(once_cell)] 3 | #![feature(thread_local)] 4 | 5 | extern crate alloc; 6 | 7 | mod atomic; 8 | pub mod channel; 9 | pub mod epoch; 10 | mod mutex; 11 | mod rcu; 12 | mod rw_lock; 13 | mod semaphore; 14 | 15 | use core::{ 16 | future::Future, 17 | pin::pin, 18 | task::{Context, Poll}, 19 | }; 20 | 21 | pub use event_listener as event; 22 | use futures_util::task::noop_waker; 23 | pub use ksync_core::*; 24 | 25 | pub use self::{atomic::AtomicArsc, mutex::*, rcu::*, rw_lock::*, semaphore::*}; 26 | 27 | pub fn poll_once(f: F) -> Option { 28 | let noop = noop_waker(); 29 | let mut cx = Context::from_waker(&noop); 30 | match pin!(f).poll(&mut cx) { 31 | Poll::Ready(output) => Some(output), 32 | Poll::Pending => None, 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /mizu/lib/devices/src/common.rs: -------------------------------------------------------------------------------- 1 | use core::{mem, ptr::NonNull}; 2 | 3 | use volatile::VolatilePtr; 4 | 5 | pub trait MmioReg { 6 | const OFFSET: usize; 7 | type Repr: Sized; 8 | type Access; 9 | 10 | /// # Safety 11 | /// 12 | /// The caller must ensure the exclusive access of the given `Self::Access` 13 | /// at the given base during the given `'a` lifetime. 14 | unsafe fn at<'a>(base: NonNull<()>) -> VolatilePtr<'a, Self::Repr, Self::Access> { 15 | mem::transmute(base.as_ptr().byte_add(Self::OFFSET).cast::()) 16 | } 17 | } 18 | 19 | pub fn bitmap_index_u32(index: usize) -> (usize, u32) { 20 | let bit = index; 21 | let byte = bit / u32::BITS as usize; 22 | let bit_in_byte_mask = 1 << (bit % u32::BITS as usize); 23 | (byte, bit_in_byte_mask) 24 | } 25 | 26 | #[derive(Debug, Clone, Copy)] 27 | pub struct Token(pub usize); 28 | -------------------------------------------------------------------------------- /mizu/lib/ksync-core/src/state.rs: -------------------------------------------------------------------------------- 1 | use core::{arch::asm, marker::PhantomData}; 2 | 3 | pub struct PreemptState { 4 | flags: usize, 5 | _non_send: PhantomData<*mut ()>, 6 | } 7 | 8 | const SIE: usize = 1 << 1; 9 | 10 | impl PreemptState { 11 | pub fn new() -> Self { 12 | let flags = unsafe { disable() }; 13 | PreemptState { 14 | flags, 15 | _non_send: PhantomData, 16 | } 17 | } 18 | } 19 | 20 | impl Drop for PreemptState { 21 | fn drop(&mut self) { 22 | unsafe { enable(self.flags) } 23 | } 24 | } 25 | 26 | impl Default for PreemptState { 27 | fn default() -> Self { 28 | Self::new() 29 | } 30 | } 31 | 32 | pub unsafe fn disable() -> usize { 33 | let sstatus: usize; 34 | asm!("csrrc {}, sstatus, {}", out(reg) sstatus, in(reg) SIE); 35 | sstatus 36 | } 37 | 38 | pub unsafe fn enable(flags: usize) { 39 | asm!("csrs sstatus, {}", in(reg) (flags & SIE)); 40 | } 41 | -------------------------------------------------------------------------------- /mizu/lib/ktime-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "test"), no_std)] 2 | #![feature(const_trait_impl)] 3 | 4 | #[cfg(not(feature = "test"))] 5 | mod instant; 6 | 7 | #[cfg(feature = "test")] 8 | pub use std::time::Instant; 9 | 10 | #[cfg(not(feature = "test"))] 11 | pub use self::instant::Instant; 12 | 13 | pub trait InstantExt { 14 | fn to_su(&self) -> (u64, u64); 15 | 16 | fn from_su(secs: u64, micros: u64) -> Self; 17 | 18 | /// # Safety 19 | /// 20 | /// The `raw` must be a valid value that can be transformed into an instant. 21 | unsafe fn from_raw(raw: u64) -> Self; 22 | } 23 | 24 | #[cfg(feature = "test")] 25 | impl InstantExt for Instant { 26 | fn to_su(&self) -> (u64, u64) { 27 | (0, 0) 28 | } 29 | 30 | fn from_su(secs: u64, micros: u64) -> Self { 31 | unimplemented!("{secs} * 1000000 + {micros}") 32 | } 33 | 34 | unsafe fn from_raw(raw: u64) -> Self { 35 | unimplemented!("Instant::from_raw({raw})") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /mizu/kernel/Makefile: -------------------------------------------------------------------------------- 1 | KERNEL := mizu 2 | 3 | KERNEL_RAW := $(TARGET_DIR)/$(KERNEL) 4 | KERNEL_SYM := $(DEBUG_DIR)/$(KERNEL).sym 5 | KERNEL_META := $(DEBUG_DIR)/$(KERNEL).txt 6 | KERNEL_ASM := $(DEBUG_DIR)/$(KERNEL).asm 7 | 8 | CARGO_ARGS := --target $(TARGET) \ 9 | --no-default-features \ 10 | --features $(BOARD) \ 11 | -Zbuild-std=core,compiler_builtins,alloc,panic_abort \ 12 | -Zbuild-std-features=compiler-builtins-mem 13 | 14 | ifeq ($(MODE),release) 15 | CARGO_ARGS += --release 16 | endif 17 | 18 | .PHONY: build 19 | 20 | build: 21 | mkdir -p .cargo 22 | cp -rf cargo-config/* .cargo 23 | cargo build $(CARGO_ARGS) 24 | cp $(KERNEL_RAW) $(KERNEL_SYM) 25 | rust-strip --strip-debug --strip-symbol="" \ 26 | $(KERNEL_RAW) -o $(ROOT)/kernel-qemu 27 | rust-objdump $(ROOT)/kernel-qemu -CxR > $(KERNEL_META) 28 | rust-objdump $(ROOT)/kernel-qemu -Cd > $(KERNEL_ASM) 29 | ifeq ($(BOARD),cv1811h) 30 | rust-objcopy --binary-architecture=riscv64 $(ROOT)/kernel-qemu \ 31 | --strip-all -O binary $(ROOT)/os.bin 32 | endif -------------------------------------------------------------------------------- /mizu/lib/kalloc/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "test"), no_std)] 2 | #![feature(once_cell)] 3 | #![feature(result_option_inspect)] 4 | 5 | mod imp; 6 | 7 | pub use imp::{Allocator, Stat}; 8 | 9 | #[cfg(not(feature = "test"))] 10 | #[global_allocator] 11 | static GLOBAL_ALLOC: imp::Allocator = imp::Allocator::new(); 12 | 13 | /// Initialize the kernel heap 14 | /// 15 | /// # Safety 16 | /// 17 | /// - `end` must be greater than `start` in addresses and must be properly 18 | /// aligned. 19 | /// - Must be called only once at initialization. 20 | #[cfg(not(feature = "test"))] 21 | pub unsafe fn init(start: &mut T, end: &mut T) { 22 | let start_ptr = (start as *mut T).cast(); 23 | let end_ptr = (end as *mut T).cast::(); 24 | let len = end_ptr.offset_from(start_ptr); 25 | GLOBAL_ALLOC.init(start_ptr as usize, len as usize) 26 | } 27 | 28 | pub fn stat() -> Stat { 29 | #[cfg(not(feature = "test"))] 30 | return GLOBAL_ALLOC.stat(); 31 | #[cfg(feature = "test")] 32 | Default::default() 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright © 2021 Js2xxx 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /mizu/lib/ksync/src/channel.rs: -------------------------------------------------------------------------------- 1 | use arsc_rs::Arsc; 2 | use crossbeam_queue::{ArrayQueue, SegQueue}; 3 | 4 | mod broadcast; 5 | pub mod mpmc; 6 | pub mod oneshot; 7 | 8 | pub use self::broadcast::Broadcast; 9 | 10 | pub fn oneshot() -> (oneshot::Sender, oneshot::Receiver) { 11 | let packet = Arsc::new(oneshot::Packet::new()); 12 | ( 13 | oneshot::Sender::new(packet.clone()), 14 | oneshot::Receiver::new(packet), 15 | ) 16 | } 17 | 18 | pub fn with_flavor(queue: F) -> (mpmc::Sender, mpmc::Receiver) { 19 | let channel = Arsc::new(mpmc::Channel::new(queue)); 20 | ( 21 | mpmc::Sender::new(channel.clone()), 22 | mpmc::Receiver::new(channel), 23 | ) 24 | } 25 | 26 | pub fn bounded(capacity: usize) -> (mpmc::Sender>, mpmc::Receiver>) { 27 | self::with_flavor(ArrayQueue::new(capacity)) 28 | } 29 | 30 | pub fn unbounded() -> (mpmc::Sender>, mpmc::Receiver>) { 31 | self::with_flavor(SegQueue::new()) 32 | } 33 | -------------------------------------------------------------------------------- /mizu/lib/ksync/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "ksync" 5 | version = "0.1.0" 6 | 7 | [features] 8 | test = ["ktime/test", "dep:spin_on"] 9 | 10 | [dependencies] 11 | # Local crates 12 | ksync-core = {path = "../ksync-core"} 13 | ktime = {path = "../ktime"} 14 | rand-riscv = {path = "../rand-riscv"} 15 | # External crates 16 | arsc-rs = {git = "https://github.com/js2xxx/arsc"} 17 | atomic_refcell = "0" 18 | crossbeam-epoch = {version = "0", default-features = false, features = ["alloc", "nightly"]} 19 | crossbeam-queue = {version = "0", default-features = false, features = ["alloc", "nightly"]} 20 | event-listener = {git = "https://github.com/js2xxx/event-listener", default-features = false} 21 | futures-lite = {version = "1", default-features = false, features = ["alloc"]} 22 | futures-util = {version = "0", default-features = false, features = ["alloc"]} 23 | hashbrown = {version = "0", default-features = false, features = ["inline-more"]} 24 | spin = "0" 25 | spin_on = {version = "0", optional = true} 26 | -------------------------------------------------------------------------------- /mizu/kernel/src/dev/intr.rs: -------------------------------------------------------------------------------- 1 | use fdt::{node::FdtNode, Fdt}; 2 | use plic::IntrManager; 3 | use rv39_paging::{PAddr, ID_OFFSET}; 4 | use spin::{Lazy, Once}; 5 | 6 | static PLIC: Once = Once::new(); 7 | 8 | pub fn init_plic(fdt: &FdtNode, _: &Fdt) -> bool { 9 | let res: Result<&IntrManager, &str> = PLIC.try_call_once(|| { 10 | let reg = fdt.reg().and_then(|mut reg| reg.next()); 11 | let reg = reg.ok_or("should have memory registers")?; 12 | 13 | let base = PAddr::new(reg.starting_address as usize); 14 | 15 | // SAFETY: The memory is statically mapped. 16 | Ok(unsafe { 17 | let base = base.to_laddr(ID_OFFSET).as_non_null_unchecked().cast(); 18 | IntrManager::from_raw(base) 19 | }) 20 | }); 21 | res.inspect_err(|err| log::warn!("Skip invalid PLIC: {err}")) 22 | .is_ok() 23 | } 24 | 25 | pub static INTR: Lazy<&IntrManager> = Lazy::new(|| intr_man().expect("PLIC uninitialized")); 26 | 27 | pub fn intr_man() -> Option<&'static IntrManager> { 28 | PLIC.get() 29 | } 30 | -------------------------------------------------------------------------------- /mizu/lib/ksync/src/atomic.rs: -------------------------------------------------------------------------------- 1 | use core::{hint, mem, sync::atomic::Ordering}; 2 | 3 | use arsc_rs::Arsc; 4 | use atomic_refcell::AtomicRefCell; 5 | 6 | #[derive(Debug)] 7 | pub struct AtomicArsc(AtomicRefCell>); 8 | 9 | impl AtomicArsc { 10 | pub fn new(ptr: Arsc) -> Self { 11 | AtomicArsc(AtomicRefCell::new(ptr)) 12 | } 13 | 14 | pub fn load(&self, _: Ordering) -> Arsc { 15 | ksync_core::critical(|| loop { 16 | if let Ok(b) = self.0.try_borrow() { 17 | break b.clone(); 18 | } 19 | hint::spin_loop() 20 | }) 21 | } 22 | 23 | pub fn swap(&self, ptr: Arsc, _: Ordering) -> Arsc { 24 | ksync_core::critical(|| loop { 25 | if let Ok(mut b) = self.0.try_borrow_mut() { 26 | break mem::replace(&mut *b, ptr); 27 | } 28 | hint::spin_loop() 29 | }) 30 | } 31 | } 32 | 33 | impl Default for AtomicArsc { 34 | fn default() -> Self { 35 | Self::new(Default::default()) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /mizu/lib/ksync/src/epoch.rs: -------------------------------------------------------------------------------- 1 | use core::cell::LazyCell; 2 | 3 | use crossbeam_epoch::{Collector, Guard, LocalHandle}; 4 | use spin::Lazy; 5 | 6 | fn collector() -> &'static Collector { 7 | /// The global data for the default garbage collector. 8 | static COLLECTOR: Lazy = Lazy::new(Collector::new); 9 | &COLLECTOR 10 | } 11 | 12 | #[thread_local] 13 | /// The per-thread participant for the default garbage collector. 14 | static HANDLE: LazyCell = LazyCell::new(|| collector().register()); 15 | 16 | /// Pins the current thread. 17 | #[inline] 18 | pub fn pin() -> Guard { 19 | with_handle(|handle| handle.pin()) 20 | } 21 | 22 | /// Returns `true` if the current thread is pinned. 23 | #[inline] 24 | pub fn is_pinned() -> bool { 25 | with_handle(|handle| handle.is_pinned()) 26 | } 27 | 28 | /// Returns the default global collector. 29 | pub fn default_collector() -> &'static Collector { 30 | collector() 31 | } 32 | 33 | #[inline] 34 | fn with_handle(mut f: F) -> R 35 | where 36 | F: FnMut(&LocalHandle) -> R, 37 | { 38 | f(&HANDLE) 39 | } 40 | -------------------------------------------------------------------------------- /mizu/kernel/build.rs: -------------------------------------------------------------------------------- 1 | use std::{env, fs, path::PathBuf}; 2 | 3 | fn main() { 4 | if env::var("TARGET").unwrap() == "riscv64gc-unknown-none-elf" { 5 | let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); 6 | let out_dir = env::var("OUT_DIR").unwrap(); 7 | let ldscript = fs::read_to_string(PathBuf::from(manifest_dir).join("link.ld")).unwrap(); 8 | 9 | // Reduce the prefix region sized `KERNEL_OFFSET` to avoid overlapping with the 10 | // firmware. 11 | 12 | let ram_size = config::RAM_SIZE - config::KERNEL_OFFSET; 13 | 14 | let new = ldscript.replace("%RAM_START%", &config::KERNEL_START_PHYS.to_string()); 15 | let new = new.replace("%VIRT_START%", &config::KERNEL_START.to_string()); 16 | let new = new.replace("%RAM_SIZE%", &ram_size.to_string()); 17 | let new = new.replace("%MAX_HARTS%", &config::MAX_HARTS.to_string()); 18 | 19 | let dest = PathBuf::from(out_dir).join("link.ld"); 20 | fs::write(&dest, new).unwrap(); 21 | println!("cargo:rustc-link-arg=-T{}", dest.display()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /mizu/lib/kmem/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "kmem" 5 | version = "0.1.0" 6 | 7 | [features] 8 | cv1811h = ["rv39-paging/cv1811h"] 9 | test = ["dep:scoped_threadpool"] 10 | 11 | [dependencies] 12 | # Local crates 13 | hart-id = {path = "../hart-id"} 14 | ksc-core = {path = "../ksc-core"} 15 | ksync = {path = "../ksync"} 16 | rand-riscv = {path = "../rand-riscv"} 17 | range-map = {path = "../range-map"} 18 | rv39-paging = {path = "../paging"} 19 | umifs = {path = "../umifs"} 20 | umio = {path = "../umio"} 21 | # External crates 22 | arsc-rs = {git = "https://github.com/js2xxx/arsc"} 23 | async-trait = "0" 24 | crossbeam-queue = {version = "0", default-features = false, features = ["alloc", "nightly"]} 25 | futures-util = {version = "0", default-features = false, features = ["alloc"]} 26 | hashbrown = {version = "0", default-features = false, features = ["inline-more"]} 27 | log = "0" 28 | riscv = "0" 29 | sbi-rt = {git = "https://github.com/js2xxx/sbi-rt", branch = "multitarget"} 30 | scoped_threadpool = {version = "0", optional = true} 31 | spin = "0" 32 | static_assertions = "1" 33 | -------------------------------------------------------------------------------- /mizu/lib/kmem/src/insn.rs: -------------------------------------------------------------------------------- 1 | use core::{arch::asm, ops::Range}; 2 | 3 | use rv39_paging::LAddr; 4 | 5 | const CACHE_SIZE: usize = 1 << 6; 6 | 7 | // pub fn thead_inval(base: LAddr) { 8 | // unsafe { asm!(".insn r 0b1011, 0, 1, zero, {}, x6", in(reg) base.val()) } 9 | // } 10 | 11 | pub fn thead_clean(base: LAddr) { 12 | unsafe { asm!(".insn r 0b1011, 0, 1, zero, {}, x4", in(reg) base.val()) } 13 | } 14 | 15 | pub fn thead_flush(base: LAddr) { 16 | unsafe { asm!(".insn r 0b1011, 0, 1, zero, {}, x7", in(reg) base.val()) } 17 | } 18 | 19 | pub fn thead_sync_s() { 20 | unsafe { asm!(".long 0x0190000b") } 21 | } 22 | 23 | fn cmo(range: Range, f: F) { 24 | let range = (range.start.val() & !(CACHE_SIZE - 1))..range.end.val(); 25 | range.step_by(CACHE_SIZE).for_each(|addr| f(addr.into())); 26 | thead_sync_s(); 27 | } 28 | 29 | // pub fn cmo_inval(range: Range) { 30 | // cmo(range, thead_inval) 31 | // } 32 | 33 | pub fn cmo_clean(range: Range) { 34 | cmo(range, thead_clean) 35 | } 36 | 37 | pub fn cmo_flush(range: Range) { 38 | cmo(range, thead_flush) 39 | } 40 | -------------------------------------------------------------------------------- /mizu/lib/ksc-macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod bound; 2 | mod expand; 3 | mod lifetime; 4 | mod parse; 5 | mod receiver; 6 | 7 | use proc_macro::TokenStream; 8 | use syn::parse_macro_input; 9 | 10 | use crate::parse::Item; 11 | 12 | /// Attribute to an async function to make an async handler. 13 | /// 14 | /// The real async handler looks like this: 15 | /// 16 | /// ```ignore 17 | /// fn real_handler<'a>(_: &'a u32, _: Lifetimed<'a, 'a, u32>, ..) 18 | /// -> Pin + Send + 'a>> { 19 | /// Box::pin(async move { 20 | /// // Real content here... 21 | /// }) 22 | /// } 23 | /// ``` 24 | /// 25 | /// All the possible lifetimes must be equivalent the same lifetime as the 26 | /// output future, if a custom implementation is preferred. 27 | /// 28 | /// The implementation is based on [`async-trait`](https://github.com/dtolnay/async-trait). 29 | #[proc_macro_attribute] 30 | pub fn async_handler(args: TokenStream, input: TokenStream) -> TokenStream { 31 | assert!(args.is_empty(), "expect no arguments"); 32 | let mut item = parse_macro_input!(input as Item); 33 | expand::expand(&mut item); 34 | let func = item.0; 35 | quote::quote!(#func).into() 36 | } 37 | -------------------------------------------------------------------------------- /mizu/kernel/src/main.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "test"), no_std)] 2 | #![cfg_attr(not(feature = "test"), no_main)] 3 | #![feature(alloc_layout_extra)] 4 | #![feature(array_methods)] 5 | #![feature(asm_const)] 6 | #![feature(const_mut_refs)] 7 | #![feature(const_trait_impl)] 8 | #![feature(inline_const)] 9 | #![feature(link_llvm_intrinsics)] 10 | #![feature(maybe_uninit_as_bytes)] 11 | #![feature(naked_functions)] 12 | #![feature(pointer_byte_offsets)] 13 | #![feature(pointer_is_aligned)] 14 | #![feature(result_option_inspect)] 15 | #![feature(thread_local)] 16 | 17 | mod cpu; 18 | mod dev; 19 | pub mod fs; 20 | mod mem; 21 | mod rxx; 22 | mod syscall; 23 | pub mod task; 24 | mod trap; 25 | 26 | mod test; 27 | 28 | extern crate alloc; 29 | 30 | pub use self::rxx::executor; 31 | 32 | async fn main(payload: usize) { 33 | println!("Hello from UMI ^_^"); 34 | 35 | // Init devices. 36 | unsafe { 37 | let device_tree = config::device_tree(payload); 38 | crate::dev::init(device_tree).expect("failed to initialize devices") 39 | } 40 | // Init FS. 41 | fs::fs_init().await; 42 | 43 | mem::test_phys().await; 44 | fs::test_file().await; 45 | 46 | self::test::test_all().await; 47 | } 48 | -------------------------------------------------------------------------------- /mizu/lib/devices/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "devices" 5 | version = "0.1.0" 6 | 7 | [features] 8 | test = [] 9 | 10 | [dependencies] 11 | # Local crates 12 | ksc = {path = "../ksc"} 13 | ksync = {path = "../ksync"} 14 | ktime = {path = "../ktime"} 15 | rand-riscv = {path = "../rand-riscv"} 16 | umio = {path = "../umio"} 17 | # External crates 18 | arsc-rs = {git = "https://github.com/js2xxx/arsc"} 19 | async-trait = "0" 20 | crossbeam-queue = {version = "0", default-features = false, features = ["alloc", "nightly"]} 21 | futures-util = {version = "0", default-features = false, features = ["alloc"]} 22 | heapless = "0" 23 | log = "0" 24 | managed = {version = "0", default-features = false, features = ["alloc"]} 25 | spin = "0" 26 | volatile = "0" 27 | 28 | [dependencies.smoltcp] 29 | branch = "reuse_endpoints" 30 | default-features = false 31 | features = [ 32 | "log", 33 | "medium-ethernet", 34 | "medium-ip", 35 | "proto-ipv4", 36 | "proto-igmp", 37 | "proto-dhcpv4", 38 | "proto-ipv6", 39 | "proto-dns", 40 | "socket-raw", 41 | "socket-udp", 42 | "socket-tcp", 43 | "socket-dhcpv4", 44 | "socket-dns", 45 | "async", 46 | "alloc", 47 | ] 48 | git = "https://github.com/js2xxx/smoltcp" 49 | -------------------------------------------------------------------------------- /mizu/lib/hart-id/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(test), no_std)] 2 | #![feature(thread_local)] 3 | 4 | use core::sync::atomic::{ 5 | AtomicUsize, 6 | Ordering::{Relaxed, Release}, 7 | }; 8 | 9 | static BSP_ID: AtomicUsize = AtomicUsize::new(0); 10 | static COUNT: AtomicUsize = AtomicUsize::new(1); 11 | static HIDS: AtomicUsize = AtomicUsize::new(0); 12 | 13 | #[thread_local] 14 | static mut HART_ID: usize = 0; 15 | 16 | pub fn bsp_id() -> usize { 17 | BSP_ID.load(Relaxed) 18 | } 19 | 20 | pub fn hart_id() -> usize { 21 | unsafe { HART_ID } 22 | } 23 | 24 | pub fn is_bsp() -> bool { 25 | hart_id() == bsp_id() 26 | } 27 | 28 | /// # Safety 29 | /// 30 | /// This function must be called only once during initialization ofr each CPU 31 | /// core. 32 | pub unsafe fn init_hart_id(id: usize) { 33 | HART_ID = id; 34 | COUNT.fetch_add(1, Release); 35 | HIDS.fetch_or(1 << id, Release); 36 | } 37 | 38 | pub fn init_bsp_id(id: usize) { 39 | BSP_ID.store(id, Release); 40 | } 41 | 42 | pub fn count() -> usize { 43 | COUNT.load(Relaxed) 44 | } 45 | 46 | pub fn hart_ids() -> usize { 47 | HIDS.load(Relaxed) 48 | } 49 | 50 | pub fn for_each_hart(mut f: F) { 51 | let mut mask = hart_ids(); 52 | while mask > 0 { 53 | let bit = mask & (!mask + 1); 54 | f(bit.ilog2() as usize); 55 | mask -= bit; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /mizu/lib/ksc-core/src/raw_reg.rs: -------------------------------------------------------------------------------- 1 | pub trait RawReg { 2 | fn from_raw(raw: usize) -> Self; 3 | 4 | fn into_raw(self) -> usize; 5 | } 6 | 7 | impl RawReg for () { 8 | fn from_raw(_: usize) {} 9 | 10 | fn into_raw(self) -> usize { 11 | 0 12 | } 13 | } 14 | 15 | impl RawReg for bool { 16 | fn from_raw(raw: usize) -> Self { 17 | raw != 0 18 | } 19 | 20 | fn into_raw(self) -> usize { 21 | self as _ 22 | } 23 | } 24 | 25 | macro_rules! impl_int { 26 | ($type:ident) => { 27 | impl RawReg for $type { 28 | fn from_raw(raw: usize) -> Self { 29 | raw as _ 30 | } 31 | 32 | fn into_raw(self) -> usize { 33 | self as _ 34 | } 35 | } 36 | }; 37 | } 38 | 39 | impl_int!(i8); 40 | impl_int!(u8); 41 | impl_int!(i16); 42 | impl_int!(u16); 43 | impl_int!(i32); 44 | impl_int!(u32); 45 | impl_int!(i64); 46 | impl_int!(u64); 47 | impl_int!(isize); 48 | impl_int!(usize); 49 | 50 | impl RawReg for *const T { 51 | fn from_raw(raw: usize) -> Self { 52 | raw as _ 53 | } 54 | 55 | fn into_raw(self) -> usize { 56 | self as _ 57 | } 58 | } 59 | 60 | impl RawReg for *mut T { 61 | fn from_raw(raw: usize) -> Self { 62 | raw as _ 63 | } 64 | 65 | fn into_raw(self) -> usize { 66 | self as _ 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /mizu/lib/kmem/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(test), no_std)] 2 | #![feature(alloc_layout_extra)] 3 | #![feature(result_option_inspect)] 4 | #![feature(thread_local)] 5 | 6 | extern crate alloc; 7 | 8 | mod frame; 9 | #[cfg(feature = "cv1811h")] 10 | mod insn; 11 | mod lru; 12 | mod phys; 13 | mod virt; 14 | 15 | pub use rv39_paging::{ 16 | Attr, AttrBuilder, LAddr, PAddr, CANONICAL_PREFIX, ID_OFFSET, PAGE_LAYOUT, PAGE_MASK, 17 | PAGE_SHIFT, PAGE_SIZE, 18 | }; 19 | 20 | pub use self::{ 21 | frame::{frames, init_frames, Arena}, 22 | lru::LruCache, 23 | phys::{Frame, Phys, ZERO}, 24 | virt::{unset_virt, Virt, VirtCommitGuard}, 25 | }; 26 | 27 | pub fn sync_dma_for_cpu(from_device: bool, to_device: bool, range: core::ops::Range) { 28 | #[cfg(not(feature = "cv1811h"))] 29 | let _ = (from_device, to_device, range); 30 | #[cfg(feature = "cv1811h")] 31 | if from_device { 32 | let _ = to_device; 33 | insn::cmo_flush(range) 34 | } 35 | } 36 | 37 | pub fn sync_dma_for_device(from_device: bool, to_device: bool, range: core::ops::Range) { 38 | #[cfg(not(feature = "cv1811h"))] 39 | let _ = (from_device, to_device, range); 40 | #[cfg(feature = "cv1811h")] 41 | match (from_device, to_device) { 42 | (true, false) | (false, true) => insn::cmo_clean(range), 43 | (true, true) => insn::cmo_flush(range), 44 | _ => {} 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /mizu/lib/paging/src/consts.rs: -------------------------------------------------------------------------------- 1 | use core::ptr::NonNull; 2 | 3 | use crate::{LAddr, PAddr, Table}; 4 | 5 | /// also PA_OFFSET, only fixed when fixed pgsize 6 | pub const PAGE_SHIFT: u32 = 12; 7 | pub const PAGE_SIZE: usize = 1 << PAGE_SHIFT; 8 | pub const PAGE_MASK: usize = PAGE_SIZE - 1; 9 | 10 | pub const ENTRY_SIZE_SHIFT: u32 = 3; 11 | pub const NR_ENTRIES_SHIFT: u32 = PAGE_SHIFT - ENTRY_SIZE_SHIFT; 12 | pub const NR_ENTRIES: usize = 1 << NR_ENTRIES_SHIFT; 13 | 14 | pub const CANONICAL_PREFIX: usize = 0xffff_ffc0_0000_0000; 15 | pub const ID_OFFSET: usize = CANONICAL_PREFIX; 16 | 17 | pub const BLANK_BEGIN: usize = (1 << 38) - 1; 18 | pub const BLANK_END: usize = CANONICAL_PREFIX - 1; 19 | 20 | #[derive(Copy, Clone, Debug, PartialEq)] 21 | pub enum Error { 22 | OutOfMemory, 23 | AddrMisaligned { 24 | vstart: Option, 25 | vend: Option, 26 | phys: Option, 27 | }, 28 | RangeEmpty, 29 | EntryExistent(bool), 30 | PermissionDenied, 31 | } 32 | 33 | /// # Safety 34 | /// 35 | /// The `alloc` function must return a pointer that has its ownership, 36 | /// representing a zeroed `Table`. 37 | pub unsafe trait PageAlloc { 38 | fn alloc(&self) -> Option>; 39 | 40 | /// # Safety 41 | /// 42 | /// `ptr` must has its ownership and must be previously returned by the 43 | /// `alloc` function. 44 | unsafe fn dealloc(&self, ptr: NonNull); 45 | } 46 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export TARGET := riscv64gc-unknown-none-elf 2 | export MODE ?= release 3 | export BOARD ?= qemu-virt 4 | 5 | export ROOT := $(shell pwd) 6 | export TARGET_DIR := $(ROOT)/target/$(TARGET)/$(MODE) 7 | export DEBUG_DIR := $(ROOT)/debug 8 | 9 | export ROOTFS ?= $(ROOT)/third-party/img/sdcard-comp2.img 10 | export SBI ?= $(ROOT)/third-party/bin/opensbi-$(BOARD) 11 | 12 | .PHONY: all build run debug test clean 13 | 14 | all: build 15 | 16 | build: 17 | mkdir -p .cargo 18 | cp -rf cargo-config/* .cargo 19 | mkdir -p debug 20 | cd mizu/kernel && make build 21 | ifeq ($(BOARD), qemu-virt) 22 | cp $(SBI) $(ROOT)/sbi-qemu 23 | 24 | QEMU_ARGS := -monitor stdio \ 25 | -kernel kernel-qemu \ 26 | -nographic \ 27 | -serial file:debug/qemu.log \ 28 | -smp 2 -m 128M \ 29 | -drive file=$(ROOTFS),if=none,format=raw,id=x0 \ 30 | -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 \ 31 | -device virtio-net-device,netdev=net \ 32 | -netdev user,id=net \ 33 | -machine virt \ 34 | -bios $(SBI) 35 | endif 36 | 37 | run: build 38 | ifeq ($(BOARD), qemu-virt) 39 | qemu-system-riscv64 $(QEMU_ARGS) 40 | endif 41 | ifeq ($(BOARD), cv1811h) 42 | cp -f os.bin /srv/tftp 43 | endif 44 | 45 | debug: build 46 | ifeq ($(BOARD), qemu-virt) 47 | qemu-system-riscv64 $(QEMU_ARGS) -s -S 48 | endif 49 | 50 | test: 51 | ifeq ($(MODE),debug) 52 | cargo test --all-targets --features test 53 | else 54 | cargo bench --all-targets --features test 55 | endif 56 | 57 | clean: 58 | cargo clean -------------------------------------------------------------------------------- /mizu/kernel/src/dev/sdmmc.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::Arc; 2 | use core::num::NonZeroU32; 3 | 4 | use fdt::{node::FdtNode, Fdt}; 5 | use rv39_paging::{PAddr, ID_OFFSET}; 6 | use sdmmc::Sdmmc; 7 | 8 | use super::intr::intr_man; 9 | use crate::{ 10 | dev::{block::BLOCKS, interrupts}, 11 | someb, tryb, 12 | }; 13 | 14 | pub fn init(node: &FdtNode, _: &Fdt) -> bool { 15 | let intr_pin = someb!(interrupts(node).next().and_then(NonZeroU32::new)); 16 | let intr_manager = someb!(intr_man()); 17 | 18 | let reg = someb!(node.reg().and_then(|mut reg| reg.next())); 19 | let addr = PAddr::new(reg.starting_address as _).to_laddr(ID_OFFSET); 20 | 21 | let bus_width = someb!(node.property("bus-width").and_then(|p| p.as_usize())); 22 | let min_freq = someb!(node.property("min-frequency").and_then(|p| p.as_usize())) as u64; 23 | let max_freq = someb!(node.property("max-frequency").and_then(|p| p.as_usize())) as u64; 24 | 25 | let sdmmc = tryb!(unsafe { Sdmmc::new(addr.as_non_null_unchecked().cast()) } 26 | .inspect_err(|err| log::debug!("failed to initialize SDMMC structure: {err:?}"))); 27 | tryb!(sdmmc 28 | .init_bus(bus_width, min_freq..max_freq) 29 | .inspect_err(|err| log::debug!("failed to initialize SDMMC bus: {err:?}"))); 30 | 31 | let device = Arc::new(sdmmc); 32 | let d2 = device.clone(); 33 | assert!(intr_manager.insert(intr_pin, move |c| d2.ack_interrupt(c))); 34 | 35 | ksync::critical(|| BLOCKS.lock().push(device)); 36 | 37 | true 38 | } 39 | -------------------------------------------------------------------------------- /mizu/kernel/src/cpu.rs: -------------------------------------------------------------------------------- 1 | use core::sync::atomic::{self, AtomicUsize, Ordering::*}; 2 | 3 | pub struct IpiComm { 4 | cmd: AtomicUsize, 5 | result: AtomicUsize, 6 | } 7 | 8 | const IPI_CMD_FENCE: usize = 1; 9 | 10 | impl IpiComm { 11 | pub fn receive(&self) { 12 | #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] 13 | unsafe { 14 | const SIE: usize = 1 << 1; 15 | core::arch::asm!("csrc sip, {}", in(reg) SIE); 16 | } 17 | 18 | let cmd = self.cmd.load(Acquire); 19 | if let IPI_CMD_FENCE = cmd { 20 | atomic::fence(SeqCst) 21 | } 22 | self.result.fetch_add(1, SeqCst); 23 | } 24 | 25 | fn send(&self, mask: usize, cmd: usize) { 26 | let count = mask.count_ones() as usize; 27 | self.cmd.store(cmd, Release); 28 | 29 | let ret = sbi_rt::send_ipi(mask, 0).into_result(); 30 | if ret.is_ok() { 31 | loop { 32 | let cmpxchg = self.result.compare_exchange_weak(count, 0, AcqRel, Acquire); 33 | if cmpxchg.is_ok() { 34 | break; 35 | } 36 | } 37 | } 38 | } 39 | 40 | pub fn remote_fence(&self, mask: usize) { 41 | let me = hart_id::hart_id(); 42 | self.send(mask & !(1 << me), IPI_CMD_FENCE); 43 | if mask & (1 << me) != 0 { 44 | atomic::fence(SeqCst); 45 | } 46 | } 47 | } 48 | 49 | pub static IPI: IpiComm = IpiComm { 50 | cmd: AtomicUsize::new(0), 51 | result: AtomicUsize::new(0), 52 | }; 53 | -------------------------------------------------------------------------------- /mizu/dev/virtio/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(split_array)] 3 | 4 | pub mod block; 5 | pub mod net; 6 | 7 | extern crate alloc; 8 | 9 | use core::{num::NonZeroUsize, ptr::NonNull}; 10 | 11 | use kmem::{LAddr, PAddr, ID_OFFSET}; 12 | use virtio_drivers::{BufferDirection, Hal as VirtioHal}; 13 | 14 | pub struct HalImpl; 15 | 16 | unsafe impl VirtioHal for HalImpl { 17 | fn dma_alloc(pages: usize, _: BufferDirection) -> (virtio_drivers::PhysAddr, NonNull) { 18 | match NonZeroUsize::new(pages).and_then(|count| kmem::frames().allocate(count)) { 19 | Some(addr) => (*addr.to_paddr(ID_OFFSET), unsafe { 20 | addr.as_non_null_unchecked() 21 | }), 22 | None => (0, NonNull::dangling()), 23 | } 24 | } 25 | 26 | unsafe fn dma_dealloc(_: virtio_drivers::PhysAddr, ptr: NonNull, pages: usize) -> i32 { 27 | if let Some(count) = NonZeroUsize::new(pages) { 28 | let addr = LAddr::from(ptr); 29 | unsafe { kmem::frames().deallocate(addr, count) } 30 | } 31 | 0 32 | } 33 | 34 | unsafe fn mmio_phys_to_virt(paddr: virtio_drivers::PhysAddr, _: usize) -> NonNull { 35 | let laddr = PAddr::new(paddr).to_laddr(ID_OFFSET); 36 | laddr.as_non_null().expect("invalid address") 37 | } 38 | 39 | unsafe fn share(buffer: NonNull<[u8]>, _: BufferDirection) -> virtio_drivers::PhysAddr { 40 | *LAddr::from(buffer).to_paddr(ID_OFFSET) 41 | } 42 | 43 | unsafe fn unshare(_: virtio_drivers::PhysAddr, _: NonNull<[u8]>, _: BufferDirection) {} 44 | } 45 | -------------------------------------------------------------------------------- /mizu/lib/ksync-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] 4 | mod state; 5 | 6 | /// Enter a critical section in a single core. Do not use it with multi-core 7 | /// synchoronization. Intended to be used with mutexes. 8 | /// 9 | /// Doesn't have any effect on test cases. 10 | /// 11 | /// # Examples 12 | /// 13 | /// ```rust,ignore 14 | /// use spin::Mutex; 15 | /// 16 | /// let mutex = Mutex::new(0); 17 | /// ksync::critical(|| { 18 | /// let value = mutex.lock(); 19 | /// assert_eq!(*value, 0); 20 | /// }) 21 | /// ``` 22 | #[inline] 23 | pub fn critical(f: impl FnOnce() -> R) -> R { 24 | #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] 25 | let _preempt = state::PreemptState::new(); 26 | f() 27 | } 28 | 29 | /// Disable interrupts manually. 30 | /// 31 | /// # Safety 32 | /// 33 | /// The caller must care about the potential risks of functions that have sth to 34 | /// do with interrupts. 35 | pub unsafe fn disable() -> usize { 36 | #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] 37 | return 0; 38 | #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] 39 | state::disable() 40 | } 41 | 42 | /// Enable interrupts manually. 43 | /// 44 | /// # Safety 45 | /// 46 | /// The caller must care about the potential risks of functions that have sth to 47 | /// do with interrupts. 48 | pub unsafe fn enable(_flags: usize) { 49 | #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] 50 | state::enable(_flags) 51 | } 52 | 53 | pub fn is_enabled() -> bool { 54 | riscv::register::sstatus::read().sie() 55 | } 56 | -------------------------------------------------------------------------------- /mizu/lib/ksync/src/channel/broadcast.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | 3 | use arsc_rs::Arsc; 4 | use spin::{Mutex, Once}; 5 | 6 | use super::mpmc::{Flavor, Sender}; 7 | 8 | #[derive(Debug)] 9 | pub struct Broadcast { 10 | senders: Arsc>>>, 11 | closed: Once, 12 | } 13 | 14 | impl Broadcast { 15 | pub fn new() -> Self { 16 | Broadcast { 17 | senders: Arsc::new(Mutex::new(Vec::new())), 18 | closed: Once::new(), 19 | } 20 | } 21 | 22 | pub fn subscribe(&self, sender: Sender) { 23 | ksync_core::critical(|| self.senders.lock().push(sender)) 24 | } 25 | 26 | pub fn close(&self) { 27 | self.closed.call_once(|| ()); 28 | } 29 | 30 | pub fn is_closed(&self) -> bool { 31 | self.closed.is_completed() 32 | } 33 | 34 | pub async fn send(&self, data: &F::Item) 35 | where 36 | F::Item: Clone, 37 | { 38 | if self.closed.is_completed() { 39 | return; 40 | } 41 | let mut senders = ksync_core::critical(|| self.senders.lock().clone()); 42 | if senders.is_empty() { 43 | return; 44 | } 45 | let mut pos = senders.len() - 1; 46 | loop { 47 | let sender = &senders[pos]; 48 | if sender.send(data.clone()).await.is_err() { 49 | senders.swap_remove(pos); 50 | } 51 | if pos == 0 { 52 | ksync_core::critical(|| *self.senders.lock() = senders); 53 | break; 54 | } 55 | pos -= 1; 56 | } 57 | } 58 | } 59 | 60 | impl Default for Broadcast { 61 | fn default() -> Self { 62 | Self::new() 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /mizu/kernel/src/mem.rs: -------------------------------------------------------------------------------- 1 | mod futex; 2 | mod shm; 3 | mod syscall; 4 | mod user; 5 | 6 | use alloc::sync::Arc; 7 | use core::ops::Range; 8 | 9 | use arsc_rs::Arsc; 10 | use kmem::{Phys, Virt}; 11 | use ksc::Error; 12 | use rv39_paging::{CANONICAL_PREFIX, PAGE_SIZE}; 13 | use umifs::traits::{IntoAnyExt, Io, IoExt}; 14 | 15 | pub use self::{ 16 | futex::{FutexWait, Futexes}, 17 | shm::Shm, 18 | syscall::*, 19 | user::{In, InOut, Out, UserBuffer, UserPtr, UA_FAULT}, 20 | }; 21 | use crate::rxx::KERNEL_PAGES; 22 | 23 | pub const USER_RANGE: Range = 0x1000..((!CANONICAL_PREFIX) + 1); 24 | 25 | pub fn new_virt() -> Arsc { 26 | Virt::new(USER_RANGE.start.into()..USER_RANGE.end.into(), KERNEL_PAGES) 27 | } 28 | 29 | pub fn new_phys(from: Arc, cow: bool) -> Phys { 30 | if let Some(phys) = from.clone().downcast::() { 31 | return phys.clone_as(cow, 0, None); 32 | } 33 | let (phys, flusher) = Phys::with_backend(from, 0, cow); 34 | crate::executor().spawn(flusher).detach(); 35 | phys 36 | } 37 | 38 | pub async fn deep_fork(virt: &Arsc) -> Result, Error> { 39 | virt.deep_fork(KERNEL_PAGES).await 40 | } 41 | 42 | #[allow(dead_code)] 43 | pub async fn test_phys() { 44 | let p = Phys::new(false); 45 | 46 | p.write_all_at(0, &[1, 2, 3, 4, 5]).await.unwrap(); 47 | 48 | let p1 = p.clone_as(true, 0, None); 49 | 50 | let mut buf = [0; 5]; 51 | p1.read_exact_at(0, &mut buf).await.unwrap(); 52 | assert_eq!(buf, [1, 2, 3, 4, 5]); 53 | 54 | let p2 = p.clone_as(false, 0, None); 55 | 56 | p.write_all_at(PAGE_SIZE, &[6, 7, 8, 9, 10]).await.unwrap(); 57 | 58 | p2.read_exact_at(0, &mut buf).await.unwrap(); 59 | assert_eq!(buf, [1, 2, 3, 4, 5]); 60 | p2.read_exact_at(PAGE_SIZE, &mut buf).await.unwrap(); 61 | assert_eq!(buf, [6, 7, 8, 9, 10]); 62 | p1.read_exact_at(PAGE_SIZE, &mut buf).await.unwrap(); 63 | assert_eq!(buf, [0; 5]); 64 | } 65 | -------------------------------------------------------------------------------- /mizu/kernel/src/syscall/ffi.rs: -------------------------------------------------------------------------------- 1 | use core::time::Duration; 2 | 3 | use ktime::{Instant, InstantExt}; 4 | 5 | #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] 6 | #[repr(C, packed)] 7 | pub struct Tv { 8 | pub sec: u64, 9 | pub usec: u64, 10 | } 11 | 12 | impl From for Tv { 13 | fn from(value: Instant) -> Self { 14 | let (secs, usecs) = value.to_su(); 15 | Tv { 16 | sec: secs, 17 | usec: usecs, 18 | } 19 | } 20 | } 21 | 22 | impl From for Tv { 23 | fn from(value: Duration) -> Self { 24 | Tv { 25 | sec: value.as_secs(), 26 | usec: value.subsec_micros().into(), 27 | } 28 | } 29 | } 30 | 31 | impl From for Instant { 32 | fn from(value: Tv) -> Self { 33 | Instant::from_su(value.sec, value.usec) 34 | } 35 | } 36 | 37 | impl From for Duration { 38 | fn from(value: Tv) -> Self { 39 | Duration::from_secs(value.sec) + Duration::from_micros(value.usec) 40 | } 41 | } 42 | 43 | #[derive(Debug, Clone, Copy, Default)] 44 | #[repr(C, packed)] 45 | pub struct Ts { 46 | pub sec: u64, 47 | pub nsec: u64, 48 | } 49 | 50 | impl From for Ts { 51 | fn from(value: Instant) -> Self { 52 | let (secs, usecs) = value.to_su(); 53 | Ts { 54 | sec: secs, 55 | nsec: usecs * 1000, 56 | } 57 | } 58 | } 59 | 60 | impl From for Ts { 61 | fn from(value: Duration) -> Self { 62 | Ts { 63 | sec: value.as_secs(), 64 | nsec: value.subsec_nanos().into(), 65 | } 66 | } 67 | } 68 | 69 | impl From for Instant { 70 | fn from(value: Ts) -> Self { 71 | Instant::from_su(value.sec, value.nsec / 1000) 72 | } 73 | } 74 | 75 | impl From for Duration { 76 | fn from(value: Ts) -> Self { 77 | Duration::from_secs(value.sec) + Duration::from_nanos(value.nsec) 78 | } 79 | } 80 | 81 | #[derive(Debug, Clone, Copy, Default)] 82 | #[repr(C, packed)] 83 | pub struct Itv { 84 | pub interval: Tv, 85 | pub next_diff: Tv, 86 | } 87 | -------------------------------------------------------------------------------- /.github/workflows/code.yml: -------------------------------------------------------------------------------- 1 | name: "Code check" 2 | 3 | on: 4 | push: 5 | branches: ["master"] 6 | pull_request: 7 | branches: ["master"] 8 | 9 | jobs: 10 | clippy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | 15 | - name: Install Rust toolchains 16 | uses: dtolnay/rust-toolchain@master 17 | with: 18 | toolchain: nightly-2023-03-29-x86_64-unknown-linux-gnu 19 | components: clippy 20 | 21 | - name: Check clippy 22 | run: cargo clippy --target riscv64gc-unknown-none-elf 23 | 24 | format: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@v3 28 | 29 | - name: Install Rust toolchains 30 | uses: dtolnay/rust-toolchain@master 31 | with: 32 | toolchain: nightly-2023-03-29-x86_64-unknown-linux-gnu 33 | components: rustfmt 34 | 35 | - name: Check format 36 | run: cargo fmt --all --check 37 | 38 | build: 39 | runs-on: ubuntu-latest 40 | steps: 41 | - uses: actions/checkout@v3 42 | 43 | - name: Install Rust toolchains 44 | uses: dtolnay/rust-toolchain@master 45 | with: 46 | toolchain: nightly-2023-03-29-x86_64-unknown-linux-gnu 47 | components: llvm-tools 48 | 49 | - name: Install other dependencies 50 | run: | 51 | sudo apt install make 52 | cargo install cargo-binutils 53 | 54 | - name: Build 55 | run: make build 56 | 57 | - name: Build release 58 | run: make build MODE=release 59 | 60 | test: 61 | runs-on: ubuntu-latest 62 | steps: 63 | - uses: actions/checkout@v3 64 | 65 | - name: Install Rust toolchains 66 | uses: dtolnay/rust-toolchain@master 67 | with: 68 | toolchain: nightly-2023-03-29-x86_64-unknown-linux-gnu 69 | components: llvm-tools 70 | 71 | - name: Install other dependencies 72 | run: | 73 | sudo apt install make 74 | cargo install cargo-binutils 75 | 76 | - name: Test 77 | run: make test 78 | -------------------------------------------------------------------------------- /mizu/lib/umifs/src/traits.rs: -------------------------------------------------------------------------------- 1 | use alloc::{boxed::Box, sync::Arc}; 2 | 3 | use arsc_rs::Arsc; 4 | use async_trait::async_trait; 5 | use ksc_core::Error; 6 | use umio::IoPoll; 7 | pub use umio::{IntoAny, IntoAnyExt, Io, IoExt, ToIo}; 8 | 9 | use crate::{ 10 | path::Path, 11 | types::{DirEntry, FsStat, Metadata, OpenOptions, Permissions, SetMetadata}, 12 | }; 13 | 14 | #[async_trait] 15 | pub trait FileSystem: IntoAny + Send + Sync + 'static { 16 | async fn root_dir(self: Arsc) -> Result, Error>; 17 | 18 | async fn flush(&self) -> Result<(), Error>; 19 | 20 | async fn stat(&self) -> FsStat; 21 | } 22 | 23 | #[async_trait] 24 | pub trait Entry: IntoAny + Send + ToIo + IoPoll + Sync + 'static { 25 | async fn open( 26 | self: Arc, 27 | path: &Path, 28 | options: OpenOptions, 29 | perm: Permissions, 30 | ) -> Result<(Arc, bool), Error>; 31 | 32 | async fn metadata(&self) -> Metadata; 33 | 34 | async fn set_metadata(&self, metadata: SetMetadata) -> Result<(), Error> { 35 | let _ = metadata; 36 | Ok(()) 37 | } 38 | 39 | fn to_dir(self: Arc) -> Option> { 40 | None 41 | } 42 | 43 | fn to_dir_mut(self: Arc) -> Option> { 44 | None 45 | } 46 | } 47 | 48 | pub trait File: Entry + Io {} 49 | impl File for T {} 50 | 51 | #[async_trait] 52 | pub trait Directory: Entry { 53 | async fn next_dirent(&self, last: Option<&DirEntry>) -> Result, Error>; 54 | } 55 | 56 | #[async_trait] 57 | pub trait DirectoryMut: Directory { 58 | async fn rename( 59 | self: Arc, 60 | src_path: &Path, 61 | dst_parent: Arc, 62 | dst_path: &Path, 63 | ) -> Result<(), Error>; 64 | 65 | async fn link( 66 | self: Arc, 67 | src_path: &Path, 68 | dst_parent: Arc, 69 | dst_path: &Path, 70 | ) -> Result<(), Error>; 71 | 72 | async fn unlink(&self, path: &Path, expect_dir: Option) -> Result<(), Error>; 73 | } 74 | -------------------------------------------------------------------------------- /mizu/kernel/src/mem/shm.rs: -------------------------------------------------------------------------------- 1 | use core::sync::atomic::{AtomicI32, Ordering::SeqCst}; 2 | 3 | use hashbrown::HashMap; 4 | use kmem::Phys; 5 | use ksc::Error::{self, EEXIST, ENOENT}; 6 | use rand_riscv::RandomState; 7 | use rv39_paging::LAddr; 8 | use spin::{mutex::Mutex, MutexGuard}; 9 | 10 | pub struct Shm { 11 | id_alloc: AtomicI32, 12 | map: Mutex>, 13 | mapping: Mutex>, 14 | } 15 | 16 | impl Default for Shm { 17 | fn default() -> Self { 18 | Shm { 19 | id_alloc: AtomicI32::new(2), 20 | map: Default::default(), 21 | mapping: Default::default(), 22 | } 23 | } 24 | } 25 | 26 | impl Shm { 27 | pub fn get(&self, key: i32) -> Option<(Phys, usize)> { 28 | ksync::critical(|| self.map.lock().get(&key).cloned()) 29 | } 30 | 31 | pub fn insert(&self, key: i32, len: usize, flags: i32) -> Result { 32 | const IPC_CREAT: i32 = 0o1000; 33 | const IPC_EXCL: i32 = 0o2000; 34 | 35 | if key == 0 { 36 | let key = self.id_alloc.fetch_add(1, SeqCst); 37 | ksync::critical(|| self.map.lock().insert(key, (Phys::new(false), len))); 38 | Ok(key) 39 | } else if flags & IPC_CREAT == 0 { 40 | ksync::critical(|| self.map.lock().contains_key(&key)) 41 | .then_some(key) 42 | .ok_or(ENOENT) 43 | } else if flags & IPC_EXCL == 0 { 44 | ksync::critical(|| { 45 | self.map 46 | .lock() 47 | .entry(key) 48 | .or_insert_with(|| (Phys::new(false), len)); 49 | Ok(key) 50 | }) 51 | } else { 52 | ksync::critical(|| { 53 | self.map 54 | .lock() 55 | .try_insert(key, (Phys::new(false), len)) 56 | .is_ok() 57 | }) 58 | .then_some(key) 59 | .ok_or(EEXIST) 60 | } 61 | } 62 | 63 | pub fn mapping(&self) -> MutexGuard> { 64 | self.mapping.lock() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /mizu/lib/kalloc/src/imp.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | alloc::{GlobalAlloc, Layout}, 3 | ptr::{self, NonNull}, 4 | }; 5 | 6 | use buddy_system_allocator::Heap; 7 | use spin::Mutex; 8 | 9 | pub struct Allocator(Mutex>); 10 | 11 | #[derive(Debug, Default)] 12 | pub struct Stat { 13 | pub total: usize, 14 | pub used: usize, 15 | } 16 | 17 | impl Allocator { 18 | pub const fn new() -> Self { 19 | Allocator(Mutex::new(Heap::new())) 20 | } 21 | 22 | pub fn stat(&self) -> Stat { 23 | ksync_core::critical(|| { 24 | let heap = self.0.lock(); 25 | Stat { 26 | total: heap.stats_total_bytes(), 27 | used: heap.stats_alloc_actual(), 28 | } 29 | }) 30 | } 31 | 32 | /// # Safety 33 | /// 34 | /// The function must be called only once during initialization 35 | pub unsafe fn init(&self, start: usize, len: usize) { 36 | ksync_core::critical(|| self.0.lock().init(start, len)); 37 | } 38 | } 39 | 40 | unsafe impl GlobalAlloc for Allocator { 41 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 42 | let res = ksync_core::critical(|| self.0.lock().alloc(layout)); 43 | // if let Ok(ptr) = res { 44 | // log::trace!("+++ {ptr:?} {layout:?}"); 45 | // } 46 | res.map_or(ptr::null_mut(), NonNull::as_ptr) 47 | } 48 | 49 | unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { 50 | if let Some(ptr) = NonNull::new(ptr) { 51 | // log::trace!("--- {ptr:?} {layout:?}"); 52 | ksync_core::critical(|| self.0.lock().dealloc(ptr, layout)) 53 | } 54 | } 55 | } 56 | 57 | #[cfg(test)] 58 | mod tests { 59 | use super::*; 60 | 61 | #[test] 62 | fn alloc_dealloc() { 63 | static mut SPACE: [u64; 256] = [0; 256]; 64 | let layout = Layout::from_size_align(4, 8).unwrap(); 65 | unsafe { 66 | let allocator = Allocator::new(); 67 | assert!(allocator.alloc(layout).is_null()); 68 | 69 | allocator.init(SPACE.as_ptr() as usize, SPACE.len() * 8); 70 | 71 | let ptr = allocator.alloc(layout); 72 | assert!(!ptr.is_null()); 73 | allocator.dealloc(ptr, layout); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /.github/workflows/mirror.yml: -------------------------------------------------------------------------------- 1 | name: "Mirror to Gitlab" 2 | 3 | on: 4 | push: 5 | branches: ["master"] 6 | 7 | jobs: 8 | approve: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Approve 12 | run: echo All PR should be approved first to prevent any malicious code to be mirrored to the destination. 13 | 14 | deploy: 15 | runs-on: ubuntu-latest 16 | needs: [approve] 17 | steps: 18 | - uses: actions/checkout@v3 19 | with: 20 | ref: deployed 21 | fetch-depth: 0 22 | 23 | - name: Config git 24 | run: | 25 | git config user.name github-actions 26 | git config user.email github-actions@github.com 27 | git config pull.rebase true 28 | 29 | - name: Rebase on master 30 | run: | 31 | git fetch --all 32 | git pull --all 33 | git checkout master 34 | git checkout deployed 35 | git rebase master 36 | git pull --all 37 | git push 38 | 39 | - name: Revendor dependencies 40 | run: sh scripts/revendor.sh 41 | 42 | - name: Test build 43 | run: | 44 | cargo install cargo-binutils 45 | make all MODE=release 46 | 47 | - name: Deploy to the branch 48 | run: | 49 | date > deployment-time.log 50 | git add . 51 | git commit -m "Deployment" 52 | git push 53 | 54 | mirror: 55 | runs-on: ubuntu-latest 56 | needs: [deploy] 57 | environment: 58 | name: Integrate Pull Request 59 | env: 60 | SRC: "https://github.com/js2xxx/umi.git" 61 | DST: "https://PLNTRY:${{ secrets.PASSWORD }}@gitlab.eduxiji.net/PLNTRY/OSKernel2023-umi.git" 62 | steps: 63 | - name: Clone the source 64 | run: git clone --mirror "$SRC" 65 | 66 | - name: Set remote 67 | run: cd `basename "$SRC"` && git remote set-url --push origin "$DST" 68 | 69 | - name: Fetch remote 70 | run: cd `basename "$SRC"` && git fetch -p origin 71 | 72 | - name: Update branches 73 | run: cd `basename "$SRC"` && git for-each-ref --format 'delete %(refname)' refs/pull | git update-ref --stdin 74 | 75 | - name: Push to the destination 76 | run: cd `basename "$SRC"` && git push --mirror 77 | -------------------------------------------------------------------------------- /mizu/kernel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | license = "MIT OR Apache-2.0" 4 | name = "mizu" 5 | version = "0.1.0" 6 | 7 | [features] 8 | cv1811h = [ 9 | "config/cv1811h", 10 | "rv39-paging/cv1811h", 11 | "kmem/cv1811h", 12 | "uart/uart-status", 13 | "sdmmc/mars", 14 | ] 15 | qemu-virt = [ 16 | "config/qemu-virt", 17 | "rv39-paging/qemu-virt", 18 | ] 19 | test = [ 20 | "art/test", 21 | "kalloc/test", 22 | "ktime/test", 23 | ] 24 | 25 | [dependencies] 26 | # Local libs 27 | afat32 = {path = "../lib/afat32"} 28 | art = {path = "../lib/art"} 29 | co-trap = {path = "../lib/co-trap"} 30 | config = {path = "../lib/config"} 31 | devices = {path = "../lib/devices"} 32 | hart-id = {path = "../lib/hart-id"} 33 | kalloc = {path = "../lib/kalloc"} 34 | kmem = {path = "../lib/kmem"} 35 | ksc = {path = "../lib/ksc"} 36 | ksync = {path = "../lib/ksync"} 37 | ktime = {path = "../lib/ktime"} 38 | rand-riscv = {path = "../lib/rand-riscv"} 39 | rv39-paging = {path = "../lib/paging"} 40 | sygnal = {path = "../lib/sygnal"} 41 | umifs = {path = "../lib/umifs"} 42 | umio = {path = "../lib/umio"} 43 | # Local drivers 44 | plic = {path = "../dev/plic"} 45 | sdmmc = {path = "../dev/sdmmc"} 46 | uart = {path = "../dev/uart"} 47 | virtio = {path = "../dev/virtio"} 48 | # External crates 49 | arsc-rs = {git = "https://github.com/js2xxx/arsc"} 50 | async-trait = "0" 51 | bitflags = "2" 52 | crossbeam-queue = {version = "0", default-features = false, features = ["alloc", "nightly"]} 53 | fdt = "0" 54 | futures-util = {version = "0", default-features = false, features = ["alloc"]} 55 | goblin = {version = "0", default-features = false, features = ["elf32", "elf64", "endian_fd"]} 56 | hashbrown = {version = "0", default-features = false, features = ["inline-more"]} 57 | heapless = "0" 58 | log = "0" 59 | pin-project = "1" 60 | r0 = "1" 61 | riscv = "0" 62 | sbi-rt = {git = "https://github.com/js2xxx/sbi-rt", branch = "multitarget", features = ["legacy"]} 63 | scoped-tls = {git = "https://github.com/js2xxx/scoped-tls", branch = "no_std"} 64 | spin = "0" 65 | static_assertions = "1" 66 | virtio-drivers = {git = "https://github.com/rcore-os/virtio-drivers", branch = "new-netdev"} 67 | zerocopy = {version = "0", features = ["derive"]} 68 | 69 | [dependencies.smoltcp] 70 | branch = "reuse_endpoints" 71 | default-features = false 72 | features = [ 73 | "log", 74 | "proto-ipv4", 75 | "proto-ipv6", 76 | ] 77 | git = "https://github.com/js2xxx/smoltcp" 78 | 79 | [build-dependencies] 80 | config = {path = "../lib/config", default-features = false} 81 | -------------------------------------------------------------------------------- /mizu/lib/ksc-core/src/handler.rs: -------------------------------------------------------------------------------- 1 | use alloc::boxed::Box; 2 | use core::{future::Future, ops::ControlFlow, pin::Pin}; 3 | 4 | use bevy_utils_proc_macros::all_tuples; 5 | 6 | pub type Boxed<'a, T> = Pin + Send + 'a>>; 7 | 8 | pub trait Param { 9 | type Item<'a>: Param + 'a; 10 | } 11 | 12 | impl Param for &'_ mut T { 13 | type Item<'a> = &'a mut T; 14 | } 15 | 16 | impl Param for &T { 17 | type Item<'a> = &'a T; 18 | } 19 | 20 | impl FromParam<&'_ mut T> for &'_ mut T { 21 | fn from_param<'a>(item: <&'_ mut T as Param>::Item<'a>) -> Self::Item<'a> { 22 | item 23 | } 24 | } 25 | 26 | impl FromParam<&'_ T> for &'_ T { 27 | fn from_param<'a>(item: <&'_ T as Param>::Item<'a>) -> Self::Item<'a> { 28 | item 29 | } 30 | } 31 | 32 | impl Param for Boxed<'_, T> { 33 | type Item<'a> = Boxed<'a, T::Item<'a>>; 34 | } 35 | 36 | pub trait FromParam: Param { 37 | fn from_param(item: ::Item<'_>) -> Self::Item<'_>; 38 | } 39 | 40 | macro_rules! impl_param { 41 | ($(($source:ident, $param:ident)),*) => { 42 | impl<$($param: Param),*> Param for ($($param,)*) { 43 | type Item<'a> = ($(<$param as Param>::Item<'a>,)*); 44 | } 45 | 46 | #[allow(clippy::unused_unit)] 47 | #[allow(non_snake_case)] 48 | impl<$($source: Param, $param: FromParam<$source>),*> FromParam<($($source,)*)> for($($param,)*) { 49 | fn from_param(item: <($($source,)*) as Param>::Item<'_>) -> Self::Item<'_> { 50 | let ($($source,)*) = item; 51 | ($(<$param as FromParam<$source>>::from_param($source),)*) 52 | } 53 | } 54 | }; 55 | } 56 | 57 | all_tuples!(impl_param, 0, 12, S, P); 58 | 59 | macro_rules! impl_primitives { 60 | ($($type:ident),* $(,)?) => { 61 | $( 62 | impl Param for $type { 63 | type Item<'a> = $type; 64 | } 65 | )* 66 | }; 67 | } 68 | impl_primitives!(bool, char, u8, u16, u32, u64, usize, i8, i16, i32, i64, isize); 69 | 70 | impl Param for Result { 71 | type Item<'a> = Result<::Item<'a>, ::Item<'a>>; 72 | } 73 | 74 | impl Param for Option { 75 | type Item<'a> = Option<::Item<'a>>; 76 | } 77 | 78 | impl Param for ControlFlow { 79 | type Item<'a> = ControlFlow<::Item<'a>, ::Item<'a>>; 80 | } 81 | -------------------------------------------------------------------------------- /mizu/kernel/link.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH(riscv) 2 | 3 | SECTIONS 4 | { 5 | .text : 6 | { 7 | KEEP(*(.init*)); 8 | *(.text*); 9 | *(.plt*); 10 | } >VIRT AT>RAM :text 11 | .dynamic : { *(.dynamic*) } >VIRT AT>RAM :text :dynamic 12 | .dynsym : { *(.dynsym) } >VIRT AT>RAM 13 | .gnu.hash : { *(.gnu.hash) } >VIRT AT>RAM 14 | .hash : { *(.hash) } >VIRT AT>RAM 15 | .dynstr : { *(.dynstr) } >VIRT AT>RAM 16 | 17 | .rodata : ALIGN(0x10) 18 | { 19 | *(.srodata*); 20 | *(.rodata*); 21 | } >VIRT AT>RAM :text 22 | 23 | .data : ALIGN(0x10) 24 | { 25 | _sidata = LOADADDR(.data); 26 | _sdata = .; 27 | PROVIDE(__global_pointer$ = . + 2K); 28 | *(.sdata*); 29 | *(.data*); 30 | . = ALIGN(0x10); 31 | _edata = .; 32 | } >VIRT AT>RAM :data 33 | 34 | .tdata : ALIGN(0x10) 35 | { 36 | _stdata = .; 37 | *(.tdata*); 38 | . = ALIGN(0x10); 39 | _etdata = .; 40 | _tdata_size = _etdata - _stdata; 41 | } >VIRT AT>RAM :data :tls 42 | 43 | .itbss (NOLOAD) : ALIGN(0x10) 44 | { 45 | _stbss = .; 46 | *(.tbss*); 47 | . = ALIGN(0x10); 48 | _etbss = .; 49 | _tbss_size = _etbss - _stbss; 50 | } >VIRT AT>RAM :data :tls 51 | 52 | .bss (NOLOAD) : ALIGN(0x10) 53 | { 54 | _sbss = .; 55 | *(.sbss*); 56 | *(.bss*); 57 | . = ALIGN(0x10); 58 | _ebss = .; 59 | 60 | . = ALIGN(4K); 61 | _sstack = .; 62 | . += _stack_size * %MAX_HARTS%; 63 | _estack = .; 64 | 65 | _sheap = .; 66 | . += _heap_size; 67 | _eheap = .; 68 | } >VIRT AT>RAM 69 | 70 | .tbss (NOLOAD) : ALIGN(0x10) 71 | { 72 | _stp = .; 73 | . += (_tdata_size + _tbss_size + 8) * %MAX_HARTS%; 74 | _etp = .; 75 | } >VIRT AT>RAM 76 | 77 | . = ALIGN(4K); 78 | _end = .; 79 | 80 | .eh_frame (INFO) : { KEEP(*(.eh_frame)) } 81 | .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) } 82 | } 83 | 84 | PHDRS 85 | { 86 | phdr PT_PHDR PHDRS FLAGS(4); 87 | text PT_LOAD FLAGS(5); 88 | data PT_LOAD FLAGS(6); 89 | dynamic PT_DYNAMIC FLAGS(6); 90 | note PT_NOTE FLAGS(4); 91 | tls PT_TLS FLAGS(4); 92 | } 93 | 94 | MEMORY 95 | { 96 | VIRT : ORIGIN = %VIRT_START%, LENGTH = %RAM_SIZE% 97 | RAM : ORIGIN = %RAM_START%, LENGTH = %RAM_SIZE% 98 | } 99 | 100 | PROVIDE(_heap_size = 36M); 101 | PROVIDE(_stack_size = 400K); 102 | -------------------------------------------------------------------------------- /mizu/lib/kmem/src/virt/tlb.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | mem, 3 | ptr::{self, NonNull}, 4 | sync::atomic::Ordering::SeqCst, 5 | }; 6 | 7 | use arsc_rs::Arsc; 8 | use riscv::{ 9 | asm::{sfence_vma, sfence_vma_all}, 10 | register::{satp, satp::Mode::Sv39}, 11 | }; 12 | use rv39_paging::{LAddr, PAddr, ID_OFFSET, PAGE_SHIFT}; 13 | 14 | use crate::Virt; 15 | 16 | #[thread_local] 17 | static mut CUR_VIRT: *const Virt = ptr::null(); 18 | 19 | pub fn set_virt(virt: Arsc) { 20 | let addr = unsafe { (*virt.root.as_ptr()).as_mut_ptr() }; 21 | 22 | virt.cpu_mask.fetch_or(1 << hart_id::hart_id(), SeqCst); 23 | let new = Arsc::into_raw(virt); 24 | let old = unsafe { mem::replace(&mut CUR_VIRT, new) }; 25 | 26 | let ret = NonNull::new(old.cast_mut()).map(|old| unsafe { Arsc::from_raw(old.as_ptr()) }); 27 | 28 | if old != new { 29 | let paddr = *LAddr::from(addr).to_paddr(ID_OFFSET); 30 | unsafe { 31 | satp::set(Sv39, 0, paddr >> PAGE_SHIFT); 32 | sfence_vma_all() 33 | } 34 | if let Some(old) = ret { 35 | log::debug!("tlb::set_virt: {:p} => {:p}", old.root.as_ptr(), addr); 36 | old.cpu_mask.fetch_and(!(1 << hart_id::hart_id()), SeqCst); 37 | } else { 38 | log::debug!("tlb::set_virt: K => {:p}", addr); 39 | } 40 | } 41 | } 42 | 43 | /// # Safety 44 | /// 45 | /// The caller must ensure the validity of the page tables contained in 46 | /// `default_pt`. 47 | pub unsafe fn unset_virt(default_pt: PAddr) { 48 | let old = unsafe { mem::replace(&mut CUR_VIRT, ptr::null()) }; 49 | 50 | let ret = NonNull::new(old.cast_mut()).map(|old| unsafe { Arsc::from_raw(old.as_ptr()) }); 51 | 52 | unsafe { 53 | satp::set(Sv39, 0, *default_pt >> PAGE_SHIFT); 54 | sfence_vma_all() 55 | } 56 | if let Some(ref old) = ret { 57 | log::debug!("tlb::set_virt: {:p} => K", old.root.as_ptr()); 58 | old.cpu_mask.fetch_and(!(1 << hart_id::hart_id()), SeqCst); 59 | } 60 | } 61 | 62 | pub fn flush(cpu_mask: usize, addr: LAddr, count: usize) { 63 | if count == 0 { 64 | return; 65 | } 66 | log::trace!("tlb::flush cpu_mask = {cpu_mask:#b}, addr = {addr:?}, count = {count}"); 67 | let others = cpu_mask & !(1 << hart_id::hart_id()); 68 | if others != 0 { 69 | let _ = sbi_rt::remote_sfence_vma(others, 0, addr.val(), count << PAGE_SHIFT); 70 | } 71 | if cpu_mask != others { 72 | unsafe { 73 | if count == 1 { 74 | sfence_vma(0, addr.val()) 75 | } else { 76 | sfence_vma_all() 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /mizu/dev/plic/src/intr.rs: -------------------------------------------------------------------------------- 1 | use alloc::collections::BTreeMap; 2 | use core::{num::NonZeroU32, ptr::NonNull}; 3 | 4 | use devices::intr::{Completion, IntrHandler}; 5 | use ksc::Handlers; 6 | use spin::{Mutex, RwLock, RwLockUpgradableGuard}; 7 | 8 | use crate::dev::Plic; 9 | 10 | pub struct IntrManager { 11 | plic: Plic, 12 | map: RwLock>, 13 | counts: Mutex>, 14 | } 15 | 16 | impl IntrManager { 17 | pub fn new(plic: Plic) -> Self { 18 | hart_id::for_each_hart(|hid| plic.set_priority_threshold(Self::hid_to_cx(hid), 0)); 19 | IntrManager { 20 | plic, 21 | map: RwLock::new(Default::default()), 22 | counts: Default::default(), 23 | } 24 | } 25 | 26 | /// # Safety 27 | /// 28 | /// See [`crate::dev::Plic`] for more information. 29 | pub unsafe fn from_raw(base: NonNull<()>) -> Self { 30 | Self::new(Plic::new(base)) 31 | } 32 | 33 | fn hid_to_cx(hid: usize) -> usize { 34 | hid * 2 + 1 35 | } 36 | 37 | pub fn insert(&self, pin: NonZeroU32, handler: impl IntrHandler) -> bool { 38 | let pin = pin.get(); 39 | if !ksync::critical(|| self.map.write().try_insert(pin, handler)) { 40 | return false; 41 | } 42 | hart_id::for_each_hart(|hid| self.plic.enable(pin, Self::hid_to_cx(hid), true)); 43 | self.plic.set_priority(pin, 1); 44 | true 45 | } 46 | 47 | pub fn check_pending(&self, pin: NonZeroU32) -> bool { 48 | self.plic.pending(pin.get()) 49 | } 50 | 51 | pub fn counts(&self) -> BTreeMap { 52 | ksync::critical(|| self.counts.lock().clone()) 53 | } 54 | 55 | pub fn notify(&'static self, hid: usize) { 56 | let cx = Self::hid_to_cx(hid); 57 | let pin = self.plic.claim(cx); 58 | if pin == 0 { 59 | return; 60 | } 61 | // log::trace!("Intr::notify cx = {cx}, pin = {pin}"); 62 | let exist = ksync::critical(move || { 63 | let map = self.map.upgradeable_read(); 64 | *self.counts.lock().entry(pin).or_default() += 1; 65 | let ret = map.handle(pin, &move || self.plic.complete(cx, pin)); 66 | match ret { 67 | Some(false) => { 68 | let mut map = RwLockUpgradableGuard::upgrade(map); 69 | map.remove(pin); 70 | false 71 | } 72 | Some(true) => true, 73 | None => false, 74 | } 75 | }); 76 | if !exist { 77 | self.plic.enable(pin, cx, false); 78 | self.plic.set_priority(pin, 0); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /mizu/lib/co-trap/src/imp.S: -------------------------------------------------------------------------------- 1 | // a0 <- trap_frame: *mut TrapFrame 2 | // a1 <- scratch register 3 | 4 | .macro scratch op 5 | \op a1, 17*8(a0) 6 | .endm 7 | 8 | .macro save_scratch 9 | scratch sd 10 | .endm 11 | 12 | .macro load_scratch 13 | scratch ld 14 | .endm 15 | 16 | .macro xchg reg addr 17 | ld a1, \addr 18 | sd \reg, \addr 19 | mv \reg, a1 20 | .endm 21 | 22 | .macro xchg_sx 23 | xchg s0, 0*8(a0) 24 | xchg s1, 1*8(a0) 25 | xchg s2, 2*8(a0) 26 | xchg s3, 3*8(a0) 27 | xchg s4, 4*8(a0) 28 | xchg s5, 5*8(a0) 29 | xchg s6, 6*8(a0) 30 | xchg s7, 7*8(a0) 31 | xchg s8, 8*8(a0) 32 | xchg s9, 9*8(a0) 33 | xchg s10, 10*8(a0) 34 | xchg s11, 11*8(a0) 35 | .endm 36 | 37 | .macro tx op 38 | xchg ra, 12*8(a0) 39 | xchg sp, 13*8(a0) 40 | xchg gp, 14*8(a0) 41 | xchg tp, 15*8(a0) 42 | \op a2, 18*8(a0) 43 | \op a3, 19*8(a0) 44 | \op a4, 20*8(a0) 45 | \op a5, 21*8(a0) 46 | \op a6, 22*8(a0) 47 | \op a7, 23*8(a0) 48 | \op t0, 24*8(a0) 49 | \op t1, 25*8(a0) 50 | \op t2, 26*8(a0) 51 | \op t3, 27*8(a0) 52 | \op t4, 28*8(a0) 53 | \op t5, 29*8(a0) 54 | \op t6, 30*8(a0) 55 | .endm 56 | 57 | .macro save_tx 58 | tx sd 59 | .endm 60 | 61 | .macro load_tx 62 | tx ld 63 | .endm 64 | 65 | .macro save_ux 66 | csrr a1, sepc 67 | sd a1, 31*8(a0) 68 | csrr a1, sstatus 69 | sd a1, 32*8(a0) 70 | csrr a1, stval 71 | sd a1, 33*8(a0) 72 | csrr a1, scause 73 | sd a1, 34*8(a0) 74 | .endm 75 | 76 | .macro load_ux 77 | ld a1, 31*8(a0) 78 | csrw sepc, a1 79 | ld a1, 32*8(a0) 80 | csrw sstatus, a1 81 | .endm 82 | 83 | .section .text 84 | 85 | .global _return_to_user 86 | .type _return_to_user, @function 87 | _return_to_user: 88 | xchg_sx 89 | 90 | .global _fast_return_to_user 91 | .type _fast_return_to_user, @function 92 | _fast_return_to_user: 93 | load_ux 94 | load_tx 95 | load_scratch 96 | 97 | csrw sscratch, a0 98 | ld a0, 16*8(a0) 99 | sret 100 | 101 | .extern _fast_func 102 | 103 | .global _user_entry 104 | .type _user_entry, @function 105 | .align 4 106 | _user_entry: 107 | csrrw a0, sscratch, a0 108 | save_scratch 109 | save_tx 110 | save_ux 111 | csrr a1, sscratch 112 | sd a1, 16*8(a0) 113 | 114 | addi sp, sp, -16 115 | sd ra, 8(sp) 116 | call _fast_func 117 | ld ra, 8(sp) 118 | addi sp, sp, 16 119 | 120 | // `a0` did not change; 121 | // `a1` <- status; 122 | bnez a1, .Lresume 123 | j _fast_return_to_user 124 | 125 | .Lresume: 126 | mv t0, a1 127 | xchg_sx 128 | mv a0, t0 129 | ret 130 | -------------------------------------------------------------------------------- /mizu/kernel/src/dev.rs: -------------------------------------------------------------------------------- 1 | mod block; 2 | mod intr; 3 | mod net; 4 | mod sdmmc; 5 | mod serial; 6 | mod virtio; 7 | 8 | use alloc::vec::Vec; 9 | use core::mem; 10 | 11 | use fdt::{node::FdtNode, Fdt, FdtError}; 12 | use ksc::Handlers; 13 | use spin::{Lazy, Once}; 14 | 15 | pub use self::{ 16 | block::{block, blocks}, 17 | intr::INTR, 18 | net::{net, nets}, 19 | serial::{init_logger, Stdin, Stdout}, 20 | }; 21 | 22 | static DEV_INIT: Lazy> = Lazy::new(|| { 23 | Handlers::new() 24 | .map("ns16550a", serial::init_ns16550a) 25 | .map("snps,dw-apb-uart", serial::init_dw_apb_uart) 26 | .map("riscv,plic0", intr::init_plic) 27 | .map("virtio,mmio", virtio::init_mmio) 28 | .map("cvitek,mars-sd", sdmmc::init) 29 | }); 30 | 31 | fn interrupts<'a>(node: &'a FdtNode) -> impl Iterator + 'a { 32 | let size = node 33 | .interrupt_parent() 34 | .and_then(|ip| ip.interrupt_cells()) 35 | .unwrap_or(1); 36 | let value = node.property("interrupts").map_or(&[] as _, |p| p.value); 37 | value 38 | .chunks(size * mem::size_of::()) 39 | .map(|v| u32::from_be_bytes(v[..4].try_into().unwrap())) 40 | } 41 | 42 | /// Initialize all the possible devices in this crate using FDT. 43 | /// 44 | /// # Errors 45 | /// 46 | /// This function will return an error if the given base pointer contains an 47 | /// invalid FDT. 48 | /// 49 | /// # Safety 50 | /// 51 | /// `fdt_base` must have `'static` read access to a valid FDT struct. 52 | pub unsafe fn init(fdt_base: *const ()) -> Result<(), FdtError> { 53 | static FDT: Once = Once::new(); 54 | let fdt = FDT.try_call_once(|| unsafe { fdt::Fdt::from_ptr(fdt_base.cast()) })?; 55 | 56 | // Some devices may depend on other devices (like interrupts), so we should keep 57 | // trying until no device get initialized in a turn. 58 | 59 | let mut nodes = fdt.all_nodes().collect::>(); 60 | let mut count = nodes.len(); 61 | loop { 62 | if nodes.is_empty() { 63 | break; 64 | } 65 | 66 | nodes.retain(|node| { 67 | if let Some(compat) = node.compatible() { 68 | let init = compat.all().any(|key| { 69 | let ret = DEV_INIT.handle(key, (node, fdt)); 70 | matches!(ret, Some(true)) 71 | }); 72 | if init { 73 | log::debug!("{} initialized", node.name); 74 | } 75 | return !init; 76 | } 77 | false 78 | }); 79 | 80 | if count == nodes.len() { 81 | break; 82 | } 83 | count = nodes.len(); 84 | } 85 | 86 | Ok(()) 87 | } 88 | -------------------------------------------------------------------------------- /mizu/kernel/src/dev/virtio.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::Arc; 2 | use core::num::NonZeroU32; 3 | 4 | use devices::{intr::Completion, net::Net}; 5 | use fdt::{node::FdtNode, Fdt}; 6 | use rv39_paging::{PAddr, ID_OFFSET}; 7 | use spin::RwLock; 8 | use virtio::{block::VirtioBlock, net::VirtioNet}; 9 | use virtio_drivers::transport::{mmio::MmioTransport, DeviceType, Transport}; 10 | 11 | use super::{block::BLOCKS, interrupts, net::NETS}; 12 | use crate::{dev::intr::intr_man, someb, tryb}; 13 | 14 | pub fn init_mmio(node: &FdtNode, _: &Fdt) -> bool { 15 | let intr_pin = someb!(interrupts(node).next().and_then(NonZeroU32::new)); 16 | let intr_manager = someb!(intr_man()); 17 | 18 | let reg = someb!(node.reg().and_then(|mut reg| reg.next())); 19 | let addr = PAddr::new(reg.starting_address as _).to_laddr(ID_OFFSET); 20 | 21 | let header = someb!(addr.as_non_null()); 22 | let mmio = tryb!(unsafe { 23 | MmioTransport::new(header.cast()).inspect_err(|err| { 24 | log::trace!("Invalid VirtIO MMIO header: {err}"); 25 | }) 26 | }); 27 | 28 | match mmio.device_type() { 29 | DeviceType::Block => { 30 | let device = tryb!(VirtioBlock::new(mmio).inspect_err(|err| { 31 | log::debug!("Failed to initialize VirtIO block device: {err}"); 32 | })); 33 | let device = Arc::new(device); 34 | 35 | let d2 = device.clone(); 36 | if !intr_manager.insert(intr_pin, move |c| d2.ack_interrupt(c)) { 37 | return false; 38 | } 39 | 40 | ksync::critical(|| BLOCKS.lock().push(device)); 41 | 42 | true 43 | } 44 | DeviceType::Network => { 45 | let device = tryb!(VirtioNet::<16>::new(mmio).inspect_err(|err| { 46 | log::debug!("Failed to initialize VirtIO block device: {err}"); 47 | })); 48 | let device = Arc::new(RwLock::new(device)); 49 | ksync::critical(|| device.write().startup()); 50 | let d2 = device.clone(); 51 | 52 | let ack = move |completion: &Completion| { 53 | ksync::critical(|| loop { 54 | if let Some(device) = d2.try_read() { 55 | completion(); 56 | device.ack_interrupt(); 57 | break true; 58 | } 59 | core::hint::spin_loop() 60 | }) 61 | }; 62 | if !intr_manager.insert(intr_pin, ack) { 63 | return false; 64 | } 65 | 66 | ksync::critical(|| NETS.lock().push(device)); 67 | true 68 | } 69 | ty => { 70 | log::debug!("Unsupported VirtIO MMIO device type {ty:?}"); 71 | false 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /mizu/lib/paging/src/level.rs: -------------------------------------------------------------------------------- 1 | use crate::{NR_ENTRIES, NR_ENTRIES_SHIFT, PAGE_SHIFT}; 2 | 3 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] 4 | pub struct Level(u8); // the u8 indicates what level it is. 5 | 6 | // something about rv pgtbl levels, 2->0 from root to leaf 7 | impl Level { 8 | pub const fn new(level: u8) -> Self { 9 | assert!(level < 3, "RISC-V sv39 paging only supports 3 levels"); 10 | Self(level) 11 | } 12 | 13 | pub const fn pt() -> Self { 14 | Self::new(0) 15 | } 16 | 17 | pub const fn max() -> Self { 18 | Self::new(2) 19 | } 20 | 21 | pub fn val(&self) -> u8 { 22 | self.0 23 | } 24 | 25 | #[inline] 26 | pub const fn page_shift(&self) -> u32 { 27 | PAGE_SHIFT + self.0 as u32 * NR_ENTRIES_SHIFT 28 | } 29 | 30 | #[inline] 31 | pub const fn page_size(&self) -> usize { 32 | 1usize << self.page_shift() 33 | } 34 | 35 | /// If `self.0 = n`, return 36 | /// 37 | /// 0...011...1 /* (12+9n) 1s */ 38 | #[inline] 39 | pub const fn page_mask(&self) -> usize { 40 | self.page_size() - 1 41 | } 42 | 43 | /// If `self.0 = n`, return 44 | /// 45 | /// 1...100...0 /* (12+9n) 0s */ 46 | #[inline] 47 | pub const fn paddr_mask(&self) -> usize { 48 | ((1 << 56) - 1) & !self.page_mask() 49 | } 50 | 51 | #[inline] 52 | pub const fn laddr_mask(&self) -> usize { 53 | Level(3).page_mask() & !self.page_mask() 54 | } 55 | 56 | /// Return PPN \[ `self.0` \] with given `la` and `end`. 57 | /// 58 | /// # Examples 59 | /// 60 | /// If `self.0 = 0`, return the lowest 9-bit PPN of `la`. 61 | #[inline] 62 | pub const fn addr_idx(&self, laddr: usize, end: bool) -> usize { 63 | let ret = ((laddr & self.laddr_mask()) >> self.page_shift()) & (NR_ENTRIES - 1); 64 | if end && ret == 0 { 65 | NR_ENTRIES 66 | } else { 67 | ret 68 | } 69 | } 70 | 71 | #[inline] 72 | pub const fn decrease(&self) -> Option { 73 | self.0.checked_sub(1).map(Level) 74 | } 75 | } 76 | 77 | #[const_trait] 78 | pub trait AddrExt { 79 | fn round_down(self, level: Level) -> Self; 80 | 81 | fn round_up(self, level: Level) -> Self; 82 | } 83 | 84 | impl const AddrExt for usize { 85 | #[inline] 86 | fn round_down(self, level: Level) -> Self { 87 | self & !level.page_mask() 88 | } 89 | 90 | #[inline] 91 | fn round_up(self, level: Level) -> Self { 92 | (self + level.page_mask()) & !level.page_mask() 93 | } 94 | } 95 | 96 | #[cfg(test)] 97 | mod tests { 98 | use crate::{Level, PAGE_SHIFT, PAGE_SIZE}; 99 | 100 | #[test] 101 | fn test_page_size() { 102 | assert_eq!(PAGE_SHIFT, Level::pt().page_shift()); 103 | assert_eq!(PAGE_SIZE, Level::pt().page_size()); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /mizu/lib/sygnal/src/action.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | 3 | use array_macro::array; 4 | use rv39_paging::LAddr; 5 | use spin::Mutex; 6 | 7 | use crate::{Sig, SigSet, NR_SIGNALS}; 8 | 9 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 10 | pub enum ActionType { 11 | Ignore, 12 | Kill, 13 | Suspend, 14 | Resume, 15 | User { 16 | entry: LAddr, 17 | use_extra_cx: bool, 18 | use_alt_stack: bool, 19 | }, 20 | } 21 | 22 | impl ActionType { 23 | pub const fn default(sig: Sig) -> Self { 24 | use ActionType::*; 25 | match sig { 26 | Sig::SIGCHLD | Sig::SIGURG => Ignore, 27 | Sig::SIGSTOP => Suspend, 28 | Sig::SIGCONT => Resume, 29 | _ => ActionType::Kill, 30 | } 31 | } 32 | } 33 | 34 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 35 | pub struct Action { 36 | pub ty: ActionType, 37 | pub mask: SigSet, 38 | } 39 | 40 | impl Action { 41 | pub const fn default(sig: Sig) -> Self { 42 | Action { 43 | ty: ActionType::default(sig), 44 | mask: SigSet::EMPTY, 45 | } 46 | } 47 | 48 | pub fn default_sig_mask() -> SigSet { 49 | (0..NR_SIGNALS) 50 | .filter_map(Sig::from_index) 51 | .fold(Default::default(), |ss, sig| { 52 | if ActionType::default(sig) == ActionType::Ignore { 53 | ss | sig 54 | } else { 55 | ss 56 | } 57 | }) 58 | } 59 | } 60 | 61 | pub struct ActionSet { 62 | data: [Mutex; NR_SIGNALS], 63 | } 64 | 65 | impl ActionSet { 66 | pub const fn new() -> Self { 67 | ActionSet { 68 | data: array![ 69 | index => match Sig::from_index(index) { 70 | Some(sig) => Mutex::new(Action::default(sig)), 71 | None => panic!("unsupported index for signal") 72 | }; 73 | NR_SIGNALS 74 | ], 75 | } 76 | } 77 | 78 | pub fn get(&self, sig: Sig) -> Action { 79 | ksync::critical(|| *self.data[sig.index()].lock()) 80 | } 81 | 82 | pub fn replace(&self, sig: Sig, new: Action) -> Action { 83 | ksync::critical(|| { 84 | let mut action = self.data[sig.index()].lock(); 85 | let old = mem::replace(&mut *action, new); 86 | if sig.should_never_capture() { 87 | *action = Action::default(sig); 88 | } 89 | old 90 | }) 91 | } 92 | 93 | pub fn deep_fork(&self) -> Self { 94 | ActionSet { 95 | data: array![ 96 | index => Mutex::new(ksync::critical(|| *self.data[index].lock())); 97 | NR_SIGNALS 98 | ], 99 | } 100 | } 101 | } 102 | 103 | impl const Default for ActionSet { 104 | fn default() -> Self { 105 | Self::new() 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /mizu/lib/ktime/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "test"), no_std)] 2 | 3 | extern crate alloc; 4 | 5 | mod timer; 6 | 7 | use core::{ 8 | future::Future, 9 | pin::Pin, 10 | task::{Context, Poll}, 11 | time::Duration, 12 | }; 13 | 14 | use futures_lite::FutureExt; 15 | pub use ktime_core::*; 16 | use pin_project::pin_project; 17 | 18 | pub use self::timer::{Period, Timer}; 19 | 20 | pub fn timer_tick() { 21 | timer::TIMER_QUEUE.tick(); 22 | } 23 | 24 | pub async fn sleep(duration: Duration) { 25 | Timer::after(duration).await; 26 | } 27 | 28 | pub async fn sleep_until(deadline: Instant) { 29 | Timer::deadline(deadline).await; 30 | } 31 | 32 | #[must_use = "futures do nothing unless polled"] 33 | #[pin_project] 34 | pub struct OnTimeout { 35 | #[pin] 36 | fut: F, 37 | timer: Timer, 38 | out: Option, 39 | } 40 | 41 | impl Future for OnTimeout 42 | where 43 | F: Future, 44 | G: FnOnce() -> T, 45 | F::Output: From, 46 | { 47 | type Output = F::Output; 48 | 49 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 50 | let this = self.project(); 51 | if let Poll::Ready(data) = this.fut.poll(cx) { 52 | Poll::Ready(data) 53 | } else if this.timer.poll(cx).is_ready() { 54 | Poll::Ready((this.out.take().unwrap())().into()) 55 | } else { 56 | Poll::Pending 57 | } 58 | } 59 | } 60 | 61 | #[must_use = "futures do nothing unless polled"] 62 | #[pin_project] 63 | pub struct OkOrTimeout { 64 | #[pin] 65 | fut: F, 66 | timer: Timer, 67 | out: Option, 68 | } 69 | 70 | impl Future for OkOrTimeout 71 | where 72 | F: Future, 73 | G: FnOnce() -> E, 74 | { 75 | type Output = Result; 76 | 77 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 78 | let this = self.project(); 79 | if let Poll::Ready(data) = this.fut.poll(cx) { 80 | Poll::Ready(Ok(data)) 81 | } else if this.timer.poll(cx).is_ready() { 82 | Poll::Ready(Err((this.out.take().unwrap())())) 83 | } else { 84 | Poll::Pending 85 | } 86 | } 87 | } 88 | 89 | pub trait TimeOutExt: Future + Sized { 90 | fn on_timeout T>( 91 | self, 92 | timer: impl Into, 93 | out: G, 94 | ) -> OnTimeout { 95 | OnTimeout { 96 | fut: self, 97 | timer: timer.into(), 98 | out: Some(out), 99 | } 100 | } 101 | 102 | fn ok_or_timeout E>( 103 | self, 104 | timer: impl Into, 105 | err: G, 106 | ) -> OkOrTimeout { 107 | OkOrTimeout { 108 | fut: self, 109 | timer: timer.into(), 110 | out: Some(err), 111 | } 112 | } 113 | } 114 | impl TimeOutExt for F {} 115 | -------------------------------------------------------------------------------- /mizu/lib/rand-riscv/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(build_hasher_simple_hash_one)] 3 | #![feature(const_trait_impl)] 4 | 5 | use core::{ 6 | fmt, 7 | hash::{BuildHasher, Hash}, 8 | marker::Destruct, 9 | }; 10 | 11 | pub use rand_chacha::rand_core; 12 | use rand_chacha::rand_core::SeedableRng; 13 | 14 | pub fn seed64() -> u64 { 15 | #[cfg(target_arch = "riscv64")] 16 | unsafe { 17 | let ret: u64; 18 | core::arch::asm!("rdcycle {}", out(reg) ret); 19 | ret 20 | } 21 | #[cfg(target_arch = "riscv32")] 22 | unsafe { 23 | loop { 24 | let (h1, low, high): (u32, u32, u32); 25 | core::arch::asm!( 26 | "rdcycleh {}; 27 | rdcycle {}; 28 | rdcycleh {}", 29 | out(reg) h1, 30 | out(reg) low, 31 | out(reg) high 32 | ); 33 | if h1 == high { 34 | break low as u64 | ((high as u64) << u32::BITS); 35 | } 36 | } 37 | } 38 | #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))] 39 | unimplemented!("not on RISC-V architecture") 40 | } 41 | 42 | pub fn seed(fill: &mut [u8]) { 43 | fill.chunks_mut(core::mem::size_of::()) 44 | .for_each(|fill| { 45 | let ne_bytes = seed64().to_ne_bytes(); 46 | let len = fill.len(); 47 | fill.copy_from_slice(&ne_bytes[..len]) 48 | }) 49 | } 50 | 51 | pub type Rng = rand_chacha::ChaChaRng; 52 | 53 | pub fn rng() -> Rng { 54 | Rng::from_seed({ 55 | let mut seed = [0; 32]; 56 | self::seed(&mut seed); 57 | seed 58 | }) 59 | } 60 | 61 | /// A wrapper around `ahash::RandomState`, using built-in seeds. 62 | #[derive(Clone)] 63 | pub struct RandomState(ahash::RandomState); 64 | 65 | impl fmt::Debug for RandomState { 66 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 67 | f.debug_struct("RandomState").finish_non_exhaustive() 68 | } 69 | } 70 | 71 | impl RandomState { 72 | pub fn new() -> Self { 73 | #[cfg(not(feature = "test"))] 74 | return RandomState(ahash::RandomState::with_seeds( 75 | seed64(), 76 | seed64(), 77 | seed64(), 78 | seed64(), 79 | )); 80 | #[cfg(feature = "test")] 81 | RandomState(ahash::RandomState::with_seed(rand::random())) 82 | } 83 | 84 | #[inline] 85 | pub fn hash_one(&self, x: T) -> u64 { 86 | self.0.hash_one(x) 87 | } 88 | } 89 | 90 | impl Default for RandomState { 91 | fn default() -> Self { 92 | Self::new() 93 | } 94 | } 95 | 96 | impl BuildHasher for RandomState { 97 | type Hasher = ahash::AHasher; 98 | 99 | fn build_hasher(&self) -> Self::Hasher { 100 | self.0.build_hasher() 101 | } 102 | 103 | fn hash_one(&self, x: T) -> u64 { 104 | self.0.hash_one(x) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /mizu/lib/ktime-core/src/instant.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | fmt, 3 | ops::{Add, AddAssign, Sub, SubAssign}, 4 | time::Duration, 5 | }; 6 | 7 | use crate::InstantExt; 8 | 9 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 10 | pub struct Instant(u128); 11 | 12 | impl Instant { 13 | pub fn now() -> Self { 14 | // SAFETY: The raw value is valid. 15 | unsafe { Self::from_raw(Self::now_raw()) } 16 | } 17 | 18 | /// Used for atomic storages. 19 | pub fn now_raw() -> u64 { 20 | riscv::register::time::read64() 21 | } 22 | 23 | #[must_use] 24 | pub fn checked_duration_since(&self, earlier: Self) -> Option { 25 | let micros = self.0.checked_sub(earlier.0)?; 26 | let secs = (micros / 1_000_000) as u64; 27 | let micros = (micros % 1_000_000) as u32; 28 | Some(Duration::new(secs, micros)) 29 | } 30 | 31 | #[must_use] 32 | pub fn duration_since(&self, earlier: Self) -> Duration { 33 | self.checked_duration_since(earlier).unwrap_or_default() 34 | } 35 | 36 | pub fn checked_add(&self, duration: Duration) -> Option { 37 | self.0.checked_add(duration.as_micros()).map(Instant) 38 | } 39 | 40 | pub fn checked_sub(&self, duration: Duration) -> Option { 41 | self.0.checked_sub(duration.as_micros()).map(Instant) 42 | } 43 | 44 | #[inline] 45 | #[must_use] 46 | pub fn elapsed(&self) -> Duration { 47 | Instant::now() - *self 48 | } 49 | } 50 | 51 | impl Add for Instant { 52 | type Output = Instant; 53 | 54 | fn add(self, rhs: Duration) -> Self::Output { 55 | self.checked_add(rhs) 56 | .expect("overflow when adding duration to instant") 57 | } 58 | } 59 | 60 | impl AddAssign for Instant { 61 | fn add_assign(&mut self, rhs: Duration) { 62 | *self = *self + rhs; 63 | } 64 | } 65 | 66 | impl Sub for Instant { 67 | type Output = Instant; 68 | 69 | fn sub(self, rhs: Duration) -> Self::Output { 70 | self.checked_sub(rhs) 71 | .expect("overflow when substracting duration to instant") 72 | } 73 | } 74 | 75 | impl SubAssign for Instant { 76 | fn sub_assign(&mut self, rhs: Duration) { 77 | *self = *self - rhs; 78 | } 79 | } 80 | 81 | impl Sub for Instant { 82 | type Output = Duration; 83 | 84 | fn sub(self, rhs: Self) -> Self::Output { 85 | self.duration_since(rhs) 86 | } 87 | } 88 | 89 | impl fmt::Debug for Instant { 90 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 91 | let (secs, usecs) = self.to_su(); 92 | write!(f, "{secs}.{usecs:06}") 93 | } 94 | } 95 | 96 | impl super::InstantExt for Instant { 97 | fn to_su(&self) -> (u64, u64) { 98 | ((self.0 / 1_000_000) as u64, (self.0 % 1_000_000) as u64) 99 | } 100 | 101 | fn from_su(secs: u64, micros: u64) -> Self { 102 | Instant(secs as u128 * 1_000_000 + micros as u128) 103 | } 104 | 105 | unsafe fn from_raw(raw: u64) -> Self { 106 | let micros = config::TIME_FREQ_M.numer() * raw as u128 / config::TIME_FREQ_M.denom(); 107 | Instant(micros) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /mizu/lib/ksc-core/src/scn.rs: -------------------------------------------------------------------------------- 1 | use enum_primitive_derive::Primitive; 2 | pub use Scn::*; 3 | 4 | #[allow(non_camel_case_types)] 5 | #[allow(clippy::upper_case_acronyms)] 6 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Primitive)] 7 | #[repr(u16)] 8 | pub enum Scn { 9 | #[cfg(feature = "test")] 10 | __TEST0 = 0, 11 | #[cfg(feature = "test")] 12 | __TEST1 = 1, 13 | #[cfg(feature = "test")] 14 | __TEST2 = 2, 15 | 16 | GETCWD = 17, 17 | DUP = 23, 18 | DUP3 = 24, 19 | FCNTL = 25, 20 | IOCTL = 29, 21 | MKDIRAT = 34, 22 | UNLINKAT = 35, 23 | UMOUNT2 = 39, 24 | MOUNT = 40, 25 | STATFS = 43, 26 | TRUNCATE = 45, 27 | FTRUNCATE = 46, 28 | FACCESSAT = 48, 29 | CHDIR = 49, 30 | FCHMOD = 52, 31 | FCHMODAT = 53, 32 | FCHOWN = 55, 33 | OPENAT = 56, 34 | CLOSE = 57, 35 | PIPE2 = 59, 36 | GETDENTS64 = 61, 37 | LSEEK = 62, 38 | READ = 63, 39 | WRITE = 64, 40 | READV = 65, 41 | WRITEV = 66, 42 | PREAD64 = 67, 43 | PWRITE64 = 68, 44 | PREADV64 = 69, 45 | PWRITEV64 = 70, 46 | SENDFILE = 71, 47 | PSELECT6 = 72, 48 | PPOLL = 73, 49 | READLINKAT = 78, 50 | NEWFSTATAT = 79, 51 | FSTAT = 80, 52 | SYNC = 81, 53 | FSYNC = 82, 54 | UTIMENSAT = 88, 55 | EXIT = 93, 56 | EXIT_GROUP = 94, 57 | SET_TID_ADDRESS = 96, 58 | FUTEX = 98, 59 | SET_ROBUST_LIST = 99, 60 | GET_ROBUST_LIST = 100, 61 | NANOSLEEP = 101, 62 | SETITIMER = 103, 63 | CLOCK_GETTIME = 113, 64 | CLOCK_GETRES = 114, 65 | CLOCK_NANOSLEEP = 115, 66 | SYSLOG = 116, 67 | SCHED_SETSCHEDULER = 119, 68 | SCHED_GETSCHEDULER = 120, 69 | SCHED_GETPARAM = 121, 70 | SCHED_SETAFFINITY = 122, 71 | SCHED_GETAFFINITY = 123, 72 | SCHED_YIELD = 124, 73 | KILL = 129, 74 | TKILL = 130, 75 | TGKILL = 131, 76 | SIGALTSTACK = 132, 77 | RT_SIGSUSPEND = 133, 78 | RT_SIGACTION = 134, 79 | RT_SIGPROCMASK = 135, 80 | RT_SIGPENDING = 136, 81 | RT_SIGTIMEDWAIT = 137, 82 | RT_SIGQUEUEINFO = 138, 83 | RT_SIGRETURN = 139, 84 | TIMES = 153, 85 | SETPGID = 154, 86 | GETPGID = 155, 87 | SETSID = 157, 88 | UNAME = 160, 89 | GETRUSAGE = 165, 90 | UMASK = 166, 91 | GETTIMEOFDAY = 169, 92 | GETPID = 172, 93 | GETPPID = 173, 94 | GETUID = 174, 95 | GETEUID = 175, 96 | GETGID = 176, 97 | GETEGID = 177, 98 | GETTID = 178, 99 | SYSINFO = 179, 100 | SHMGET = 194, 101 | SHMCTL = 195, 102 | SHMAT = 196, 103 | SHMDT = 197, 104 | SOCKET = 198, 105 | SOCKETPAIR = 199, 106 | BIND = 200, 107 | LISTEN = 201, 108 | ACCEPT = 202, 109 | CONNECT = 203, 110 | GETSOCKNAME = 204, 111 | GETPEERNAME = 205, 112 | SENDTO = 206, 113 | RECVFROM = 207, 114 | SETSOCKOPT = 208, 115 | GETSOCKOPT = 209, 116 | SHUTDOWN = 210, 117 | BRK = 214, 118 | MUNMAP = 215, 119 | CLONE = 220, 120 | EXECVE = 221, 121 | MMAP = 222, 122 | MPROTECT = 226, 123 | MSYNC = 227, 124 | MADVISE = 233, 125 | WAIT4 = 260, 126 | PRLIMIT64 = 261, 127 | RENAMEAT2 = 276, 128 | GETRANDOM = 278, 129 | MEMBARRIER = 283, 130 | COPY_FILE_RANGE = 285, 131 | } 132 | -------------------------------------------------------------------------------- /mizu/lib/ksc-macros/src/lifetime.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | 3 | use proc_macro2::{Span, TokenStream}; 4 | use syn::{ 5 | parse_quote_spanned, token, 6 | visit_mut::{self, VisitMut}, 7 | Expr, GenericArgument, Lifetime, Receiver, ReturnType, Token, Type, TypeBareFn, TypeImplTrait, 8 | TypeParen, TypePtr, TypeReference, 9 | }; 10 | 11 | pub struct CollectLifetimes; 12 | 13 | impl CollectLifetimes { 14 | fn visit_opt_lifetime(&mut self, reference: Token![&], lifetime: &mut Option) { 15 | match lifetime { 16 | None => *lifetime = Some(self.next_lifetime(reference.span)), 17 | Some(lifetime) => self.visit_lifetime(lifetime), 18 | } 19 | } 20 | 21 | fn visit_lifetime(&mut self, lifetime: &mut Lifetime) { 22 | *lifetime = self.next_lifetime(lifetime.span()); 23 | } 24 | 25 | fn next_lifetime(&mut self, span: Span) -> Lifetime { 26 | Lifetime::new("'async_trait", span) 27 | } 28 | } 29 | 30 | impl VisitMut for CollectLifetimes { 31 | fn visit_receiver_mut(&mut self, arg: &mut Receiver) { 32 | if let Some((reference, lifetime)) = &mut arg.reference { 33 | self.visit_opt_lifetime(*reference, lifetime); 34 | } else { 35 | visit_mut::visit_type_mut(self, &mut arg.ty); 36 | } 37 | } 38 | 39 | fn visit_type_reference_mut(&mut self, ty: &mut TypeReference) { 40 | self.visit_opt_lifetime(ty.and_token, &mut ty.lifetime); 41 | visit_mut::visit_type_reference_mut(self, ty); 42 | } 43 | 44 | fn visit_generic_argument_mut(&mut self, gen: &mut GenericArgument) { 45 | if let GenericArgument::Lifetime(lifetime) = gen { 46 | self.visit_lifetime(lifetime); 47 | } 48 | visit_mut::visit_generic_argument_mut(self, gen); 49 | } 50 | } 51 | 52 | pub struct AddLifetimeToImplTrait; 53 | 54 | impl VisitMut for AddLifetimeToImplTrait { 55 | fn visit_type_impl_trait_mut(&mut self, ty: &mut TypeImplTrait) { 56 | let span = ty.impl_token.span; 57 | let lifetime = parse_quote_spanned!(span=> 'async_trait); 58 | ty.bounds.insert(0, lifetime); 59 | if let Some(punct) = ty.bounds.pairs_mut().next().unwrap().punct_mut() { 60 | punct.span = span; 61 | } 62 | visit_mut::visit_type_impl_trait_mut(self, ty); 63 | } 64 | 65 | fn visit_type_reference_mut(&mut self, ty: &mut TypeReference) { 66 | parenthesize_impl_trait(&mut ty.elem, ty.and_token.span); 67 | visit_mut::visit_type_reference_mut(self, ty); 68 | } 69 | 70 | fn visit_type_ptr_mut(&mut self, ty: &mut TypePtr) { 71 | parenthesize_impl_trait(&mut ty.elem, ty.star_token.span); 72 | visit_mut::visit_type_ptr_mut(self, ty); 73 | } 74 | 75 | fn visit_type_bare_fn_mut(&mut self, ty: &mut TypeBareFn) { 76 | if let ReturnType::Type(arrow, return_type) = &mut ty.output { 77 | parenthesize_impl_trait(return_type, arrow.spans[0]); 78 | } 79 | visit_mut::visit_type_bare_fn_mut(self, ty); 80 | } 81 | 82 | fn visit_expr_mut(&mut self, _e: &mut Expr) { 83 | // Do not recurse into impl Traits inside of an array length expression. 84 | // 85 | // fn outer(arg: [u8; { fn inner(_: impl Trait) {}; 0 }]); 86 | } 87 | } 88 | 89 | fn parenthesize_impl_trait(elem: &mut Type, paren_span: Span) { 90 | if let Type::ImplTrait(_) = *elem { 91 | let placeholder = Type::Verbatim(TokenStream::new()); 92 | *elem = Type::Paren(TypeParen { 93 | paren_token: token::Paren(paren_span), 94 | elem: Box::new(mem::replace(elem, placeholder)), 95 | }); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /mizu/kernel/src/fs/serial.rs: -------------------------------------------------------------------------------- 1 | use alloc::{boxed::Box, sync::Arc}; 2 | use core::future::ready; 3 | 4 | use async_trait::async_trait; 5 | use futures_util::{stream, FutureExt, StreamExt}; 6 | use ksc::{ 7 | Boxed, 8 | Error::{self, EBADF, ENOSYS, ENOTDIR}, 9 | }; 10 | use umifs::{ 11 | path::Path, 12 | traits::{Entry, Io}, 13 | types::{FileType, Metadata, OpenOptions, Permissions}, 14 | }; 15 | use umio::{Event, IoPoll, IoSlice, IoSliceMut, SeekFrom}; 16 | 17 | pub struct Serial { 18 | read: bool, 19 | write: bool, 20 | } 21 | 22 | impl Serial { 23 | pub fn new() -> Self { 24 | Serial { 25 | read: true, 26 | write: true, 27 | } 28 | } 29 | } 30 | 31 | impl Default for Serial { 32 | fn default() -> Self { 33 | Self::new() 34 | } 35 | } 36 | 37 | #[async_trait] 38 | impl Io for Serial { 39 | async fn read(&self, buffer: &mut [IoSliceMut]) -> Result { 40 | if !self.read { 41 | return Err(EBADF); 42 | } 43 | Ok(stream::iter(buffer.iter_mut()) 44 | .fold(0, |acc, buf| async move { 45 | stream::iter(buf.iter_mut()) 46 | .zip(crate::dev::Stdin::new()) 47 | .for_each(|(dst, b)| async move { *dst = b }) 48 | .await; 49 | acc + buf.len() 50 | }) 51 | .await) 52 | } 53 | 54 | async fn write(&self, buffer: &mut [IoSlice]) -> Result { 55 | if !self.write { 56 | return Err(EBADF); 57 | } 58 | Ok(ksync::critical(|| { 59 | let mut stdout = crate::dev::Stdout::new(); 60 | buffer.iter().fold(0, |acc, buf| { 61 | stdout.write_bytes(buf); 62 | acc + buf.len() 63 | }) 64 | })) 65 | } 66 | 67 | async fn seek(&self, _: SeekFrom) -> Result { 68 | Err(ENOSYS) 69 | } 70 | 71 | async fn read_at(&self, _: usize, _: &mut [IoSliceMut]) -> Result { 72 | Err(ENOSYS) 73 | } 74 | 75 | async fn write_at(&self, _: usize, _: &mut [IoSlice]) -> Result { 76 | Err(ENOSYS) 77 | } 78 | 79 | async fn flush(&self) -> Result<(), Error> { 80 | Ok(()) 81 | } 82 | } 83 | 84 | #[async_trait] 85 | impl Entry for Serial { 86 | async fn open( 87 | self: Arc, 88 | path: &Path, 89 | options: OpenOptions, 90 | _perm: Permissions, 91 | ) -> Result<(Arc, bool), Error> { 92 | if !path.as_str().is_empty() || options.contains(OpenOptions::DIRECTORY) { 93 | return Err(ENOTDIR); 94 | } 95 | let (read, write) = match options.intersection(OpenOptions::ACCMODE) { 96 | OpenOptions::RDWR => (true, true), 97 | OpenOptions::WRONLY => (false, true), 98 | OpenOptions::RDONLY => (true, false), 99 | _ => unreachable!(), 100 | }; 101 | Ok((Arc::new(Serial { read, write }), false)) 102 | } 103 | 104 | async fn metadata(&self) -> Metadata { 105 | Metadata { 106 | ty: FileType::FILE | FileType::REG, 107 | len: 0, 108 | offset: 0, 109 | block_size: 1, 110 | block_count: isize::MAX as usize, 111 | perm: Permissions::all_same(self.read, self.write, false), 112 | times: Default::default(), 113 | } 114 | } 115 | } 116 | 117 | impl IoPoll for Serial { 118 | fn event<'s: 'r, 'r>(&'s self, expected: Event) -> Boxed<'r, Option> { 119 | if expected != Event::READABLE { 120 | return Box::pin(ready(None)); 121 | } 122 | Box::pin(crate::dev::Stdin::event().map(Some)) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /mizu/lib/umio/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(test), no_std)] 2 | #![feature(async_fn_in_trait)] 3 | #![allow(incomplete_features)] 4 | 5 | mod event; 6 | mod io; 7 | 8 | use alloc::sync::Arc; 9 | use core::{any::Any, mem, slice}; 10 | 11 | use arsc_rs::Arsc; 12 | 13 | pub use self::{event::*, io::*}; 14 | 15 | extern crate alloc; 16 | 17 | #[derive(Copy, PartialEq, Eq, Clone, Debug)] 18 | pub enum SeekFrom { 19 | /// Sets the offset to the provided number of bytes. 20 | Start(usize), 21 | 22 | /// Sets the offset to the size of this object plus the specified number of 23 | /// bytes. 24 | /// 25 | /// It is possible to seek beyond the end of an object, but it's an error to 26 | /// seek before byte 0. 27 | End(isize), 28 | 29 | /// Sets the offset to the current position plus the specified number of 30 | /// bytes. 31 | /// 32 | /// It is possible to seek beyond the end of an object, but it's an error to 33 | /// seek before byte 0. 34 | Current(isize), 35 | } 36 | 37 | pub type IoSlice<'a> = &'a [u8]; 38 | 39 | pub type IoSliceMut<'a> = &'a mut [u8]; 40 | 41 | #[allow(clippy::len_without_is_empty)] 42 | pub trait IoSliceExt { 43 | fn len(&self) -> usize; 44 | 45 | fn advance(&mut self, n: usize); 46 | } 47 | 48 | impl IoSliceExt for IoSlice<'_> { 49 | fn len(&self) -> usize { 50 | (**self).len() 51 | } 52 | 53 | fn advance(&mut self, n: usize) { 54 | if self.len() < n { 55 | panic!("advancing IoSlice beyond its length"); 56 | } 57 | 58 | *self = &self[n..]; 59 | } 60 | } 61 | 62 | impl IoSliceExt for IoSliceMut<'_> { 63 | fn len(&self) -> usize { 64 | (**self).len() 65 | } 66 | 67 | fn advance(&mut self, n: usize) { 68 | if self.len() < n { 69 | panic!("advancing IoSlice beyond its length"); 70 | } 71 | 72 | *self = unsafe { slice::from_raw_parts_mut(self.as_mut_ptr().add(n), self.len() - n) }; 73 | } 74 | } 75 | 76 | pub fn ioslice_len(bufs: &&mut [impl IoSliceExt]) -> usize { 77 | bufs.iter().fold(0, |sum, buf| sum + buf.len()) 78 | } 79 | 80 | pub fn ioslice_is_empty(bufs: &&mut [impl IoSliceExt]) -> bool { 81 | bufs.iter().all(|b| b.len() == 0) 82 | } 83 | 84 | #[track_caller] 85 | pub fn advance_slices(bufs: &mut &mut [impl IoSliceExt], n: usize) { 86 | // Number of buffers to remove. 87 | let mut remove = 0; 88 | // Total length of all the to be removed buffers. 89 | let mut accumulated_len = 0; 90 | for buf in bufs.iter() { 91 | if accumulated_len + buf.len() > n { 92 | break; 93 | } else { 94 | accumulated_len += buf.len(); 95 | remove += 1; 96 | } 97 | } 98 | 99 | *bufs = &mut mem::take(bufs)[remove..]; 100 | if bufs.is_empty() { 101 | assert!( 102 | n == accumulated_len, 103 | "advancing io slices beyond their length, {n} == {accumulated_len}" 104 | ); 105 | } else { 106 | bufs[0].advance(n - accumulated_len) 107 | } 108 | } 109 | 110 | pub trait IntoAny: Any + Send + Sync { 111 | fn into_any(self: Arc) -> Arc; 112 | 113 | fn into_any_arsc(self: Arsc) -> Arsc; 114 | } 115 | 116 | impl IntoAny for T { 117 | fn into_any(self: Arc) -> Arc { 118 | self as _ 119 | } 120 | 121 | fn into_any_arsc(self: Arsc) -> Arsc { 122 | self as _ 123 | } 124 | } 125 | 126 | pub trait IntoAnyExt: IntoAny { 127 | fn downcast(self: Arc) -> Option> { 128 | self.into_any().downcast().ok() 129 | } 130 | 131 | fn downcast_arsc(self: Arsc) -> Option> { 132 | self.into_any_arsc().downcast().ok() 133 | } 134 | } 135 | 136 | impl IntoAnyExt for T {} 137 | -------------------------------------------------------------------------------- /mizu/lib/devices/src/net/socket.rs: -------------------------------------------------------------------------------- 1 | pub mod dns; 2 | pub mod tcp; 3 | pub mod udp; 4 | 5 | use futures_util::Future; 6 | use ksc::Error::{self, EOPNOTSUPP}; 7 | use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; 8 | 9 | pub const BUFFER_CAP: usize = 212992; 10 | const META_CAP: usize = 8; 11 | 12 | #[derive(Debug)] 13 | pub enum Socket { 14 | Tcp(tcp::Socket), 15 | Udp(udp::Socket), 16 | } 17 | 18 | impl Socket { 19 | pub async fn send( 20 | &self, 21 | buf: &[u8], 22 | remote_endpoint: Option, 23 | ) -> Result { 24 | match self { 25 | Socket::Tcp(socket) => socket.send(buf).await, 26 | Socket::Udp(socket) => socket.send(buf, remote_endpoint).await, 27 | } 28 | } 29 | 30 | pub async fn receive( 31 | &self, 32 | buf: &mut [u8], 33 | remote_endpoint: Option<&mut IpEndpoint>, 34 | ) -> Result { 35 | match self { 36 | Socket::Tcp(socket) => socket.receive(buf).await, 37 | Socket::Udp(socket) => { 38 | let (len, remote) = socket.receive(buf).await?; 39 | if let Some(remote_endpoint) = remote_endpoint { 40 | *remote_endpoint = remote; 41 | } 42 | Ok(len) 43 | } 44 | } 45 | } 46 | 47 | pub async fn wait_for_send(&self) { 48 | match self { 49 | Socket::Tcp(socket) => socket.wait_for_send().await, 50 | Socket::Udp(socket) => socket.wait_for_send().await, 51 | } 52 | } 53 | 54 | pub async fn wait_for_recv(&self) { 55 | match self { 56 | Socket::Tcp(socket) => socket.wait_for_recv().await, 57 | Socket::Udp(socket) => socket.wait_for_recv().await, 58 | } 59 | } 60 | 61 | pub async fn connect(&self, endpoint: IpEndpoint) -> Result<(), Error> { 62 | match self { 63 | Socket::Tcp(socket) => socket.connect(endpoint).await, 64 | Socket::Udp(socket) => socket.connect(endpoint), 65 | } 66 | } 67 | 68 | pub fn bind(&self, endpoint: IpListenEndpoint) -> Result<(), Error> { 69 | match self { 70 | Socket::Tcp(socket) => socket.bind(endpoint), 71 | Socket::Udp(socket) => socket.bind(endpoint), 72 | } 73 | } 74 | 75 | pub fn listen(&self, backlog: usize) -> Result + 'static, Error> { 76 | match self { 77 | Socket::Tcp(socket) => socket.listen(backlog), 78 | Socket::Udp(_) => Err(EOPNOTSUPP), 79 | } 80 | } 81 | 82 | pub async fn accept(&self) -> Result { 83 | match self { 84 | Socket::Tcp(socket) => socket.accept().await.map(Socket::Tcp), 85 | Socket::Udp(_) => Err(EOPNOTSUPP), 86 | } 87 | } 88 | 89 | pub fn listen_endpoint(&self) -> Option { 90 | match self { 91 | Socket::Tcp(socket) => socket.listen_endpoint(), 92 | Socket::Udp(socket) => Some(socket.listen_endpoint()), 93 | } 94 | } 95 | 96 | pub fn remote_endpoint(&self) -> Option { 97 | match self { 98 | Socket::Tcp(socket) => socket.remote_endpoint(), 99 | Self::Udp(socket) => socket.remote_endpoint(), 100 | } 101 | } 102 | 103 | pub async fn flush(&self) { 104 | match self { 105 | Socket::Tcp(socket) => socket.flush().await, 106 | Socket::Udp(socket) => socket.flush().await, 107 | } 108 | } 109 | 110 | pub fn is_closed(&self) -> bool { 111 | match self { 112 | Socket::Tcp(socket) => !socket.is_open(), 113 | Socket::Udp(_) => false, 114 | } 115 | } 116 | 117 | pub async fn close(&self) { 118 | match self { 119 | Socket::Tcp(socket) => socket.close().await, 120 | Socket::Udp(socket) => socket.flush().await, 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /mizu/lib/devices/src/block.rs: -------------------------------------------------------------------------------- 1 | use alloc::boxed::Box; 2 | 3 | use async_trait::async_trait; 4 | use ksc::Error; 5 | use umio::Io; 6 | 7 | use crate::intr::Completion; 8 | 9 | #[async_trait] 10 | pub trait Block: Io { 11 | fn block_shift(&self) -> u32; 12 | 13 | #[inline] 14 | fn block_size(&self) -> usize { 15 | 1 << self.block_shift() 16 | } 17 | 18 | fn capacity_blocks(&self) -> usize; 19 | 20 | fn ack_interrupt(&self, completion: &Completion) -> bool; 21 | 22 | async fn read(&self, block: usize, buf: &mut [u8]) -> Result; 23 | 24 | async fn write(&self, block: usize, buf: &[u8]) -> Result; 25 | } 26 | 27 | #[macro_export] 28 | macro_rules! impl_io_for_block { 29 | ($type:ident) => { 30 | #[async_trait::async_trait] 31 | impl umio::Io for $type { 32 | async fn seek(&self, whence: umio::SeekFrom) -> Result { 33 | match whence { 34 | umio::SeekFrom::End(0) => Ok(self.capacity_blocks() << self.block_shift()), 35 | umio::SeekFrom::Start(0) | umio::SeekFrom::Current(0) => Ok(0), 36 | _ => Err(ksc::ENOSYS), 37 | } 38 | } 39 | 40 | async fn stream_len(&self) -> Result { 41 | Ok(self.capacity_blocks() << self.block_shift()) 42 | } 43 | 44 | async fn read_at( 45 | &self, 46 | offset: usize, 47 | mut buffer: &mut [umio::IoSliceMut], 48 | ) -> Result { 49 | let block_shift = self.block_shift(); 50 | if offset & ((1 << block_shift) - 1) != 0 { 51 | return Ok(0); 52 | } 53 | let cap = self.capacity_blocks(); 54 | let mut block = offset >> block_shift; 55 | let mut read_len = 0; 56 | loop { 57 | if buffer.is_empty() || block >= cap { 58 | break Ok(read_len); 59 | } 60 | let buf = &mut buffer[0]; 61 | let len = buf.len().min((cap - block) << block_shift); 62 | let actual_len = Block::read(self, block, &mut buf[..len]).await?; 63 | read_len += actual_len; 64 | if actual_len < len || len < buf.len() { 65 | break Ok(read_len); 66 | } 67 | umio::advance_slices(&mut buffer, actual_len); 68 | block += actual_len >> block_shift; 69 | } 70 | } 71 | 72 | async fn write_at( 73 | &self, 74 | offset: usize, 75 | mut buffer: &mut [umio::IoSlice], 76 | ) -> Result { 77 | let block_shift = self.block_shift(); 78 | if offset & ((1 << block_shift) - 1) != 0 { 79 | return Ok(0); 80 | } 81 | let cap = self.capacity_blocks(); 82 | let mut block = offset >> block_shift; 83 | let mut written_len = 0; 84 | loop { 85 | if buffer.is_empty() || block >= cap { 86 | break Ok(written_len); 87 | } 88 | let buf = buffer[0]; 89 | let len = buf.len().min((cap - block) << block_shift); 90 | let actual_len = Block::write(self, block, &buf[..len]).await?; 91 | written_len += actual_len; 92 | if actual_len < len || len < buf.len() { 93 | break Ok(written_len); 94 | } 95 | umio::advance_slices(&mut buffer, actual_len); 96 | block += actual_len >> block_shift; 97 | } 98 | } 99 | 100 | async fn flush(&self) -> Result<(), Error> { 101 | Ok(()) 102 | } 103 | } 104 | }; 105 | } 106 | -------------------------------------------------------------------------------- /mizu/lib/umifs/src/misc.rs: -------------------------------------------------------------------------------- 1 | use alloc::{boxed::Box, sync::Arc}; 2 | 3 | use async_trait::async_trait; 4 | use ksc_core::Error::{self, EEXIST, ENOTDIR, EPERM}; 5 | use umio::{ioslice_len, Io, IoPoll, IoSlice, IoSliceMut, SeekFrom}; 6 | 7 | use crate::{ 8 | path::Path, 9 | traits::Entry, 10 | types::{FileType, Metadata, OpenOptions, Permissions}, 11 | }; 12 | 13 | pub struct Null; 14 | 15 | #[async_trait] 16 | impl Io for Null { 17 | async fn seek(&self, _: SeekFrom) -> Result { 18 | Ok(0) 19 | } 20 | 21 | async fn stream_len(&self) -> Result { 22 | Ok(isize::MAX as usize + 1) 23 | } 24 | 25 | async fn read_at(&self, _: usize, _: &mut [IoSliceMut]) -> Result { 26 | Ok(0) 27 | } 28 | 29 | async fn write_at(&self, _: usize, buffer: &mut [IoSlice]) -> Result { 30 | Ok(ioslice_len(&buffer)) 31 | } 32 | 33 | async fn flush(&self) -> Result<(), Error> { 34 | Ok(()) 35 | } 36 | } 37 | 38 | #[async_trait] 39 | impl Entry for Null { 40 | async fn open( 41 | self: Arc, 42 | path: &Path, 43 | options: OpenOptions, 44 | perm: Permissions, 45 | ) -> Result<(Arc, bool), Error> { 46 | open_file( 47 | self, 48 | path, 49 | options, 50 | perm, 51 | Permissions::all_same(true, true, false), 52 | ) 53 | .await 54 | } 55 | 56 | async fn metadata(&self) -> Metadata { 57 | Metadata { 58 | ty: FileType::CHR, 59 | len: 0, 60 | offset: 0, 61 | perm: Permissions::all_same(true, true, false), 62 | block_size: 0, 63 | block_count: 0, 64 | times: Default::default(), 65 | } 66 | } 67 | } 68 | 69 | impl IoPoll for Null {} 70 | 71 | pub struct Zero; 72 | 73 | #[async_trait] 74 | impl Io for Zero { 75 | async fn seek(&self, _: SeekFrom) -> Result { 76 | Ok(0) 77 | } 78 | 79 | async fn stream_len(&self) -> Result { 80 | Ok(isize::MAX as usize + 1) 81 | } 82 | 83 | async fn read_at(&self, _: usize, buffer: &mut [IoSliceMut]) -> Result { 84 | Ok(buffer.iter_mut().fold(0, |len, buf| { 85 | buf.fill(0); 86 | len + buf.len() 87 | })) 88 | } 89 | 90 | async fn write_at(&self, _: usize, buffer: &mut [IoSlice]) -> Result { 91 | Ok(ioslice_len(&buffer)) 92 | } 93 | 94 | async fn flush(&self) -> Result<(), Error> { 95 | Ok(()) 96 | } 97 | } 98 | 99 | #[async_trait] 100 | impl Entry for Zero { 101 | async fn open( 102 | self: Arc, 103 | path: &Path, 104 | options: OpenOptions, 105 | perm: Permissions, 106 | ) -> Result<(Arc, bool), Error> { 107 | open_file( 108 | self, 109 | path, 110 | options, 111 | perm, 112 | Permissions::all_same(true, true, false), 113 | ) 114 | .await 115 | } 116 | 117 | async fn metadata(&self) -> Metadata { 118 | Metadata { 119 | ty: FileType::CHR, 120 | len: 0, 121 | offset: 0, 122 | perm: Permissions::all_same(true, true, false), 123 | block_size: 0, 124 | block_count: 0, 125 | times: Default::default(), 126 | } 127 | } 128 | } 129 | 130 | impl IoPoll for Zero {} 131 | 132 | pub async fn open_file( 133 | this: Arc, 134 | path: &Path, 135 | options: OpenOptions, 136 | perm: Permissions, 137 | self_perm: Permissions, 138 | ) -> Result<(Arc, bool), Error> { 139 | if !path.as_str().is_empty() || options.contains(OpenOptions::DIRECTORY) { 140 | return Err(ENOTDIR); 141 | } 142 | if options.contains(OpenOptions::CREAT | OpenOptions::EXCL) { 143 | return Err(EEXIST); 144 | } 145 | if !self_perm.contains(perm) { 146 | return Err(EPERM); 147 | } 148 | Ok((this, false)) 149 | } 150 | -------------------------------------------------------------------------------- /mizu/lib/co-trap/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(target_arch = "riscv64", no_std)] 2 | #![feature(const_trait_impl)] 3 | #![feature(macro_metavar_expr)] 4 | 5 | mod tf; 6 | 7 | use core::{ 8 | marker::PhantomData, 9 | sync::atomic::{compiler_fence, Ordering::SeqCst}, 10 | }; 11 | 12 | use enum_primitive_derive::Primitive; 13 | use num_traits::FromPrimitive; 14 | use riscv::register::{ 15 | scause::Scause, 16 | stvec::{self, Stvec, TrapMode}, 17 | }; 18 | 19 | pub use self::tf::*; 20 | 21 | #[cfg(target_arch = "riscv64")] 22 | core::arch::global_asm!(include_str!("imp.S")); 23 | 24 | pub fn user_entry() -> usize { 25 | extern "C" { 26 | fn _user_entry(); 27 | } 28 | _user_entry as _ 29 | } 30 | 31 | /// A temporary write to `stvec` register. 32 | pub struct StvecTemp { 33 | data: Stvec, 34 | _non_send: PhantomData<*mut ()>, 35 | } 36 | 37 | impl StvecTemp { 38 | /// Creates a new [`StvecGuard`]. 39 | /// 40 | /// # Safety 41 | /// 42 | /// - Interrupts **MUST BE DISABLED** on the current CPU during the whole 43 | /// lifetime of this struct. 44 | /// - `entry` and `mode` must be valid. 45 | pub unsafe fn new(entry: usize, mode: TrapMode) -> Self { 46 | let old = stvec::read(); 47 | compiler_fence(SeqCst); 48 | unsafe { stvec::write(entry, mode) }; 49 | StvecTemp { 50 | data: old, 51 | _non_send: PhantomData, 52 | } 53 | } 54 | } 55 | 56 | impl Drop for StvecTemp { 57 | fn drop(&mut self) { 58 | // SAFETY: The caller is aware of that safety notice of `Self::new`. 59 | unsafe { 60 | stvec::write( 61 | self.data.address(), 62 | self.data.trap_mode().unwrap_or(TrapMode::Direct), 63 | ) 64 | }; 65 | } 66 | } 67 | 68 | pub fn yield_to_user(frame: &mut TrapFrame) -> (Scause, FastResult) { 69 | extern "C" { 70 | fn _return_to_user(frame: *mut TrapFrame) -> usize; 71 | } 72 | 73 | ksync_core::critical(|| unsafe { 74 | let _stvec = StvecTemp::new(user_entry(), TrapMode::Direct); 75 | let res = _return_to_user(frame); 76 | ( 77 | core::mem::transmute(frame.scause), 78 | FastResult::from_usize(res).unwrap(), 79 | ) 80 | }) 81 | } 82 | 83 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Primitive)] 84 | #[repr(usize)] 85 | pub enum FastResult { 86 | /// Finished fast-path execution without error, and directly yield to user. 87 | Yield = 0, 88 | /// The fast-path execution cannot be done, should do the ordinary path in 89 | /// the async context. 90 | Continue = 1, 91 | /// Finished fast-path execution without error, but the current task has 92 | /// some pending events (signals). 93 | Pending = 2, 94 | /// Should directly exit the current task. 95 | Break = 3, 96 | } 97 | 98 | #[doc(hidden)] 99 | #[repr(C)] 100 | pub struct FastRet { 101 | pub cx: &'static mut TrapFrame, 102 | pub res: FastResult, 103 | } 104 | 105 | /// Set the fast path function (conventional trap handler). 106 | /// 107 | /// ``` 108 | /// use co_trap::{TrapFrame, FastResult, fast_func}; 109 | /// 110 | /// fn some_fast_func(_cx: &mut TrapFrame) -> FastResult { 111 | /// FastResult::Continue 112 | /// } 113 | /// fast_func!(some_fast_func); 114 | /// ``` 115 | /// 116 | /// If a fast path is not desired, just use `co_trap::fast_func!()` instead. 117 | /// 118 | /// The interrupt **MUST BE DISABLED** during the execution of the function. 119 | #[macro_export] 120 | macro_rules! fast_func { 121 | ($func:ident) => { 122 | #[no_mangle] 123 | extern "C" fn _fast_func(cx: &'static mut $crate::TrapFrame) -> $crate::FastRet { 124 | let res = ($func)(cx); 125 | $crate::FastRet { cx, res } 126 | } 127 | }; 128 | () => { 129 | #[no_mangle] 130 | extern "C" fn _fast_func(cx: &'static mut $crate::TrapFrame) -> $crate::FastRet { 131 | $crate::FastRet { 132 | cx, 133 | res: $crate::FastResult::Continue, 134 | } 135 | } 136 | }; 137 | } 138 | -------------------------------------------------------------------------------- /mizu/lib/devices/src/net/config.rs: -------------------------------------------------------------------------------- 1 | use core::time::Duration; 2 | 3 | use heapless::Vec; 4 | use smoltcp::{ 5 | socket::dhcpv4::RetryConfig, 6 | wire::{Ipv4Address, Ipv4Cidr, Ipv6Address, Ipv6Cidr}, 7 | }; 8 | 9 | /// Static IP address configuration. 10 | #[derive(Debug, Clone, PartialEq, Eq)] 11 | pub struct StaticConfigV4 { 12 | /// IP address and subnet mask. 13 | pub address: Ipv4Cidr, 14 | /// Default gateway. 15 | pub gateway: Option, 16 | /// DNS servers. 17 | pub dns_servers: Vec, 18 | } 19 | 20 | /// Static IPv6 address configuration 21 | #[derive(Debug, Clone, PartialEq, Eq)] 22 | pub struct StaticConfigV6 { 23 | /// IP address and subnet mask. 24 | pub address: Ipv6Cidr, 25 | /// Default gateway. 26 | pub gateway: Option, 27 | /// DNS servers. 28 | pub dns_servers: Vec, 29 | } 30 | 31 | /// DHCP configuration. 32 | #[derive(Debug, Clone, PartialEq, Eq)] 33 | pub struct DhcpV4Config { 34 | /// Maximum lease duration. 35 | /// 36 | /// If not set, the lease duration specified by the server will be used. 37 | /// If set, the lease duration will be capped at this value. 38 | pub max_lease_duration: Option, 39 | /// Retry configuration. 40 | pub retry_config: RetryConfig, 41 | /// Ignore NAKs from DHCP servers. 42 | /// 43 | /// This is not compliant with the DHCP RFCs, since theoretically we must 44 | /// stop using the assigned IP when receiving a NAK. This can increase 45 | /// reliability on broken networks with buggy routers or rogue DHCP servers, 46 | /// however. 47 | pub ignore_naks: bool, 48 | /// Server port. This is almost always 67. Do not change unless you know 49 | /// what you're doing. 50 | pub server_port: u16, 51 | /// Client port. This is almost always 68. Do not change unless you know 52 | /// what you're doing. 53 | pub client_port: u16, 54 | } 55 | 56 | impl Default for DhcpV4Config { 57 | fn default() -> Self { 58 | Self { 59 | max_lease_duration: Default::default(), 60 | retry_config: Default::default(), 61 | ignore_naks: Default::default(), 62 | server_port: smoltcp::wire::DHCP_SERVER_PORT, 63 | client_port: smoltcp::wire::DHCP_CLIENT_PORT, 64 | } 65 | } 66 | } 67 | 68 | /// Network stack configuration. 69 | #[derive(Default)] 70 | pub struct Config { 71 | /// IPv4 configuration 72 | pub ipv4: ConfigV4, 73 | /// IPv6 configuration 74 | pub ipv6: ConfigV6, 75 | } 76 | 77 | impl Config { 78 | /// IPv4 configuration with static addressing. 79 | pub fn ipv4_static(config: StaticConfigV4) -> Self { 80 | Self { 81 | ipv4: ConfigV4::Static(config), 82 | ipv6: ConfigV6::None, 83 | } 84 | } 85 | 86 | /// IPv6 configuration with static addressing. 87 | pub fn ipv6_static(config: StaticConfigV6) -> Self { 88 | Self { 89 | ipv4: ConfigV4::None, 90 | ipv6: ConfigV6::Static(config), 91 | } 92 | } 93 | 94 | /// IPv6 configuration with dynamic addressing. 95 | /// 96 | /// # Example 97 | /// ```rust 98 | /// let _cfg = Config::dhcpv4(Default::default()); 99 | /// ``` 100 | pub fn dhcpv4(config: DhcpV4Config) -> Self { 101 | Self { 102 | ipv4: ConfigV4::Dhcp(config), 103 | ipv6: ConfigV6::None, 104 | } 105 | } 106 | } 107 | 108 | /// Network stack IPv4 configuration. 109 | pub enum ConfigV4 { 110 | /// Use a static IPv4 address configuration. 111 | Static(StaticConfigV4), 112 | /// Use DHCP to obtain an IP address configuration. 113 | Dhcp(DhcpV4Config), 114 | /// Do not configure IPv6. 115 | None, 116 | } 117 | 118 | impl Default for ConfigV4 { 119 | fn default() -> Self { 120 | ConfigV4::Dhcp(Default::default()) 121 | } 122 | } 123 | 124 | /// Network stack IPv6 configuration. 125 | #[derive(Default)] 126 | pub enum ConfigV6 { 127 | /// Use a static IPv6 address configuration. 128 | Static(StaticConfigV6), 129 | /// Do not configure IPv6. 130 | #[default] 131 | None, 132 | } 133 | -------------------------------------------------------------------------------- /mizu/kernel/src/trap.S: -------------------------------------------------------------------------------- 1 | .extern ktrap_handler 2 | 3 | .global ktrap_entry 4 | .type ktrap_entry, @function 5 | .align 4 6 | ktrap_entry: 7 | addi sp, sp, -20*8 8 | 9 | sd ra, 1*8(sp) 10 | sd sp, 2*8(sp) // Should be added 16 in the dump 11 | sd gp, 3*8(sp) 12 | sd tp, 4*8(sp) 13 | sd a0, 5*8(sp) 14 | sd a1, 6*8(sp) 15 | sd a2, 7*8(sp) 16 | sd a3, 8*8(sp) 17 | sd a4, 9*8(sp) 18 | sd a5, 10*8(sp) 19 | sd a6, 11*8(sp) 20 | sd a7, 12*8(sp) 21 | sd t0, 13*8(sp) 22 | sd t1, 14*8(sp) 23 | sd t2, 15*8(sp) 24 | sd t3, 16*8(sp) 25 | sd t4, 17*8(sp) 26 | sd t5, 18*8(sp) 27 | sd t6, 19*8(sp) 28 | 29 | mv a0, sp 30 | addi a0, a0, 8 31 | call ktrap_handler 32 | 33 | ld ra, 1*8(sp) 34 | // Ignore sp 35 | ld gp, 3*8(sp) 36 | ld tp, 4*8(sp) 37 | ld a0, 5*8(sp) 38 | ld a1, 6*8(sp) 39 | ld a2, 7*8(sp) 40 | ld a3, 8*8(sp) 41 | ld a4, 9*8(sp) 42 | ld a5, 10*8(sp) 43 | ld a6, 11*8(sp) 44 | ld a7, 12*8(sp) 45 | ld t0, 13*8(sp) 46 | ld t1, 14*8(sp) 47 | ld t2, 15*8(sp) 48 | ld t3, 16*8(sp) 49 | ld t4, 17*8(sp) 50 | ld t5, 18*8(sp) 51 | ld t6, 19*8(sp) 52 | 53 | addi sp, sp, 20*8 54 | sret 55 | 56 | .attribute arch, "rv64gc" 57 | 58 | .global _save_fp 59 | .type _save_fp, @function 60 | _save_fp: 61 | li t1, 0x6000 // Dirty 62 | csrs sstatus, t1 63 | 64 | frcsr t0 65 | fsd f0, 0*8(a0) 66 | fsd f1, 1*8(a0) 67 | fsd f2, 2*8(a0) 68 | fsd f3, 3*8(a0) 69 | fsd f4, 4*8(a0) 70 | fsd f5, 5*8(a0) 71 | fsd f6, 6*8(a0) 72 | fsd f7, 7*8(a0) 73 | fsd f8, 8*8(a0) 74 | fsd f9, 9*8(a0) 75 | fsd f10, 10*8(a0) 76 | fsd f11, 11*8(a0) 77 | fsd f12, 12*8(a0) 78 | fsd f13, 13*8(a0) 79 | fsd f14, 14*8(a0) 80 | fsd f15, 15*8(a0) 81 | fsd f16, 16*8(a0) 82 | fsd f17, 17*8(a0) 83 | fsd f18, 18*8(a0) 84 | fsd f19, 19*8(a0) 85 | fsd f20, 20*8(a0) 86 | fsd f21, 21*8(a0) 87 | fsd f22, 22*8(a0) 88 | fsd f23, 23*8(a0) 89 | fsd f24, 24*8(a0) 90 | fsd f25, 25*8(a0) 91 | fsd f26, 26*8(a0) 92 | fsd f27, 27*8(a0) 93 | fsd f28, 28*8(a0) 94 | fsd f29, 29*8(a0) 95 | fsd f30, 30*8(a0) 96 | fsd f31, 31*8(a0) 97 | sw t0, 32*8(a0) 98 | 99 | csrc sstatus, t1 100 | ret 101 | 102 | .global _load_fp 103 | .type _load_fp, @function 104 | _load_fp: 105 | li t1, 0x6000 // Dirty 106 | csrs sstatus, t1 107 | 108 | lw t0, 32*8(a0) 109 | fld f0, 0*8(a0) 110 | fld f1, 1*8(a0) 111 | fld f2, 2*8(a0) 112 | fld f3, 3*8(a0) 113 | fld f4, 4*8(a0) 114 | fld f5, 5*8(a0) 115 | fld f6, 6*8(a0) 116 | fld f7, 7*8(a0) 117 | fld f8, 8*8(a0) 118 | fld f9, 9*8(a0) 119 | fld f10, 10*8(a0) 120 | fld f11, 11*8(a0) 121 | fld f12, 12*8(a0) 122 | fld f13, 13*8(a0) 123 | fld f14, 14*8(a0) 124 | fld f15, 15*8(a0) 125 | fld f16, 16*8(a0) 126 | fld f17, 17*8(a0) 127 | fld f18, 18*8(a0) 128 | fld f19, 19*8(a0) 129 | fld f20, 20*8(a0) 130 | fld f21, 21*8(a0) 131 | fld f22, 22*8(a0) 132 | fld f23, 23*8(a0) 133 | fld f24, 24*8(a0) 134 | fld f25, 25*8(a0) 135 | fld f26, 26*8(a0) 136 | fld f27, 27*8(a0) 137 | fld f28, 28*8(a0) 138 | fld f29, 29*8(a0) 139 | fld f30, 30*8(a0) 140 | fld f31, 31*8(a0) 141 | fscsr t0 142 | 143 | csrc sstatus, t1 144 | ret 145 | 146 | .global _checked_copy 147 | .type _checked_copy, @function 148 | _checked_copy: 149 | .Lcopy_loop: 150 | beqz a2, .Lcopy_ret 151 | lb t0, 0(a0) 152 | sb t0, 0(a1) 153 | addi a0, a0, 1 154 | addi a1, a1, 1 155 | addi a2, a2, -1 156 | j .Lcopy_loop 157 | .Lcopy_ret: 158 | li a0, 0 159 | ret 160 | 161 | .global _checked_zero 162 | .type _checked_zero, @function 163 | _checked_zero: 164 | mv t0, a0 165 | .Lzero_loop: 166 | beqz a2, .Lzero_ret 167 | sb t0, 0(a1) 168 | addi a0, a0, 1 169 | addi a1, a1, 1 170 | addi a2, a2, -1 171 | j .Lzero_loop 172 | .Lzero_ret: 173 | li a0, 0 174 | ret 175 | 176 | .global _checked_load_u32 177 | .type _checked_load_u32, @function 178 | _checked_load_u32: 179 | lw t0, 0(a0) 180 | fence r, rw 181 | sd t0, 0(a1) 182 | li a0, 0 183 | ret 184 | 185 | .global _checked_ua_fault 186 | .type _checked_ua_fault, @function 187 | _checked_ua_fault: 188 | ret 189 | -------------------------------------------------------------------------------- /mizu/lib/devices/src/net/driver.rs: -------------------------------------------------------------------------------- 1 | use core::task::Context; 2 | 3 | use smoltcp::phy; 4 | 5 | use crate::Token; 6 | 7 | pub trait Net: Send + Sync { 8 | fn features(&self) -> Features; 9 | 10 | fn address(&self) -> [u8; 6]; 11 | 12 | fn ack_interrupt(&self); 13 | 14 | fn is_link_up(&self) -> bool; 15 | 16 | fn queues(&mut self) -> (&mut dyn NetTx, &mut dyn NetRx); 17 | } 18 | 19 | impl Net for &'_ mut T { 20 | fn features(&self) -> Features { 21 | (**self).features() 22 | } 23 | 24 | fn address(&self) -> [u8; 6] { 25 | (**self).address() 26 | } 27 | 28 | fn ack_interrupt(&self) { 29 | (**self).ack_interrupt() 30 | } 31 | 32 | fn is_link_up(&self) -> bool { 33 | (**self).is_link_up() 34 | } 35 | 36 | fn queues(&mut self) -> (&mut dyn NetTx, &mut dyn NetRx) { 37 | (**self).queues() 38 | } 39 | } 40 | 41 | pub trait NetTx: Send { 42 | fn tx_peek(&mut self, cx: &mut Context<'_>) -> Option; 43 | fn tx_buffer(&mut self, token: Token) -> &mut [u8]; 44 | fn transmit(&mut self, token: Token, len: usize); 45 | } 46 | 47 | pub trait NetRx: Send { 48 | fn rx_peek(&mut self, cx: &mut Context<'_>) -> Option; 49 | fn rx_buffer(&mut self, token: Token) -> &mut [u8]; 50 | fn receive(&mut self, token: Token); 51 | } 52 | 53 | pub(in crate::net) trait NetExt: Net { 54 | fn with_cx<'device, 'cx>( 55 | &'device mut self, 56 | cx: Option<&'device mut Context<'cx>>, 57 | ) -> WithCx<'device, 'cx, Self> { 58 | WithCx { cx, device: self } 59 | } 60 | } 61 | impl NetExt for T {} 62 | 63 | #[derive(Debug, Clone, Copy, Default)] 64 | #[non_exhaustive] 65 | pub struct Features { 66 | pub max_unit: usize, 67 | } 68 | 69 | pub(in crate::net) struct WithCx<'device, 'cx, T: Net + ?Sized> { 70 | cx: Option<&'device mut Context<'cx>>, 71 | device: &'device mut T, 72 | } 73 | 74 | pub(in crate::net) struct TxToken<'driver> { 75 | device: &'driver mut dyn NetTx, 76 | token: Token, 77 | } 78 | 79 | pub(in crate::net) struct RxToken<'driver> { 80 | device: &'driver mut dyn NetRx, 81 | token: Token, 82 | } 83 | 84 | impl<'device, 'cx, T: Net + ?Sized> phy::Device for WithCx<'device, 'cx, T> { 85 | type RxToken<'a> = RxToken<'a> 86 | where 87 | Self: 'a; 88 | 89 | type TxToken<'a> = TxToken<'a> 90 | where 91 | Self: 'a; 92 | 93 | fn receive( 94 | &mut self, 95 | _: smoltcp::time::Instant, 96 | ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { 97 | let (tx, rx) = self.device.queues(); 98 | match ( 99 | tx.tx_peek(self.cx.as_mut().unwrap()), 100 | rx.rx_peek(self.cx.as_mut().unwrap()), 101 | ) { 102 | (Some(tx_token), Some(rx_token)) => Some(( 103 | RxToken { 104 | device: rx, 105 | token: rx_token, 106 | }, 107 | TxToken { 108 | device: tx, 109 | token: tx_token, 110 | }, 111 | )), 112 | _ => None, 113 | } 114 | } 115 | 116 | fn transmit(&mut self, _: smoltcp::time::Instant) -> Option> { 117 | let net_tx = self.device.queues().0; 118 | let token = net_tx.tx_peek(self.cx.as_mut().unwrap())?; 119 | Some(TxToken { 120 | device: net_tx, 121 | token, 122 | }) 123 | } 124 | 125 | fn capabilities(&self) -> phy::DeviceCapabilities { 126 | let Features { max_unit } = self.device.features(); 127 | 128 | let mut ret = phy::DeviceCapabilities::default(); 129 | ret.max_transmission_unit = max_unit; 130 | ret 131 | } 132 | } 133 | 134 | impl phy::TxToken for TxToken<'_> { 135 | fn consume(self, len: usize, f: F) -> R 136 | where 137 | F: FnOnce(&mut [u8]) -> R, 138 | { 139 | let buf = self.device.tx_buffer(self.token); 140 | let res = f(&mut buf[..len]); 141 | self.device.transmit(self.token, len); 142 | res 143 | } 144 | } 145 | 146 | impl phy::RxToken for RxToken<'_> { 147 | fn consume(self, f: F) -> R 148 | where 149 | F: FnOnce(&mut [u8]) -> R, 150 | { 151 | let res = f(self.device.rx_buffer(self.token)); 152 | self.device.receive(self.token); 153 | res 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /mizu/lib/umifs/src/types.rs: -------------------------------------------------------------------------------- 1 | use alloc::string::String; 2 | 3 | use bitflags::bitflags; 4 | use ktime_core::Instant; 5 | 6 | bitflags! { 7 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] 8 | pub struct OpenOptions: i32 { 9 | const ACCMODE = 0x0007; 10 | const EXEC = 1; 11 | const RDONLY = 2; 12 | const RDWR = 3; 13 | const SEARCH = 4; 14 | const WRONLY = 5; 15 | 16 | const CREAT = 0o100; 17 | const EXCL = 0o200; 18 | const NOCTTY = 0o400; 19 | const TRUNC = 0o1000; 20 | const APPEND = 0o2000; 21 | const NONBLOCK = 0o4000; 22 | const DSYNC = 0o10000; 23 | const SYNC = 0o4010000; 24 | const RSYNC = 0o4010000; 25 | const DIRECTORY = 0o200000; 26 | const NOFOLLOW = 0o400000; 27 | const CLOEXEC = 0o2000000; 28 | 29 | const ASYNC = 0o20000; 30 | const DIRECT = 0o40000; 31 | const LARGEFILE = 0o100000; 32 | const NOATIME = 0o1000000; 33 | const PATH = 0o10000000; 34 | const TMPFILE = 0o20200000; 35 | const NDELAY = Self::NONBLOCK.bits(); 36 | } 37 | 38 | 39 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] 40 | pub struct Permissions: u32 { 41 | const SELF_R = 1 << 8; 42 | const SELF_W = 1 << 7; 43 | const SELF_X = 1 << 6; 44 | const GROUP_R = 1 << 5; 45 | const GROUP_W = 1 << 4; 46 | const GROUP_X = 1 << 3; 47 | const OTHERS_R = 1 << 2; 48 | const OTHERS_W = 1 << 1; 49 | const OTHERS_X = 1 << 0; 50 | } 51 | 52 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 53 | pub struct FileType: u8 { 54 | const UNKNOWN = 0; 55 | const FIFO = 1; 56 | const CHR = 2; 57 | const DIR = 4; 58 | const BLK = 6; 59 | const REG = 8; 60 | const FILE = Self::REG.bits(); 61 | const LNK = 10; 62 | const SOCK = 12; 63 | const WHT = 14; 64 | } 65 | } 66 | 67 | impl FileType { 68 | pub const fn is(&self, ty: Self) -> bool { 69 | self.contains(ty) 70 | } 71 | } 72 | 73 | impl Permissions { 74 | pub fn all_same(readable: bool, writable: bool, executable: bool) -> Self { 75 | let mut ret = Permissions::empty(); 76 | if readable { 77 | ret |= Permissions::SELF_R | Permissions::GROUP_R | Permissions::OTHERS_R; 78 | } 79 | if writable { 80 | ret |= Permissions::SELF_W | Permissions::GROUP_W | Permissions::OTHERS_W; 81 | } 82 | if executable { 83 | ret |= Permissions::SELF_X | Permissions::GROUP_X | Permissions::OTHERS_X; 84 | } 85 | ret 86 | } 87 | 88 | pub fn me(readable: bool, writable: bool, executable: bool) -> Self { 89 | let mut ret = Permissions::empty(); 90 | if readable { 91 | ret |= Permissions::SELF_R; 92 | } 93 | if writable { 94 | ret |= Permissions::SELF_W; 95 | } 96 | if executable { 97 | ret |= Permissions::SELF_X; 98 | } 99 | ret 100 | } 101 | } 102 | 103 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 104 | pub struct Metadata { 105 | pub ty: FileType, 106 | pub len: usize, 107 | pub offset: u64, 108 | pub perm: Permissions, 109 | pub block_size: usize, 110 | pub block_count: usize, 111 | pub times: Times, 112 | } 113 | 114 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)] 115 | pub struct SetMetadata { 116 | pub len: Option, 117 | pub perm: Option, 118 | pub times: Times, 119 | } 120 | 121 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)] 122 | pub struct Times { 123 | pub last_access: Option, 124 | pub last_modified: Option, 125 | pub last_created: Option, 126 | } 127 | 128 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] 129 | pub struct DirEntry { 130 | pub name: String, 131 | pub metadata: Metadata, 132 | } 133 | 134 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] 135 | pub struct FsStat { 136 | pub ty: &'static str, 137 | pub block_size: usize, 138 | pub block_count: usize, 139 | pub block_free: usize, 140 | pub file_count: usize, 141 | } 142 | -------------------------------------------------------------------------------- /mizu/kernel/src/fs/debug.rs: -------------------------------------------------------------------------------- 1 | use alloc::{boxed::Box, sync::Arc}; 2 | use core::{ 3 | mem, 4 | sync::atomic::{AtomicBool, Ordering::SeqCst}, 5 | }; 6 | 7 | use arsc_rs::Arsc; 8 | use async_trait::async_trait; 9 | use kmem::Phys; 10 | use ksc::Error::{self, ENOENT, ENOTDIR}; 11 | use rv39_paging::PAGE_SIZE; 12 | use scoped_tls::scoped_thread_local; 13 | use umifs::{ 14 | path::Path, 15 | traits::*, 16 | types::{FileType, FsStat, Metadata, OpenOptions, Permissions}, 17 | }; 18 | use umio::{Io, IoPoll}; 19 | 20 | pub struct Coverage { 21 | data: Arc, 22 | enabled: AtomicBool, 23 | } 24 | 25 | impl Coverage { 26 | pub fn new() -> Self { 27 | Coverage { 28 | data: Arc::new(Phys::new(false)), 29 | enabled: AtomicBool::new(false), 30 | } 31 | } 32 | 33 | pub fn enable(&self, enable: bool) { 34 | self.enabled.store(enable, SeqCst) 35 | } 36 | } 37 | 38 | impl Default for Coverage { 39 | fn default() -> Self { 40 | Self::new() 41 | } 42 | } 43 | 44 | scoped_thread_local!(pub static COVERAGE: Coverage); 45 | 46 | pub async fn coverage() { 47 | extern "C" { 48 | #[link_name = "llvm.returnaddress"] 49 | fn return_address(x: i32) -> *const u8; 50 | } 51 | 52 | let Some(data) = COVERAGE 53 | .try_with(|cov| cov.enabled.load(SeqCst).then(|| cov.data.clone())) 54 | .flatten() else 55 | { 56 | return; 57 | }; 58 | 59 | const SZ: usize = mem::size_of::(); 60 | 61 | let mut buffer = [0u8; SZ]; 62 | if data.read_at(0, &mut [&mut buffer]).await != Ok(SZ) { 63 | return; 64 | } 65 | 66 | let count = usize::from_le_bytes(buffer); 67 | let return_address = unsafe { return_address(0) } as usize; 68 | let _ = data 69 | .write_at((count + 1) * SZ, &mut [&return_address.to_le_bytes()]) 70 | .await; 71 | let _ = data.write_at(0, &mut [&(count + 1).to_le_bytes()]).await; 72 | } 73 | 74 | pub struct DebugFs; 75 | 76 | #[async_trait] 77 | impl FileSystem for DebugFs { 78 | async fn root_dir(self: Arsc) -> Result, Error> { 79 | Ok(Arc::new(DebugRoot)) 80 | } 81 | 82 | async fn flush(&self) -> Result<(), Error> { 83 | Ok(()) 84 | } 85 | 86 | async fn stat(&self) -> FsStat { 87 | FsStat { 88 | ty: "debugfs", 89 | block_size: PAGE_SIZE, 90 | block_count: 0, 91 | block_free: 0, 92 | file_count: 1, 93 | } 94 | } 95 | } 96 | 97 | pub struct DebugRoot; 98 | 99 | impl ToIo for DebugRoot {} 100 | 101 | #[async_trait] 102 | impl Entry for DebugRoot { 103 | async fn open( 104 | self: Arc, 105 | path: &Path, 106 | options: OpenOptions, 107 | perm: Permissions, 108 | ) -> Result<(Arc, bool), Error> { 109 | match path.as_str() { 110 | "kcov" => { 111 | let null = Arc::new(CoverageFile); 112 | null.open(Path::new(""), options, perm).await 113 | } 114 | _ => { 115 | let dir = { 116 | let mut comp = path.components(); 117 | comp.next().ok_or(ENOENT)?.as_str() 118 | }; 119 | match dir { 120 | "kcov" => Err(ENOTDIR), 121 | _ => Err(ENOENT), 122 | } 123 | } 124 | } 125 | } 126 | 127 | async fn metadata(&self) -> Metadata { 128 | todo!() 129 | } 130 | } 131 | 132 | impl IoPoll for DebugRoot {} 133 | 134 | pub struct CoverageFile; 135 | 136 | impl CoverageFile { 137 | pub fn enable(&self, enable: bool) { 138 | COVERAGE.try_with(|cov| cov.enable(enable)); 139 | } 140 | } 141 | 142 | impl ToIo for CoverageFile { 143 | fn to_io(self: Arc) -> Option> { 144 | COVERAGE.try_with(|cov| cov.data.clone() as _) 145 | } 146 | } 147 | 148 | #[async_trait] 149 | impl Entry for CoverageFile { 150 | async fn open( 151 | self: Arc, 152 | path: &Path, 153 | options: OpenOptions, 154 | perm: Permissions, 155 | ) -> Result<(Arc, bool), Error> { 156 | umifs::misc::open_file( 157 | self, 158 | path, 159 | options, 160 | perm, 161 | Permissions::all_same(true, true, false), 162 | ) 163 | .await 164 | } 165 | 166 | async fn metadata(&self) -> Metadata { 167 | Metadata { 168 | ty: FileType::FILE, 169 | len: 0, 170 | offset: 0, 171 | perm: Permissions::all_same(true, false, false), 172 | block_size: 0, 173 | block_count: 0, 174 | times: Default::default(), 175 | } 176 | } 177 | } 178 | 179 | impl IoPoll for CoverageFile {} 180 | -------------------------------------------------------------------------------- /mizu/dev/sdmmc/src/imp/mars.rs: -------------------------------------------------------------------------------- 1 | use core::time::Duration; 2 | 3 | use kmem::ID_OFFSET; 4 | use volatile::{map_field, VolatilePtr}; 5 | 6 | use super::stall; 7 | use crate::reg::{SdmmcRegs, UhsMode}; 8 | 9 | const TOP_BASE: usize = 0x300_0000 + ID_OFFSET; 10 | const PINMUX_BASE: usize = TOP_BASE + 0x1000; 11 | 12 | const TOP_SD_PWRSW_CTRL: usize = TOP_BASE + 0x1f4; 13 | 14 | const PAD_SDIO0_CD_REG: usize = PINMUX_BASE + 0x34; 15 | const PAD_SDIO0_PWR_EN_REG: usize = PINMUX_BASE + 0x38; 16 | const PAD_SDIO0_CLK_REG: usize = PINMUX_BASE + 0x1C; 17 | const PAD_SDIO0_CMD_REG: usize = PINMUX_BASE + 0x20; 18 | const PAD_SDIO0_D0_REG: usize = PINMUX_BASE + 0x24; 19 | const PAD_SDIO0_D1_REG: usize = PINMUX_BASE + 0x28; 20 | const PAD_SDIO0_D2_REG: usize = PINMUX_BASE + 0x2C; 21 | const PAD_SDIO0_D3_REG: usize = PINMUX_BASE + 0x30; 22 | 23 | const REG_SDIO0_CD_PAD_REG: usize = PINMUX_BASE + 0x900; 24 | const REG_SDIO0_PWR_EN_PAD_REG: usize = PINMUX_BASE + 0x904; 25 | const REG_SDIO0_CLK_PAD_REG: usize = PINMUX_BASE + 0xA00; 26 | const REG_SDIO0_CMD_PAD_REG: usize = PINMUX_BASE + 0xA04; 27 | const REG_SDIO0_DAT0_PAD_REG: usize = PINMUX_BASE + 0xA08; 28 | const REG_SDIO0_DAT1_PAD_REG: usize = PINMUX_BASE + 0xA0C; 29 | const REG_SDIO0_DAT2_PAD_REG: usize = PINMUX_BASE + 0xA10; 30 | const REG_SDIO0_DAT3_PAD_REG: usize = PINMUX_BASE + 0xA14; 31 | 32 | const SDHCI_OFFSET: usize = 0x200; 33 | const SDHCI_VENDOR_MSHC_CTRL_R: usize = SDHCI_OFFSET; 34 | const SDHCI_PHY_TX_RX_DLY: usize = SDHCI_OFFSET + 0x40; 35 | // const SDHCI_PHY_DS_DLY: usize = SDHCI_OFFSET + 0x44; 36 | // const SDHCI_PHY_DLY_STS: usize = SDHCI_OFFSET + 0x48; 37 | const SDHCI_PHY_CONFIG: usize = SDHCI_OFFSET + 0x4C; 38 | 39 | macro_rules! map_vendor { 40 | ($regs:ident. $offset:ident) => { 41 | (($regs).as_raw_ptr().as_ptr() as usize) + $offset 42 | }; 43 | } 44 | 45 | unsafe fn mmio_write(addr: usize, value: u32) { 46 | (addr as *mut u32).write_volatile(value) 47 | } 48 | 49 | unsafe fn mmio_read(addr: usize) -> u32 { 50 | (addr as *const u32).read_volatile() 51 | } 52 | 53 | unsafe fn mmio_clear_set(addr: usize, clear: u32, set: u32) { 54 | mmio_write(addr, (mmio_read(addr) & !clear) | set) 55 | } 56 | 57 | unsafe fn setup_pad(bunplug: bool) { 58 | let value = if bunplug { 3 } else { 0 }; 59 | 60 | mmio_write(PAD_SDIO0_CD_REG, 0); 61 | mmio_write(PAD_SDIO0_PWR_EN_REG, 0); 62 | mmio_write(PAD_SDIO0_CLK_REG, value); 63 | mmio_write(PAD_SDIO0_CMD_REG, value); 64 | mmio_write(PAD_SDIO0_D0_REG, value); 65 | mmio_write(PAD_SDIO0_D1_REG, value); 66 | mmio_write(PAD_SDIO0_D2_REG, value); 67 | mmio_write(PAD_SDIO0_D3_REG, value); 68 | } 69 | 70 | unsafe fn setup_io(reset: bool) { 71 | let set = 1 << if reset { 3 } else { 2 }; 72 | let clear = 1 << if reset { 2 } else { 3 }; 73 | 74 | // Pad settings 75 | mmio_clear_set(REG_SDIO0_CD_PAD_REG, 1 << 3, 1 << 2); 76 | mmio_clear_set(REG_SDIO0_PWR_EN_PAD_REG, 1 << 2, 1 << 3); 77 | mmio_clear_set(REG_SDIO0_CLK_PAD_REG, 1 << 2, 1 << 3); 78 | 79 | mmio_clear_set(REG_SDIO0_CMD_PAD_REG, clear, set); 80 | mmio_clear_set(REG_SDIO0_DAT0_PAD_REG, clear, set); 81 | mmio_clear_set(REG_SDIO0_DAT1_PAD_REG, clear, set); 82 | mmio_clear_set(REG_SDIO0_DAT2_PAD_REG, clear, set); 83 | mmio_clear_set(REG_SDIO0_DAT3_PAD_REG, clear, set); 84 | } 85 | 86 | pub fn power_on_epilog(regs: &mut VolatilePtr) { 87 | unsafe { voltage_restore(false, regs) }; 88 | unsafe { setup_pad(false) }; 89 | unsafe { setup_io(false) }; 90 | } 91 | 92 | pub fn power_off_prolog(regs: &mut VolatilePtr) { 93 | unsafe { voltage_restore(true, regs) }; 94 | unsafe { setup_pad(true) }; 95 | unsafe { setup_io(true) }; 96 | } 97 | 98 | pub fn voltage_switch() { 99 | unsafe { mmio_clear_set(REG_SDIO0_CLK_PAD_REG, 0, (1 << 5) | (1 << 6) | (1 << 7)) }; 100 | unsafe { mmio_write(TOP_SD_PWRSW_CTRL, 0b1011) } 101 | } 102 | 103 | unsafe fn voltage_restore(bunplug: bool, regs: &mut VolatilePtr) { 104 | mmio_clear_set(TOP_SD_PWRSW_CTRL, 0xf, if bunplug { 0xe } else { 0x9 }); 105 | stall(Duration::from_millis(1)); 106 | 107 | mmio_clear_set( 108 | map_vendor!(regs.SDHCI_VENDOR_MSHC_CTRL_R), 109 | 0, 110 | (1 << 1) | (1 << 8) | (1 << 9), 111 | ); 112 | mmio_write(map_vendor!(regs.SDHCI_PHY_TX_RX_DLY), 0x100_0100); 113 | mmio_write(map_vendor!(regs.SDHCI_PHY_CONFIG), 1); 114 | } 115 | 116 | pub fn reset(regs: &mut VolatilePtr) { 117 | let mut hc2 = map_field!(regs.host_control_2).read(); 118 | if hc2.uhs_mode().get() == UhsMode::Sdr104 { 119 | unsafe { 120 | mmio_clear_set(map_vendor!(regs.SDHCI_VENDOR_MSHC_CTRL_R), 1 << 1, 0); 121 | mmio_clear_set(map_vendor!(regs.SDHCI_PHY_CONFIG), 1 << 0, 0); 122 | mmio_write(map_vendor!(regs.SDHCI_PHY_TX_RX_DLY), 1 << 8); 123 | } 124 | } else { 125 | unsafe { 126 | mmio_clear_set(map_vendor!(regs.SDHCI_VENDOR_MSHC_CTRL_R), 0, 1 << 1); 127 | mmio_clear_set(map_vendor!(regs.SDHCI_PHY_CONFIG), 0, 1 << 0); 128 | mmio_write(map_vendor!(regs.SDHCI_PHY_TX_RX_DLY), 0x100_0100); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /mizu/lib/co-trap/src/tf.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | marker::PhantomData, 3 | ops::{Deref, DerefMut}, 4 | }; 5 | 6 | use bevy_utils_proc_macros::all_tuples; 7 | use ksc_core::{ 8 | handler::{FromParam, Param}, 9 | RawReg, Scn, 10 | }; 11 | use num_traits::FromPrimitive; 12 | use static_assertions::const_assert_eq; 13 | 14 | #[derive(Debug, Clone, Copy, Default)] 15 | #[repr(C)] 16 | pub struct Tx { 17 | pub ra: usize, // 12 18 | pub sp: usize, // 13 19 | pub gp: usize, // 14 20 | pub tp: usize, // 15 21 | pub a: [usize; 8], // 16..24 22 | pub t: [usize; 7], // 24..31 23 | } 24 | 25 | #[derive(Debug, Clone, Copy, Default)] 26 | #[repr(C)] 27 | pub struct Gpr { 28 | pub s: [usize; 12], // 0..12 29 | pub tx: Tx, // 12..31 30 | } 31 | const_assert_eq!( 32 | core::mem::size_of::(), 33 | core::mem::size_of::() * 31 34 | ); 35 | 36 | impl Gpr { 37 | pub fn copy_to_x(&self, output: &mut [usize; 31]) { 38 | output[0..4].copy_from_slice(&[self.tx.ra, self.tx.sp, self.tx.gp, self.tx.tp]); 39 | output[4..7].copy_from_slice(&self.tx.t[..3]); 40 | output[7..9].copy_from_slice(&self.s[..2]); 41 | output[9..17].copy_from_slice(&self.tx.a); 42 | output[17..27].copy_from_slice(&self.s[2..]); 43 | output[27..31].copy_from_slice(&self.tx.t[3..]); 44 | } 45 | 46 | pub fn copy_from_x(&mut self, input: &[usize; 31]) { 47 | self.tx.ra = input[0]; 48 | self.tx.sp = input[1]; 49 | self.tx.gp = input[2]; 50 | self.tx.tp = input[3]; 51 | self.tx.t[..3].copy_from_slice(&input[4..7]); 52 | self.s[..2].copy_from_slice(&input[7..9]); 53 | self.tx.a.copy_from_slice(&input[9..17]); 54 | self.s[2..].copy_from_slice(&input[17..27]); 55 | self.tx.t[3..].copy_from_slice(&input[27..31]); 56 | } 57 | } 58 | 59 | #[derive(Clone, Copy, Debug, Default)] 60 | #[repr(C)] 61 | pub struct TrapFrame { 62 | pub gpr: Gpr, // 0..31 63 | pub sepc: usize, // 31 64 | pub sstatus: usize, // 32 65 | pub stval: usize, // 33 66 | pub scause: usize, // 34 67 | } 68 | 69 | impl TrapFrame { 70 | pub const fn syscall_arg(&self) -> usize { 71 | self.gpr.tx.a[N] 72 | } 73 | 74 | pub fn scn(&self) -> Result { 75 | let raw = self.syscall_arg::<7>(); 76 | Scn::from_usize(raw).ok_or(raw) 77 | } 78 | 79 | pub fn set_syscall_ret(&mut self, ret: usize) { 80 | self.sepc += 4; 81 | self.gpr.tx.a[0] = ret; 82 | } 83 | } 84 | 85 | /// A wrapper around `TrapFrame` to make it easier to access the arguments and 86 | /// return values from user's syscalls. 87 | /// 88 | /// Pass a function prototype to the generic parameter to utilize its max 89 | /// functionality: 90 | /// 91 | /// ``` 92 | /// use ksc::UserCx; 93 | /// 94 | /// let mut tf = Default::default(); 95 | /// 96 | /// let user: UserCx<'_, fn(u32, *const u8) -> usize> = 97 | /// UserCx::from(&mut tf); 98 | /// 99 | /// let (a, b): (u32, *const u8) = user.args(); 100 | /// user.ret(a as usize + b as usize); 101 | /// ``` 102 | pub struct UserCx<'a, A> { 103 | tf: &'a mut TrapFrame, 104 | _marker: PhantomData, 105 | } 106 | 107 | impl<'a, A> From<&'a mut TrapFrame> for UserCx<'a, A> { 108 | fn from(tf: &'a mut TrapFrame) -> Self { 109 | UserCx { 110 | tf, 111 | _marker: PhantomData, 112 | } 113 | } 114 | } 115 | 116 | impl<'a, A> Deref for UserCx<'a, A> { 117 | type Target = TrapFrame; 118 | 119 | fn deref(&self) -> &Self::Target { 120 | self.tf 121 | } 122 | } 123 | 124 | impl<'a, A> DerefMut for UserCx<'a, A> { 125 | fn deref_mut(&mut self) -> &mut Self::Target { 126 | self.tf 127 | } 128 | } 129 | 130 | impl<'a, A> UserCx<'a, A> { 131 | /// Get the underlying `TrapFrame`, consuming `self`. 132 | pub fn into_inner(self) -> &'a mut TrapFrame { 133 | self.tf 134 | } 135 | } 136 | 137 | macro_rules! impl_arg { 138 | ($($arg:ident),*) => { 139 | impl<'a, $($arg: RawReg,)* T: RawReg> UserCx<'a, fn($($arg),*) -> T> { 140 | #[allow(clippy::unused_unit)] 141 | #[allow(non_snake_case)] 142 | #[allow(unused_parens)] 143 | /// Get the arguments with the same prototype as the parameters in the function prototype. 144 | pub fn args(&self) -> ($($arg),*) { 145 | $( 146 | let $arg = self.tf.syscall_arg::<${index()}>(); 147 | )* 148 | ($(RawReg::from_raw($arg)),*) 149 | } 150 | 151 | /// Gives the return value to the user context, consuming `self`. 152 | pub fn ret(self, value: T) { 153 | self.tf.set_syscall_ret(RawReg::into_raw(value)) 154 | } 155 | } 156 | }; 157 | } 158 | 159 | all_tuples!(impl_arg, 0, 6, P); 160 | 161 | impl Param for UserCx<'_, A> { 162 | type Item<'a> = UserCx<'a, A>; 163 | } 164 | 165 | impl FromParam<&'_ mut TrapFrame> for UserCx<'_, A> { 166 | fn from_param<'a>(item: <&'_ mut TrapFrame as Param>::Item<'a>) -> Self::Item<'a> { 167 | UserCx::from(item) 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /mizu/dev/plic/src/dev.rs: -------------------------------------------------------------------------------- 1 | //! Based on [PLIC specification](https://github.com/riscv/riscv-plic-spec). 2 | 3 | use core::{mem, ptr::NonNull}; 4 | 5 | use devices::{bitmap_index_u32, MmioReg}; 6 | use static_assertions::const_assert_eq; 7 | use volatile::{ 8 | access::{ReadOnly, ReadWrite}, 9 | map_field, 10 | }; 11 | 12 | pub const NR_INTR: usize = 1024; 13 | const NR_INTR_BY_BITS: usize = NR_INTR / u32::BITS as usize; 14 | pub const NR_INTR_CX: usize = 15872; 15 | 16 | type IntrValues = [u32; NR_INTR]; 17 | type IntrBitmap = [u32; NR_INTR_BY_BITS]; 18 | 19 | struct Priority; 20 | impl MmioReg for Priority { 21 | const OFFSET: usize = 0; 22 | type Repr = IntrValues; 23 | type Access = ReadWrite; 24 | } 25 | const_assert_eq!(mem::size_of::<::Repr>(), 0x1000); 26 | 27 | struct Pending; 28 | impl MmioReg for Pending { 29 | const OFFSET: usize = 0x1000; 30 | type Repr = IntrBitmap; 31 | type Access = ReadOnly; 32 | } 33 | const_assert_eq!(mem::size_of::<::Repr>(), 0x80); 34 | 35 | struct Enable; 36 | impl MmioReg for Enable { 37 | const OFFSET: usize = 0x2000; 38 | type Repr = [IntrBitmap; NR_INTR_CX]; 39 | type Access = ReadWrite; 40 | } 41 | const_assert_eq!(mem::size_of::<::Repr>(), 0x1f0000); 42 | 43 | #[repr(C, align(0x1000))] 44 | struct CxStruct { 45 | priority_threshold: u32, 46 | claim_complete: u32, 47 | } 48 | struct Cx; 49 | impl MmioReg for Cx { 50 | const OFFSET: usize = 0x200000; 51 | type Repr = [CxStruct; NR_INTR_CX]; 52 | type Access = ReadWrite; 53 | } 54 | const_assert_eq!(mem::size_of::<::Repr>(), 0x3e00000); 55 | 56 | macro_rules! map_index { 57 | ($volatile:ident, $index:expr) => { 58 | unsafe { $volatile.map(|ptr| core::ptr::NonNull::get_unchecked_mut(ptr, $index)) } 59 | }; 60 | } 61 | 62 | #[derive(Debug, Clone)] 63 | pub struct Plic(NonNull<()>); 64 | 65 | unsafe impl Send for Plic {} 66 | unsafe impl Sync for Plic {} 67 | 68 | impl Plic { 69 | /// Creates a new [`Plic`] at a specified base. 70 | /// 71 | /// # Safety 72 | /// 73 | /// The caller must ensure that the memory region at `base` sized 0x4000000 74 | /// is valid for exclusive, uncached read and write access in the `'static` 75 | /// lifetime. 76 | pub unsafe fn new(base: NonNull<()>) -> Self { 77 | Plic(base) 78 | } 79 | 80 | pub fn priority(&self, pin: u32) -> u32 { 81 | let cell = unsafe { Priority::at(self.0) }; 82 | unsafe { 83 | cell.map(|s| NonNull::get_unchecked_mut(s, pin as usize)) 84 | .read() 85 | } 86 | } 87 | 88 | pub fn set_priority(&self, pin: u32, priority: u32) { 89 | let cell = unsafe { Priority::at(self.0) }; 90 | map_index!(cell, pin as usize).write(priority) 91 | } 92 | 93 | pub fn pending(&self, pin: u32) -> bool { 94 | let (byte, bit_in_byte_mask) = bitmap_index_u32(pin as usize); 95 | let cell = unsafe { Pending::at(self.0) }; 96 | map_index!(cell, byte).read() & bit_in_byte_mask != 0 97 | } 98 | 99 | pub fn is_enabled(&self, pin: u32, cx: usize) -> bool { 100 | let (byte, bit_in_byte_mask) = bitmap_index_u32(pin as usize); 101 | let all_cell = unsafe { Enable::at(self.0) }; 102 | let cx_cell = map_index!(all_cell, cx); 103 | map_index!(cx_cell, byte).read() & bit_in_byte_mask != 0 104 | } 105 | 106 | pub fn enable(&self, pin: u32, cx: usize, enable: bool) { 107 | log::trace!( 108 | "Plic::enable base = {:p}, pin = {pin}, cx = {cx}, {}", 109 | self.0, 110 | if enable { "enable" } else { "disable" } 111 | ); 112 | 113 | let (byte, bit_in_byte_mask) = bitmap_index_u32(pin as usize); 114 | let all_cell = unsafe { Enable::at(self.0) }; 115 | let cx_cell = map_index!(all_cell, cx); 116 | let cell = map_index!(cx_cell, byte); 117 | 118 | log::trace!( 119 | "Plic::enable byte get_unchecked_mut = {:#x}, bit_in_byte_mask = {:#b}, cell base = {:p}", 120 | byte, 121 | bit_in_byte_mask, 122 | cell.as_raw_ptr() 123 | ); 124 | 125 | cell.update(|value| { 126 | if enable { 127 | value | bit_in_byte_mask 128 | } else { 129 | value & !bit_in_byte_mask 130 | } 131 | }) 132 | } 133 | 134 | pub fn priority_threshold(&self, cx: usize) -> u32 { 135 | let cx_cell = unsafe { Cx::at(self.0) }; 136 | let cell = map_index!(cx_cell, cx); 137 | map_field!(cell.priority_threshold).read() 138 | } 139 | 140 | pub fn set_priority_threshold(&self, cx: usize, threshold: u32) { 141 | let cx_cell = unsafe { Cx::at(self.0) }; 142 | let cell = map_index!(cx_cell, cx); 143 | map_field!(cell.priority_threshold).write(threshold) 144 | } 145 | 146 | pub fn claim(&self, cx: usize) -> u32 { 147 | let cx_cell = unsafe { Cx::at(self.0) }; 148 | let cell = map_index!(cx_cell, cx); 149 | map_field!(cell.claim_complete).read() 150 | } 151 | 152 | pub fn complete(&self, cx: usize, pin: u32) { 153 | let cx_cell = unsafe { Cx::at(self.0) }; 154 | let cell = map_index!(cx_cell, cx); 155 | map_field!(cell.claim_complete).write(pin) 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /mizu/dev/sdmmc/src/adma.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | mem, 3 | sync::atomic::{self, Ordering::SeqCst}, 4 | }; 5 | 6 | use bitflags::bitflags; 7 | use kmem::{Frame, LAddr, PAddr, ID_OFFSET, PAGE_SIZE}; 8 | use ksc::Error; 9 | use static_assertions::const_assert_eq; 10 | 11 | pub const MAX_SEGMENTS: usize = 256; 12 | 13 | pub const ADDR_ALIGN: usize = 4; 14 | pub const ADDR_MASK: usize = ADDR_ALIGN - 1; 15 | 16 | #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] 17 | #[repr(C, packed)] 18 | pub struct Descriptor { 19 | attr: Attr, 20 | len: u16, 21 | addr: u64, 22 | _reserved: u32, 23 | } 24 | const_assert_eq!(mem::size_of::(), mem::size_of::()); 25 | 26 | bitflags! { 27 | #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] 28 | pub struct Attr: u16 { 29 | const VALID = 1 << 0; 30 | const END = 1 << 1; 31 | const GEN_INTR = 1 << 2; 32 | 33 | const ACTION_NONE = 0; 34 | const ACTION_RSVD = 0b010_000; 35 | const ACTION_XFER = 0b100_000; 36 | const ACTION_LINK = 0b110_000; 37 | 38 | const ACTION_MASK = 0b111_000; 39 | } 40 | } 41 | 42 | impl Descriptor { 43 | const MAX_LEN: usize = 1 << 16; 44 | 45 | fn set(&mut self, addr: u64, len: u16, attr: Attr) { 46 | self.addr = addr; 47 | self.len = len; 48 | self.attr = attr; 49 | self._reserved = 0; 50 | } 51 | } 52 | 53 | #[derive(Debug)] 54 | pub struct DescTable { 55 | table: Frame, 56 | bounce_buffer: Frame, 57 | } 58 | const_assert_eq!(PAGE_SIZE, mem::size_of::<[Descriptor; MAX_SEGMENTS]>()); 59 | 60 | unsafe impl Send for DescTable {} 61 | unsafe impl Sync for DescTable {} 62 | 63 | macro_rules! as_mut { 64 | ($x:expr) => { 65 | unsafe { 66 | &mut *(($x).table.base().to_laddr(ID_OFFSET)).cast::<[Descriptor; MAX_SEGMENTS]>() 67 | } 68 | }; 69 | } 70 | 71 | impl DescTable { 72 | pub const MAX_LEN: usize = (MAX_SEGMENTS - 2) * Descriptor::MAX_LEN; 73 | 74 | pub fn new() -> Result { 75 | Ok(DescTable { 76 | table: Frame::new()?, 77 | bounce_buffer: Frame::new()?, 78 | }) 79 | } 80 | 81 | pub fn dma_addr(&self) -> PAddr { 82 | self.table.base() 83 | } 84 | 85 | /// # Safety 86 | /// 87 | /// `buf` must be alive until the transfer is complete. 88 | pub unsafe fn fill(&mut self, buf: &mut [u8], read: bool) -> usize { 89 | assert!(buf.len() <= Self::MAX_LEN); 90 | kmem::sync_dma_for_device(read, !read, LAddr::from_slice(&*buf)); 91 | 92 | let table = as_mut!(self); 93 | let mut iter = table.iter_mut(); 94 | let mut base = LAddr::new(buf.as_mut_ptr()).to_paddr(ID_OFFSET); 95 | 96 | let mut filled = 0; 97 | let mut count = 0; 98 | 99 | let align_offset = (ADDR_ALIGN - (*base & ADDR_MASK)) & ADDR_MASK; 100 | if align_offset > 0 { 101 | let len = align_offset.min(buf.len()); 102 | if !read { 103 | self.bounce_buffer[..len].copy_from_slice(&buf[..len]); 104 | } 105 | let addr = self.bounce_buffer.base(); 106 | 107 | let desc = iter.next().unwrap(); 108 | desc.set(*addr as u64, len as u16, Attr::ACTION_XFER | Attr::VALID); 109 | kmem::sync_dma_for_device(read, !read, LAddr::from_slice(&self.bounce_buffer[..len])); 110 | // log::trace!("Set at {desc:p}: {desc:#x?}"); 111 | 112 | filled += len; 113 | base += len; 114 | count += 1; 115 | } 116 | 117 | while filled + Descriptor::MAX_LEN <= buf.len() { 118 | let desc = iter.next().unwrap(); 119 | desc.set(*base as u64, 0, Attr::ACTION_XFER | Attr::VALID); 120 | // log::trace!("Set at {desc:p}: {desc:#x?}"); 121 | 122 | filled += Descriptor::MAX_LEN; 123 | base += Descriptor::MAX_LEN; 124 | count += 1; 125 | } 126 | 127 | if filled < buf.len() { 128 | let len = buf.len() - filled; 129 | 130 | let desc = iter.next().unwrap(); 131 | desc.set(*base as u64, len as u16, Attr::ACTION_XFER | Attr::VALID); 132 | // log::trace!("Set at {desc:p}: {desc:#x?}"); 133 | 134 | filled += len; 135 | // base += len; 136 | count += 1; 137 | } 138 | 139 | let desc = iter.next().unwrap(); 140 | desc.set(0, 0, Attr::ACTION_NONE | Attr::END | Attr::VALID); 141 | // log::trace!("Set at {desc:p}: {desc:#x?}"); 142 | count += 1; 143 | 144 | atomic::fence(SeqCst); 145 | kmem::sync_dma_for_device(true, true, LAddr::from_slice(&table[..count])); 146 | filled 147 | } 148 | 149 | /// # Safety 150 | /// 151 | /// `buf` must be the same as the last `fill`ed. 152 | pub unsafe fn extract(&mut self, buf: &mut [u8], read: bool) { 153 | kmem::sync_dma_for_cpu(read, !read, LAddr::from_slice(&*buf)); 154 | atomic::fence(SeqCst); 155 | 156 | if !read { 157 | return; 158 | } 159 | let base = *LAddr::new(buf.as_mut_ptr()).to_paddr(ID_OFFSET); 160 | let align_offset = (ADDR_ALIGN - (base & ADDR_MASK)) & ADDR_MASK; 161 | if align_offset > 0 { 162 | let len = align_offset.min(buf.len()); 163 | let src = &self.bounce_buffer[..len]; 164 | 165 | kmem::sync_dma_for_cpu(read, !read, LAddr::from_slice(src)); 166 | buf[..len].copy_from_slice(src); 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /mizu/kernel/src/trap.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | sync::atomic::{AtomicU8, AtomicUsize, Ordering::Relaxed}, 3 | time::Duration, 4 | }; 5 | 6 | use co_trap::{fast_func, FastResult, TrapFrame, Tx}; 7 | use futures_util::Future; 8 | use ksc::Error::{self, EAGAIN, ETIMEDOUT}; 9 | use ktime::TimeOutExt; 10 | use riscv::register::{ 11 | scause::{self, Exception, Interrupt, Scause, Trap}, 12 | sepc, sstatus, stval, 13 | }; 14 | 15 | pub type KTrapFrame = Tx; 16 | 17 | pub fn poll_once(fut: impl Future>) -> Result { 18 | match ksync::poll_once(fut) { 19 | Some(res) => res, 20 | None => Err(EAGAIN), 21 | } 22 | } 23 | 24 | pub async fn poll_with( 25 | fut: impl Future>, 26 | timeout: Option, 27 | ) -> Result { 28 | match timeout { 29 | None => fut.await, 30 | Some(Duration::ZERO) => poll_once(fut), 31 | Some(dur) => fut.on_timeout(dur, || Err(ETIMEDOUT)).await, 32 | } 33 | } 34 | 35 | #[cfg(target_arch = "riscv64")] 36 | core::arch::global_asm!(include_str!("trap.S")); 37 | 38 | const TIMER_GRAN_DIV: u64 = 200; 39 | 40 | #[no_mangle] 41 | extern "C" fn ktrap_handler(_tf: &mut KTrapFrame) { 42 | match scause::read().cause() { 43 | Trap::Interrupt(intr) => handle_intr(intr, "kernel"), 44 | Trap::Exception(excep) => match excep { 45 | Exception::Breakpoint => sepc::write(sepc::read() + 2), 46 | Exception::LoadPageFault | Exception::StorePageFault => { 47 | if let Some(cf) = crate::mem::UA_FAULT.try_with(|&s| s) { 48 | sepc::write(cf); 49 | _tf.a[0] = stval::read(); 50 | return; 51 | } 52 | panic!( 53 | "unhandled exception in kernel: {excep:?} at {:#x?}, stval = {:#x}", 54 | sepc::read(), 55 | stval::read(), 56 | ) 57 | } 58 | _ => panic!( 59 | "unhandled exception in kernel: {excep:?} at {:#x}, stval = {:#x}", 60 | sepc::read(), 61 | stval::read(), 62 | ), 63 | }, 64 | } 65 | } 66 | 67 | pub static TIMER_COUNT: AtomicUsize = AtomicUsize::new(0); 68 | 69 | pub fn handle_intr(intr: Interrupt, from: &str) { 70 | match intr { 71 | Interrupt::SupervisorTimer => { 72 | ktime::timer_tick(); 73 | #[cfg(not(feature = "test"))] 74 | let raw = ktime::Instant::now_raw(); 75 | #[cfg(feature = "test")] 76 | let raw = 0; 77 | // log::trace!("timer tick at {raw}"); 78 | sbi_rt::set_timer(raw + config::TIME_FREQ as u64 / TIMER_GRAN_DIV); 79 | TIMER_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed); 80 | } 81 | Interrupt::SupervisorExternal => crate::dev::INTR.notify(hart_id::hart_id()), 82 | Interrupt::SupervisorSoft => crate::cpu::IPI.receive(), 83 | _ => log::info!("unhandled interrupt in {from}: {intr:?}"), 84 | } 85 | } 86 | 87 | #[cfg(not(feature = "test"))] 88 | pub unsafe fn init() { 89 | use riscv::register::{fcsr, stvec, stvec::TrapMode}; 90 | extern "C" { 91 | fn ktrap_entry(); 92 | } 93 | stvec::write(ktrap_entry as _, TrapMode::Direct); 94 | 95 | fcsr::set_rounding_mode(fcsr::RoundingMode::RoundToNearestEven); 96 | fcsr::clear_flag(fcsr::Flag::DZ); 97 | fcsr::clear_flag(fcsr::Flag::OF); 98 | fcsr::clear_flag(fcsr::Flag::UF); 99 | fcsr::clear_flag(fcsr::Flag::NX); 100 | 101 | sstatus::set_fs(sstatus::FS::Off); 102 | } 103 | 104 | fast_func!(); 105 | 106 | const CLEAN: u8 = 0; 107 | const DIRTY: u8 = 1; 108 | const YIELD: u8 = 2; 109 | const RESET: u8 = 3; 110 | 111 | #[repr(C)] 112 | pub struct Fp { 113 | /// 0 ~ 31 => f0 ~ f31, 32 => fcsr 114 | regs: [u64; 33], 115 | 116 | state: AtomicU8, 117 | } 118 | 119 | impl Default for Fp { 120 | fn default() -> Self { 121 | Fp { 122 | regs: [0; 33], 123 | state: AtomicU8::new(YIELD), 124 | } 125 | } 126 | } 127 | 128 | impl Fp { 129 | pub fn copy(other: &Self) -> Self { 130 | Fp { 131 | regs: other.regs, 132 | state: AtomicU8::new(YIELD), 133 | } 134 | } 135 | 136 | fn enter_user(&self) { 137 | extern "C" { 138 | fn _load_fp(regs: *const [u64; 33]); 139 | } 140 | if self.state.swap(CLEAN, Relaxed) == YIELD { 141 | unsafe { _load_fp(&self.regs) } 142 | } 143 | } 144 | 145 | fn leave_user(&self, sstatus: &mut usize) { 146 | let fs = (*sstatus & 0x6000) >> 13; 147 | if fs == sstatus::FS::Dirty as _ { 148 | self.state.store(DIRTY, Relaxed); 149 | } 150 | } 151 | 152 | pub fn mark_reset(&self) { 153 | self.state.store(RESET, Relaxed); 154 | } 155 | 156 | pub fn yield_now(&mut self) { 157 | extern "C" { 158 | fn _save_fp(regs: *mut [u64; 33]); 159 | } 160 | match self.state.swap(YIELD, Relaxed) { 161 | DIRTY => unsafe { _save_fp(&mut self.regs) }, 162 | RESET => self.regs.fill(0), 163 | _ => {} 164 | } 165 | } 166 | } 167 | 168 | scoped_tls::scoped_thread_local!(pub static FP: Fp); 169 | 170 | pub fn yield_to_user(tf: &mut TrapFrame) -> (Scause, FastResult) { 171 | FP.with(|fp| { 172 | fp.enter_user(); 173 | let ret = co_trap::yield_to_user(&mut *tf); 174 | fp.leave_user(&mut tf.sstatus); 175 | ret 176 | }) 177 | } 178 | -------------------------------------------------------------------------------- /mizu/kernel/src/task/time.rs: -------------------------------------------------------------------------------- 1 | use alloc::sync::{Arc, Weak}; 2 | use core::{ 3 | sync::atomic::{ 4 | AtomicU64, 5 | Ordering::{Relaxed, SeqCst}, 6 | }, 7 | time::Duration, 8 | }; 9 | 10 | use ktime::{Instant, InstantExt}; 11 | use sygnal::{Sig, SigCode, SigFields, SigInfo}; 12 | 13 | const USER: usize = 0; 14 | const SYSTEM: usize = 1; 15 | 16 | #[derive(Debug, Default)] 17 | pub struct Times { 18 | tgroup_submitter: Weak, 19 | 20 | me: [AtomicU64; 2], 21 | process: [AtomicU64; 2], 22 | children: [AtomicU64; 2], 23 | } 24 | 25 | impl Times { 26 | pub fn new_thread(tgroup: &Arc) -> Arc { 27 | Arc::new(Times { 28 | tgroup_submitter: Arc::downgrade(tgroup), 29 | ..Default::default() 30 | }) 31 | } 32 | 33 | fn update(&self, delta: u64) { 34 | self.me[INDEX].fetch_add(delta, SeqCst); 35 | match self.tgroup_submitter.upgrade() { 36 | Some(tgroup) => tgroup.process[INDEX].fetch_add(delta, SeqCst), 37 | None => self.process[INDEX].fetch_add(delta, SeqCst), 38 | }; 39 | } 40 | 41 | pub fn update_user(&self, delta: u64) { 42 | self.update::(delta) 43 | } 44 | 45 | pub fn update_system(&self, delta: u64) { 46 | self.update::(delta) 47 | } 48 | 49 | pub fn append_child(&self, child: &Times) { 50 | self.update_user(child.me[USER].load(SeqCst)); 51 | self.update_system(child.me[SYSTEM].load(SeqCst)); 52 | } 53 | 54 | pub fn get(&self, process: bool) -> [u64; 4] { 55 | let mut storage = None; 56 | let times = match self.tgroup_submitter.upgrade() { 57 | Some(tg) if process => &*storage.insert(tg), 58 | _ => self, 59 | }; 60 | [ 61 | times.me[USER].load(Relaxed), 62 | times.me[SYSTEM].load(Relaxed), 63 | times.children[USER].load(Relaxed), 64 | times.children[SYSTEM].load(Relaxed), 65 | ] 66 | } 67 | 68 | fn get_process_raw(&self) -> [u64; 2] { 69 | let mut storage = None; 70 | let times = match self.tgroup_submitter.upgrade() { 71 | Some(tg) => &*storage.insert(tg), 72 | _ => self, 73 | }; 74 | times.process.each_ref().map(|s| s.load(Relaxed)) 75 | } 76 | 77 | pub fn get_process(&self) -> [Duration; 2] { 78 | self.get_process_raw().map(config::to_duration) 79 | } 80 | 81 | pub fn get_thread(&self) -> [Duration; 2] { 82 | self.me 83 | .each_ref() 84 | .map(|s| config::to_duration(s.load(Relaxed))) 85 | } 86 | 87 | pub fn get_children(&self) -> [Duration; 2] { 88 | let mut storage = None; 89 | let times = match self.tgroup_submitter.upgrade() { 90 | Some(tg) => &*storage.insert(tg), 91 | _ => self, 92 | }; 93 | times 94 | .children 95 | .each_ref() 96 | .map(|s| config::to_duration(s.load(Relaxed))) 97 | } 98 | } 99 | 100 | #[derive(Debug)] 101 | pub struct Counter { 102 | interval: Duration, 103 | next_tick: Instant, 104 | activated: bool, 105 | now: fn(&Times) -> Instant, 106 | sig: Sig, 107 | } 108 | 109 | impl Counter { 110 | pub fn new_real() -> Self { 111 | Counter { 112 | interval: Duration::ZERO, 113 | next_tick: Instant::now(), 114 | activated: false, 115 | now: |_| Instant::now(), 116 | sig: Sig::SIGALRM, 117 | } 118 | } 119 | 120 | pub fn new_virtual() -> Self { 121 | Counter { 122 | interval: Duration::ZERO, 123 | next_tick: Instant::from_su(0, 0), 124 | activated: false, 125 | now: |times| { 126 | let [user, _] = times.get_process_raw(); 127 | unsafe { Instant::from_raw(user) } 128 | }, 129 | sig: Sig::SIGVTALRM, 130 | } 131 | } 132 | 133 | pub fn new_profile() -> Self { 134 | Counter { 135 | interval: Duration::ZERO, 136 | next_tick: Instant::from_su(0, 0), 137 | activated: false, 138 | now: |times| { 139 | let [user, system] = times.get_process_raw(); 140 | unsafe { Instant::from_raw(user + system) } 141 | }, 142 | sig: Sig::SIGPROF, 143 | } 144 | } 145 | 146 | pub fn next_deadline(&self) -> Option { 147 | self.activated.then_some(self.next_tick) 148 | } 149 | 150 | pub fn update(&mut self, times: &Times) -> Option { 151 | if !self.activated { 152 | return None; 153 | } 154 | let now = (self.now)(times); 155 | if self.next_tick <= now { 156 | self.next_tick = now + self.interval; 157 | if self.interval.is_zero() { 158 | self.activated = false; 159 | } 160 | return Some(SigInfo { 161 | sig: self.sig, 162 | code: SigCode::TIMER as _, 163 | fields: SigFields::None, 164 | }); 165 | } 166 | None 167 | } 168 | 169 | pub fn set( 170 | &mut self, 171 | times: &Times, 172 | set: Option<(Duration, Duration)>, 173 | ) -> (Duration, Duration) { 174 | let now = (self.now)(times); 175 | let old = (self.interval, self.next_tick - now); 176 | if let Some((interval, next_diff)) = set { 177 | self.interval = interval; 178 | self.next_tick = now + next_diff; 179 | self.activated = true; 180 | } 181 | old 182 | } 183 | } 184 | 185 | pub fn counters() -> [Counter; 3] { 186 | [ 187 | Counter::new_real(), 188 | Counter::new_virtual(), 189 | Counter::new_profile(), 190 | ] 191 | } 192 | -------------------------------------------------------------------------------- /mizu/lib/sygnal/src/queue.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | array, 3 | future::{pending, Future}, 4 | pin::Pin, 5 | sync::atomic::{AtomicU64, Ordering::SeqCst}, 6 | task::{ready, Context, Poll}, 7 | }; 8 | 9 | use crossbeam_queue::ArrayQueue; 10 | use futures_util::{future, FutureExt}; 11 | use ksc_core::handler::Param; 12 | use ksync::event::{Event, EventListener}; 13 | use rv39_paging::LAddr; 14 | 15 | use crate::{Sig, SigSet, NR_SIGNALS}; 16 | 17 | const CAP_PER_SIG: usize = 8; 18 | 19 | #[derive(Debug)] 20 | struct SigPending { 21 | queue: ArrayQueue, 22 | event: Event, 23 | } 24 | 25 | #[derive(Debug)] 26 | pub struct Signals { 27 | set: AtomicU64, 28 | pending: [SigPending; NR_SIGNALS], 29 | } 30 | 31 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 32 | pub struct SigInfo { 33 | pub sig: Sig, 34 | pub code: i32, 35 | pub fields: SigFields, 36 | } 37 | 38 | impl Param for SigInfo { 39 | type Item<'a> = SigInfo; 40 | } 41 | 42 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 43 | #[non_exhaustive] 44 | pub enum SigFields { 45 | None, 46 | SigKill { pid: usize, uid: usize }, 47 | SigChld { pid: usize, uid: usize, status: i32 }, 48 | SigSys { addr: LAddr, num: u32 }, 49 | } 50 | 51 | impl Signals { 52 | pub fn new() -> Self { 53 | Signals { 54 | set: AtomicU64::new(0), 55 | pending: array::from_fn(|index| SigPending { 56 | queue: ArrayQueue::new(match Sig::from_index(index) { 57 | // Each legacy signal only needs 1 entry. 58 | Some(sig) if sig.is_legacy() => 1, 59 | _ => CAP_PER_SIG, 60 | }), 61 | event: Event::new(), 62 | }), 63 | } 64 | } 65 | 66 | pub fn push(&self, info: SigInfo) { 67 | let old: SigSet = self.set.fetch_or(info.sig.mask(), SeqCst).into(); 68 | 69 | if !(info.sig.is_legacy() && old.contains(info.sig)) { 70 | let sig_pending = &self.pending[info.sig.index()]; 71 | let res = sig_pending.queue.push(info); 72 | if res.is_ok() { 73 | sig_pending.event.notify_additional(1); 74 | } 75 | } 76 | } 77 | 78 | pub fn is_empty(&self) -> bool { 79 | self.set.load(SeqCst) == 0 80 | } 81 | 82 | pub fn pop(&self, mut masked: SigSet) -> Option { 83 | if self.is_empty() { 84 | return None; 85 | } 86 | masked.clear_never_capture(); 87 | let iter = self.pending.iter().enumerate(); 88 | 89 | let (info, is_empty) = iter 90 | .filter(|&(index, _)| !masked.contains_index(index)) 91 | .find_map(|(_, pending)| pending.queue.pop().map(|s| (s, pending.queue.is_empty())))?; 92 | 93 | if is_empty { 94 | self.set.fetch_and(!info.sig.mask(), SeqCst); 95 | } 96 | Some(info) 97 | } 98 | 99 | pub fn wait_one(&self, sig: Sig) -> WaitOne { 100 | WaitOne { 101 | pending: &self.pending[sig.index()], 102 | set: &self.set, 103 | listener: None, 104 | } 105 | } 106 | 107 | pub async fn wait(&self, sigset: SigSet) -> SigInfo { 108 | assert!(!sigset.is_empty()); 109 | let wait_one = move |sig| self.wait_one(sig); 110 | let wait_any = future::select_all(sigset.map(wait_one)); 111 | wait_any.await.0 112 | } 113 | 114 | pub fn wait_one_event(&self, sig: Sig) -> WaitOneEvent { 115 | WaitOneEvent { 116 | pending: &self.pending[sig.index()], 117 | listener: None, 118 | } 119 | } 120 | 121 | pub async fn wait_event(&self, sigset: SigSet) { 122 | if sigset.is_empty() { 123 | return pending().await; 124 | } 125 | let wait_one = move |sig| self.wait_one_event(sig); 126 | let wait_any = future::select_all(sigset.map(wait_one)); 127 | wait_any.await; 128 | } 129 | } 130 | 131 | #[must_use = "futures do nothing unless you `.await` or poll them"] 132 | pub struct WaitOne<'a> { 133 | pending: &'a SigPending, 134 | set: &'a AtomicU64, 135 | listener: Option, 136 | } 137 | 138 | impl Future for WaitOne<'_> { 139 | type Output = SigInfo; 140 | 141 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 142 | loop { 143 | match self.pending.queue.pop() { 144 | Some(info) => { 145 | if self.pending.queue.is_empty() { 146 | self.set.fetch_and(!info.sig.mask(), SeqCst); 147 | } 148 | break Poll::Ready(info); 149 | } 150 | None => match self.listener.as_mut() { 151 | Some(listener) => { 152 | ready!(listener.poll_unpin(cx)); 153 | self.listener = None; 154 | } 155 | None => self.listener = Some(self.pending.event.listen()), 156 | }, 157 | } 158 | } 159 | } 160 | } 161 | 162 | #[must_use = "futures do nothing unless you `.await` or poll them"] 163 | pub struct WaitOneEvent<'a> { 164 | pending: &'a SigPending, 165 | listener: Option, 166 | } 167 | 168 | impl Future for WaitOneEvent<'_> { 169 | type Output = (); 170 | 171 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 172 | loop { 173 | if !self.pending.queue.is_empty() { 174 | break Poll::Ready(()); 175 | } 176 | match self.listener.as_mut() { 177 | Some(listener) => { 178 | ready!(listener.poll_unpin(cx)); 179 | self.listener = None; 180 | } 181 | None => self.listener = Some(self.pending.event.listen()), 182 | } 183 | } 184 | } 185 | } 186 | 187 | impl Default for Signals { 188 | fn default() -> Self { 189 | Self::new() 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /mizu/lib/paging/src/addr.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | alloc::Layout, 3 | mem, 4 | num::NonZeroUsize, 5 | ops::{Add, AddAssign, Deref, DerefMut, Range, Sub, SubAssign}, 6 | ptr::NonNull, 7 | slice, 8 | }; 9 | 10 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)] 11 | #[repr(transparent)] 12 | pub struct PAddr(usize); 13 | 14 | impl PAddr { 15 | #[inline] 16 | pub const fn new(addr: usize) -> Self { 17 | PAddr(addr) 18 | } 19 | 20 | pub const fn as_non_zero(self) -> Option { 21 | NonZeroUsize::new(self.0) 22 | } 23 | 24 | #[inline] 25 | #[track_caller] 26 | pub fn to_laddr(self, id_off: usize) -> LAddr { 27 | LAddr::from(self.0 + id_off) 28 | } 29 | 30 | pub fn in_page_offset(self) -> usize { 31 | self.0 & crate::PAGE_MASK 32 | } 33 | 34 | #[inline] 35 | pub fn val(self) -> usize { 36 | self.0 37 | } 38 | 39 | pub fn range_to_laddr(this: Range, id_off: usize) -> Range { 40 | this.start.to_laddr(id_off)..this.end.to_laddr(id_off) 41 | } 42 | } 43 | 44 | impl Add for PAddr { 45 | type Output = Self; 46 | 47 | fn add(self, rhs: usize) -> Self::Output { 48 | PAddr::new(*self + rhs) 49 | } 50 | } 51 | 52 | impl AddAssign for PAddr { 53 | fn add_assign(&mut self, rhs: usize) { 54 | **self += rhs 55 | } 56 | } 57 | 58 | impl Sub for PAddr { 59 | type Output = Self; 60 | 61 | fn sub(self, rhs: usize) -> Self::Output { 62 | PAddr::new(*self - rhs) 63 | } 64 | } 65 | 66 | impl SubAssign for PAddr { 67 | fn sub_assign(&mut self, rhs: usize) { 68 | **self -= rhs 69 | } 70 | } 71 | 72 | impl const Deref for PAddr { 73 | type Target = usize; 74 | 75 | #[inline] 76 | fn deref(&self) -> &Self::Target { 77 | &self.0 78 | } 79 | } 80 | 81 | impl const DerefMut for PAddr { 82 | #[inline] 83 | fn deref_mut(&mut self) -> &mut Self::Target { 84 | &mut self.0 85 | } 86 | } 87 | 88 | impl core::fmt::Debug for PAddr { 89 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 90 | write!(f, "PAddr({:#x})", self.0) 91 | } 92 | } 93 | 94 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 95 | #[repr(transparent)] 96 | pub struct LAddr(usize); 97 | 98 | impl LAddr { 99 | #[inline] 100 | pub fn new(ptr: *mut u8) -> Self { 101 | LAddr(ptr as _) 102 | } 103 | 104 | #[inline] 105 | pub fn val(&self) -> usize { 106 | self.0 107 | } 108 | 109 | #[inline] 110 | pub fn as_non_null(self) -> Option> { 111 | NonNull::new(self.0 as _) 112 | } 113 | 114 | /// # Safety 115 | /// 116 | /// `self` must be non-null. 117 | #[inline] 118 | pub unsafe fn as_non_null_unchecked(self) -> NonNull { 119 | NonNull::new_unchecked(self.0 as _) 120 | } 121 | 122 | /// transfer kernel va to corresponding la 123 | #[inline] 124 | pub fn to_paddr(self, id_off: usize) -> PAddr { 125 | PAddr(self.val() - id_off) 126 | } 127 | 128 | pub fn in_page_offset(self) -> usize { 129 | self.val() & crate::PAGE_MASK 130 | } 131 | 132 | pub fn to_range(self, layout: Layout) -> Range { 133 | self..Self::new(self.wrapping_add(layout.size())) 134 | } 135 | 136 | /// # Safety 137 | /// 138 | /// See ['slice::from_raw_parts'] for more info. 139 | pub unsafe fn as_slice<'a>(this: Range) -> &'a [u8] { 140 | unsafe { slice::from_raw_parts(*this.start, this.end.val() - this.start.val()) } 141 | } 142 | 143 | /// # Safety 144 | /// 145 | /// See ['slice::from_raw_parts_mut'] for more info. 146 | pub unsafe fn as_mut_slice<'a>(this: Range) -> &'a mut [u8] { 147 | unsafe { slice::from_raw_parts_mut(*this.start, this.end.val() - this.start.val()) } 148 | } 149 | 150 | pub fn from_slice(slice: &[T]) -> Range { 151 | let ptrs = slice.as_ptr_range(); 152 | LAddr::from(ptrs.start)..LAddr::from(ptrs.end) 153 | } 154 | } 155 | 156 | impl const Deref for LAddr { 157 | type Target = *mut u8; 158 | 159 | #[inline] 160 | fn deref(&self) -> &Self::Target { 161 | unsafe { mem::transmute(&self.0) } 162 | } 163 | } 164 | 165 | impl const DerefMut for LAddr { 166 | #[inline] 167 | fn deref_mut(&mut self) -> &mut Self::Target { 168 | unsafe { mem::transmute(&mut self.0) } 169 | } 170 | } 171 | 172 | impl const From for LAddr { 173 | #[inline] 174 | fn from(val: usize) -> Self { 175 | LAddr(val as _) 176 | } 177 | } 178 | 179 | impl const From for LAddr { 180 | #[inline] 181 | fn from(val: u64) -> Self { 182 | LAddr(val as _) 183 | } 184 | } 185 | 186 | impl From<*const T> for LAddr { 187 | #[inline] 188 | fn from(val: *const T) -> Self { 189 | LAddr(val as _) 190 | } 191 | } 192 | 193 | impl From<*mut T> for LAddr { 194 | #[inline] 195 | fn from(val: *mut T) -> Self { 196 | LAddr(val as _) 197 | } 198 | } 199 | 200 | impl From> for LAddr { 201 | #[inline] 202 | fn from(ptr: NonNull) -> Self { 203 | LAddr::new(ptr.as_ptr().cast()) 204 | } 205 | } 206 | 207 | impl Add for LAddr { 208 | type Output = Self; 209 | 210 | fn add(self, rhs: usize) -> Self::Output { 211 | LAddr::from(self.val() + rhs) 212 | } 213 | } 214 | 215 | impl AddAssign for LAddr { 216 | fn add_assign(&mut self, rhs: usize) { 217 | *self = *self + rhs; 218 | } 219 | } 220 | 221 | impl Sub for LAddr { 222 | type Output = Self; 223 | 224 | fn sub(self, rhs: usize) -> Self::Output { 225 | LAddr::from(self.val() - rhs) 226 | } 227 | } 228 | 229 | impl SubAssign for LAddr { 230 | fn sub_assign(&mut self, rhs: usize) { 231 | *self = *self - rhs; 232 | } 233 | } 234 | 235 | impl core::fmt::Debug for LAddr { 236 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 237 | write!(f, "LAddr({:#x})", self.0) 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /mizu/lib/ksc-macros/src/receiver.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Group, Ident, Span, TokenStream, TokenTree}; 2 | use syn::{ 3 | visit_mut::{self, VisitMut}, 4 | Block, ExprPath, Item, Macro, Pat, PatIdent, Path, Receiver, Signature, Token, TypePath, 5 | }; 6 | 7 | pub fn has_self_in_sig(sig: &mut Signature) -> bool { 8 | let mut visitor = HasSelf(false); 9 | visitor.visit_signature_mut(sig); 10 | visitor.0 11 | } 12 | 13 | pub fn has_self_in_block(block: &mut Block) -> bool { 14 | let mut visitor = HasSelf(false); 15 | visitor.visit_block_mut(block); 16 | visitor.0 17 | } 18 | 19 | struct HasSelf(bool); 20 | 21 | impl VisitMut for HasSelf { 22 | fn visit_expr_path_mut(&mut self, expr: &mut ExprPath) { 23 | self.0 |= expr.path.segments[0].ident == "Self"; 24 | visit_mut::visit_expr_path_mut(self, expr); 25 | } 26 | 27 | fn visit_type_path_mut(&mut self, ty: &mut TypePath) { 28 | self.0 |= ty.path.segments[0].ident == "Self"; 29 | visit_mut::visit_type_path_mut(self, ty); 30 | } 31 | 32 | fn visit_receiver_mut(&mut self, _arg: &mut Receiver) { 33 | self.0 = true; 34 | } 35 | 36 | fn visit_item_mut(&mut self, _: &mut Item) { 37 | // Do not recurse into nested items. 38 | } 39 | 40 | fn visit_macro_mut(&mut self, mac: &mut Macro) { 41 | if !contains_fn(mac.tokens.clone()) { 42 | self.0 |= has_self_in_token_stream(mac.tokens.clone()); 43 | } 44 | } 45 | } 46 | 47 | fn has_self_in_token_stream(tokens: TokenStream) -> bool { 48 | tokens.into_iter().any(|tt| match tt { 49 | TokenTree::Ident(ident) => ident == "Self", 50 | TokenTree::Group(group) => has_self_in_token_stream(group.stream()), 51 | _ => false, 52 | }) 53 | } 54 | 55 | pub fn mut_pat(pat: &mut Pat) -> Option { 56 | let mut visitor = HasMutPat(None); 57 | visitor.visit_pat_mut(pat); 58 | visitor.0 59 | } 60 | 61 | struct HasMutPat(Option); 62 | 63 | impl VisitMut for HasMutPat { 64 | fn visit_pat_ident_mut(&mut self, i: &mut PatIdent) { 65 | if let Some(m) = i.mutability { 66 | self.0 = Some(m); 67 | } else { 68 | visit_mut::visit_pat_ident_mut(self, i); 69 | } 70 | } 71 | } 72 | 73 | pub struct ReplaceSelf(pub Span); 74 | 75 | impl ReplaceSelf { 76 | #[cfg_attr(not(self_span_hack), allow(clippy::unused_self))] 77 | fn prepend_underscore_to_self(&self, ident: &mut Ident) -> bool { 78 | let modified = ident == "self"; 79 | if modified { 80 | *ident = Ident::new("__self", ident.span()); 81 | #[cfg(self_span_hack)] 82 | ident.set_span(self.0); 83 | } 84 | modified 85 | } 86 | 87 | fn visit_token_stream(&mut self, tokens: &mut TokenStream) -> bool { 88 | let mut out = Vec::new(); 89 | let mut modified = false; 90 | visit_token_stream_impl(self, tokens.clone(), &mut modified, &mut out); 91 | if modified { 92 | *tokens = TokenStream::from_iter(out); 93 | } 94 | return modified; 95 | 96 | fn visit_token_stream_impl( 97 | visitor: &mut ReplaceSelf, 98 | tokens: TokenStream, 99 | modified: &mut bool, 100 | out: &mut Vec, 101 | ) { 102 | for tt in tokens { 103 | match tt { 104 | TokenTree::Ident(mut ident) => { 105 | *modified |= visitor.prepend_underscore_to_self(&mut ident); 106 | out.push(TokenTree::Ident(ident)); 107 | } 108 | TokenTree::Group(group) => { 109 | let mut content = group.stream(); 110 | *modified |= visitor.visit_token_stream(&mut content); 111 | let mut new = Group::new(group.delimiter(), content); 112 | new.set_span(group.span()); 113 | out.push(TokenTree::Group(new)); 114 | } 115 | other => out.push(other), 116 | } 117 | } 118 | } 119 | } 120 | } 121 | 122 | impl VisitMut for ReplaceSelf { 123 | fn visit_ident_mut(&mut self, i: &mut Ident) { 124 | self.prepend_underscore_to_self(i); 125 | } 126 | 127 | fn visit_path_mut(&mut self, p: &mut Path) { 128 | if p.segments.len() == 1 { 129 | // Replace `self`, but not `self::function`. 130 | self.visit_ident_mut(&mut p.segments[0].ident); 131 | } 132 | for segment in &mut p.segments { 133 | self.visit_path_arguments_mut(&mut segment.arguments); 134 | } 135 | } 136 | 137 | fn visit_item_mut(&mut self, i: &mut Item) { 138 | // Visit `macro_rules!` because locally defined macros can refer to 139 | // `self`. 140 | // 141 | // Visit `futures::select` and similar select macros, which commonly 142 | // appear syntactically like an item despite expanding to an expression. 143 | // 144 | // Otherwise, do not recurse into nested items. 145 | if let Item::Macro(i) = i { 146 | if i.mac.path.is_ident("macro_rules") 147 | || i.mac.path.segments.last().unwrap().ident == "select" 148 | { 149 | self.visit_macro_mut(&mut i.mac); 150 | } 151 | } 152 | } 153 | 154 | fn visit_macro_mut(&mut self, mac: &mut Macro) { 155 | // We can't tell in general whether `self` inside a macro invocation 156 | // refers to the self in the argument list or a different self 157 | // introduced within the macro. Heuristic: if the macro input contains 158 | // `fn`, then `self` is more likely to refer to something other than the 159 | // outer function's self argument. 160 | if !contains_fn(mac.tokens.clone()) { 161 | self.visit_token_stream(&mut mac.tokens); 162 | } 163 | } 164 | } 165 | 166 | fn contains_fn(tokens: TokenStream) -> bool { 167 | tokens.into_iter().any(|tt| match tt { 168 | TokenTree::Ident(ident) => ident == "fn", 169 | TokenTree::Group(group) => contains_fn(group.stream()), 170 | _ => false, 171 | }) 172 | } 173 | --------------------------------------------------------------------------------