Compare commits
No commits in common. "master" and "2b5aa0350527b4d1db5b435d99f9ae519a600f2e" have entirely different histories.
master
...
2b5aa03505
257
Cargo.lock
generated
257
Cargo.lock
generated
@ -3,16 +3,11 @@
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
name = "address"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@ -22,266 +17,34 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cortex-a"
|
||||
version = "7.0.1"
|
||||
version = "6.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bd95fd055d118f77d4e4d527201b6ceccd13586b19b4dac1270f7081fef0f98"
|
||||
checksum = "509fc35485a2b4ddbacabe0bf2212cdfff88da93658608e5cc651afcb75b7733"
|
||||
dependencies = [
|
||||
"tock-registers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "endian-type-rs"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6419a5c75e40011b9fe0174db3fe24006ab122fbe1b7e9cc5974b338a755c76"
|
||||
|
||||
[[package]]
|
||||
name = "enum-repr"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bad30c9c0fa1aaf1ae5010dab11f1117b15d35faf62cda4bbbc53b9987950f18"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
||||
|
||||
[[package]]
|
||||
name = "fat32"
|
||||
name = "error"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fs-macros",
|
||||
"libsys",
|
||||
"vfs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fdt-rs"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99a40cabc11c8258822a593f5c51f2d9f4923e715ca9e2a0630cf77ae15f390b"
|
||||
dependencies = [
|
||||
"endian-type-rs",
|
||||
"fallible-iterator",
|
||||
"memoffset 0.5.6",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"rustc_version",
|
||||
"static_assertions",
|
||||
"unsafe_unwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs-macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"address",
|
||||
"cfg-if",
|
||||
"cortex-a",
|
||||
"fdt-rs",
|
||||
"fs-macros",
|
||||
"kernel-macros",
|
||||
"libsys",
|
||||
"memfs",
|
||||
"error",
|
||||
"tock-registers",
|
||||
"vfs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel-macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
dependencies = [
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsys"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"enum-repr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libusr"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libsys",
|
||||
"memoffset 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memfs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fs-macros",
|
||||
"libsys",
|
||||
"vfs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "osdev5"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tock-registers"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ee8fba06c1f4d0b396ef61a54530bb6b28f0dc61c38bc8bc5a5a48161e6282e"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe_unwrap"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1230ec65f13e0f9b28d789da20d2d419511893ea9dac2c1f4ef67b8b14e5da80"
|
||||
|
||||
[[package]]
|
||||
name = "user"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libsys",
|
||||
"libusr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vfs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fs-macros",
|
||||
"libsys",
|
||||
]
|
||||
|
10
Cargo.toml
10
Cargo.toml
@ -9,13 +9,7 @@ edition = "2018"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"fs/fat32",
|
||||
"fs/macros",
|
||||
"fs/memfs",
|
||||
"fs/vfs",
|
||||
"kernel",
|
||||
"kernel/macros",
|
||||
"libsys",
|
||||
"libusr",
|
||||
"user",
|
||||
"address",
|
||||
"error"
|
||||
]
|
||||
|
144
Makefile
144
Makefile
@ -1,144 +0,0 @@
|
||||
ARCH?=aarch64
|
||||
ifeq ($(ARCH),aarch64)
|
||||
MACH?=qemu
|
||||
endif
|
||||
GDB?=gdb-multiarch
|
||||
|
||||
LLVM_BASE=$(shell llvm-config --bindir)
|
||||
CLANG=clang-14
|
||||
LDLLD=ld.lld-12
|
||||
OBJCOPY=$(LLVM_BASE)/llvm-objcopy
|
||||
MKIMAGE?=mkimage
|
||||
|
||||
PROFILE?=debug
|
||||
O=target/$(ARCH)-$(MACH)/$(PROFILE)
|
||||
|
||||
CARGO_COMMON_OPTS=
|
||||
ifeq ($(PROFILE),release)
|
||||
CARGO_COMMON_OPTS+=--release
|
||||
endif
|
||||
ifeq ($(VERBOSE),1)
|
||||
CARGO_COMMON_OPTS+=--features verbose
|
||||
endif
|
||||
|
||||
CARGO_BUILD_OPTS=$(CARGO_COMMON_OPTS) \
|
||||
--target=../etc/$(ARCH)-$(MACH).json
|
||||
ifneq ($(MACH),)
|
||||
CARGO_BUILD_OPTS+=--features mach_$(MACH)
|
||||
endif
|
||||
|
||||
QEMU_OPTS=-s
|
||||
ifeq ($(ARCH),x86_64)
|
||||
$(error TODO)
|
||||
else
|
||||
ifeq ($(MACH),qemu)
|
||||
QEMU_OPTS+=-kernel $(O)/kernel.bin \
|
||||
-initrd $(O)/initrd.img \
|
||||
-M virt,virtualization=on \
|
||||
-cpu cortex-a72 \
|
||||
-m 512 \
|
||||
-serial mon:stdio \
|
||||
-device qemu-xhci \
|
||||
-display none \
|
||||
-net none
|
||||
endif
|
||||
ifeq ($(MACH),rpi3)
|
||||
QEMU_OPTS+=-kernel $(O)/kernel.bin \
|
||||
-initrd $(O)/initrd.img \
|
||||
-M raspi3b \
|
||||
-serial mon:stdio \
|
||||
-display none \
|
||||
-net none
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(QEMU_SDCARD),)
|
||||
QEMU_OPTS+=-drive if=sd,file=$(QEMU_SDCARD)
|
||||
endif
|
||||
|
||||
ifeq ($(QEMU_DINT),1)
|
||||
QEMU_OPTS+=-d int
|
||||
endif
|
||||
ifeq ($(QEMU_PAUSE),1)
|
||||
QEMU_OPTS+=-S
|
||||
endif
|
||||
|
||||
.PHONY: address error etc kernel src
|
||||
|
||||
all: kernel initrd
|
||||
|
||||
kernel:
|
||||
cd kernel && cargo build $(CARGO_BUILD_OPTS)
|
||||
ifeq ($(ARCH),aarch64)
|
||||
$(LLVM_BASE)/llvm-strip -o $(O)/kernel.strip $(O)/kernel
|
||||
$(LLVM_BASE)/llvm-size $(O)/kernel.strip
|
||||
$(OBJCOPY) -O binary $(O)/kernel.strip $(O)/kernel.bin
|
||||
endif
|
||||
ifeq ($(MACH),orangepi3)
|
||||
$(MKIMAGE) \
|
||||
-A arm64 \
|
||||
-O linux \
|
||||
-T kernel \
|
||||
-C none \
|
||||
-a 0x48000000 \
|
||||
-e 0x48000000 \
|
||||
-n kernel \
|
||||
-d $(O)/kernel.bin \
|
||||
$(O)/uImage
|
||||
endif
|
||||
|
||||
initrd:
|
||||
cd user && cargo build \
|
||||
--target=../etc/$(ARCH)-osdev5.json \
|
||||
-Z build-std=core,alloc,compiler_builtins \
|
||||
$(CARGO_COMMON_OPTS)
|
||||
mkdir -p $(O)/rootfs/bin $(O)/rootfs/sbin $(O)/rootfs/dev $(O)/rootfs/etc $(O)/rootfs/sys
|
||||
cp etc/initrd/passwd $(O)/rootfs/etc
|
||||
cp etc/initrd/shadow $(O)/rootfs/etc
|
||||
touch $(O)/rootfs/dev/.do_not_remove
|
||||
touch $(O)/rootfs/sys/.do_not_remove
|
||||
cp target/$(ARCH)-osdev5/$(PROFILE)/init $(O)/rootfs/init
|
||||
cp target/$(ARCH)-osdev5/$(PROFILE)/shell $(O)/rootfs/bin
|
||||
cp target/$(ARCH)-osdev5/$(PROFILE)/fuzzy $(O)/rootfs/bin
|
||||
cp target/$(ARCH)-osdev5/$(PROFILE)/ls $(O)/rootfs/bin
|
||||
cp target/$(ARCH)-osdev5/$(PROFILE)/cat $(O)/rootfs/bin
|
||||
cp target/$(ARCH)-osdev5/$(PROFILE)/hexd $(O)/rootfs/bin
|
||||
cp target/$(ARCH)-osdev5/$(PROFILE)/login $(O)/rootfs/sbin
|
||||
cd $(O)/rootfs && tar cf ../initrd.img `find -type f -printf "%P\n"`
|
||||
ifeq ($(MACH),orangepi3)
|
||||
$(MKIMAGE) \
|
||||
-A arm64 \
|
||||
-O linux \
|
||||
-T ramdisk \
|
||||
-C none \
|
||||
-a 0x80000000 \
|
||||
-n initrd \
|
||||
-d $(O)/initrd.img \
|
||||
$(O)/uRamdisk
|
||||
endif
|
||||
|
||||
test:
|
||||
cd fs/vfs && cargo test
|
||||
cd fs/memfs && cargo test
|
||||
cd fs/fat32 && cargo test
|
||||
|
||||
clean:
|
||||
cargo clean
|
||||
|
||||
doc:
|
||||
cd kernel && cargo doc --all-features --target=../etc/$(ARCH)-$(MACH).json
|
||||
|
||||
doc-open:
|
||||
cd kernel && cargo doc --open --all-features --target=../etc/$(ARCH)-$(MACH).json
|
||||
|
||||
clippy:
|
||||
cd kernel && cargo clippy $(CARGO_BUILD_OPTS)
|
||||
cd user && cargo clippy \
|
||||
--target=../etc/$(ARCH)-osdev5.json \
|
||||
-Zbuild-std=core,alloc,compiler_builtins $(CARGO_COMMON_OPTS)
|
||||
|
||||
qemu: all
|
||||
$(QEMU_PREFIX)qemu-system-$(ARCH) $(QEMU_OPTS)
|
||||
|
||||
gdb: all
|
||||
$(GDB) -x etc/gdbrc $(O)/kernel
|
@ -1,13 +1,9 @@
|
||||
[package]
|
||||
name = "libsys"
|
||||
name = "address"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bitflags = "^1.3.0"
|
||||
enum-repr = "^0.2.6"
|
||||
|
||||
[features]
|
||||
user = []
|
||||
error = { path = "../error" }
|
0
address/src/base/mod.rs
Normal file
0
address/src/base/mod.rs
Normal file
21
address/src/lib.rs
Normal file
21
address/src/lib.rs
Normal file
@ -0,0 +1,21 @@
|
||||
//! Type-safe wrappers for different address kinds
|
||||
#![no_std]
|
||||
#![feature(
|
||||
step_trait,
|
||||
const_fn_trait_bound,
|
||||
const_trait_impl,
|
||||
const_panic
|
||||
)]
|
||||
// #![warn(missing_docs)]
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
pub mod virt;
|
||||
pub mod phys;
|
||||
|
||||
trait Address {}
|
||||
|
||||
pub use phys::PhysicalAddress;
|
||||
pub use virt::{AddressSpace, NoTrivialConvert, TrivialConvert, VirtualAddress};
|
177
address/src/phys.rs
Normal file
177
address/src/phys.rs
Normal file
@ -0,0 +1,177 @@
|
||||
use crate::{AddressSpace, TrivialConvert, VirtualAddress};
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt;
|
||||
use core::iter::Step;
|
||||
use core::ops::{Add, AddAssign, Sub, SubAssign};
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(PartialEq, PartialOrd, Copy, Clone)]
|
||||
pub struct PhysicalAddress(usize);
|
||||
|
||||
// Arithmetic
|
||||
impl<A: Into<usize>> Add<A> for PhysicalAddress {
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn add(self, rhs: A) -> Self {
|
||||
// Will panic on overflow
|
||||
Self::from(self.0 + rhs.into())
|
||||
}
|
||||
}
|
||||
impl<A: Into<usize>> AddAssign<A> for PhysicalAddress {
|
||||
#[inline(always)]
|
||||
fn add_assign(&mut self, rhs: A) {
|
||||
// Will panic on overflow
|
||||
*self = Self::from(self.0 + rhs.into());
|
||||
}
|
||||
}
|
||||
impl Sub<usize> for PhysicalAddress {
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn sub(self, rhs: usize) -> Self {
|
||||
Self::from(self.0 - rhs)
|
||||
}
|
||||
}
|
||||
impl SubAssign<usize> for PhysicalAddress {
|
||||
#[inline(always)]
|
||||
fn sub_assign(&mut self, rhs: usize) {
|
||||
*self = Self::from(self.0 - rhs);
|
||||
}
|
||||
}
|
||||
|
||||
// Construction
|
||||
impl From<usize> for PhysicalAddress {
|
||||
fn from(p: usize) -> Self {
|
||||
Self(p)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
impl From<u64> for PhysicalAddress {
|
||||
fn from(p: u64) -> Self {
|
||||
Self(p as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl PhysicalAddress {
|
||||
pub const fn new(value: usize) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
|
||||
pub const fn add(self, value: usize) -> Self {
|
||||
Self(self.0 + value)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn diff(start: PhysicalAddress, end: PhysicalAddress) -> isize {
|
||||
if end >= start {
|
||||
isize::try_from(end.0 - start.0).expect("Address subtraction overflowed")
|
||||
} else {
|
||||
-isize::try_from(start.0 - end.0).expect("Address subtraction overflowed")
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn diff_unchecked(start: PhysicalAddress, end: PhysicalAddress) -> isize {
|
||||
end.0 as isize - start.0 as isize
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn is_paligned(self) -> bool {
|
||||
return self.0 & 0xFFF == 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn page_index(self) -> usize {
|
||||
self.0 >> 12
|
||||
}
|
||||
}
|
||||
|
||||
// Trivial conversion PhysicalAddress -> VirtualAddress
|
||||
impl<T: AddressSpace + TrivialConvert> const From<PhysicalAddress> for VirtualAddress<T> {
|
||||
fn from(p: PhysicalAddress) -> Self {
|
||||
VirtualAddress::from(p.0 + T::OFFSET)
|
||||
}
|
||||
}
|
||||
|
||||
impl const From<PhysicalAddress> for usize {
|
||||
#[inline(always)]
|
||||
fn from(p: PhysicalAddress) -> Self {
|
||||
p.0 as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
impl From<PhysicalAddress> for u64 {
|
||||
#[inline(always)]
|
||||
fn from(p: PhysicalAddress) -> Self {
|
||||
p.0 as u64
|
||||
}
|
||||
}
|
||||
|
||||
// Formatting
|
||||
impl fmt::Debug for PhysicalAddress {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "<phys {:#018x}>", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
// Step
|
||||
impl Step for PhysicalAddress {
|
||||
#[inline]
|
||||
fn steps_between(_p0: &Self, _p1: &Self) -> Option<usize> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn forward_checked(p: Self, steps: usize) -> Option<Self> {
|
||||
p.0.checked_add(steps).map(Self::from)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn backward_checked(p: Self, steps: usize) -> Option<Self> {
|
||||
p.0.checked_sub(steps).map(Self::from)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{AddressSpace, NoTrivialConvert, TrivialConvert, VirtualAddress};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, PartialOrd)]
|
||||
struct S0;
|
||||
impl AddressSpace for S0 {
|
||||
const NAME: &'static str = "S0";
|
||||
const OFFSET: usize = 0x8000;
|
||||
const LIMIT: usize = Self::OFFSET + 0x4000;
|
||||
}
|
||||
impl TrivialConvert for S0 {}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, PartialOrd)]
|
||||
struct S1;
|
||||
impl AddressSpace for S1 {
|
||||
const NAME: &'static str = "S1";
|
||||
const OFFSET: usize = 0;
|
||||
const LIMIT: usize = 0;
|
||||
}
|
||||
impl NoTrivialConvert for S1 {}
|
||||
|
||||
#[test]
|
||||
fn test_virt_convert_valid() {
|
||||
let p0 = PhysicalAddress::from(0x1234usize);
|
||||
assert_eq!(
|
||||
VirtualAddress::<S0>::from(p0),
|
||||
VirtualAddress::<S0>::from(0x9234usize)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_virt_convert_invalid() {
|
||||
let p0 = PhysicalAddress::from(0x4321usize);
|
||||
let _v = VirtualAddress::<S0>::from(p0);
|
||||
}
|
||||
}
|
364
address/src/virt.rs
Normal file
364
address/src/virt.rs
Normal file
@ -0,0 +1,364 @@
|
||||
use super::PhysicalAddress;
|
||||
use core::convert::TryFrom;
|
||||
use core::fmt;
|
||||
use core::iter::Step;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Add, AddAssign, Neg, Sub, SubAssign};
|
||||
|
||||
pub trait AddressSpace: Copy + Clone + PartialEq + PartialOrd {
|
||||
const NAME: &'static str;
|
||||
const OFFSET: usize;
|
||||
const LIMIT: usize;
|
||||
}
|
||||
|
||||
pub trait NoTrivialConvert {}
|
||||
pub trait TrivialConvert {}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone, PartialOrd, PartialEq)]
|
||||
pub struct VirtualAddress<Kind: AddressSpace>(usize, PhantomData<Kind>);
|
||||
|
||||
// Arithmetic
|
||||
impl<T: AddressSpace> Add<usize> for VirtualAddress<T> {
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn add(self, rhs: usize) -> Self {
|
||||
// Will panic on overflow
|
||||
Self::from(self.0 + rhs)
|
||||
}
|
||||
}
|
||||
impl<T: AddressSpace> AddAssign<usize> for VirtualAddress<T> {
|
||||
#[inline(always)]
|
||||
fn add_assign(&mut self, rhs: usize) {
|
||||
// Will panic on overflow
|
||||
*self = Self::from(self.0 + rhs);
|
||||
}
|
||||
}
|
||||
impl<T: AddressSpace> Sub<usize> for VirtualAddress<T> {
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn sub(self, rhs: usize) -> Self {
|
||||
// Will panic on underflow
|
||||
Self::from(self.0 - rhs)
|
||||
}
|
||||
}
|
||||
impl<T: AddressSpace> SubAssign<usize> for VirtualAddress<T> {
|
||||
#[inline(always)]
|
||||
fn sub_assign(&mut self, rhs: usize) {
|
||||
// Will panic on underflow
|
||||
*self = Self::from(self.0 - rhs);
|
||||
}
|
||||
}
|
||||
|
||||
// Trivial conversion VirtualAddress -> PhysicalAddress
|
||||
impl<T: AddressSpace + TrivialConvert> From<VirtualAddress<T>> for PhysicalAddress {
|
||||
#[inline(always)]
|
||||
fn from(virt: VirtualAddress<T>) -> Self {
|
||||
assert!(virt.0 < T::LIMIT);
|
||||
PhysicalAddress::from(virt.0 - T::OFFSET)
|
||||
}
|
||||
}
|
||||
|
||||
// Formatting
|
||||
impl<T: AddressSpace> fmt::Debug for VirtualAddress<T> {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "<{} {:#018x}>", T::NAME, self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AddressSpace> VirtualAddress<T> {
|
||||
#[inline(always)]
|
||||
pub const fn null() -> Self {
|
||||
Self(0, PhantomData)
|
||||
}
|
||||
|
||||
pub fn try_subtract(self, p: usize) -> Option<Self> {
|
||||
let (res, overflow) = self.0.overflowing_sub(p);
|
||||
if overflow || res < T::OFFSET || res >= T::LIMIT {
|
||||
None
|
||||
} else {
|
||||
Some(Self(res, PhantomData))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn diff(start: Self, end: Self) -> isize {
|
||||
if end >= start {
|
||||
isize::try_from(end.0 - start.0).expect("Address subtraction overflowed")
|
||||
} else {
|
||||
-isize::try_from(start.0 - end.0).expect("Address subtraction overflowed")
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn try_diff(start: Self, end: Self) -> Option<isize> {
|
||||
if end >= start {
|
||||
isize::try_from(end.0 - start.0).ok()
|
||||
} else {
|
||||
isize::try_from(start.0 - end.0).map(Neg::neg).ok()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn as_slice_mut<U>(self, count: usize) -> &'static mut [U] {
|
||||
core::slice::from_raw_parts_mut(self.0 as *mut _, count)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_mut_ptr<U>(self) -> *mut U {
|
||||
self.0 as *mut U
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn as_ptr<U>(self) -> *const U {
|
||||
self.0 as *const U
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn as_mut<U>(self) -> Option<&'static mut U> {
|
||||
(self.0 as *mut U).as_mut()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn from_ptr<U>(r: *const U) -> Self {
|
||||
Self::from(r as usize)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn from_ref<U>(r: &U) -> Self {
|
||||
Self(r as *const U as usize, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
// Step
|
||||
impl<T: AddressSpace> Step for VirtualAddress<T> {
|
||||
#[inline]
|
||||
fn steps_between(_p0: &Self, _p1: &Self) -> Option<usize> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn forward_checked(p: Self, steps: usize) -> Option<Self> {
|
||||
p.0.checked_add(steps).map(Self::from)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn backward_checked(p: Self, steps: usize) -> Option<Self> {
|
||||
p.0.checked_sub(steps).map(Self::from)
|
||||
}
|
||||
}
|
||||
|
||||
// Conversion into VirtualAddress
|
||||
impl<T: AddressSpace> const From<usize> for VirtualAddress<T> {
|
||||
#[inline(always)]
|
||||
fn from(p: usize) -> Self {
|
||||
if T::LIMIT > 0 {
|
||||
assert!(p >= T::OFFSET && p < T::LIMIT);
|
||||
}
|
||||
Self(p, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
impl<T: AddressSpace> From<u64> for VirtualAddress<T> {
|
||||
#[inline(always)]
|
||||
fn from(p: u64) -> Self {
|
||||
Self::from(p as usize)
|
||||
}
|
||||
}
|
||||
|
||||
// Conversion from VirtualAddress
|
||||
impl<T: AddressSpace> From<VirtualAddress<T>> for usize {
|
||||
#[inline(always)]
|
||||
fn from(p: VirtualAddress<T>) -> Self {
|
||||
p.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
impl<T: AddressSpace> From<VirtualAddress<T>> for u64 {
|
||||
#[inline(always)]
|
||||
fn from(p: VirtualAddress<T>) -> Self {
|
||||
p.0 as u64
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::PhysicalAddress;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, PartialOrd)]
|
||||
struct S0;
|
||||
impl AddressSpace for S0 {
|
||||
const NAME: &'static str = "S0";
|
||||
const OFFSET: usize = 0x8000;
|
||||
const LIMIT: usize = Self::OFFSET + 0x4000;
|
||||
}
|
||||
impl TrivialConvert for S0 {}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, PartialOrd)]
|
||||
struct S1;
|
||||
impl AddressSpace for S1 {
|
||||
const NAME: &'static str = "S1";
|
||||
const OFFSET: usize = 0;
|
||||
const LIMIT: usize = 0;
|
||||
}
|
||||
impl NoTrivialConvert for S1 {}
|
||||
|
||||
#[test]
|
||||
fn test_trivial_construct_valid() {
|
||||
for i in 0x8000usize..0xC000 {
|
||||
VirtualAddress::<S0>::from(i);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_trivial_construct_invalid_0() {
|
||||
let _v = VirtualAddress::<S0>::from(0x1234usize);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_trivial_construct_invalid_1() {
|
||||
let _v = VirtualAddress::<S0>::from(0xD123usize);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trivial_convert() {
|
||||
let v0 = VirtualAddress::<S0>::from(0x8123usize);
|
||||
assert_eq!(
|
||||
PhysicalAddress::from(v0),
|
||||
PhysicalAddress::from(0x123usize)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_valid() {
|
||||
let v0 = VirtualAddress::<S0>::from(0x8100usize);
|
||||
assert_eq!(VirtualAddress::<S0>::from(0x8223usize), v0 + 0x123usize);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_add_overflow() {
|
||||
let v0 = VirtualAddress::<S0>::from(0x8100usize);
|
||||
let _v = v0 - 0xF123usize;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subtract_valid() {
|
||||
let v0 = VirtualAddress::<S0>::from(0x8100usize);
|
||||
assert_eq!(VirtualAddress::<S0>::from(0x8023usize), v0 - 0xDDusize);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_subtract_overflow() {
|
||||
let v0 = VirtualAddress::<S0>::from(0x8100usize);
|
||||
let _v = v0 - 0x1234usize;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_subtract() {
|
||||
let v0 = VirtualAddress::<S0>::from(0x8100usize);
|
||||
assert_eq!(v0.try_subtract(0x1234usize), None);
|
||||
assert_eq!(
|
||||
v0.try_subtract(0x12usize),
|
||||
Some(VirtualAddress::<S0>::from(0x80EEusize))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_assign_valid() {
|
||||
let mut v0 = VirtualAddress::<S0>::from(0x8100usize);
|
||||
v0 += 0x123usize;
|
||||
assert_eq!(v0, VirtualAddress::<S0>::from(0x8223usize));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sub_assign_valid() {
|
||||
let mut v0 = VirtualAddress::<S0>::from(0x8321usize);
|
||||
v0 -= 0x123usize;
|
||||
assert_eq!(v0, VirtualAddress::<S0>::from(0x81FEusize));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_sub_assign_overflow() {
|
||||
let mut v0 = VirtualAddress::<S0>::from(0x8321usize);
|
||||
v0 -= 0x1234usize;
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_add_assign_overflow() {
|
||||
let mut v0 = VirtualAddress::<S0>::from(0x8321usize);
|
||||
v0 += 0xF234usize;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format() {
|
||||
let v0 = VirtualAddress::<S0>::from(0x8123usize);
|
||||
assert_eq!(&format!("{:?}", v0), "<S0 0x0000000000008123>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_diff() {
|
||||
let v0 = VirtualAddress::<S0>::from(0x8123usize);
|
||||
let v1 = VirtualAddress::<S0>::from(0x8321usize);
|
||||
|
||||
// Ok
|
||||
assert_eq!(VirtualAddress::diff(v0, v1), 510);
|
||||
assert_eq!(VirtualAddress::diff(v1, v0), -510);
|
||||
assert_eq!(VirtualAddress::diff(v0, v0), 0);
|
||||
assert_eq!(VirtualAddress::diff(v1, v1), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_diff_overflow() {
|
||||
let v0 = VirtualAddress::<S1>::from(0usize);
|
||||
let v1 = VirtualAddress::<S1>::from(usize::MAX);
|
||||
|
||||
let _v = VirtualAddress::diff(v0, v1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_step() {
|
||||
let mut count = 0;
|
||||
for _ in VirtualAddress::<S0>::from(0x8000usize)..VirtualAddress::<S0>::from(0x8300usize) {
|
||||
count += 1;
|
||||
}
|
||||
assert_eq!(count, 0x300);
|
||||
|
||||
let mut count = 0;
|
||||
for _ in (VirtualAddress::<S0>::from(0x8000usize)..VirtualAddress::<S0>::from(0x8300usize))
|
||||
.step_by(0x100)
|
||||
{
|
||||
count += 1;
|
||||
}
|
||||
assert_eq!(count, 3);
|
||||
|
||||
let mut count = 0;
|
||||
for _ in
|
||||
(VirtualAddress::<S0>::from(0x8000usize)..VirtualAddress::<S0>::from(0x8300usize)).rev()
|
||||
{
|
||||
count += 1;
|
||||
}
|
||||
assert_eq!(count, 0x300);
|
||||
|
||||
let mut count = 0;
|
||||
for _ in (VirtualAddress::<S0>::from(0x8000usize)..VirtualAddress::<S0>::from(0x8300usize))
|
||||
.rev()
|
||||
.step_by(0x100)
|
||||
{
|
||||
count += 1;
|
||||
}
|
||||
assert_eq!(count, 3);
|
||||
}
|
||||
}
|
35
build.sh
Executable file
35
build.sh
Executable file
@ -0,0 +1,35 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
. etc/common.sh
|
||||
|
||||
CARGO_OPTS="--target ../etc/${ARCH}-${MACH}.json"
|
||||
CARGO_FEATURES=""
|
||||
LLVM_BIN=$(llvm-config --bindir)
|
||||
|
||||
if [ ! "$MACH" = "none" ]; then
|
||||
CARGO_FEATURES="${CARGO_FEATURES}mach_${MACH},"
|
||||
fi
|
||||
|
||||
CARGO_OPTS="$CARGO_OPTS --features=$CARGO_FEATURES"
|
||||
|
||||
if [ "$PROFILE" = "release" ]; then
|
||||
CARGO_OPTS="$CARGO_OPTS --release"
|
||||
fi
|
||||
|
||||
cd kernel
|
||||
cargo build ${CARGO_OPTS}
|
||||
cd ..
|
||||
|
||||
case $ARCH in
|
||||
aarch64)
|
||||
${LLVM_BIN}/llvm-objcopy -O binary ${OUT_DIR}/kernel ${OUT_DIR}/kernel.bin
|
||||
;;
|
||||
x86_64)
|
||||
mkdir -p ${OUT_DIR}/cdrom/boot/grub
|
||||
cp etc/x86_64-none.grub ${OUT_DIR}/cdrom/boot/grub/grub.cfg
|
||||
cp ${OUT_DIR}/kernel ${OUT_DIR}/cdrom/boot/kernel.elf
|
||||
grub-mkrescue -o ${OUT_DIR}/cdrom.iso ${OUT_DIR}/cdrom
|
||||
;;
|
||||
esac
|
@ -1,13 +1,8 @@
|
||||
[package]
|
||||
name = "kernel-macros"
|
||||
name = "error"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "^1.0.81"
|
||||
quote = "^1.0.10"
|
10
error/src/lib.rs
Normal file
10
error/src/lib.rs
Normal file
@ -0,0 +1,10 @@
|
||||
#![no_std]
|
||||
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
pub enum Errno {
|
||||
InvalidArgument,
|
||||
DoesNotExist,
|
||||
NotADirectory,
|
||||
OutOfMemory,
|
||||
WouldBlock,
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"arch": "aarch64",
|
||||
"data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
|
||||
"disable-redzone": true,
|
||||
"executables": true,
|
||||
"features": "+strict-align,+neon,+fp-armv8",
|
||||
"linker": "rust-lld",
|
||||
"linker-flavor": "ld.lld",
|
||||
"llvm-target": "aarch64-unknown-none",
|
||||
"max-atomic-width": 128,
|
||||
"panic-strategy": "abort",
|
||||
"relocation-model": "static",
|
||||
"target-pointer-width": "64",
|
||||
"pre-link-args": {
|
||||
"ld.lld": [ "-Tetc/aarch64-orangepi3.ld" ]
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
ENTRY(_entry);
|
||||
|
||||
KERNEL_OFFSET = 0xFFFFFF8000000000;
|
||||
BASE_OFFSET = 0x48000000;
|
||||
|
||||
SECTIONS {
|
||||
. = BASE_OFFSET;
|
||||
|
||||
.text.lower : {
|
||||
*(.text._entry)
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
. = . + KERNEL_OFFSET;
|
||||
|
||||
PROVIDE(__kernel_start = .);
|
||||
|
||||
.text : AT(. - KERNEL_OFFSET) {
|
||||
*(.text._entry_upper)
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.rodata : AT(. - KERNEL_OFFSET) {
|
||||
*(.rodata*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.data : AT(. - KERNEL_OFFSET) {
|
||||
*(.data*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_start_phys = . - KERNEL_OFFSET);
|
||||
PROVIDE(__bss_start = .);
|
||||
.bss : AT(. - KERNEL_OFFSET) {
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
PROVIDE(__bss_end_phys = . - KERNEL_OFFSET);
|
||||
|
||||
PROVIDE(__kernel_end = .);
|
||||
PROVIDE(__kernel_end_phys = . - KERNEL_OFFSET);
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"arch": "aarch64",
|
||||
"data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
|
||||
"disable-redzone": true,
|
||||
"executables": true,
|
||||
"features": "+strict-align,+neon,+fp-armv8",
|
||||
"linker": "rust-lld",
|
||||
"linker-flavor": "ld.lld",
|
||||
"llvm-target": "aarch64-unknown-none",
|
||||
"max-atomic-width": 128,
|
||||
"panic-strategy": "abort",
|
||||
"relocation-model": "static",
|
||||
"target-pointer-width": "64",
|
||||
"pre-link-args": {
|
||||
"ld.lld": [ "-Tetc/aarch64-osdev5.ld" ]
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
ENTRY(_start);
|
||||
|
||||
PHDRS {
|
||||
text PT_LOAD ;
|
||||
rodata PT_LOAD ;
|
||||
data PT_LOAD ;
|
||||
}
|
||||
|
||||
SECTIONS {
|
||||
. = 0x400000;
|
||||
|
||||
.text : {
|
||||
*(.text._start)
|
||||
*(.text*)
|
||||
*(.eh_frame*)
|
||||
} :text
|
||||
|
||||
. = ALIGN(0x1000);
|
||||
.rodata : {
|
||||
*(.rodata*)
|
||||
} :rodata
|
||||
|
||||
. = ALIGN(0x1000);
|
||||
.data : {
|
||||
*(.data*)
|
||||
} :data
|
||||
|
||||
.bss : {
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
} :data
|
||||
}
|
@ -9,7 +9,7 @@
|
||||
"llvm-target": "aarch64-unknown-none",
|
||||
"max-atomic-width": 128,
|
||||
"panic-strategy": "abort",
|
||||
"relocation-model": "pic",
|
||||
"relocation-model": "static",
|
||||
"target-pointer-width": "64",
|
||||
"pre-link-args": {
|
||||
"ld.lld": [ "-Tetc/aarch64-qemu.ld" ]
|
||||
|
@ -1,22 +1,14 @@
|
||||
ENTRY(_entry);
|
||||
|
||||
KERNEL_OFFSET = 0xFFFFFF8000000000;
|
||||
BASE_OFFSET = 0x40080000;
|
||||
KERNEL_OFFSET = 0; /* 0xFFFFFF8000000000; */
|
||||
|
||||
SECTIONS {
|
||||
. = BASE_OFFSET;
|
||||
|
||||
.text.lower : {
|
||||
*(.text._entry)
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
. = . + KERNEL_OFFSET;
|
||||
. = 0x40080000 + KERNEL_OFFSET;
|
||||
|
||||
PROVIDE(__kernel_start = .);
|
||||
|
||||
.text : AT(. - KERNEL_OFFSET) {
|
||||
*(.text._entry_upper)
|
||||
*(.text._entry)
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
@ -31,15 +23,13 @@ SECTIONS {
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_start_phys = . - KERNEL_OFFSET);
|
||||
PROVIDE(__bss_start = .);
|
||||
.bss : AT(. - KERNEL_OFFSET) {
|
||||
PROVIDE(__bss_start = .);
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_end = .);
|
||||
}
|
||||
PROVIDE(__bss_end_phys = . - KERNEL_OFFSET);
|
||||
|
||||
PROVIDE(__kernel_end = .);
|
||||
PROVIDE(__kernel_end_phys = . - KERNEL_OFFSET);
|
||||
}
|
||||
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"arch": "aarch64",
|
||||
"data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
|
||||
"disable-redzone": true,
|
||||
"executables": true,
|
||||
"features": "+strict-align,+neon,+fp-armv8",
|
||||
"linker": "rust-lld",
|
||||
"linker-flavor": "ld.lld",
|
||||
"llvm-target": "aarch64-unknown-none",
|
||||
"max-atomic-width": 128,
|
||||
"panic-strategy": "abort",
|
||||
"relocation-model": "static",
|
||||
"target-pointer-width": "64",
|
||||
"pre-link-args": {
|
||||
"ld.lld": [ "-Tetc/aarch64-rpi3.ld" ]
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
ENTRY(_entry);
|
||||
|
||||
KERNEL_OFFSET = 0xFFFFFF8000000000;
|
||||
BASE_OFFSET = 0x80000;
|
||||
|
||||
SECTIONS {
|
||||
. = BASE_OFFSET;
|
||||
|
||||
.text.lower : {
|
||||
*(.text._entry)
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
. = . + KERNEL_OFFSET;
|
||||
|
||||
PROVIDE(__kernel_start = .);
|
||||
|
||||
.text : AT(. - KERNEL_OFFSET) {
|
||||
*(.text._entry_upper)
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.rodata : AT(. - KERNEL_OFFSET) {
|
||||
*(.rodata*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.data : AT(. - KERNEL_OFFSET) {
|
||||
*(.data*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_start_phys = . - KERNEL_OFFSET);
|
||||
PROVIDE(__bss_start = .);
|
||||
.bss : AT(. - KERNEL_OFFSET) {
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
PROVIDE(__bss_end_phys = . - KERNEL_OFFSET);
|
||||
|
||||
PROVIDE(__kernel_end = .);
|
||||
PROVIDE(__kernel_end_phys = . - KERNEL_OFFSET);
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
root:0:0:root:/:/bin/shell
|
||||
alnyan:1000:1000:alnyan:/:/bin/shell
|
@ -1,2 +0,0 @@
|
||||
root:toor
|
||||
alnyan:
|
@ -1,11 +0,0 @@
|
||||
[package]
|
||||
name = "fat32"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
vfs = { path = "../vfs" }
|
||||
fs-macros = { path = "../macros" }
|
||||
libsys = { path = "../../libsys" }
|
@ -1,30 +0,0 @@
|
||||
use libsys::mem::{read_le16, read_le32};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Bpb {
|
||||
sectors_per_cluster: u8,
|
||||
reserved_sectors: u16,
|
||||
fat_count: u8,
|
||||
sectors_per_fat: u32,
|
||||
}
|
||||
|
||||
impl Bpb {
|
||||
pub fn from_sector(data: &[u8]) -> Self {
|
||||
Self {
|
||||
fat_count: data[16],
|
||||
reserved_sectors: read_le16(&data[14..]),
|
||||
sectors_per_cluster: data[13],
|
||||
sectors_per_fat: read_le32(&data[36..]),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn cluster_base_sector(&self, cluster: u32) -> u32 {
|
||||
let first_data_sector =
|
||||
self.reserved_sectors as u32 + (self.fat_count as u32 * self.sectors_per_fat as u32);
|
||||
((cluster - 2) * self.sectors_per_cluster as u32) + first_data_sector
|
||||
}
|
||||
|
||||
pub const fn sectors_per_cluster(&self) -> u8 {
|
||||
self.sectors_per_cluster
|
||||
}
|
||||
}
|
@ -1,181 +0,0 @@
|
||||
use crate::{Bpb, FileInode};
|
||||
use alloc::{borrow::ToOwned, boxed::Box, string::String};
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
ioctl::IoctlCmd,
|
||||
mem::{read_le16, read_le32},
|
||||
stat::{OpenFlags, Stat},
|
||||
};
|
||||
use vfs::{BlockDevice, Vnode, VnodeImpl, VnodeKind, VnodeRef};
|
||||
|
||||
pub struct DirectoryInode {
|
||||
pub cluster: u32,
|
||||
}
|
||||
|
||||
pub struct FatIterator<'a> {
|
||||
dev: &'a dyn BlockDevice,
|
||||
sector: u32,
|
||||
sector_off: usize,
|
||||
len: u32,
|
||||
lfn: [u8; 128],
|
||||
lfn_len: u8,
|
||||
buf: [u8; 512],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Dirent {
|
||||
pub name: String,
|
||||
pub size: u32,
|
||||
pub attrs: u8,
|
||||
pub cluster: u32,
|
||||
}
|
||||
|
||||
#[auto_inode]
|
||||
impl VnodeImpl for DirectoryInode {
|
||||
fn lookup(&mut self, parent: VnodeRef, name: &str) -> Result<VnodeRef, Errno> {
|
||||
let fs = parent.fs().unwrap();
|
||||
let dirent = {
|
||||
let dev = fs.clone().dev().unwrap();
|
||||
let fs_data = fs.data();
|
||||
let bpb: &Bpb = fs_data.as_ref().and_then(|e| e.downcast_ref()).unwrap();
|
||||
let sector = bpb.cluster_base_sector(self.cluster);
|
||||
|
||||
FatIterator::new(dev, sector, bpb.sectors_per_cluster())
|
||||
.find(|ent| ent.name == name)
|
||||
.ok_or(Errno::DoesNotExist)
|
||||
}?;
|
||||
|
||||
let kind = if dirent.attrs & 0x10 != 0 {
|
||||
VnodeKind::Directory
|
||||
} else {
|
||||
VnodeKind::Regular
|
||||
};
|
||||
|
||||
let vnode = Vnode::new(&dirent.name, kind, Vnode::SEEKABLE);
|
||||
if kind == VnodeKind::Directory {
|
||||
vnode.set_data(Box::new(DirectoryInode {
|
||||
cluster: dirent.cluster,
|
||||
}));
|
||||
} else {
|
||||
vnode.set_data(Box::new(FileInode {
|
||||
cluster: dirent.cluster,
|
||||
size: dirent.size,
|
||||
}));
|
||||
}
|
||||
Ok(vnode)
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for FatIterator<'_> {
|
||||
type Item = Dirent;
|
||||
|
||||
fn next(&mut self) -> Option<Dirent> {
|
||||
loop {
|
||||
if self.len == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.sector_off == 0 {
|
||||
self.dev
|
||||
.read(self.sector as usize * 512, &mut self.buf)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
while self.sector_off < 512 {
|
||||
let off = self.sector_off;
|
||||
if self.buf[off] == 0 {
|
||||
self.len = 0;
|
||||
return None;
|
||||
}
|
||||
self.sector_off += 32;
|
||||
|
||||
// Check for LFN entries
|
||||
if self.buf[off + 11] == 0x0F {
|
||||
let lfn_order = self.buf[off];
|
||||
let lfn_index = (lfn_order & 0x3F) as usize;
|
||||
assert!(lfn_index > 0);
|
||||
let mut lfn8 = [0u8; 13];
|
||||
|
||||
for j in 0..5 {
|
||||
lfn8[j] = self.buf[off + 1 + j * 2];
|
||||
}
|
||||
for j in 0..6 {
|
||||
lfn8[j + 5] = self.buf[off + 14 + j * 2];
|
||||
}
|
||||
for j in 0..2 {
|
||||
lfn8[j + 11] = self.buf[off + 28 + j * 2];
|
||||
}
|
||||
|
||||
let len = lfn8.iter().position(|&c| c == 0).unwrap_or(13);
|
||||
let off = (lfn_index - 1) * 13;
|
||||
|
||||
if lfn_order & 0x40 != 0 {
|
||||
// Last entry
|
||||
self.lfn_len = (off + len) as u8;
|
||||
} else {
|
||||
assert_eq!(len, 13);
|
||||
}
|
||||
self.lfn[off..off + len].copy_from_slice(&lfn8[..len]);
|
||||
} else {
|
||||
let size = read_le32(&self.buf[off + 28..]);
|
||||
let attrs = self.buf[off + 11];
|
||||
let cluster = ((read_le16(&self.buf[off + 20..]) as u32) << 16)
|
||||
| (read_le16(&self.buf[off + 26..]) as u32);
|
||||
|
||||
if self.lfn_len != 0 {
|
||||
let len = self.lfn_len as usize;
|
||||
self.lfn_len = 0;
|
||||
return Some(Dirent {
|
||||
name: core::str::from_utf8(&self.lfn[..len as usize])
|
||||
.unwrap()
|
||||
.to_owned(),
|
||||
attrs,
|
||||
size,
|
||||
cluster,
|
||||
});
|
||||
} else {
|
||||
let len = self.buf[off..off + 11]
|
||||
.iter()
|
||||
.position(|&c| (c == 0) || (c == b' '))
|
||||
.unwrap_or(11);
|
||||
let name =
|
||||
core::str::from_utf8(&self.buf[off..off + core::cmp::min(len, 8)])
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
let ext = if len > 8 {
|
||||
".".to_owned()
|
||||
+ core::str::from_utf8(&self.buf[off + 8..off + len]).unwrap()
|
||||
} else {
|
||||
"".to_owned()
|
||||
};
|
||||
|
||||
return Some(Dirent {
|
||||
name: name + &ext,
|
||||
attrs,
|
||||
size,
|
||||
cluster,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.sector_off = 0;
|
||||
self.len -= 1;
|
||||
self.sector += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FatIterator<'_> {
|
||||
pub fn new(dev: &'static dyn BlockDevice, sector: u32, sectors_per_cluster: u8) -> Self {
|
||||
Self {
|
||||
dev,
|
||||
sector,
|
||||
len: sectors_per_cluster as u32,
|
||||
sector_off: 0,
|
||||
lfn_len: 0,
|
||||
lfn: [0; 128],
|
||||
buf: [0; 512],
|
||||
}
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
use crate::Bpb;
|
||||
use libsys::{
|
||||
stat::{Stat, OpenFlags},
|
||||
ioctl::IoctlCmd,
|
||||
error::Errno
|
||||
};
|
||||
use vfs::{VnodeImpl, VnodeKind, VnodeRef};
|
||||
|
||||
pub struct FileInode {
|
||||
pub cluster: u32,
|
||||
pub size: u32,
|
||||
}
|
||||
|
||||
#[auto_inode]
|
||||
impl VnodeImpl for FileInode {
|
||||
fn open(&mut self, _node: VnodeRef, flags: OpenFlags) -> Result<usize, Errno> {
|
||||
if flags & OpenFlags::O_ACCESS != OpenFlags::O_RDONLY {
|
||||
return Err(Errno::ReadOnly);
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn read(&mut self, node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
let size = self.size as usize;
|
||||
if pos >= size {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let fs = node.fs().unwrap();
|
||||
let dev = fs.clone().dev().unwrap();
|
||||
let fs_data = fs.data();
|
||||
let bpb: &Bpb = fs_data.as_ref().and_then(|e| e.downcast_ref()).unwrap();
|
||||
let base_sector = bpb.cluster_base_sector(self.cluster);
|
||||
|
||||
let mut rem = core::cmp::min(size - pos, data.len());
|
||||
let mut off = 0usize;
|
||||
let mut buf = [0; 512];
|
||||
|
||||
while rem != 0 {
|
||||
let sector_index = (pos + off) / 512;
|
||||
let sector_offset = (pos + off) % 512;
|
||||
let count = core::cmp::min(rem, 512 - sector_offset);
|
||||
|
||||
dev.read((base_sector as usize + sector_index) * 512, &mut buf)?;
|
||||
let src = &buf[sector_offset..sector_offset + count];
|
||||
let dst = &mut data[off..off + count];
|
||||
dst.copy_from_slice(src);
|
||||
|
||||
rem -= count;
|
||||
off += count;
|
||||
}
|
||||
|
||||
Ok(off)
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
#![no_std]
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
#[macro_use]
|
||||
extern crate fs_macros;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::{boxed::Box, rc::Rc};
|
||||
use core::any::Any;
|
||||
use core::cell::{Ref, RefCell};
|
||||
use libsys::{
|
||||
mem::read_le32,
|
||||
error::Errno,
|
||||
};
|
||||
use vfs::{BlockDevice, Filesystem, Vnode, VnodeKind, VnodeRef};
|
||||
|
||||
pub mod dir;
|
||||
pub use dir::{DirectoryInode, Dirent as FatEntry, FatIterator};
|
||||
pub mod file;
|
||||
pub use file::FileInode;
|
||||
pub mod data;
|
||||
pub use data::Bpb;
|
||||
|
||||
pub struct Fat32 {
|
||||
bpb: RefCell<Bpb>,
|
||||
root: RefCell<Option<VnodeRef>>,
|
||||
dev: &'static dyn BlockDevice,
|
||||
}
|
||||
|
||||
impl Filesystem for Fat32 {
|
||||
fn root(self: Rc<Self>) -> Result<VnodeRef, Errno> {
|
||||
self.root.borrow().clone().ok_or(Errno::DoesNotExist)
|
||||
}
|
||||
|
||||
fn dev(self: Rc<Self>) -> Option<&'static dyn BlockDevice> {
|
||||
Some(self.dev)
|
||||
}
|
||||
|
||||
fn data(&self) -> Option<Ref<dyn Any>> {
|
||||
Some(self.bpb.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
impl Fat32 {
|
||||
pub fn open(dev: &'static dyn BlockDevice) -> Result<Rc<Self>, Errno> {
|
||||
let mut buf = [0u8; 512];
|
||||
|
||||
dev.read(0, &mut buf)?;
|
||||
|
||||
if buf[0x42] != 0x28 && buf[0x42] != 0x29 {
|
||||
panic!("Not a FAT32");
|
||||
}
|
||||
|
||||
let root_cluster = read_le32(&buf[44..]);
|
||||
|
||||
let res = Rc::new(Self {
|
||||
bpb: RefCell::new(Bpb::from_sector(&buf)),
|
||||
dev,
|
||||
root: RefCell::new(None),
|
||||
});
|
||||
|
||||
let root = Vnode::new("", VnodeKind::Directory, Vnode::SEEKABLE);
|
||||
root.set_fs(res.clone());
|
||||
root.set_data(Box::new(DirectoryInode {
|
||||
cluster: root_cluster,
|
||||
}));
|
||||
*res.root.borrow_mut() = Some(root);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
Binary file not shown.
@ -1,13 +0,0 @@
|
||||
[package]
|
||||
name = "fs-macros"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "^1.0.81", features = ["full"] }
|
||||
quote = "^1.0.10"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
@ -1,162 +0,0 @@
|
||||
extern crate proc_macro;
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use std::collections::HashSet;
|
||||
use syn::{parse_macro_input, ImplItem, ItemImpl, Ident};
|
||||
|
||||
fn impl_inode_fn<T: ToTokens>(name: &str, behavior: T) -> ImplItem {
|
||||
// TODO somehow know if current crate is vfs or not?
|
||||
ImplItem::Verbatim(match name {
|
||||
"create" => quote! {
|
||||
fn create(&mut self, _at: VnodeRef, _name: &str, kind: VnodeKind) ->
|
||||
Result<VnodeRef, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"remove" => quote! {
|
||||
fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), libsys::error::Errno> {
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"lookup" => quote! {
|
||||
fn lookup(&mut self, _at: VnodeRef, _name: &str) ->
|
||||
Result<VnodeRef, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"stat" => quote! {
|
||||
fn stat(&mut self, _at: VnodeRef) ->
|
||||
Result<libsys::stat::Stat, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"truncate" => quote! {
|
||||
fn truncate(&mut self, _node: VnodeRef, _size: usize) ->
|
||||
Result<(), libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"size" => quote! {
|
||||
fn size(&mut self, _node: VnodeRef) -> Result<usize, libsys::error::Errno> {
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"read" => quote! {
|
||||
fn read(&mut self, _node: VnodeRef, _pos: usize, _data: &mut [u8]) ->
|
||||
Result<usize, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"write" => quote! {
|
||||
fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) ->
|
||||
Result<usize, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"open" => quote! {
|
||||
fn open(&mut self, _node: VnodeRef, _flags: libsys::stat::OpenFlags) ->
|
||||
Result<usize, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"close" => quote! {
|
||||
fn close(&mut self, _node: VnodeRef) -> Result<(), libsys::error::Errno> {
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"ioctl" => quote! {
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
_node: VnodeRef,
|
||||
_cmd: libsys::ioctl::IoctlCmd,
|
||||
_ptr: usize,
|
||||
_len: usize) ->
|
||||
Result<usize, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"is_ready" => quote! {
|
||||
fn is_ready(&mut self, _node: VnodeRef, _write: bool) ->
|
||||
Result<bool, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
"readdir" => quote! {
|
||||
fn readdir(
|
||||
&mut self,
|
||||
_node: VnodeRef,
|
||||
_pos: usize,
|
||||
_entries: &mut [libsys::stat::DirectoryEntry]
|
||||
) ->
|
||||
Result<usize, libsys::error::Errno>
|
||||
{
|
||||
#behavior
|
||||
}
|
||||
},
|
||||
_ => panic!("TODO implement {:?}", name),
|
||||
})
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn auto_inode(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let mut impl_item = parse_macro_input!(input as ItemImpl);
|
||||
let mut missing = HashSet::<String>::new();
|
||||
let behavior = if attr.is_empty() {
|
||||
"unimplemented".to_string()
|
||||
} else {
|
||||
parse_macro_input!(attr as Ident).to_string()
|
||||
};
|
||||
let behavior = match behavior.as_str() {
|
||||
"unimplemented" => quote! { unimplemented!() },
|
||||
"panic" => quote! { panic!() },
|
||||
"error" => quote! { Err(libsys::error::Errno::NotImplemented) },
|
||||
_ => panic!("Unknown #[auto_inode] behavior: {:?}", behavior)
|
||||
};
|
||||
|
||||
missing.insert("create".to_string());
|
||||
missing.insert("remove".to_string());
|
||||
missing.insert("lookup".to_string());
|
||||
missing.insert("open".to_string());
|
||||
missing.insert("close".to_string());
|
||||
missing.insert("truncate".to_string());
|
||||
missing.insert("read".to_string());
|
||||
missing.insert("write".to_string());
|
||||
missing.insert("stat".to_string());
|
||||
missing.insert("size".to_string());
|
||||
missing.insert("ioctl".to_string());
|
||||
missing.insert("is_ready".to_string());
|
||||
missing.insert("readdir".to_string());
|
||||
|
||||
for item in &impl_item.items {
|
||||
match item {
|
||||
ImplItem::Method(method) => {
|
||||
let name = &method.sig.ident.to_string();
|
||||
if missing.contains(name) {
|
||||
missing.remove(name);
|
||||
}
|
||||
}
|
||||
_ => panic!("Unexpected impl item"),
|
||||
}
|
||||
}
|
||||
|
||||
for item in &missing {
|
||||
impl_item
|
||||
.items
|
||||
.push(impl_inode_fn(item, behavior.clone()));
|
||||
}
|
||||
|
||||
impl_item.to_token_stream().into()
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
[package]
|
||||
name = "memfs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
vfs = { path = "../vfs" }
|
||||
fs-macros = { path = "../macros" }
|
||||
libsys = { path = "../../libsys" }
|
||||
|
||||
[features]
|
||||
cow = []
|
||||
default = ["cow"]
|
@ -1,138 +0,0 @@
|
||||
use core::mem::{size_of, MaybeUninit};
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use libsys::error::Errno;
|
||||
|
||||
pub const SIZE: usize = 4096;
|
||||
pub const ENTRY_COUNT: usize = SIZE / size_of::<usize>();
|
||||
|
||||
// Should be the same as "usize" in layout
|
||||
pub struct BlockRef<'a, A: BlockAllocator + Copy> {
|
||||
inner: Option<&'a mut [u8; SIZE]>,
|
||||
alloc: MaybeUninit<A>,
|
||||
}
|
||||
|
||||
pub unsafe trait BlockAllocator {
|
||||
fn alloc(&self) -> *mut u8;
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: accepts arbitrary block addresses
|
||||
unsafe fn dealloc(&self, block: *mut u8);
|
||||
}
|
||||
|
||||
impl<'a, A: BlockAllocator + Copy> BlockRef<'a, A> {
|
||||
pub fn new(alloc: A) -> Result<Self, Errno> {
|
||||
assert!(size_of::<A>() == 0);
|
||||
let ptr = alloc.alloc();
|
||||
if ptr.is_null() {
|
||||
Err(Errno::OutOfMemory)
|
||||
} else {
|
||||
Ok(unsafe { Self::from_raw(alloc, ptr) })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_indirect(alloc: A) -> Result<Self, Errno> {
|
||||
let mut res = Self::new(alloc)?;
|
||||
for it in res.as_mut_ref_array().iter_mut() {
|
||||
it.write(BlockRef::null());
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub const fn null() -> Self {
|
||||
Self {
|
||||
inner: None,
|
||||
alloc: MaybeUninit::uninit(),
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: does not perform checks on `data` pointer
|
||||
pub unsafe fn from_raw(alloc: A, data: *mut u8) -> Self {
|
||||
Self {
|
||||
inner: Some(&mut *(data as *mut _)),
|
||||
alloc: MaybeUninit::new(alloc),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn is_null(&self) -> bool {
|
||||
self.inner.is_none()
|
||||
}
|
||||
|
||||
pub fn as_mut_ref_array(&mut self) -> &mut [MaybeUninit<BlockRef<'a, A>>; ENTRY_COUNT] {
|
||||
assert_eq!(size_of::<Self>(), 8);
|
||||
unsafe { &mut *(self.deref_mut() as *mut _ as *mut _) }
|
||||
}
|
||||
|
||||
pub fn as_ref_array(&self) -> &[MaybeUninit<BlockRef<'a, A>>; ENTRY_COUNT] {
|
||||
assert_eq!(size_of::<Self>(), 8);
|
||||
unsafe { &*(self.deref() as *const _ as *const _) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: BlockAllocator + Copy> Drop for BlockRef<'a, A> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(inner) = self.inner.take() {
|
||||
unsafe {
|
||||
self.alloc
|
||||
.assume_init_ref()
|
||||
.dealloc(inner as *mut _ as *mut _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: BlockAllocator + Copy> Deref for BlockRef<'a, A> {
|
||||
type Target = [u8; SIZE];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.inner.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: BlockAllocator + Copy> DerefMut for BlockRef<'a, A> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.inner.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::boxed::Box;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
static A_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
#[test]
|
||||
fn block_allocator() {
|
||||
#[derive(Clone, Copy)]
|
||||
struct A;
|
||||
unsafe impl BlockAllocator for A {
|
||||
fn alloc(&self) -> *mut u8 {
|
||||
let b = Box::leak(Box::new([0; SIZE]));
|
||||
A_COUNTER.fetch_add(1, Ordering::SeqCst);
|
||||
b.as_mut_ptr() as *mut _
|
||||
}
|
||||
unsafe fn dealloc(&self, ptr: *mut u8) {
|
||||
A_COUNTER.fetch_sub(1, Ordering::SeqCst);
|
||||
drop(Box::from_raw(ptr as *mut [u8; SIZE]));
|
||||
}
|
||||
}
|
||||
const N: usize = 13;
|
||||
{
|
||||
let mut s: [MaybeUninit<BlockRef<A>>; N] = MaybeUninit::uninit_array();
|
||||
assert_eq!(A_COUNTER.load(Ordering::Acquire), 0);
|
||||
for i in 0..N {
|
||||
let mut block = BlockRef::new(A {}).unwrap();
|
||||
block.fill(1);
|
||||
s[i].write(block);
|
||||
}
|
||||
assert_eq!(A_COUNTER.load(Ordering::Acquire), N);
|
||||
for i in 0..N {
|
||||
unsafe {
|
||||
s[i].assume_init_drop();
|
||||
}
|
||||
}
|
||||
assert_eq!(A_COUNTER.load(Ordering::Acquire), 0);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,745 +0,0 @@
|
||||
use crate::{block, BlockAllocator, BlockRef};
|
||||
use core::cmp::min;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ops::{Index, IndexMut};
|
||||
use libsys::error::Errno;
|
||||
|
||||
const L0_BLOCKS: usize = 32; // 128K
|
||||
const L1_BLOCKS: usize = 8; // 16M
|
||||
|
||||
pub struct Bvec<'a, A: BlockAllocator + Copy> {
|
||||
capacity: usize,
|
||||
size: usize,
|
||||
l0: [MaybeUninit<BlockRef<'a, A>>; L0_BLOCKS],
|
||||
l1: [MaybeUninit<BlockRef<'a, A>>; L1_BLOCKS],
|
||||
l2: MaybeUninit<BlockRef<'a, A>>,
|
||||
#[cfg(feature = "cow")]
|
||||
cow_source: *const u8,
|
||||
alloc: A,
|
||||
}
|
||||
impl<'a, A: BlockAllocator + Copy> Bvec<'a, A> {
|
||||
pub fn new(alloc: A) -> Self {
|
||||
let mut res = Self {
|
||||
capacity: 0,
|
||||
size: 0,
|
||||
l0: MaybeUninit::uninit_array(),
|
||||
l1: MaybeUninit::uninit_array(),
|
||||
l2: MaybeUninit::uninit(),
|
||||
alloc,
|
||||
#[cfg(feature = "cow")]
|
||||
cow_source: core::ptr::null_mut(),
|
||||
};
|
||||
for it in res.l0.iter_mut() {
|
||||
it.write(BlockRef::null());
|
||||
}
|
||||
for it in res.l1.iter_mut() {
|
||||
it.write(BlockRef::null());
|
||||
}
|
||||
res.l2.write(BlockRef::null());
|
||||
res
|
||||
}
|
||||
|
||||
#[cfg(feature = "cow")]
|
||||
pub unsafe fn new_copy_on_write(alloc: A, cow_source: *const u8, cow_len: usize) -> Self {
|
||||
let mut res = Bvec::new(alloc);
|
||||
res.cow_source = cow_source;
|
||||
res.size = cow_len;
|
||||
res
|
||||
}
|
||||
|
||||
#[cfg(feature = "cow")]
|
||||
pub fn is_cow(&self) -> bool {
|
||||
!self.cow_source.is_null()
|
||||
}
|
||||
|
||||
pub const fn size(&self) -> usize {
|
||||
self.size
|
||||
}
|
||||
|
||||
#[cfg(feature = "cow")]
|
||||
pub fn drop_cow(&mut self) {
|
||||
assert!(self.is_cow());
|
||||
let src_slice = unsafe { core::slice::from_raw_parts(self.cow_source, self.size) };
|
||||
self.cow_source = core::ptr::null_mut();
|
||||
|
||||
self.resize((self.size + 4095) / 4096).unwrap();
|
||||
self.write(0, src_slice).unwrap();
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, cap: usize) -> Result<(), Errno> {
|
||||
#[cfg(feature = "cow")]
|
||||
assert!(!self.is_cow());
|
||||
|
||||
if cap <= self.capacity {
|
||||
let mut curr = self.capacity;
|
||||
while curr != cap {
|
||||
curr -= 1;
|
||||
let mut index = curr;
|
||||
if index >= L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT {
|
||||
index -= L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT;
|
||||
let l1i = index / block::ENTRY_COUNT;
|
||||
let l0i = index % block::ENTRY_COUNT;
|
||||
let l2r = unsafe { self.l2.assume_init_mut() };
|
||||
assert!(!l2r.is_null());
|
||||
let l1r = unsafe { l2r.as_mut_ref_array()[l1i].assume_init_mut() };
|
||||
assert!(!l1r.is_null());
|
||||
let l0r = unsafe { l1r.as_mut_ref_array()[l0i].assume_init_mut() };
|
||||
assert!(!l0r.is_null());
|
||||
*l0r = BlockRef::null();
|
||||
if l0i == 0 {
|
||||
*l1r = BlockRef::null();
|
||||
}
|
||||
if index == 0 {
|
||||
*l2r = BlockRef::null();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if index >= L0_BLOCKS {
|
||||
index -= L0_BLOCKS;
|
||||
let l1i = index / block::ENTRY_COUNT;
|
||||
let l0i = index % block::ENTRY_COUNT;
|
||||
let l1r = unsafe { self.l1[l1i].assume_init_mut() };
|
||||
assert!(!l1r.is_null());
|
||||
let l0r = unsafe { l1r.as_mut_ref_array()[l0i].assume_init_mut() };
|
||||
assert!(!l0r.is_null());
|
||||
*l0r = BlockRef::null();
|
||||
if l0i == 0 {
|
||||
*l1r = BlockRef::null();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let l0r = unsafe { self.l0[index].assume_init_mut() };
|
||||
assert!(!l0r.is_null());
|
||||
*l0r = BlockRef::null();
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
for mut index in self.capacity..cap {
|
||||
if index < L0_BLOCKS {
|
||||
let l0r = unsafe { self.l0[index].assume_init_mut() };
|
||||
assert!(l0r.is_null());
|
||||
*l0r = BlockRef::new(self.alloc)?;
|
||||
continue;
|
||||
}
|
||||
index -= L0_BLOCKS;
|
||||
if index < L1_BLOCKS * block::ENTRY_COUNT {
|
||||
let l1i = index / block::ENTRY_COUNT;
|
||||
let l0i = index % block::ENTRY_COUNT;
|
||||
let l1r = unsafe { self.l1[l1i].assume_init_mut() };
|
||||
if l1r.is_null() {
|
||||
*l1r = BlockRef::new_indirect(self.alloc)?;
|
||||
}
|
||||
let l0r = unsafe { l1r.as_mut_ref_array()[l0i].assume_init_mut() };
|
||||
assert!(l0r.is_null());
|
||||
*l0r = BlockRef::new(self.alloc)?;
|
||||
continue;
|
||||
}
|
||||
index -= L1_BLOCKS * block::ENTRY_COUNT;
|
||||
if index < block::ENTRY_COUNT * block::ENTRY_COUNT {
|
||||
let l1i = index / block::ENTRY_COUNT;
|
||||
let l0i = index % block::ENTRY_COUNT;
|
||||
let l2r = unsafe { self.l2.assume_init_mut() };
|
||||
if l2r.is_null() {
|
||||
*l2r = BlockRef::new_indirect(self.alloc)?;
|
||||
}
|
||||
let l1r = unsafe { l2r.as_mut_ref_array()[l1i].assume_init_mut() };
|
||||
if l1r.is_null() {
|
||||
*l1r = BlockRef::new_indirect(self.alloc)?;
|
||||
}
|
||||
let l0r = unsafe { l1r.as_mut_ref_array()[l0i].assume_init_mut() };
|
||||
assert!(l0r.is_null());
|
||||
*l0r = BlockRef::new(self.alloc)?;
|
||||
continue;
|
||||
}
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
self.capacity = cap;
|
||||
Ok(())
|
||||
}
|
||||
pub fn write(&mut self, mut pos: usize, data: &[u8]) -> Result<usize, Errno> {
|
||||
if pos > self.size {
|
||||
return Err(Errno::InvalidFile);
|
||||
}
|
||||
|
||||
#[cfg(feature = "cow")]
|
||||
if self.is_cow() {
|
||||
self.drop_cow();
|
||||
}
|
||||
|
||||
let mut rem = data.len();
|
||||
let mut doff = 0usize;
|
||||
if pos + rem > self.size {
|
||||
self.size = pos + rem;
|
||||
self.resize((pos + rem + block::SIZE - 1) / block::SIZE)?;
|
||||
}
|
||||
while rem > 0 {
|
||||
let index = pos / block::SIZE;
|
||||
let off = pos % block::SIZE;
|
||||
let count = min(block::SIZE - off, rem);
|
||||
let block = &mut self[index];
|
||||
let dst = &mut block[off..off + count];
|
||||
let src = &data[doff..doff + count];
|
||||
dst.copy_from_slice(src);
|
||||
doff += count;
|
||||
pos += count;
|
||||
rem -= count;
|
||||
}
|
||||
Ok(doff)
|
||||
}
|
||||
pub fn read(&self, mut pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
if pos > self.size {
|
||||
return Err(Errno::InvalidFile);
|
||||
}
|
||||
|
||||
let mut rem = min(self.size - pos, data.len());
|
||||
|
||||
#[cfg(feature = "cow")]
|
||||
if self.is_cow() {
|
||||
let cow_data = unsafe { core::slice::from_raw_parts(self.cow_source, self.size) };
|
||||
data[..rem].copy_from_slice(&cow_data[pos..pos + rem]);
|
||||
return Ok(rem);
|
||||
}
|
||||
|
||||
let mut doff = 0usize;
|
||||
while rem > 0 {
|
||||
let index = pos / block::SIZE;
|
||||
let off = pos % block::SIZE;
|
||||
let count = min(block::SIZE - off, rem);
|
||||
let block = &self[index];
|
||||
let src = &block[off..off + count];
|
||||
let dst = &mut data[doff..doff + count];
|
||||
dst.copy_from_slice(src);
|
||||
doff += count;
|
||||
pos += count;
|
||||
rem -= count;
|
||||
}
|
||||
Ok(doff)
|
||||
}
|
||||
}
|
||||
impl<'a, A: BlockAllocator + Copy> Index<usize> for Bvec<'a, A> {
|
||||
type Output = BlockRef<'a, A>;
|
||||
fn index(&self, mut index: usize) -> &Self::Output {
|
||||
if index >= self.capacity {
|
||||
panic!(
|
||||
"Index exceeds bvec capacity ({} >= {})",
|
||||
index, self.capacity
|
||||
);
|
||||
}
|
||||
if index < L0_BLOCKS {
|
||||
return unsafe { self.l0[index].assume_init_ref() };
|
||||
}
|
||||
index -= L0_BLOCKS;
|
||||
if index < L1_BLOCKS * block::ENTRY_COUNT {
|
||||
return unsafe {
|
||||
let l1 = self.l1[index / block::ENTRY_COUNT].assume_init_ref();
|
||||
l1.as_ref_array()[index % block::ENTRY_COUNT].assume_init_ref()
|
||||
};
|
||||
}
|
||||
index -= L1_BLOCKS * block::ENTRY_COUNT;
|
||||
if index < block::ENTRY_COUNT * block::ENTRY_COUNT {
|
||||
return unsafe {
|
||||
let l2 = self.l2.assume_init_ref();
|
||||
let l1 = l2.as_ref_array()[index / block::ENTRY_COUNT].assume_init_ref();
|
||||
l1.as_ref_array()[index % block::ENTRY_COUNT].assume_init_ref()
|
||||
};
|
||||
}
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
impl<'a, A: BlockAllocator + Copy> IndexMut<usize> for Bvec<'a, A> {
|
||||
fn index_mut(&mut self, mut index: usize) -> &mut Self::Output {
|
||||
if index >= self.capacity {
|
||||
panic!(
|
||||
"Index exceeds bvec capacity ({} >= {})",
|
||||
index, self.capacity
|
||||
);
|
||||
}
|
||||
if index < L0_BLOCKS {
|
||||
return unsafe { self.l0[index].assume_init_mut() };
|
||||
}
|
||||
index -= L0_BLOCKS;
|
||||
if index < L1_BLOCKS * block::ENTRY_COUNT {
|
||||
return unsafe {
|
||||
let l1 = self.l1[index / block::ENTRY_COUNT].assume_init_mut();
|
||||
l1.as_mut_ref_array()[index % block::ENTRY_COUNT].assume_init_mut()
|
||||
};
|
||||
}
|
||||
index -= L1_BLOCKS * block::ENTRY_COUNT;
|
||||
if index < block::ENTRY_COUNT * block::ENTRY_COUNT {
|
||||
return unsafe {
|
||||
let l2 = self.l2.assume_init_mut();
|
||||
let l1 = l2.as_mut_ref_array()[index / block::ENTRY_COUNT].assume_init_mut();
|
||||
l1.as_mut_ref_array()[index % block::ENTRY_COUNT].assume_init_mut()
|
||||
};
|
||||
}
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
impl<'a, A: BlockAllocator + Copy> Drop for Bvec<'a, A> {
|
||||
fn drop(&mut self) {
|
||||
for i in 0..min(L0_BLOCKS, self.capacity) {
|
||||
unsafe {
|
||||
self.l0[i].assume_init_drop();
|
||||
}
|
||||
}
|
||||
if self.capacity > L0_BLOCKS {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cow")]
|
||||
#[cfg(test)]
|
||||
mod cow_tests {
|
||||
use super::*;
|
||||
use std::boxed::Box;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct TestAlloc;
|
||||
unsafe impl BlockAllocator for TestAlloc {
|
||||
fn alloc(&self) -> *mut u8 {
|
||||
let b = Box::leak(Box::new([0; block::SIZE]));
|
||||
b.as_mut_ptr() as *mut _
|
||||
}
|
||||
unsafe fn dealloc(&self, ptr: *mut u8) {
|
||||
drop(Box::from_raw(ptr as *mut [u8; block::SIZE]));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bvec_write_copy_simple() {
|
||||
//let mut bvec = Bvec::new(TestAlloc {});
|
||||
let mut buf = [0u8; 512];
|
||||
let source_data = b"This is initial data\n";
|
||||
//unsafe {
|
||||
// bvec.setup_cow(source_data.as_ptr(), source_data.len());
|
||||
//}
|
||||
let mut bvec = unsafe {
|
||||
Bvec::new_copy_on_write(TestAlloc {}, source_data.as_ptr(), source_data.len())
|
||||
};
|
||||
assert!(bvec.is_cow());
|
||||
assert_eq!(bvec.size(), source_data.len());
|
||||
assert_eq!(bvec.capacity, 0);
|
||||
|
||||
bvec.write(8, b"testing").unwrap();
|
||||
|
||||
assert!(!bvec.is_cow());
|
||||
assert_eq!(bvec.size(), source_data.len());
|
||||
assert_eq!(bvec.capacity, 1);
|
||||
|
||||
assert_eq!(bvec.read(0, &mut buf).unwrap(), source_data.len());
|
||||
assert_eq!(&mut buf[..source_data.len()], b"This is testing data\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bvec_write_copy_l0() {
|
||||
let mut source_data = [0u8; 4096 * 2 - 2];
|
||||
let mut buf = [0u8; 512];
|
||||
for i in 0..source_data.len() {
|
||||
source_data[i] = (i & 0xFF) as u8;
|
||||
}
|
||||
let mut bvec = unsafe {
|
||||
Bvec::new_copy_on_write(TestAlloc {}, source_data.as_ptr(), source_data.len())
|
||||
};
|
||||
assert!(bvec.is_cow());
|
||||
assert_eq!(bvec.size(), source_data.len());
|
||||
assert_eq!(bvec.capacity, 0);
|
||||
|
||||
bvec.write(0, b"test").unwrap();
|
||||
|
||||
assert!(!bvec.is_cow());
|
||||
assert_eq!(bvec.size(), source_data.len());
|
||||
assert_eq!(bvec.capacity, 2);
|
||||
|
||||
assert_eq!(bvec.read(0, &mut buf).unwrap(), 512);
|
||||
assert_eq!(&buf[..4], b"test");
|
||||
for i in 4..512 {
|
||||
assert_eq!(buf[i], (i & 0xFF) as u8);
|
||||
}
|
||||
assert_eq!(bvec.read(512, &mut buf).unwrap(), 512);
|
||||
for i in 0..512 {
|
||||
assert_eq!(buf[i], ((i + 512) & 0xFF) as u8);
|
||||
}
|
||||
|
||||
bvec.write(source_data.len(), b"test").unwrap();
|
||||
assert_eq!(bvec.size(), 4096 * 2 + 2);
|
||||
assert_eq!(bvec.capacity, 3);
|
||||
|
||||
assert_eq!(bvec.read(4096 * 2, &mut buf).unwrap(), 2);
|
||||
assert_eq!(&buf[..2], b"st");
|
||||
assert_eq!(bvec.read(4096 * 2 - 2, &mut buf).unwrap(), 4);
|
||||
assert_eq!(&buf[..4], b"test");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "test_bvec")]
|
||||
#[cfg(test)]
|
||||
mod bvec_tests {
|
||||
use super::*;
|
||||
use std::boxed::Box;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
static A_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
#[derive(Clone, Copy)]
|
||||
struct TestAlloc;
|
||||
unsafe impl BlockAllocator for TestAlloc {
|
||||
fn alloc(&self) -> *mut u8 {
|
||||
let b = Box::leak(Box::new([0; block::SIZE]));
|
||||
eprintln!("alloc {:p}", b);
|
||||
b.as_mut_ptr() as *mut _
|
||||
}
|
||||
unsafe fn dealloc(&self, ptr: *mut u8) {
|
||||
eprintln!("drop {:p}", ptr);
|
||||
drop(Box::from_raw(ptr as *mut [u8; block::SIZE]));
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn bvec_allocation() {
|
||||
#[derive(Clone, Copy)]
|
||||
struct A;
|
||||
unsafe impl BlockAllocator for A {
|
||||
fn alloc(&self) -> *mut u8 {
|
||||
let b = Box::leak(Box::new([0; block::SIZE]));
|
||||
A_COUNTER.fetch_add(1, Ordering::SeqCst);
|
||||
b.as_mut_ptr() as *mut _
|
||||
}
|
||||
unsafe fn dealloc(&self, ptr: *mut u8) {
|
||||
A_COUNTER.fetch_sub(1, Ordering::SeqCst);
|
||||
drop(Box::from_raw(ptr as *mut [u8; block::SIZE]));
|
||||
}
|
||||
}
|
||||
let mut bvec = Bvec::new(A {});
|
||||
assert_eq!(A_COUNTER.load(Ordering::Acquire), 0);
|
||||
bvec.resize(123).unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
let l1r = bvec.l1[0].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
for i in 0..123 - L0_BLOCKS {
|
||||
assert!(!l1r.as_ref_array()[i].assume_init_ref().is_null());
|
||||
}
|
||||
}
|
||||
assert_eq!(A_COUNTER.load(Ordering::Acquire), 123 + 1);
|
||||
bvec.resize(123 + block::ENTRY_COUNT).unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
for i in 0..(123 + block::ENTRY_COUNT) - L0_BLOCKS {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = bvec.l1[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
A_COUNTER.load(Ordering::Acquire),
|
||||
123 + block::ENTRY_COUNT + 2
|
||||
);
|
||||
bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT)
|
||||
.unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
for i in 0..L1_BLOCKS * block::ENTRY_COUNT {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = bvec.l1[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
A_COUNTER.load(Ordering::Acquire),
|
||||
L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + L1_BLOCKS
|
||||
);
|
||||
bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 4)
|
||||
.unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
for i in 0..L1_BLOCKS * block::ENTRY_COUNT {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = bvec.l1[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
let l2r = bvec.l2.assume_init_ref();
|
||||
assert!(!l2r.is_null());
|
||||
for i in 0..block::ENTRY_COUNT * 4 {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = l2r.as_ref_array()[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
A_COUNTER.load(Ordering::Acquire),
|
||||
L0_BLOCKS + // L0
|
||||
L1_BLOCKS * block::ENTRY_COUNT + L1_BLOCKS + // L1
|
||||
block::ENTRY_COUNT * 4 + 4 + 1
|
||||
);
|
||||
bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 3 + 1)
|
||||
.unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
for i in 0..L1_BLOCKS * block::ENTRY_COUNT {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = bvec.l1[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
let l2r = bvec.l2.assume_init_ref();
|
||||
assert!(!l2r.is_null());
|
||||
for i in 0..block::ENTRY_COUNT * 3 + 1 {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = l2r.as_ref_array()[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
A_COUNTER.load(Ordering::Acquire),
|
||||
L0_BLOCKS + // L0
|
||||
L1_BLOCKS * block::ENTRY_COUNT + L1_BLOCKS + // L1
|
||||
block::ENTRY_COUNT * 3 + 1 + 4 + 1
|
||||
);
|
||||
bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 2 + 1)
|
||||
.unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
for i in 0..L1_BLOCKS * block::ENTRY_COUNT {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = bvec.l1[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
let l2r = bvec.l2.assume_init_ref();
|
||||
assert!(!l2r.is_null());
|
||||
for i in 0..block::ENTRY_COUNT * 2 + 1 {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = l2r.as_ref_array()[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
A_COUNTER.load(Ordering::Acquire),
|
||||
L0_BLOCKS + // L0
|
||||
L1_BLOCKS * block::ENTRY_COUNT + L1_BLOCKS + // L1
|
||||
block::ENTRY_COUNT * 2 + 1 + 3 + 1
|
||||
);
|
||||
bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 1)
|
||||
.unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
for i in 0..L1_BLOCKS * block::ENTRY_COUNT {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = bvec.l1[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
let l2r = bvec.l2.assume_init_ref();
|
||||
assert!(!l2r.is_null());
|
||||
let l1r = l2r.as_ref_array()[0].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[0].assume_init_ref().is_null());
|
||||
}
|
||||
assert_eq!(
|
||||
A_COUNTER.load(Ordering::Acquire),
|
||||
L0_BLOCKS + // L0
|
||||
L1_BLOCKS * block::ENTRY_COUNT + L1_BLOCKS + // L1
|
||||
1 + 1 + 1
|
||||
);
|
||||
bvec.resize(L0_BLOCKS + 3 * block::ENTRY_COUNT + 1).unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
for i in 0..3 * block::ENTRY_COUNT + 1 {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let l1r = bvec.l1[l1i].assume_init_ref();
|
||||
assert!(!l1r.is_null());
|
||||
assert!(!l1r.as_ref_array()[l0i].assume_init_ref().is_null());
|
||||
}
|
||||
let l2r = bvec.l2.assume_init_ref();
|
||||
assert!(l2r.is_null());
|
||||
}
|
||||
assert_eq!(
|
||||
A_COUNTER.load(Ordering::Acquire),
|
||||
L0_BLOCKS + // L0
|
||||
3 * block::ENTRY_COUNT + 1 + 4
|
||||
);
|
||||
bvec.resize(L0_BLOCKS).unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
assert!(bvec.l1[0].assume_init_ref().is_null());
|
||||
}
|
||||
assert_eq!(A_COUNTER.load(Ordering::Acquire), L0_BLOCKS);
|
||||
bvec.resize(12).unwrap();
|
||||
unsafe {
|
||||
for i in 0..12 {
|
||||
assert!(!bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
}
|
||||
assert_eq!(A_COUNTER.load(Ordering::Acquire), 12);
|
||||
bvec.resize(0).unwrap();
|
||||
unsafe {
|
||||
for i in 0..L0_BLOCKS {
|
||||
assert!(bvec.l0[i].assume_init_ref().is_null());
|
||||
}
|
||||
}
|
||||
assert_eq!(A_COUNTER.load(Ordering::Acquire), 0);
|
||||
}
|
||||
#[test]
|
||||
fn bvec_index_l0() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(L0_BLOCKS).unwrap();
|
||||
for i in 0..L0_BLOCKS {
|
||||
let block = &bvec[i];
|
||||
assert_eq!(block as *const _, bvec.l0[i].as_ptr());
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn bvec_index_l1() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(L0_BLOCKS + block::ENTRY_COUNT * 2 + 3).unwrap();
|
||||
for i in 0..block::ENTRY_COUNT * 2 + 3 {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let block = &bvec[i + L0_BLOCKS];
|
||||
let l1r = unsafe { bvec.l1[l1i].assume_init_ref() };
|
||||
assert_eq!(block as *const _, l1r.as_ref_array()[l0i].as_ptr());
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn bvec_index_l2() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 3)
|
||||
.unwrap();
|
||||
for i in 0..3 {
|
||||
let l1i = i / block::ENTRY_COUNT;
|
||||
let l0i = i % block::ENTRY_COUNT;
|
||||
let block = &bvec[i + L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT];
|
||||
let l2r = unsafe { bvec.l2.assume_init_ref() };
|
||||
let l1r = unsafe { l2r.as_ref_array()[l1i].assume_init_ref() };
|
||||
assert_eq!(block as *const _, l1r.as_ref_array()[l0i].as_ptr());
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l0_0() {
|
||||
let bvec = Bvec::new(TestAlloc {});
|
||||
let _block = &bvec[0];
|
||||
}
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l0_1() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(13).unwrap();
|
||||
let _block = &bvec[15];
|
||||
}
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l1_0() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(13).unwrap();
|
||||
let _block = &bvec[L0_BLOCKS + 2];
|
||||
}
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l1_1() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(L0_BLOCKS + block::ENTRY_COUNT * 2 + 3).unwrap();
|
||||
let _block = &bvec[L0_BLOCKS + block::ENTRY_COUNT * 2 + 6];
|
||||
}
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l1_2() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(L0_BLOCKS + block::ENTRY_COUNT * 2 + 3).unwrap();
|
||||
let _block = &bvec[L0_BLOCKS + block::ENTRY_COUNT * 3 + 1];
|
||||
}
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l2_0() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(13).unwrap();
|
||||
let _block = &bvec[L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 3];
|
||||
}
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l2_1() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(L0_BLOCKS + block::ENTRY_COUNT * 3 + 13)
|
||||
.unwrap();
|
||||
let _block = &bvec[L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 3];
|
||||
}
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l2_2() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 6)
|
||||
.unwrap();
|
||||
let _block = &bvec[L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 8];
|
||||
}
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l2_3() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 2 + 7)
|
||||
.unwrap();
|
||||
let _block =
|
||||
&bvec[L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 2 + 13];
|
||||
}
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn bvec_index_invalid_l2_4() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
bvec.resize(L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 2 + 13)
|
||||
.unwrap();
|
||||
let _block = &bvec[L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + block::ENTRY_COUNT * 3 + 2];
|
||||
}
|
||||
#[test]
|
||||
fn bvec_write_read() {
|
||||
let mut bvec = Bvec::new(TestAlloc {});
|
||||
const N: usize = block::SIZE * (L0_BLOCKS + L1_BLOCKS * block::ENTRY_COUNT + 3);
|
||||
let mut data = vec![0u8; N];
|
||||
for i in 0..N {
|
||||
data[i] = (i & 0xFF) as u8;
|
||||
}
|
||||
assert_eq!(bvec.write(0, &data[..]), Ok(N));
|
||||
let mut buf = vec![0u8; 327];
|
||||
let mut off = 0usize;
|
||||
let mut rem = N;
|
||||
while rem != 0 {
|
||||
let count = min(rem, buf.len());
|
||||
assert_eq!(bvec.read(off, &mut buf[..]), Ok(count));
|
||||
for i in 0..count {
|
||||
assert_eq!(buf[i], ((i + off) & 0xFF) as u8);
|
||||
}
|
||||
rem -= count;
|
||||
off += count;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
use crate::{BlockAllocator, Bvec, FileInode};
|
||||
use alloc::boxed::Box;
|
||||
use libsys::{error::Errno, stat::Stat};
|
||||
use vfs::{Vnode, VnodeImpl, VnodeKind, VnodeRef};
|
||||
|
||||
pub struct DirInode<A: BlockAllocator + Copy + 'static> {
|
||||
alloc: A,
|
||||
}
|
||||
|
||||
#[auto_inode]
|
||||
impl<A: BlockAllocator + Copy + 'static> VnodeImpl for DirInode<A> {
|
||||
fn create(
|
||||
&mut self,
|
||||
_parent: VnodeRef,
|
||||
name: &str,
|
||||
kind: VnodeKind,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
let vnode = Vnode::new(name, kind, Vnode::SEEKABLE | Vnode::CACHE_READDIR);
|
||||
match kind {
|
||||
VnodeKind::Directory => vnode.set_data(Box::new(DirInode { alloc: self.alloc })),
|
||||
VnodeKind::Regular => vnode.set_data(Box::new(FileInode::new(Bvec::new(self.alloc)))),
|
||||
_ => todo!(),
|
||||
}
|
||||
Ok(vnode)
|
||||
}
|
||||
|
||||
fn lookup(&mut self, _parent: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
|
||||
Err(Errno::DoesNotExist)
|
||||
}
|
||||
|
||||
fn remove(&mut self, _parent: VnodeRef, _name: &str) -> Result<(), Errno> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stat(&mut self, node: VnodeRef) -> Result<Stat, Errno> {
|
||||
let props = node.props();
|
||||
Ok(Stat {
|
||||
size: 0,
|
||||
blksize: 4096,
|
||||
mode: props.mode,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: BlockAllocator + Copy + 'static> DirInode<A> {
|
||||
pub const fn new(alloc: A) -> Self {
|
||||
Self { alloc }
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
use crate::{BlockAllocator, Bvec};
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
stat::{OpenFlags, Stat},
|
||||
};
|
||||
use vfs::{VnodeImpl, VnodeKind, VnodeRef};
|
||||
|
||||
pub struct FileInode<'a, A: BlockAllocator + Copy + 'static> {
|
||||
data: Bvec<'a, A>,
|
||||
}
|
||||
|
||||
#[auto_inode]
|
||||
impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> {
|
||||
fn open(&mut self, _node: VnodeRef, _mode: OpenFlags) -> Result<usize, Errno> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
self.data.read(pos, data)
|
||||
}
|
||||
|
||||
fn write(&mut self, _node: VnodeRef, pos: usize, data: &[u8]) -> Result<usize, Errno> {
|
||||
self.data.write(pos, data)
|
||||
}
|
||||
|
||||
fn truncate(&mut self, _node: VnodeRef, size: usize) -> Result<(), Errno> {
|
||||
self.data.resize((size + 4095) / 4096)
|
||||
}
|
||||
|
||||
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
|
||||
Ok(self.data.size())
|
||||
}
|
||||
|
||||
fn stat(&mut self, node: VnodeRef) -> Result<Stat, Errno> {
|
||||
let props = node.props();
|
||||
Ok(Stat {
|
||||
size: self.data.size() as u64,
|
||||
blksize: 4096,
|
||||
mode: props.mode
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: BlockAllocator + Copy + 'static> FileInode<'a, A> {
|
||||
pub fn new(data: Bvec<'a, A>) -> Self {
|
||||
Self { data }
|
||||
}
|
||||
}
|
@ -1,203 +0,0 @@
|
||||
#![feature(
|
||||
const_fn_trait_bound,
|
||||
const_mut_refs,
|
||||
maybe_uninit_uninit_array
|
||||
)]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
#[macro_use]
|
||||
extern crate fs_macros;
|
||||
|
||||
use alloc::{boxed::Box, rc::Rc};
|
||||
use core::any::Any;
|
||||
use core::cell::{Ref, RefCell};
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
path::{path_component_left, path_component_right},
|
||||
stat::FileMode,
|
||||
};
|
||||
use vfs::{BlockDevice, Filesystem, Vnode, VnodeKind, VnodeRef};
|
||||
|
||||
mod block;
|
||||
pub use block::{BlockAllocator, BlockRef};
|
||||
mod bvec;
|
||||
use bvec::Bvec;
|
||||
mod tar;
|
||||
use tar::{TarIterator, Tar};
|
||||
mod file;
|
||||
use file::FileInode;
|
||||
mod dir;
|
||||
use dir::DirInode;
|
||||
|
||||
pub struct Ramfs<A: BlockAllocator + Copy + 'static> {
|
||||
root: RefCell<Option<VnodeRef>>,
|
||||
alloc: A,
|
||||
}
|
||||
|
||||
impl<A: BlockAllocator + Copy + 'static> Filesystem for Ramfs<A> {
|
||||
fn root(self: Rc<Self>) -> Result<VnodeRef, Errno> {
|
||||
self.root.borrow().clone().ok_or(Errno::DoesNotExist)
|
||||
}
|
||||
|
||||
fn data(&self) -> Option<Ref<dyn Any>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn dev(self: Rc<Self>) -> Option<&'static dyn BlockDevice> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: BlockAllocator + Copy + 'static> Ramfs<A> {
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: accepts arbitrary `base` and `size` parameters
|
||||
pub unsafe fn open(base: *const u8, size: usize, alloc: A) -> Result<Rc<Self>, Errno> {
|
||||
let res = Rc::new(Self {
|
||||
root: RefCell::new(None),
|
||||
alloc,
|
||||
});
|
||||
*res.root.borrow_mut() = Some(res.clone().load_tar(base, size)?);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn create_node_initial(self: Rc<Self>, name: &str, tar: &Tar) -> VnodeRef {
|
||||
let kind = tar.node_kind();
|
||||
let node = Vnode::new(name, kind, Vnode::SEEKABLE | Vnode::CACHE_READDIR);
|
||||
node.props_mut().mode = tar.mode();
|
||||
node.set_fs(self.clone());
|
||||
match kind {
|
||||
VnodeKind::Directory => node.set_data(Box::new(DirInode::new(self.alloc))),
|
||||
VnodeKind::Regular => {}
|
||||
VnodeKind::Char => todo!(),
|
||||
VnodeKind::Block => todo!(),
|
||||
};
|
||||
node
|
||||
}
|
||||
|
||||
fn make_path(
|
||||
self: Rc<Self>,
|
||||
at: VnodeRef,
|
||||
path: &str,
|
||||
do_create: bool,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
if path.is_empty() {
|
||||
return Ok(at);
|
||||
}
|
||||
let (element, rest) = path_component_left(path);
|
||||
assert!(!element.is_empty());
|
||||
|
||||
let node = at.lookup(element);
|
||||
let node = match node {
|
||||
Some(node) => node,
|
||||
None => {
|
||||
if !do_create {
|
||||
return Err(Errno::DoesNotExist);
|
||||
}
|
||||
// TODO file modes
|
||||
at.create(element, FileMode::default_dir(), VnodeKind::Directory)?
|
||||
}
|
||||
};
|
||||
|
||||
if rest.is_empty() {
|
||||
Ok(node)
|
||||
} else {
|
||||
self.make_path(node, rest, do_create)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn load_tar(self: Rc<Self>, base: *const u8, size: usize) -> Result<VnodeRef, Errno> {
|
||||
let root = Vnode::new("", VnodeKind::Directory, Vnode::SEEKABLE | Vnode::CACHE_READDIR);
|
||||
root.set_fs(self.clone());
|
||||
root.set_data(Box::new(DirInode::new(self.alloc)));
|
||||
root.props_mut().mode = FileMode::default_dir();
|
||||
|
||||
// 1. Create all the paths in TAR
|
||||
for block in TarIterator::new(base, base.add(size)) {
|
||||
let (dirname, basename) = path_component_right(block.path()?);
|
||||
|
||||
let parent = self.clone().make_path(root.clone(), dirname, true)?;
|
||||
let node = self
|
||||
.clone()
|
||||
.create_node_initial(basename, block);
|
||||
assert_eq!(node.kind(), block.node_kind());
|
||||
parent.attach(node);
|
||||
}
|
||||
|
||||
// 2. Setup data blocks
|
||||
for block in TarIterator::new(base, base.add(size)) {
|
||||
if block.is_file() {
|
||||
// Will not create any dirs
|
||||
let node = self.clone().make_path(root.clone(), block.path()?, false)?;
|
||||
assert_eq!(node.kind(), block.node_kind());
|
||||
|
||||
#[cfg(feature = "cow")]
|
||||
{
|
||||
let data = block.data();
|
||||
node.set_data(Box::new(FileInode::new(Bvec::new_copy_on_write(
|
||||
self.alloc,
|
||||
data.as_ptr(),
|
||||
data.len(),
|
||||
))));
|
||||
}
|
||||
#[cfg(not(feature = "cow"))]
|
||||
{
|
||||
node.set_data(Box::new(FileInode::new(Bvec::new(self.alloc))));
|
||||
|
||||
let size = block.size();
|
||||
node.truncate(size)?;
|
||||
if node.write(0, block.data())? != size {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(root)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use alloc::boxed::Box;
|
||||
use libcommon::Read;
|
||||
use vfs::Ioctx;
|
||||
|
||||
#[test]
|
||||
fn ramfs_open() {
|
||||
#[derive(Clone, Copy)]
|
||||
struct A;
|
||||
unsafe impl BlockAllocator for A {
|
||||
fn alloc(&self) -> *mut u8 {
|
||||
let b = Box::leak(Box::new([0; block::SIZE]));
|
||||
b.as_mut_ptr() as *mut _
|
||||
}
|
||||
unsafe fn dealloc(&self, ptr: *mut u8) {
|
||||
drop(Box::from_raw(ptr as *mut [u8; block::SIZE]));
|
||||
}
|
||||
}
|
||||
unsafe impl Sync for A {}
|
||||
|
||||
let data = include_str!("../test/test1.tar");
|
||||
let fs = unsafe { Ramfs::open(data.as_ptr(), data.bytes().len(), A {}).unwrap() };
|
||||
|
||||
let root = fs.root().unwrap();
|
||||
let ioctx = Ioctx::new(root.clone());
|
||||
|
||||
assert!(Rc::ptr_eq(&ioctx.find(None, "/", true).unwrap(), &root));
|
||||
|
||||
let node = ioctx.find(None, "/test1.txt", true).unwrap();
|
||||
let mut file = node.open().unwrap();
|
||||
let mut buf = [0u8; 1024];
|
||||
|
||||
assert_eq!(file.read(&mut buf).unwrap(), 20);
|
||||
let s = core::str::from_utf8(&buf[..20]).unwrap();
|
||||
assert_eq!(s, "This is a test file\n");
|
||||
}
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
use libsys::{error::Errno, stat::FileMode};
|
||||
use vfs::VnodeKind;
|
||||
|
||||
#[repr(packed)]
|
||||
#[allow(dead_code)]
|
||||
pub struct Tar {
|
||||
name: [u8; 100],
|
||||
mode: [u8; 8],
|
||||
uid: [u8; 8],
|
||||
gid: [u8; 8],
|
||||
size: [u8; 12],
|
||||
mtime: [u8; 12],
|
||||
checksum: [u8; 8],
|
||||
type_: u8,
|
||||
link_name: [u8; 100],
|
||||
magic: [u8; 8],
|
||||
user: [u8; 32],
|
||||
group: [u8; 32],
|
||||
dev_major: [u8; 8],
|
||||
dev_minor: [u8; 8],
|
||||
prefix: [u8; 155],
|
||||
}
|
||||
|
||||
pub struct TarIterator {
|
||||
address: *const u8,
|
||||
limit: *const u8,
|
||||
zero_blocks: usize,
|
||||
}
|
||||
|
||||
impl TarIterator {
|
||||
pub const fn new(address: *const u8, limit: *const u8) -> Self {
|
||||
Self {
|
||||
address,
|
||||
limit,
|
||||
zero_blocks: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for TarIterator {
|
||||
type Item = &'static Tar;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.address >= self.limit || self.zero_blocks == 2 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let bytes: &[u8; 512] = unsafe { (self.address as *const [u8; 512]).as_ref() }.unwrap();
|
||||
if bytes.iter().all(|&x| x == 0) {
|
||||
self.zero_blocks += 1;
|
||||
self.address = unsafe { self.address.add(512) };
|
||||
self.next()
|
||||
} else {
|
||||
let block: &Tar = unsafe { (self.address as *const Tar).as_ref() }.unwrap();
|
||||
self.zero_blocks = 0;
|
||||
self.address = unsafe { self.address.add(512 + align_up(block.size())) };
|
||||
Some(block)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tar {
|
||||
pub fn is_file(&self) -> bool {
|
||||
self.type_ == 0 || self.type_ == b'0'
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
from_octal(&self.size)
|
||||
}
|
||||
|
||||
pub fn path(&self) -> Result<&str, Errno> {
|
||||
let zero_index = self.name.iter().position(|&c| c == 0).unwrap();
|
||||
core::str::from_utf8(&self.name[..zero_index]).map_err(|_| Errno::InvalidArgument)
|
||||
}
|
||||
|
||||
pub fn node_kind(&self) -> VnodeKind {
|
||||
match self.type_ {
|
||||
0 | b'0' => VnodeKind::Regular,
|
||||
b'5' => VnodeKind::Directory,
|
||||
p => panic!("Unrecognized tar entry type: '{}'", p as char),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mode(&self) -> FileMode {
|
||||
let t = match self.node_kind() {
|
||||
VnodeKind::Regular => FileMode::S_IFREG,
|
||||
VnodeKind::Directory => FileMode::S_IFDIR,
|
||||
_ => todo!()
|
||||
};
|
||||
FileMode::from_bits(from_octal(&self.mode) as u32).unwrap() | t
|
||||
}
|
||||
|
||||
pub fn data(&self) -> &[u8] {
|
||||
unsafe {
|
||||
core::slice::from_raw_parts(
|
||||
((self as *const _ as usize) + 512) as *const _,
|
||||
self.size(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_octal(oct: &[u8]) -> usize {
|
||||
let mut res = 0usize;
|
||||
for &byte in oct {
|
||||
if byte == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
res <<= 3;
|
||||
res |= (byte - b'0') as usize;
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
const fn align_up(size: usize) -> usize {
|
||||
(size + 511) & !511
|
||||
}
|
Binary file not shown.
@ -1 +0,0 @@
|
||||
This is a test file
|
@ -1,10 +0,0 @@
|
||||
[package]
|
||||
name = "vfs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
libsys = { path = "../../libsys" }
|
||||
fs-macros = { path = "../macros" }
|
@ -1,10 +0,0 @@
|
||||
use libsys::error::Errno;
|
||||
|
||||
/// Block device interface
|
||||
pub trait BlockDevice {
|
||||
/// Reads blocks at offset `pos` into `buf`
|
||||
fn read(&self, pos: usize, buf: &mut [u8]) -> Result<(), Errno>;
|
||||
/// Writes blocks at offset `pos` from `buf`
|
||||
fn write(&self, pos: usize, buf: &[u8]) -> Result<(), Errno>;
|
||||
// TODO ioctl and stuff
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
use crate::{VnodeImpl, VnodeKind, VnodeRef};
|
||||
use libsys::{error::Errno, ioctl::IoctlCmd, stat::OpenFlags};
|
||||
|
||||
/// Generic character device trait
|
||||
pub trait CharDevice {
|
||||
/// Performs a read from the device into [data] buffer.
|
||||
///
|
||||
/// If no data is available and `blocking` is set, will ask
|
||||
/// the OS to suspend the calling thread until data arrives.
|
||||
/// Otherwise, will immediately return an error.
|
||||
fn read(&self, blocking: bool, data: &mut [u8]) -> Result<usize, Errno>;
|
||||
/// Performs a write to the device from [data] buffer.
|
||||
///
|
||||
/// If the device cannot (at the moment) accept data and
|
||||
/// `blocking` is set, will block until it's available. Otherwise,
|
||||
/// will immediately return an error.
|
||||
fn write(&self, blocking: bool, data: &[u8]) -> Result<usize, Errno>;
|
||||
|
||||
/// Performs a TTY control request
|
||||
fn ioctl(&self, cmd: IoctlCmd, ptr: usize, lim: usize) -> Result<usize, Errno>;
|
||||
|
||||
/// Returns `true` if the device is ready for an operation
|
||||
fn is_ready(&self, write: bool) -> Result<bool, Errno>;
|
||||
}
|
||||
|
||||
/// Wrapper struct to attach [VnodeImpl] implementation
|
||||
/// to [CharDevice]s
|
||||
pub struct CharDeviceWrapper {
|
||||
device: &'static dyn CharDevice,
|
||||
}
|
||||
|
||||
#[auto_inode(error)]
|
||||
impl VnodeImpl for CharDeviceWrapper {
|
||||
fn open(&mut self, _node: VnodeRef, _opts: OpenFlags) -> Result<usize, Errno> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(&mut self, _node: VnodeRef, _pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
self.device.read(true, data)
|
||||
}
|
||||
|
||||
fn write(&mut self, _node: VnodeRef, _pos: usize, data: &[u8]) -> Result<usize, Errno> {
|
||||
self.device.write(true, data)
|
||||
}
|
||||
|
||||
fn is_ready(&mut self, _node: VnodeRef, write: bool) -> Result<bool, Errno> {
|
||||
self.device.is_ready(write)
|
||||
}
|
||||
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
_node: VnodeRef,
|
||||
cmd: IoctlCmd,
|
||||
ptr: usize,
|
||||
len: usize,
|
||||
) -> Result<usize, Errno> {
|
||||
self.device.ioctl(cmd, ptr, len)
|
||||
}
|
||||
}
|
||||
|
||||
impl CharDeviceWrapper {
|
||||
/// Creates a wrapper for static [CharDevice] trait object to
|
||||
/// auto-implement [VnodeImpl] trait for the device
|
||||
pub const fn new(device: &'static dyn CharDevice) -> Self {
|
||||
Self { device }
|
||||
}
|
||||
}
|
@ -1,274 +0,0 @@
|
||||
use crate::{VnodeKind, VnodeRef, Vnode};
|
||||
use alloc::rc::Rc;
|
||||
use core::cell::RefCell;
|
||||
use core::cmp::min;
|
||||
use core::str::FromStr;
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
stat::DirectoryEntry,
|
||||
traits::{Read, Seek, SeekDir, Write},
|
||||
};
|
||||
|
||||
struct NormalFile {
|
||||
vnode: VnodeRef,
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
enum FileInner {
|
||||
Normal(NormalFile),
|
||||
// TODO
|
||||
#[allow(dead_code)]
|
||||
Socket,
|
||||
}
|
||||
|
||||
/// Convenience wrapper type for a [File] struct reference
|
||||
pub type FileRef = Rc<RefCell<File>>;
|
||||
|
||||
/// Structure representing a file/socket opened for access
|
||||
pub struct File {
|
||||
inner: FileInner,
|
||||
flags: u32,
|
||||
}
|
||||
|
||||
impl Read for File {
|
||||
fn read(&mut self, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
if self.flags & Self::READ == 0 {
|
||||
return Err(Errno::InvalidOperation);
|
||||
}
|
||||
|
||||
match &mut self.inner {
|
||||
FileInner::Normal(inner) => {
|
||||
let count = inner.vnode.read(inner.pos, data)?;
|
||||
if inner.vnode.kind() != VnodeKind::Char {
|
||||
inner.pos += count;
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for File {
|
||||
fn write(&mut self, data: &[u8]) -> Result<usize, Errno> {
|
||||
if self.flags & Self::WRITE == 0 {
|
||||
return Err(Errno::ReadOnly);
|
||||
}
|
||||
|
||||
match &mut self.inner {
|
||||
FileInner::Normal(inner) => {
|
||||
let count = inner.vnode.write(inner.pos, data)?;
|
||||
if inner.vnode.kind() != VnodeKind::Char {
|
||||
inner.pos += count;
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for File {
|
||||
fn seek(&mut self, off: isize, whence: SeekDir) -> Result<usize, Errno> {
|
||||
match &mut self.inner {
|
||||
FileInner::Normal(inner) => {
|
||||
if !inner.vnode.is_seekable() {
|
||||
return Err(Errno::InvalidOperation);
|
||||
}
|
||||
|
||||
let size = inner.vnode.size()?;
|
||||
let pos = match whence {
|
||||
SeekDir::Set => min(off as usize, size),
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
inner.pos = pos;
|
||||
|
||||
Ok(pos)
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl File {
|
||||
/// File can be read
|
||||
pub const READ: u32 = 1 << 0;
|
||||
/// File can be written
|
||||
pub const WRITE: u32 = 1 << 1;
|
||||
/// File has to be closed on execve() calls
|
||||
pub const CLOEXEC: u32 = 1 << 2;
|
||||
|
||||
/// Special position for cache-readdir: "." entry
|
||||
pub const POS_CACHE_DOT: usize = usize::MAX - 1;
|
||||
/// Special position for cache-readdir: ".." entry
|
||||
pub const POS_CACHE_DOT_DOT: usize = usize::MAX;
|
||||
|
||||
/// Constructs a new file handle for a regular file
|
||||
pub fn normal(vnode: VnodeRef, pos: usize, flags: u32) -> FileRef {
|
||||
Rc::new(RefCell::new(Self {
|
||||
inner: FileInner::Normal(NormalFile { vnode, pos }),
|
||||
flags,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Returns [VnodeRef] associated with this file, if available
|
||||
pub fn node(&self) -> Option<VnodeRef> {
|
||||
match &self.inner {
|
||||
FileInner::Normal(inner) => Some(inner.vnode.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the file has to be closed when running execve() family
|
||||
/// of system calls
|
||||
pub fn is_cloexec(&self) -> bool {
|
||||
self.flags & Self::CLOEXEC != 0
|
||||
}
|
||||
|
||||
/// Returns `true` if the file is ready for an operation
|
||||
pub fn is_ready(&self, write: bool) -> Result<bool, Errno> {
|
||||
match &self.inner {
|
||||
FileInner::Normal(inner) => inner.vnode.is_ready(write),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cache_readdir(inner: &mut NormalFile, entries: &mut [DirectoryEntry]) -> Result<usize, Errno> {
|
||||
let mut count = entries.len();
|
||||
let mut offset = 0usize;
|
||||
|
||||
if inner.pos == Self::POS_CACHE_DOT {
|
||||
if count == 0 {
|
||||
return Ok(offset);
|
||||
}
|
||||
|
||||
entries[offset] = DirectoryEntry::from_str(".").unwrap();
|
||||
inner.pos = Self::POS_CACHE_DOT_DOT;
|
||||
|
||||
offset += 1;
|
||||
count -= 1;
|
||||
}
|
||||
|
||||
if inner.pos == Self::POS_CACHE_DOT_DOT {
|
||||
if count == 0 {
|
||||
return Ok(offset);
|
||||
}
|
||||
|
||||
entries[offset] = DirectoryEntry::from_str("..").unwrap();
|
||||
inner.pos = 0;
|
||||
|
||||
offset += 1;
|
||||
count -= 1;
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return Ok(offset);
|
||||
}
|
||||
|
||||
let count = inner.vnode.for_each_entry(inner.pos, count, |i, e| {
|
||||
entries[offset + i] = DirectoryEntry::from_str(e.name()).unwrap();
|
||||
});
|
||||
inner.pos += count;
|
||||
Ok(offset + count)
|
||||
}
|
||||
|
||||
/// Reads directory entries into the target buffer
|
||||
pub fn readdir(&mut self, entries: &mut [DirectoryEntry]) -> Result<usize, Errno> {
|
||||
match &mut self.inner {
|
||||
FileInner::Normal(inner) => {
|
||||
assert_eq!(inner.vnode.kind(), VnodeKind::Directory);
|
||||
|
||||
if inner.vnode.flags() & Vnode::CACHE_READDIR != 0 {
|
||||
Self::cache_readdir(inner, entries)
|
||||
} else {
|
||||
todo!();
|
||||
}
|
||||
},
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for File {
|
||||
fn drop(&mut self) {
|
||||
match &mut self.inner {
|
||||
FileInner::Normal(inner) => {
|
||||
inner.vnode.close().ok();
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{Vnode, VnodeImpl, VnodeKind, VnodeRef};
|
||||
use libsys::{stat::OpenFlags, ioctl::IoctlCmd, stat::Stat};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::rc::Rc;
|
||||
|
||||
struct DummyInode;
|
||||
|
||||
#[auto_inode]
|
||||
impl VnodeImpl for DummyInode {
|
||||
fn create(
|
||||
&mut self,
|
||||
_at: VnodeRef,
|
||||
name: &str,
|
||||
kind: VnodeKind,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
let node = Vnode::new(name, kind, 0);
|
||||
node.set_data(Box::new(DummyInode {}));
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
fn open(&mut self, _node: VnodeRef, _flags: OpenFlags) -> Result<usize, Errno> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
#[cfg(test)]
|
||||
println!("read {} at {}", data.len(), pos);
|
||||
let len = 123;
|
||||
if pos >= len {
|
||||
return Ok(0);
|
||||
}
|
||||
let rem = core::cmp::min(len - pos, data.len());
|
||||
for i in 0..rem {
|
||||
data[i] = ((pos + i) & 0xFF) as u8;
|
||||
}
|
||||
Ok(rem)
|
||||
}
|
||||
|
||||
fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result<usize, Errno> {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normal_read() {
|
||||
let node = Vnode::new("", VnodeKind::Regular, 0);
|
||||
node.set_data(Box::new(DummyInode {}));
|
||||
let mut file = node.open(OpenFlags::O_RDONLY).unwrap();
|
||||
let mut buf = [0u8; 4096];
|
||||
|
||||
assert_eq!(file.borrow_mut().read(&mut buf[0..32]).unwrap(), 32);
|
||||
for i in 0..32 {
|
||||
assert_eq!((i & 0xFF) as u8, buf[i]);
|
||||
}
|
||||
assert_eq!(file.borrow_mut().read(&mut buf[0..64]).unwrap(), 64);
|
||||
for i in 0..64 {
|
||||
assert_eq!(((i + 32) & 0xFF) as u8, buf[i]);
|
||||
}
|
||||
assert_eq!(file.borrow_mut().read(&mut buf[0..64]).unwrap(), 27);
|
||||
for i in 0..27 {
|
||||
assert_eq!(((i + 96) & 0xFF) as u8, buf[i]);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
use crate::{BlockDevice, VnodeRef};
|
||||
use alloc::rc::Rc;
|
||||
use core::any::Any;
|
||||
use core::cell::Ref;
|
||||
use libsys::error::Errno;
|
||||
|
||||
/// General filesystem interface
|
||||
pub trait Filesystem {
|
||||
/// Returns root node of the filesystem
|
||||
fn root(self: Rc<Self>) -> Result<VnodeRef, Errno>;
|
||||
/// Returns storage device of the filesystem (if any)
|
||||
fn dev(self: Rc<Self>) -> Option<&'static dyn BlockDevice>;
|
||||
/// Returns filesystem's private data struct (if any)
|
||||
fn data(&self) -> Option<Ref<dyn Any>>;
|
||||
}
|
@ -1,325 +0,0 @@
|
||||
use crate::{FileRef, VnodeKind, VnodeRef};
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
path::{path_component_left, path_component_right},
|
||||
stat::{FileMode, GroupId, OpenFlags, UserId},
|
||||
};
|
||||
|
||||
/// I/O context structure
|
||||
#[derive(Clone)]
|
||||
pub struct Ioctx {
|
||||
root: VnodeRef,
|
||||
cwd: VnodeRef,
|
||||
/// Process user ID
|
||||
pub uid: UserId,
|
||||
/// Process group ID
|
||||
pub gid: GroupId,
|
||||
}
|
||||
|
||||
impl Ioctx {
|
||||
/// Creates a new I/O context with given root node
|
||||
pub fn new(root: VnodeRef, uid: UserId, gid: GroupId) -> Self {
|
||||
Self {
|
||||
cwd: root.clone(),
|
||||
uid,
|
||||
gid,
|
||||
root,
|
||||
}
|
||||
}
|
||||
|
||||
fn _find(&self, mut at: VnodeRef, path: &str, follow: bool) -> Result<VnodeRef, Errno> {
|
||||
let mut element;
|
||||
let mut rest = path;
|
||||
|
||||
loop {
|
||||
(element, rest) = path_component_left(rest);
|
||||
|
||||
if !at.is_directory() {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
|
||||
match element {
|
||||
".." => {
|
||||
at = at.parent();
|
||||
}
|
||||
"." => {}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(target) = at.target() {
|
||||
assert!(at.kind() == VnodeKind::Directory);
|
||||
at = target;
|
||||
}
|
||||
|
||||
if element.is_empty() && rest.is_empty() {
|
||||
return Ok(at);
|
||||
}
|
||||
assert!(!element.is_empty());
|
||||
|
||||
let mut node = at.lookup_or_load(element)?;
|
||||
|
||||
while let Some(target) = node.target() {
|
||||
assert!(node.kind() == VnodeKind::Directory);
|
||||
node = target;
|
||||
}
|
||||
|
||||
if rest.is_empty() {
|
||||
Ok(node)
|
||||
} else {
|
||||
self._find(node, rest, follow)
|
||||
}
|
||||
}
|
||||
|
||||
/// Looks up a path in given ioctx
|
||||
pub fn find(
|
||||
&self,
|
||||
at: Option<VnodeRef>,
|
||||
mut path: &str,
|
||||
follow: bool,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
let at = if path.starts_with('/') {
|
||||
path = path.trim_start_matches('/');
|
||||
self.root.clone()
|
||||
} else if let Some(at) = at {
|
||||
at
|
||||
} else {
|
||||
self.cwd.clone()
|
||||
};
|
||||
|
||||
self._find(at, path, follow)
|
||||
}
|
||||
|
||||
/// Creates a new directory
|
||||
pub fn mkdir(
|
||||
&self,
|
||||
at: Option<VnodeRef>,
|
||||
path: &str,
|
||||
mode: FileMode,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
let (parent, name) = path_component_right(path);
|
||||
self.find(at, parent, true)?.create(
|
||||
name.trim_start_matches('/'),
|
||||
mode,
|
||||
VnodeKind::Directory,
|
||||
)
|
||||
}
|
||||
|
||||
/// Opens (and possibly creates) a filesystem path for access
|
||||
pub fn open(
|
||||
&self,
|
||||
at: Option<VnodeRef>,
|
||||
path: &str,
|
||||
mode: FileMode,
|
||||
opts: OpenFlags,
|
||||
) -> Result<FileRef, Errno> {
|
||||
let node = match self.find(at.clone(), path, true) {
|
||||
Err(Errno::DoesNotExist) => {
|
||||
let (parent, name) = path_component_right(path);
|
||||
let at = self.find(at, parent, true)?;
|
||||
at.create(name, mode, VnodeKind::Regular)
|
||||
}
|
||||
o => o,
|
||||
}?;
|
||||
|
||||
node.open(opts)
|
||||
}
|
||||
|
||||
/// Changes current working directory of the process
|
||||
pub fn chdir(&mut self, path: &str) -> Result<(), Errno> {
|
||||
let node = self.find(None, path, true)?;
|
||||
if !node.is_directory() {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
self.cwd = node;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{Vnode, VnodeImpl, VnodeKind};
|
||||
use alloc::{boxed::Box, rc::Rc};
|
||||
use libsys::{ioctl::IoctlCmd, stat::OpenFlags, stat::Stat};
|
||||
|
||||
pub struct DummyInode;
|
||||
|
||||
#[auto_inode]
|
||||
impl VnodeImpl for DummyInode {
|
||||
fn create(
|
||||
&mut self,
|
||||
_at: VnodeRef,
|
||||
name: &str,
|
||||
kind: VnodeKind,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
let vnode = Vnode::new(name, kind, 0);
|
||||
vnode.set_data(Box::new(DummyInode {}));
|
||||
Ok(vnode)
|
||||
}
|
||||
|
||||
fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
|
||||
Err(Errno::DoesNotExist)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_existing_absolute() {
|
||||
let root = Vnode::new("", VnodeKind::Directory, 0);
|
||||
let d0 = Vnode::new("dir0", VnodeKind::Directory, 0);
|
||||
let d1 = Vnode::new("dir1", VnodeKind::Directory, 0);
|
||||
let d0d0 = Vnode::new("dir0", VnodeKind::Directory, 0);
|
||||
let d0f0 = Vnode::new("file0", VnodeKind::Regular, 0);
|
||||
let d1f0 = Vnode::new("file0", VnodeKind::Regular, 0);
|
||||
|
||||
root.attach(d0.clone());
|
||||
root.attach(d1.clone());
|
||||
d0.attach(d0d0.clone());
|
||||
d0.attach(d0f0.clone());
|
||||
d1.attach(d1f0.clone());
|
||||
|
||||
let ioctx = Ioctx::new(root.clone());
|
||||
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/.", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/./.", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(
|
||||
&root,
|
||||
&ioctx.find(None, "/.///.", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/..", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(&root, &ioctx.find(None, "/../", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(
|
||||
&root,
|
||||
&ioctx.find(None, "/../.", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&root,
|
||||
&ioctx.find(None, "/../..", false).unwrap()
|
||||
));
|
||||
|
||||
assert!(Rc::ptr_eq(&d0, &ioctx.find(None, "/dir0", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(&d1, &ioctx.find(None, "/dir1", false).unwrap()));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0,
|
||||
&ioctx.find(None, "/dir1/../dir0", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d1,
|
||||
&ioctx
|
||||
.find(None, "/dir1/../dir0/./../../.././dir1", false)
|
||||
.unwrap()
|
||||
));
|
||||
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0d0,
|
||||
&ioctx.find(None, "/dir0/dir0", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0d0,
|
||||
&ioctx.find(None, "/dir0/dir0/.", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0,
|
||||
&ioctx.find(None, "/dir0/dir0/..", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0,
|
||||
&ioctx.find(None, "/dir0/dir0/../", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0,
|
||||
&ioctx.find(None, "/dir0/dir0/../.", false).unwrap()
|
||||
));
|
||||
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0f0,
|
||||
&ioctx.find(None, "/dir0/file0", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&d0f0,
|
||||
&ioctx.find(None, "/dir1/../dir0/./file0", false).unwrap()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_rejects_file_dots() {
|
||||
let root = Vnode::new("", VnodeKind::Directory, 0);
|
||||
let d0 = Vnode::new("dir0", VnodeKind::Directory, 0);
|
||||
let d0f0 = Vnode::new("file0", VnodeKind::Regular, 0);
|
||||
|
||||
root.attach(d0.clone());
|
||||
d0.attach(d0f0.clone());
|
||||
|
||||
let ioctx = Ioctx::new(root.clone());
|
||||
|
||||
assert_eq!(
|
||||
ioctx.find(None, "/dir0/file0/.", false).unwrap_err(),
|
||||
Errno::NotADirectory
|
||||
);
|
||||
assert_eq!(
|
||||
ioctx.find(None, "/dir0/file0/..", false).unwrap_err(),
|
||||
Errno::NotADirectory
|
||||
);
|
||||
|
||||
// TODO handle this case
|
||||
// assert_eq!(ioctx.find(None, "/dir0/file0/").unwrap_err(), Errno::NotADirectory);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mkdir() {
|
||||
let root = Vnode::new("", VnodeKind::Directory, 0);
|
||||
let ioctx = Ioctx::new(root.clone());
|
||||
|
||||
root.set_data(Box::new(DummyInode {}));
|
||||
|
||||
assert!(ioctx.mkdir(None, "/dir0", FileMode::default_dir()).is_ok());
|
||||
assert_eq!(
|
||||
ioctx
|
||||
.mkdir(None, "/dir0", FileMode::default_dir())
|
||||
.unwrap_err(),
|
||||
Errno::AlreadyExists
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_mount() {
|
||||
let root_outer = Vnode::new("", VnodeKind::Directory, 0);
|
||||
let dir0 = Vnode::new("dir0", VnodeKind::Directory, 0);
|
||||
let root_inner = Vnode::new("", VnodeKind::Directory, 0);
|
||||
let dir1 = Vnode::new("dir1", VnodeKind::Directory, 0);
|
||||
|
||||
root_outer.clone().attach(dir0.clone());
|
||||
root_inner.clone().attach(dir1.clone());
|
||||
|
||||
let ioctx = Ioctx::new(root_outer.clone());
|
||||
|
||||
assert_eq!(
|
||||
ioctx.find(None, "/dir0/dir1", false).unwrap_err(),
|
||||
Errno::DoesNotExist
|
||||
);
|
||||
|
||||
dir0.mount(root_inner.clone()).unwrap();
|
||||
|
||||
assert!(Rc::ptr_eq(
|
||||
&root_inner,
|
||||
&ioctx.find(None, "/dir0", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&dir1,
|
||||
&ioctx.find(None, "/dir0/dir1", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&root_inner,
|
||||
&ioctx.find(None, "/dir0/dir1/..", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&dir0,
|
||||
&ioctx.find(None, "/dir0/dir1/../..", false).unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&root_outer,
|
||||
&ioctx.find(None, "/dir0/dir1/../../..", false).unwrap()
|
||||
));
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
//! Virtual filesystem API and facilities
|
||||
#![warn(missing_docs)]
|
||||
#![feature(const_fn_trait_bound)]
|
||||
#![no_std]
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate std;
|
||||
|
||||
#[macro_use]
|
||||
extern crate fs_macros;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
// pub use libsys::stat::{FileMode, OpenFlags, Stat};
|
||||
// pub use libsys::ioctl::IoctlCmd;
|
||||
|
||||
mod block;
|
||||
pub use block::BlockDevice;
|
||||
mod fs;
|
||||
pub use fs::Filesystem;
|
||||
mod node;
|
||||
pub use node::{Vnode, VnodeImpl, VnodeKind, VnodeRef};
|
||||
mod ioctx;
|
||||
pub use ioctx::Ioctx;
|
||||
mod file;
|
||||
pub use file::{File, FileRef};
|
||||
mod char;
|
||||
pub use crate::char::{CharDevice, CharDeviceWrapper};
|
@ -1,618 +0,0 @@
|
||||
use crate::{File, FileRef, Filesystem, Ioctx};
|
||||
use alloc::{borrow::ToOwned, boxed::Box, rc::Rc, string::String, vec::Vec};
|
||||
use core::cell::{Ref, RefCell, RefMut};
|
||||
use core::fmt;
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
ioctl::IoctlCmd,
|
||||
stat::{AccessMode, DirectoryEntry, FileMode, OpenFlags, Stat},
|
||||
};
|
||||
|
||||
/// Convenience type alias for [Rc<Vnode>]
|
||||
pub type VnodeRef = Rc<Vnode>;
|
||||
|
||||
/// List of possible vnode types
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum VnodeKind {
|
||||
/// Node is a directory with create/lookup/remove operations
|
||||
Directory,
|
||||
/// Node is a regular file
|
||||
Regular,
|
||||
/// Node is a character device
|
||||
Char,
|
||||
/// Node is a block device
|
||||
Block,
|
||||
}
|
||||
|
||||
pub(crate) struct TreeNode {
|
||||
parent: Option<VnodeRef>,
|
||||
children: Vec<VnodeRef>,
|
||||
}
|
||||
|
||||
/// File property cache struct
|
||||
pub struct VnodeProps {
|
||||
/// Node permissions and type
|
||||
pub mode: FileMode,
|
||||
}
|
||||
|
||||
/// Virtual filesystem node struct, generalizes access to
|
||||
/// underlying real filesystems
|
||||
pub struct Vnode {
|
||||
name: String,
|
||||
tree: RefCell<TreeNode>,
|
||||
props: RefCell<VnodeProps>,
|
||||
|
||||
kind: VnodeKind,
|
||||
flags: u32,
|
||||
|
||||
target: RefCell<Option<VnodeRef>>,
|
||||
fs: RefCell<Option<Rc<dyn Filesystem>>>,
|
||||
data: RefCell<Option<Box<dyn VnodeImpl>>>,
|
||||
}
|
||||
|
||||
/// Interface for "inode" of a real filesystem
|
||||
pub trait VnodeImpl {
|
||||
// Directory-only operations
|
||||
/// Creates a new vnode, sets it up, attaches it (in real FS) to `at` with `name` and
|
||||
/// returns it
|
||||
fn create(&mut self, at: VnodeRef, name: &str, kind: VnodeKind) -> Result<VnodeRef, Errno>;
|
||||
/// Removes the filesystem inode from its parent by erasing its directory entry
|
||||
fn remove(&mut self, at: VnodeRef, name: &str) -> Result<(), Errno>;
|
||||
/// Looks up a corresponding directory entry for `name`. If present, loads its inode from
|
||||
/// storage medium and returns a new vnode associated with it.
|
||||
fn lookup(&mut self, at: VnodeRef, name: &str) -> Result<VnodeRef, Errno>;
|
||||
|
||||
/// Opens a vnode for access. Returns initial file position.
|
||||
fn open(&mut self, node: VnodeRef, opts: OpenFlags) -> Result<usize, Errno>;
|
||||
/// Closes a vnode
|
||||
fn close(&mut self, node: VnodeRef) -> Result<(), Errno>;
|
||||
|
||||
/// Changes file's underlying storage size
|
||||
fn truncate(&mut self, node: VnodeRef, size: usize) -> Result<(), Errno>;
|
||||
/// Reads `data.len()` bytes into the buffer from file offset `pos`
|
||||
fn read(&mut self, node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno>;
|
||||
/// Writes `data.len()` bytes from the buffer to file offset `pos`.
|
||||
/// Resizes the file storage if necessary.
|
||||
fn write(&mut self, node: VnodeRef, pos: usize, data: &[u8]) -> Result<usize, Errno>;
|
||||
|
||||
/// Read directory entries into target buffer
|
||||
fn readdir(
|
||||
&mut self,
|
||||
node: VnodeRef,
|
||||
pos: usize,
|
||||
data: &mut [DirectoryEntry],
|
||||
) -> Result<usize, Errno>;
|
||||
|
||||
/// Retrieves file status
|
||||
fn stat(&mut self, node: VnodeRef) -> Result<Stat, Errno>;
|
||||
|
||||
/// Reports the size of this filesystem object in bytes
|
||||
fn size(&mut self, node: VnodeRef) -> Result<usize, Errno>;
|
||||
|
||||
/// Returns `true` if node is ready for an operation
|
||||
fn is_ready(&mut self, node: VnodeRef, write: bool) -> Result<bool, Errno>;
|
||||
|
||||
/// Performs filetype-specific request
|
||||
fn ioctl(
|
||||
&mut self,
|
||||
node: VnodeRef,
|
||||
cmd: IoctlCmd,
|
||||
ptr: usize,
|
||||
len: usize,
|
||||
) -> Result<usize, Errno>;
|
||||
}
|
||||
|
||||
impl Vnode {
|
||||
/// If set, allows [File] structures associated with a [Vnode] to
|
||||
/// be seeked to arbitrary offsets
|
||||
pub const SEEKABLE: u32 = 1 << 0;
|
||||
|
||||
/// If set, readdir() uses only in-memory node tree
|
||||
pub const CACHE_READDIR: u32 = 1 << 1;
|
||||
/// If set, stat() uses only in-memory stat data
|
||||
pub const CACHE_STAT: u32 = 1 << 2;
|
||||
|
||||
/// Constructs a new [Vnode], wrapping it in [Rc]. The resulting node
|
||||
/// then needs to have [Vnode::set_data()] called on it to be usable.
|
||||
pub fn new(name: &str, kind: VnodeKind, flags: u32) -> VnodeRef {
|
||||
Rc::new(Self {
|
||||
name: name.to_owned(),
|
||||
kind,
|
||||
flags,
|
||||
props: RefCell::new(VnodeProps {
|
||||
mode: FileMode::empty(),
|
||||
}),
|
||||
tree: RefCell::new(TreeNode {
|
||||
parent: None,
|
||||
children: Vec::new(),
|
||||
}),
|
||||
target: RefCell::new(None),
|
||||
fs: RefCell::new(None),
|
||||
data: RefCell::new(None),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns [Vnode]'s path element name
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// Returns a borrowed reference to cached file properties
|
||||
pub fn props_mut(&self) -> RefMut<VnodeProps> {
|
||||
self.props.borrow_mut()
|
||||
}
|
||||
|
||||
/// Returns a borrowed reference to cached file properties
|
||||
pub fn props(&self) -> Ref<VnodeProps> {
|
||||
self.props.borrow()
|
||||
}
|
||||
|
||||
/// Sets an associated [VnodeImpl] for the [Vnode]
|
||||
pub fn set_data(&self, data: Box<dyn VnodeImpl>) {
|
||||
*self.data.borrow_mut() = Some(data);
|
||||
}
|
||||
|
||||
/// Sets an associated [Filesystem] for the [Vnode]
|
||||
pub fn set_fs(&self, fs: Rc<dyn Filesystem>) {
|
||||
*self.fs.borrow_mut() = Some(fs);
|
||||
}
|
||||
|
||||
/// Returns a reference to the associated [VnodeImpl]
|
||||
pub fn data(&self) -> RefMut<Option<Box<dyn VnodeImpl>>> {
|
||||
self.data.borrow_mut()
|
||||
}
|
||||
|
||||
/// Returns the associated [Fileystem]
|
||||
pub fn fs(&self) -> Option<Rc<dyn Filesystem>> {
|
||||
self.fs.borrow().clone()
|
||||
}
|
||||
|
||||
/// Returns `true` if the vnode represents a directory
|
||||
pub fn is_directory(&self) -> bool {
|
||||
self.kind == VnodeKind::Directory
|
||||
}
|
||||
|
||||
/// Returns `true` if the vnode allows arbitrary seeking
|
||||
pub fn is_seekable(&self) -> bool {
|
||||
self.flags & Self::SEEKABLE != 0
|
||||
}
|
||||
|
||||
/// Returns kind of the vnode
|
||||
#[inline(always)]
|
||||
pub const fn kind(&self) -> VnodeKind {
|
||||
self.kind
|
||||
}
|
||||
|
||||
/// Returns flags of the vnode
|
||||
#[inline(always)]
|
||||
pub const fn flags(&self) -> u32 {
|
||||
self.flags
|
||||
}
|
||||
|
||||
// Tree operations
|
||||
|
||||
/// Attaches `child` vnode to `self` in in-memory tree. NOTE: does not
|
||||
/// actually perform any real filesystem operations. Used to build
|
||||
/// hierarchies for in-memory or volatile filesystems.
|
||||
pub fn attach(self: &VnodeRef, child: VnodeRef) {
|
||||
let parent_clone = self.clone();
|
||||
let mut parent_borrow = self.tree.borrow_mut();
|
||||
assert!(child
|
||||
.tree
|
||||
.borrow_mut()
|
||||
.parent
|
||||
.replace(parent_clone)
|
||||
.is_none());
|
||||
parent_borrow.children.push(child);
|
||||
}
|
||||
|
||||
fn detach(self: &VnodeRef) {
|
||||
let mut self_borrow = self.tree.borrow_mut();
|
||||
let parent = self_borrow.parent.take().unwrap();
|
||||
let mut parent_borrow = parent.tree.borrow_mut();
|
||||
let index = parent_borrow
|
||||
.children
|
||||
.iter()
|
||||
.position(|it| Rc::ptr_eq(it, self))
|
||||
.unwrap();
|
||||
parent_borrow.children.remove(index);
|
||||
}
|
||||
|
||||
/// Attaches some filesystem's root directory node at another directory
|
||||
pub fn mount(self: &VnodeRef, root: VnodeRef) -> Result<(), Errno> {
|
||||
if !self.is_directory() {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
if !root.is_directory() {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
if self.target.borrow().is_some() {
|
||||
return Err(Errno::Busy);
|
||||
}
|
||||
|
||||
let mut child_borrow = root.tree.borrow_mut();
|
||||
if child_borrow.parent.is_some() {
|
||||
return Err(Errno::Busy);
|
||||
}
|
||||
child_borrow.parent = Some(self.clone());
|
||||
*self.target.borrow_mut() = Some(root.clone());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns this vnode's parent or itself if it has none
|
||||
pub fn parent(self: &VnodeRef) -> VnodeRef {
|
||||
self.tree.borrow().parent.as_ref().unwrap_or(self).clone()
|
||||
}
|
||||
|
||||
/// Returns this vnode's mount target (for directories)
|
||||
pub fn target(self: &VnodeRef) -> Option<VnodeRef> {
|
||||
self.target.borrow().clone()
|
||||
}
|
||||
|
||||
/// Looks up a child `name` in in-memory tree cache
|
||||
pub fn lookup(self: &VnodeRef, name: &str) -> Option<VnodeRef> {
|
||||
assert!(self.is_directory());
|
||||
self.tree
|
||||
.borrow()
|
||||
.children
|
||||
.iter()
|
||||
.find(|e| e.name == name)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
pub(crate) fn for_each_entry<F: FnMut(usize, &VnodeRef)>(
|
||||
&self,
|
||||
offset: usize,
|
||||
limit: usize,
|
||||
mut f: F,
|
||||
) -> usize {
|
||||
assert!(self.is_directory());
|
||||
let mut count = 0;
|
||||
for (index, item) in self
|
||||
.tree
|
||||
.borrow()
|
||||
.children
|
||||
.iter()
|
||||
.skip(offset)
|
||||
.take(limit)
|
||||
.enumerate()
|
||||
{
|
||||
f(index, item);
|
||||
count += 1;
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
/// Looks up a child `name` in `self`. Will first try looking up a cached
|
||||
/// vnode and will load it from disk if it's missing.
|
||||
pub fn lookup_or_load(self: &VnodeRef, name: &str) -> Result<VnodeRef, Errno> {
|
||||
if let Some(node) = self.lookup(name) {
|
||||
Ok(node)
|
||||
} else if let Some(ref mut data) = *self.data() {
|
||||
let vnode = data.lookup(self.clone(), name)?;
|
||||
if let Some(fs) = self.fs() {
|
||||
vnode.set_fs(fs);
|
||||
}
|
||||
self.attach(vnode.clone());
|
||||
Ok(vnode)
|
||||
} else {
|
||||
Err(Errno::DoesNotExist)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new node `name` in `self`
|
||||
pub fn create(
|
||||
self: &VnodeRef,
|
||||
name: &str,
|
||||
mode: FileMode,
|
||||
kind: VnodeKind,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
if self.kind != VnodeKind::Directory {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
if name.contains('/') {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
match self.lookup_or_load(name) {
|
||||
Err(Errno::DoesNotExist) => {}
|
||||
Ok(_) => return Err(Errno::AlreadyExists),
|
||||
e => return e,
|
||||
};
|
||||
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
let vnode = data.create(self.clone(), name, kind)?;
|
||||
if let Some(fs) = self.fs() {
|
||||
vnode.set_fs(fs);
|
||||
}
|
||||
vnode.props.borrow_mut().mode = mode;
|
||||
self.attach(vnode.clone());
|
||||
Ok(vnode)
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes a directory entry `name` from `self`
|
||||
pub fn unlink(self: &VnodeRef, name: &str) -> Result<(), Errno> {
|
||||
if self.kind != VnodeKind::Directory {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
if name.contains('/') {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
let vnode = self.lookup(name).ok_or(Errno::DoesNotExist)?;
|
||||
data.remove(self.clone(), name)?;
|
||||
vnode.detach();
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
/// Opens a vnode for access
|
||||
pub fn open(self: &VnodeRef, flags: OpenFlags) -> Result<FileRef, Errno> {
|
||||
let mut open_flags = 0;
|
||||
if flags.contains(OpenFlags::O_DIRECTORY) {
|
||||
if self.kind != VnodeKind::Directory {
|
||||
return Err(Errno::NotADirectory);
|
||||
}
|
||||
if flags & OpenFlags::O_ACCESS != OpenFlags::O_RDONLY {
|
||||
return Err(Errno::IsADirectory);
|
||||
}
|
||||
|
||||
open_flags = File::READ;
|
||||
} else {
|
||||
if self.kind == VnodeKind::Directory {
|
||||
return Err(Errno::IsADirectory);
|
||||
}
|
||||
|
||||
match flags & OpenFlags::O_ACCESS {
|
||||
OpenFlags::O_RDONLY => open_flags |= File::READ,
|
||||
OpenFlags::O_WRONLY => open_flags |= File::WRITE,
|
||||
OpenFlags::O_RDWR => open_flags |= File::READ | File::WRITE,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
if flags.contains(OpenFlags::O_CLOEXEC) {
|
||||
open_flags |= File::CLOEXEC;
|
||||
}
|
||||
|
||||
if self.kind == VnodeKind::Directory && self.flags & Vnode::CACHE_READDIR != 0 {
|
||||
Ok(File::normal(self.clone(), File::POS_CACHE_DOT, open_flags))
|
||||
} else if let Some(ref mut data) = *self.data() {
|
||||
let pos = data.open(self.clone(), flags)?;
|
||||
Ok(File::normal(self.clone(), pos, open_flags))
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
/// Closes a vnode
|
||||
pub fn close(self: &VnodeRef) -> Result<(), Errno> {
|
||||
if self.kind == VnodeKind::Directory && self.flags & Vnode::CACHE_READDIR != 0 {
|
||||
Ok(())
|
||||
} else if let Some(ref mut data) = *self.data() {
|
||||
data.close(self.clone())
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads data from offset `pos` into `buf`
|
||||
pub fn read(self: &VnodeRef, pos: usize, buf: &mut [u8]) -> Result<usize, Errno> {
|
||||
if self.kind == VnodeKind::Directory {
|
||||
Err(Errno::IsADirectory)
|
||||
} else if let Some(ref mut data) = *self.data() {
|
||||
data.read(self.clone(), pos, buf)
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes data from `buf` to offset `pos`
|
||||
pub fn write(self: &VnodeRef, pos: usize, buf: &[u8]) -> Result<usize, Errno> {
|
||||
if self.kind == VnodeKind::Directory {
|
||||
Err(Errno::IsADirectory)
|
||||
} else if let Some(ref mut data) = *self.data() {
|
||||
data.write(self.clone(), pos, buf)
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
/// Resizes the vnode data
|
||||
pub fn truncate(self: &VnodeRef, size: usize) -> Result<(), Errno> {
|
||||
if self.kind != VnodeKind::Regular {
|
||||
Err(Errno::IsADirectory)
|
||||
} else if let Some(ref mut data) = *self.data() {
|
||||
data.truncate(self.clone(), size)
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns current vnode data size
|
||||
pub fn size(self: &VnodeRef) -> Result<usize, Errno> {
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
data.size(self.clone())
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
/// Reports file status
|
||||
pub fn stat(self: &VnodeRef) -> Result<Stat, Errno> {
|
||||
if self.flags & Self::CACHE_STAT != 0 {
|
||||
let props = self.props();
|
||||
Ok(Stat {
|
||||
blksize: 0,
|
||||
size: 0,
|
||||
mode: props.mode,
|
||||
})
|
||||
} else if let Some(ref mut data) = *self.data() {
|
||||
data.stat(self.clone())
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs node-specific requests
|
||||
pub fn ioctl(self: &VnodeRef, cmd: IoctlCmd, ptr: usize, len: usize) -> Result<usize, Errno> {
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
data.ioctl(self.clone(), cmd, ptr, len)
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the node is ready for operation
|
||||
pub fn is_ready(self: &VnodeRef, write: bool) -> Result<bool, Errno> {
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
data.is_ready(self.clone(), write)
|
||||
} else {
|
||||
Err(Errno::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if given [Ioctx] has `access` permissions to the vnode
|
||||
pub fn check_access(&self, _ioctx: &Ioctx, access: AccessMode) -> Result<(), Errno> {
|
||||
let props = self.props.borrow();
|
||||
let mode = props.mode;
|
||||
|
||||
if access.contains(AccessMode::F_OK) {
|
||||
if access.intersects(AccessMode::R_OK | AccessMode::W_OK | AccessMode::X_OK) {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
} else {
|
||||
if access.contains(AccessMode::F_OK) {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
// Check user
|
||||
if access.contains(AccessMode::R_OK) && !mode.contains(FileMode::USER_READ) {
|
||||
return Err(Errno::PermissionDenied);
|
||||
}
|
||||
if access.contains(AccessMode::W_OK) && !mode.contains(FileMode::USER_WRITE) {
|
||||
return Err(Errno::PermissionDenied);
|
||||
}
|
||||
if access.contains(AccessMode::X_OK) && !mode.contains(FileMode::USER_EXEC) {
|
||||
return Err(Errno::PermissionDenied);
|
||||
}
|
||||
|
||||
// TODO check group
|
||||
// TODO check other
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Vnode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Vnode({:?})", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use libsys::{ioctl::IoctlCmd, stat::OpenFlags, stat::Stat};
|
||||
pub struct DummyInode;
|
||||
|
||||
#[auto_inode]
|
||||
impl VnodeImpl for DummyInode {
|
||||
fn create(
|
||||
&mut self,
|
||||
_at: VnodeRef,
|
||||
name: &str,
|
||||
kind: VnodeKind,
|
||||
) -> Result<VnodeRef, Errno> {
|
||||
let node = Vnode::new(name, kind, 0);
|
||||
node.set_data(Box::new(DummyInode {}));
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), Errno> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
|
||||
Err(Errno::DoesNotExist)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parent() {
|
||||
let root = Vnode::new("", VnodeKind::Directory, 0);
|
||||
let node = Vnode::new("dir0", VnodeKind::Directory, 0);
|
||||
|
||||
root.attach(node.clone());
|
||||
|
||||
assert!(Rc::ptr_eq(&root.parent(), &root));
|
||||
assert!(Rc::ptr_eq(&node.parent(), &root));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mkdir_unlink() {
|
||||
let root = Vnode::new("", VnodeKind::Directory, 0);
|
||||
|
||||
root.set_data(Box::new(DummyInode {}));
|
||||
|
||||
let node = root
|
||||
.create("test", FileMode::default_dir(), VnodeKind::Directory)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
root.create("test", FileMode::default_dir(), VnodeKind::Directory)
|
||||
.unwrap_err(),
|
||||
Errno::AlreadyExists
|
||||
);
|
||||
|
||||
assert_eq!(node.props.borrow().mode, FileMode::default_dir());
|
||||
assert!(Rc::ptr_eq(&node, &root.lookup("test").unwrap()));
|
||||
assert!(node.data.borrow().is_some());
|
||||
|
||||
root.unlink("test").unwrap();
|
||||
|
||||
assert!(root.lookup("test").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lookup_attach_detach() {
|
||||
let root = Vnode::new("", VnodeKind::Directory, 0);
|
||||
let dir0 = Vnode::new("dir0", VnodeKind::Directory, 0);
|
||||
let dir1 = Vnode::new("dir1", VnodeKind::Directory, 0);
|
||||
|
||||
root.attach(dir0.clone());
|
||||
root.attach(dir1.clone());
|
||||
|
||||
assert!(Rc::ptr_eq(&dir0, &root.lookup("dir0").unwrap()));
|
||||
assert!(Rc::ptr_eq(&dir1, &root.lookup("dir1").unwrap()));
|
||||
assert!(Rc::ptr_eq(
|
||||
&root,
|
||||
dir0.tree.borrow().parent.as_ref().unwrap()
|
||||
));
|
||||
assert!(Rc::ptr_eq(
|
||||
&root,
|
||||
dir1.tree.borrow().parent.as_ref().unwrap()
|
||||
));
|
||||
assert!(root.lookup("dir2").is_none());
|
||||
|
||||
dir0.detach();
|
||||
|
||||
assert!(Rc::ptr_eq(&dir1, &root.lookup("dir1").unwrap()));
|
||||
assert!(Rc::ptr_eq(
|
||||
&root,
|
||||
dir1.tree.borrow().parent.as_ref().unwrap()
|
||||
));
|
||||
assert!(dir0.tree.borrow().parent.is_none());
|
||||
assert!(root.lookup("dir0").is_none());
|
||||
assert!(root.lookup("dir2").is_none());
|
||||
}
|
||||
}
|
5
gdb.sh
Executable file
5
gdb.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
. etc/common.sh
|
||||
|
||||
gdb-multiarch -x etc/gdbrc target/${ARCH}-${MACH}/${PROFILE}/kernel
|
@ -10,26 +10,14 @@ name = "kernel"
|
||||
test = false
|
||||
|
||||
[dependencies]
|
||||
vfs = { path = "../fs/vfs" }
|
||||
memfs = { path = "../fs/memfs" }
|
||||
libsys = { path = "../libsys" }
|
||||
cfg-if = "1.x.x"
|
||||
error = { path = "../error" }
|
||||
address = { path = "../address" }
|
||||
tock-registers = "0.7.x"
|
||||
fdt-rs = { version = "0.x.x", default-features = false }
|
||||
bitflags = "^1.3.0"
|
||||
kernel-macros = { path = "macros" }
|
||||
fs-macros = { path = "../fs/macros" }
|
||||
|
||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||
cortex-a = { version = "7.0.x" }
|
||||
cortex-a = { version = "6.x.x" }
|
||||
|
||||
[features]
|
||||
default = ["aggressive_syscall"]
|
||||
pl011 = []
|
||||
pl031 = []
|
||||
verbose = []
|
||||
aggressive_syscall = []
|
||||
|
||||
mach_qemu = ["pl011", "pl031"]
|
||||
mach_orangepi3 = []
|
||||
mach_rpi3 = ["pl011"]
|
||||
mach_qemu = []
|
||||
default = ["mach_qemu"]
|
||||
|
@ -1,42 +0,0 @@
|
||||
extern crate proc_macro;
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
|
||||
#[proc_macro_derive(TtyCharDevice)]
|
||||
pub fn derive_tty_char_device(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
if !ast.generics.params.is_empty() {
|
||||
panic!(
|
||||
"Derived TtyCharDevice cannot have generic parameters: {:?}",
|
||||
ast.ident
|
||||
);
|
||||
}
|
||||
let ident = ast.ident;
|
||||
|
||||
quote! {
|
||||
impl vfs::CharDevice for #ident {
|
||||
fn read(&self, blocking: bool, data: &mut [u8]) -> Result<usize, libsys::error::Errno> {
|
||||
assert!(blocking);
|
||||
crate::dev::tty::TtyDevice::line_read(self, data)
|
||||
}
|
||||
fn write(&self, blocking: bool, data: &[u8]) -> Result<usize, libsys::error::Errno> {
|
||||
assert!(blocking);
|
||||
crate::dev::tty::TtyDevice::line_write(self, data)
|
||||
}
|
||||
fn ioctl(&self, cmd: libsys::ioctl::IoctlCmd, ptr: usize, len: usize) ->
|
||||
Result<usize, libsys::error::Errno>
|
||||
{
|
||||
crate::dev::tty::TtyDevice::tty_ioctl(self, cmd, ptr, len)
|
||||
}
|
||||
fn is_ready(&self, write: bool) -> Result<bool, libsys::error::Errno> {
|
||||
crate::dev::tty::TtyDevice::is_ready(self, write)
|
||||
}
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
37
kernel/src/arch/aarch64/boot/entry.S
Normal file
37
kernel/src/arch/aarch64/boot/entry.S
Normal file
@ -0,0 +1,37 @@
|
||||
.macro ADR_REL reg, sym
|
||||
adrp \reg, \sym
|
||||
add \reg, \reg, #:lo12:\sym
|
||||
.endm
|
||||
|
||||
.section .text._entry
|
||||
.global _entry
|
||||
_entry:
|
||||
mrs x1, mpidr_el1
|
||||
and x1, x1, #3
|
||||
beq 2f
|
||||
1:
|
||||
wfe
|
||||
b 1b
|
||||
|
||||
2:
|
||||
|
||||
// Zero .bss
|
||||
ADR_REL x0, __bss_start
|
||||
ADR_REL x1, __bss_end
|
||||
1:
|
||||
cmp x0, x1
|
||||
beq 2f
|
||||
stp xzr, xzr, [x0], #16
|
||||
b 1b
|
||||
2:
|
||||
|
||||
ADR_REL x0, bsp_stack_top
|
||||
mov sp, x0
|
||||
|
||||
b __aa64_bsp_main
|
||||
|
||||
.section .bss
|
||||
.p2align 4
|
||||
bsp_stack_bottom:
|
||||
.skip 16384
|
||||
bsp_stack_top:
|
@ -1,124 +1,19 @@
|
||||
//! aarch64 common boot logic
|
||||
|
||||
use crate::arch::{
|
||||
aarch64::reg::{CNTKCTL_EL1, CPACR_EL1},
|
||||
machine,
|
||||
};
|
||||
use core::arch::global_asm;
|
||||
use crate::config::{ConfigKey, CONFIG};
|
||||
use crate::dev::{
|
||||
fdt::{find_prop, DeviceTree},
|
||||
irq::IntSource,
|
||||
Device,
|
||||
};
|
||||
use crate::fs::{devfs, sysfs};
|
||||
use crate::dev::pseudo;
|
||||
use libsys::error::Errno;
|
||||
//use crate::debug::Level;
|
||||
use crate::mem::{
|
||||
self, heap,
|
||||
phys::{self, PageUsage},
|
||||
virt,
|
||||
};
|
||||
use crate::proc;
|
||||
use cortex_a::asm::barrier::{self, dsb, isb};
|
||||
use cortex_a::registers::{SCTLR_EL1, VBAR_EL1};
|
||||
use tock_registers::interfaces::{ReadWriteable, Writeable};
|
||||
|
||||
fn init_device_tree(fdt_base_phys: usize) -> Result<Option<DeviceTree>, Errno> {
|
||||
use fdt_rs::prelude::*;
|
||||
|
||||
let fdt = if fdt_base_phys != 0 {
|
||||
DeviceTree::from_phys(fdt_base_phys + 0xFFFFFF8000000000)?
|
||||
} else {
|
||||
warnln!("No FDT present");
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let mut cfg = CONFIG.lock();
|
||||
|
||||
if let Some(chosen) = fdt.node_by_path("/chosen") {
|
||||
if let Some(initrd_start) = find_prop(chosen.clone(), "linux,initrd-start") {
|
||||
let initrd_end = find_prop(chosen.clone(), "linux,initrd-end").unwrap();
|
||||
let initrd_start = initrd_start.u32(0).unwrap() as usize;
|
||||
let initrd_end = initrd_end.u32(0).unwrap() as usize;
|
||||
|
||||
cfg.set_usize(ConfigKey::InitrdBase, initrd_start);
|
||||
cfg.set_usize(ConfigKey::InitrdSize, initrd_end - initrd_start);
|
||||
}
|
||||
|
||||
if let Some(cmdline) = find_prop(chosen, "bootargs") {
|
||||
cfg.set_cmdline(cmdline.str().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(fdt))
|
||||
}
|
||||
use cortex_a::asm;
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn __aa64_bsp_main(fdt_base: usize) -> ! {
|
||||
// Disable FP instruction trapping
|
||||
CPACR_EL1.modify(CPACR_EL1::FPEN::TrapNone);
|
||||
|
||||
// Disable CNTPCT and CNTFRQ trapping from EL0
|
||||
CNTKCTL_EL1.modify(CNTKCTL_EL1::EL0PCTEN::SET);
|
||||
|
||||
extern "C" {
|
||||
static aa64_el1_vectors: u8;
|
||||
}
|
||||
unsafe {
|
||||
VBAR_EL1.set(&aa64_el1_vectors as *const _ as u64);
|
||||
|
||||
// Setup caching in SCTLR_EL1
|
||||
dsb(barrier::SY);
|
||||
isb(barrier::SY);
|
||||
|
||||
SCTLR_EL1
|
||||
.modify(SCTLR_EL1::I::SET + SCTLR_EL1::SA::SET + SCTLR_EL1::C::SET + SCTLR_EL1::A::SET);
|
||||
|
||||
dsb(barrier::SY);
|
||||
isb(barrier::SY);
|
||||
}
|
||||
|
||||
// Enable MMU
|
||||
virt::enable().expect("Failed to initialize virtual memory");
|
||||
|
||||
let fdt = init_device_tree(fdt_base).expect("Device tree init failed");
|
||||
|
||||
// Most basic machine init: initialize proper debug output
|
||||
// physical memory
|
||||
machine::init_board_early().unwrap();
|
||||
|
||||
// Setup a heap
|
||||
unsafe {
|
||||
let heap_base_phys = phys::alloc_contiguous_pages(PageUsage::KernelHeap, 4096)
|
||||
.expect("Failed to allocate memory for heap");
|
||||
let heap_base_virt = mem::virtualize(heap_base_phys);
|
||||
heap::init(heap_base_virt, 16 * 1024 * 1024);
|
||||
}
|
||||
|
||||
devfs::init();
|
||||
sysfs::init();
|
||||
|
||||
machine::init_board().unwrap();
|
||||
|
||||
#[cfg(feature = "verbose")]
|
||||
if let Some(fdt) = fdt {
|
||||
use crate::debug::Level;
|
||||
fdt.dump(Level::Debug);
|
||||
}
|
||||
|
||||
devfs::add_named_char_device(&pseudo::ZERO, "zero").unwrap();
|
||||
devfs::add_named_char_device(&pseudo::RANDOM, "random").unwrap();
|
||||
|
||||
infoln!("Machine init finished");
|
||||
fn __aa64_bsp_main() {
|
||||
debugln!("Test");
|
||||
use crate::arch::machine;
|
||||
use crate::dev::{Device, serial::SerialDevice};
|
||||
|
||||
unsafe {
|
||||
machine::local_timer().enable().unwrap();
|
||||
machine::local_timer().init_irqs().unwrap();
|
||||
machine::console().lock().enable().unwrap();
|
||||
}
|
||||
|
||||
proc::enter();
|
||||
loop {
|
||||
let ch = unsafe { machine::console().lock().recv(true).unwrap() };
|
||||
debugln!("{:#04x} = '{}'!", ch, ch as char);
|
||||
}
|
||||
}
|
||||
|
||||
global_asm!(include_str!("uboot.S"));
|
||||
global_asm!(include_str!("entry.S"));
|
||||
|
@ -1,190 +0,0 @@
|
||||
// vi:ft=a64asm.asm:
|
||||
|
||||
.macro MOV_L reg, value
|
||||
mov \reg, #((\value) & 0xFFFF)
|
||||
movk \reg, #((\value) >> 16), lsl #16
|
||||
.endm
|
||||
|
||||
.macro ADR_REL reg, sym
|
||||
adrp \reg, \sym
|
||||
add \reg, \reg, #:lo12:\sym
|
||||
.endm
|
||||
|
||||
.macro ADR_ABS reg, sym
|
||||
movz \reg, #:abs_g3:\sym
|
||||
movk \reg, #:abs_g2_nc:\sym
|
||||
movk \reg, #:abs_g1_nc:\sym
|
||||
movk \reg, #:abs_g0_nc:\sym
|
||||
.endm
|
||||
|
||||
.set PTE_BLOCK_AF, 1 << 10
|
||||
.set PTE_BLOCK_ISH, 3 << 8
|
||||
.set PTE_PRESENT, 1 << 0
|
||||
|
||||
.set MAIR_EL1_Attr0_Normal_Inner_NC, (4 << 0)
|
||||
.set MAIR_EL1_Attr0_Normal_Outer_NC, (4 << 4)
|
||||
.set MAIR_EL1_Attr1_Device, (0 << 12)
|
||||
.set MAIR_EL1_Attr1_Device_nGnRE, (1 << 8)
|
||||
|
||||
.set ID_AA64MMFR0_EL1_TGran4, (0xF << 28)
|
||||
|
||||
.set TCR_EL1_IPS_SHIFT, 32
|
||||
|
||||
.set TCR_EL1_TG1_4K, (2 << 30)
|
||||
.set TCR_EL1_SH1_Outer, (2 << 28)
|
||||
.set TCR_EL1_ORGN1_NC, (0 << 26)
|
||||
.set TCR_EL1_IRGN1_NC, (0 << 24)
|
||||
.set TCR_EL1_T1SZ_SHIFT, 16
|
||||
|
||||
.set TCR_EL1_TG0_4K, (0 << 14)
|
||||
.set TCR_EL1_SH0_Outer, (2 << 12)
|
||||
.set TCR_EL1_ORGN0_NC, (0 << 10)
|
||||
.set TCR_EL1_IRGN0_NC, (0 << 8)
|
||||
.set TCR_EL1_T0SZ_SHIFT, 0
|
||||
|
||||
.set TCR_EL1_ATTRS, (TCR_EL1_TG1_4K | TCR_EL1_SH1_Outer | TCR_EL1_TG0_4K | TCR_EL1_SH0_Outer | (25 << TCR_EL1_T1SZ_SHIFT) | (25 << TCR_EL1_T0SZ_SHIFT))
|
||||
|
||||
.set SCTLR_EL1_I, (1 << 12)
|
||||
.set SCTLR_EL1_C, (1 << 2)
|
||||
.set SCTLR_EL1_M, (1 << 0)
|
||||
|
||||
|
||||
.set SCTLR_EL2_RES1, 0x30C50830
|
||||
|
||||
.set SPSR_EL2_EL1h, 0x5
|
||||
.set SPSR_EL2_MASK_DAIF, 0xF << 6
|
||||
.set HCR_EL2_RW, 1 << 31
|
||||
.set HCR_EL2_HCD, 1 << 29
|
||||
|
||||
.set CNTHCTL_EL2_EL1PCEN, 1 << 1
|
||||
.set CNTHCTL_EL2_EL1PCTEN, 1 << 0
|
||||
|
||||
.section .text._entry
|
||||
.global _entry
|
||||
_entry:
|
||||
mov x8, x0
|
||||
|
||||
// Test for EL2
|
||||
mrs x0, CurrentEL
|
||||
lsr x0, x0, #2
|
||||
cmp x0, #2
|
||||
bne 1f
|
||||
|
||||
// Exit EL2
|
||||
mrs x0, cnthctl_el2
|
||||
orr x0, x0, #(CNTHCTL_EL2_EL1PCTEN | CNTHCTL_EL2_EL1PCEN)
|
||||
msr cnthctl_el2, x0
|
||||
msr cntvoff_el2, xzr
|
||||
|
||||
MOV_L x0, SCTLR_EL2_RES1
|
||||
msr sctlr_el2, x0
|
||||
|
||||
mov x0, #HCR_EL2_RW
|
||||
msr hcr_el2, x0
|
||||
|
||||
mov x0, #SPSR_EL2_EL1h
|
||||
orr x0, x0, #SPSR_EL2_MASK_DAIF
|
||||
msr spsr_el2, x0
|
||||
|
||||
adr x0, 1f
|
||||
msr elr_el2, x0
|
||||
|
||||
isb
|
||||
eret
|
||||
1:
|
||||
dsb sy
|
||||
isb
|
||||
|
||||
// Zero .bss
|
||||
ADR_ABS x0, __bss_start_phys
|
||||
ADR_ABS x1, __bss_end_phys
|
||||
1:
|
||||
cmp x0, x1
|
||||
beq 2f
|
||||
|
||||
str xzr, [x0], #8
|
||||
|
||||
b 1b
|
||||
2:
|
||||
|
||||
ADR_ABS x9, __aa64_entry_upper
|
||||
b __aa64_enter_upper
|
||||
|
||||
.global __aa64_enter_upper
|
||||
.type __aa64_enter_upper, %function
|
||||
__aa64_enter_upper:
|
||||
// x8 -- FDT base
|
||||
// x9 -- upper entry point
|
||||
|
||||
// Setup TTBR1_EL1
|
||||
// TODO fix macros
|
||||
ADR_ABS x5, KERNEL_TTBR1
|
||||
ADR_ABS x6, KERNEL_OFFSET
|
||||
|
||||
// x5 = KERNEL_TTBR1 physical address
|
||||
sub x5, x5, x6
|
||||
|
||||
// Fill KERNEL_TTBR1 table with upper-mapped Normal memory
|
||||
.fill_ttbr1:
|
||||
mov x2, #256
|
||||
1:
|
||||
sub x2, x2, #1
|
||||
|
||||
// x0 = (x2 << 30) | attrs...
|
||||
lsl x1, x2, #30
|
||||
mov x0, #(PTE_BLOCK_ISH | PTE_BLOCK_AF | PTE_PRESENT)
|
||||
orr x0, x0, x1
|
||||
|
||||
str x0, [x5, x2, lsl #3]
|
||||
|
||||
cbnz x2, 1b
|
||||
|
||||
.init_mmu_regs:
|
||||
mov x0, #(MAIR_EL1_Attr0_Normal_Outer_NC | MAIR_EL1_Attr0_Normal_Inner_NC | MAIR_EL1_Attr1_Device | MAIR_EL1_Attr1_Device_nGnRE)
|
||||
msr mair_el1, x0
|
||||
|
||||
// Test for 4KiB page support
|
||||
mrs x0, ID_AA64MMFR0_EL1
|
||||
mov x1, ID_AA64MMFR0_EL1_TGran4
|
||||
tst x0, x1
|
||||
bne .no_4k_gran
|
||||
|
||||
// x0 = PARange
|
||||
and x0, x0, #0xF
|
||||
lsl x0, x0, #TCR_EL1_IPS_SHIFT
|
||||
MOV_L x1, TCR_EL1_ATTRS
|
||||
orr x0, x0, x1
|
||||
msr tcr_el1, x0
|
||||
|
||||
msr ttbr0_el1, x5
|
||||
msr ttbr1_el1, x5
|
||||
|
||||
dsb ish
|
||||
isb
|
||||
|
||||
mrs x0, sctlr_el1
|
||||
orr x0, x0, #SCTLR_EL1_M
|
||||
msr sctlr_el1, x0
|
||||
|
||||
mov x0, x8
|
||||
br x9
|
||||
.no_4k_gran:
|
||||
b .
|
||||
.size __aa64_enter_upper, . - __aa64_enter_upper
|
||||
|
||||
.section .text._entry_upper
|
||||
__aa64_entry_upper:
|
||||
// x0 -- fdt address
|
||||
|
||||
ADR_REL x1, bsp_stack_top
|
||||
mov sp, x1
|
||||
|
||||
mov lr, xzr
|
||||
bl __aa64_bsp_main
|
||||
b .
|
||||
|
||||
.section .bss
|
||||
.p2align 12
|
||||
bsp_stack_bottom:
|
||||
.skip 32768
|
||||
bsp_stack_top:
|
@ -1,84 +0,0 @@
|
||||
.section .text
|
||||
.global __aa64_ctx_switch
|
||||
.global __aa64_ctx_switch_to
|
||||
.global __aa64_ctx_enter_kernel
|
||||
.global __aa64_ctx_enter_from_fork
|
||||
|
||||
.set PT_REGS_SIZE, 16 * 7
|
||||
|
||||
__aa64_ctx_enter_user:
|
||||
ldp x0, x1, [sp, #0]
|
||||
msr sp_el0, x0
|
||||
|
||||
msr spsr_el1, xzr
|
||||
ldp x0, x1, [sp, #16]
|
||||
msr elr_el1, x1
|
||||
add sp, sp, #32
|
||||
|
||||
mov x1, xzr
|
||||
__return_to_user:
|
||||
eret
|
||||
|
||||
__aa64_ctx_enter_kernel:
|
||||
msr sp_el0, xzr
|
||||
|
||||
mov x0, #5
|
||||
msr spsr_el1, x0
|
||||
ldp x0, x1, [sp, #0]
|
||||
msr elr_el1, x1
|
||||
add sp, sp, #16
|
||||
|
||||
mov x1, xzr
|
||||
eret
|
||||
|
||||
__aa64_ctx_enter_from_fork:
|
||||
ldp x0, x1, [sp, #16 * 0]
|
||||
msr sp_el0, x0
|
||||
msr elr_el1, x1
|
||||
msr spsr_el1, xzr
|
||||
|
||||
ldp x1, x2, [sp, #16 * 1]
|
||||
ldp x3, x4, [sp, #16 * 2]
|
||||
ldp x5, x6, [sp, #16 * 3]
|
||||
ldp x7, x8, [sp, #16 * 4]
|
||||
ldp x9, x10, [sp, #16 * 5]
|
||||
ldp x11, x12, [sp, #16 * 6]
|
||||
ldp x13, x14, [sp, #16 * 7]
|
||||
ldp x15, x16, [sp, #16 * 8]
|
||||
ldp x17, x18, [sp, #16 * 9]
|
||||
|
||||
mov x0, xzr
|
||||
|
||||
eret
|
||||
|
||||
__aa64_ctx_switch:
|
||||
sub sp, sp, #PT_REGS_SIZE
|
||||
|
||||
stp x19, x20, [sp, #16 * 0]
|
||||
stp x21, x22, [sp, #16 * 1]
|
||||
stp x23, x24, [sp, #16 * 2]
|
||||
stp x25, x26, [sp, #16 * 3]
|
||||
stp x27, x28, [sp, #16 * 4]
|
||||
stp x29, x30, [sp, #16 * 5]
|
||||
mrs x19, TTBR0_EL1
|
||||
mrs x20, TPIDR_EL0
|
||||
stp x19, x20, [sp, #16 * 6]
|
||||
|
||||
mov x19, sp
|
||||
str x19, [x1]
|
||||
__aa64_ctx_switch_to:
|
||||
ldr x0, [x0]
|
||||
mov sp, x0
|
||||
|
||||
ldp x19, x20, [sp, #16 * 6]
|
||||
msr TTBR0_EL1, x19
|
||||
msr TPIDR_EL0, x20
|
||||
ldp x19, x20, [sp, #16 * 0]
|
||||
ldp x21, x22, [sp, #16 * 1]
|
||||
ldp x23, x24, [sp, #16 * 2]
|
||||
ldp x25, x26, [sp, #16 * 3]
|
||||
ldp x27, x28, [sp, #16 * 4]
|
||||
ldp x29, x30, [sp, #16 * 5]
|
||||
add sp, sp, #PT_REGS_SIZE
|
||||
|
||||
ret
|
@ -1,216 +0,0 @@
|
||||
//! Thread context
|
||||
|
||||
use crate::arch::aarch64::exception::ExceptionFrame;
|
||||
use crate::mem::{
|
||||
self,
|
||||
phys::{self, PageUsage},
|
||||
};
|
||||
use core::mem::size_of;
|
||||
use core::arch::global_asm;
|
||||
|
||||
struct Stack {
|
||||
bp: usize,
|
||||
sp: usize,
|
||||
}
|
||||
|
||||
/// Structure representing thread context
|
||||
#[repr(C)]
|
||||
pub struct Context {
|
||||
/// Thread's kernel stack pointer
|
||||
pub k_sp: usize, // 0x00
|
||||
|
||||
stack_base: usize,
|
||||
stack_page_count: usize,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Constructs a new kernel-space thread context
|
||||
pub fn kernel(entry: usize, arg: usize) -> Self {
|
||||
let mut stack = Stack::new(8);
|
||||
|
||||
stack.push(entry);
|
||||
stack.push(arg);
|
||||
|
||||
stack.setup_common(__aa64_ctx_enter_kernel as usize, 0);
|
||||
|
||||
Self {
|
||||
k_sp: stack.sp,
|
||||
|
||||
stack_base: stack.bp,
|
||||
stack_page_count: 8,
|
||||
}
|
||||
}
|
||||
|
||||
/// Clones a process context from given `frame`
|
||||
pub fn fork(frame: &ExceptionFrame, ttbr0: usize) -> Self {
|
||||
let mut stack = Stack::new(8);
|
||||
|
||||
stack.push(frame.x[18]);
|
||||
stack.push(frame.x[17]);
|
||||
stack.push(frame.x[16]);
|
||||
stack.push(frame.x[15]);
|
||||
stack.push(frame.x[14]);
|
||||
stack.push(frame.x[13]);
|
||||
stack.push(frame.x[12]);
|
||||
stack.push(frame.x[11]);
|
||||
stack.push(frame.x[10]);
|
||||
stack.push(frame.x[9]);
|
||||
stack.push(frame.x[8]);
|
||||
stack.push(frame.x[7]);
|
||||
stack.push(frame.x[6]);
|
||||
stack.push(frame.x[5]);
|
||||
stack.push(frame.x[4]);
|
||||
stack.push(frame.x[3]);
|
||||
stack.push(frame.x[2]);
|
||||
stack.push(frame.x[1]);
|
||||
|
||||
stack.push(frame.elr_el1 as usize);
|
||||
stack.push(frame.sp_el0 as usize);
|
||||
|
||||
// Setup common
|
||||
stack.push(0); // tpidr_el0
|
||||
stack.push(ttbr0);
|
||||
stack.push(__aa64_ctx_enter_from_fork as usize); // x30/lr
|
||||
stack.push(frame.x[29]); // x29
|
||||
stack.push(frame.x[28]); // x28
|
||||
stack.push(frame.x[27]); // x27
|
||||
stack.push(frame.x[26]); // x26
|
||||
stack.push(frame.x[25]); // x25
|
||||
stack.push(frame.x[24]); // x24
|
||||
stack.push(frame.x[23]); // x23
|
||||
stack.push(frame.x[22]); // x22
|
||||
stack.push(frame.x[21]); // x21
|
||||
stack.push(frame.x[20]); // x20
|
||||
stack.push(frame.x[19]); // x19
|
||||
|
||||
Self {
|
||||
k_sp: stack.sp,
|
||||
|
||||
stack_base: stack.bp,
|
||||
stack_page_count: 8,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a new user-space thread context
|
||||
pub fn user(entry: usize, arg: usize, ttbr0: usize, ustack: usize) -> Self {
|
||||
let mut stack = Stack::new(8);
|
||||
|
||||
stack.push(entry);
|
||||
stack.push(arg);
|
||||
stack.push(0);
|
||||
stack.push(ustack);
|
||||
|
||||
stack.setup_common(__aa64_ctx_enter_user as usize, ttbr0);
|
||||
|
||||
Self {
|
||||
k_sp: stack.sp,
|
||||
|
||||
stack_base: stack.bp,
|
||||
stack_page_count: 8,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs an uninitialized thread context
|
||||
pub fn empty() -> Self {
|
||||
let stack = Stack::new(8);
|
||||
Self {
|
||||
k_sp: stack.sp,
|
||||
stack_base: stack.bp,
|
||||
stack_page_count: 8
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets up a context for signal entry
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: may clobber an already active context
|
||||
pub unsafe fn setup_signal_entry(&mut self, entry: usize, arg: usize, ttbr0: usize, ustack: usize) {
|
||||
let mut stack = Stack::from_base_size(self.stack_base, self.stack_page_count);
|
||||
|
||||
stack.push(entry);
|
||||
stack.push(arg);
|
||||
stack.push(0);
|
||||
stack.push(ustack);
|
||||
|
||||
stack.setup_common(__aa64_ctx_enter_user as usize, ttbr0);
|
||||
|
||||
self.k_sp = stack.sp;
|
||||
}
|
||||
|
||||
/// Performs initial thread entry
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: does not check if any context has already been activated
|
||||
/// before, so must only be called once.
|
||||
pub unsafe extern "C" fn enter(&mut self) -> ! {
|
||||
__aa64_ctx_switch_to(self);
|
||||
panic!("This code should not run");
|
||||
}
|
||||
|
||||
/// Performs context switch from `self` to `to`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: does not check if `self` is actually an active context.
|
||||
pub unsafe extern "C" fn switch(&mut self, to: &mut Context) {
|
||||
__aa64_ctx_switch(to, self);
|
||||
}
|
||||
}
|
||||
|
||||
impl Stack {
|
||||
pub fn new(page_count: usize) -> Stack {
|
||||
let phys = phys::alloc_contiguous_pages(PageUsage::Kernel, page_count).unwrap();
|
||||
let bp = mem::virtualize(phys);
|
||||
Stack {
|
||||
bp,
|
||||
sp: bp + page_count * mem::PAGE_SIZE,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn from_base_size(bp: usize, page_count: usize) -> Stack {
|
||||
Stack {
|
||||
bp,
|
||||
sp: bp + page_count * mem::PAGE_SIZE
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_common(&mut self, entry: usize, ttbr: usize) {
|
||||
self.push(0); // tpidr_el0
|
||||
self.push(ttbr);
|
||||
self.push(entry); // x30/lr
|
||||
self.push(0); // x29
|
||||
self.push(0); // x28
|
||||
self.push(0); // x27
|
||||
self.push(0); // x26
|
||||
self.push(0); // x25
|
||||
self.push(0); // x24
|
||||
self.push(0); // x23
|
||||
self.push(0); // x22
|
||||
self.push(0); // x21
|
||||
self.push(0); // x20
|
||||
self.push(0); // x19
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: usize) {
|
||||
if self.bp == self.sp {
|
||||
panic!("Stack overflow");
|
||||
}
|
||||
|
||||
self.sp -= size_of::<usize>();
|
||||
unsafe {
|
||||
*(self.sp as *mut usize) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn __aa64_ctx_enter_from_fork();
|
||||
fn __aa64_ctx_enter_kernel();
|
||||
fn __aa64_ctx_enter_user();
|
||||
fn __aa64_ctx_switch(dst: *mut Context, src: *mut Context);
|
||||
fn __aa64_ctx_switch_to(dst: *mut Context);
|
||||
}
|
||||
|
||||
global_asm!(include_str!("context.S"));
|
@ -1,177 +0,0 @@
|
||||
//! AArch64 exception handling
|
||||
|
||||
use crate::arch::machine;
|
||||
use crate::debug::Level;
|
||||
use crate::dev::irq::{IntController, IrqContext};
|
||||
use crate::mem;
|
||||
use crate::proc::{sched, Process, Thread};
|
||||
use crate::syscall;
|
||||
use cortex_a::registers::{ESR_EL1, FAR_EL1};
|
||||
use libsys::{abi::SystemCall, signal::Signal, error::Errno};
|
||||
use tock_registers::interfaces::Readable;
|
||||
use core::arch::global_asm;
|
||||
|
||||
/// Trapped SIMD/FP functionality
|
||||
pub const EC_FP_TRAP: u64 = 0b000111;
|
||||
/// Data Abort at current EL
|
||||
pub const EC_DATA_ABORT_ELX: u64 = 0b100101;
|
||||
/// Data Abort at lower EL
|
||||
pub const EC_DATA_ABORT_EL0: u64 = 0b100100;
|
||||
/// SVC instruction in AA64 state
|
||||
pub const EC_SVC_AA64: u64 = 0b010101;
|
||||
|
||||
/// Storage for saving context interrupted by exception
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct ExceptionFrame {
|
||||
/// General-purpose registers x0-x31
|
||||
pub x: [usize; 32],
|
||||
/// Saved processor status
|
||||
pub spsr_el1: u64,
|
||||
/// Exception return address
|
||||
pub elr_el1: u64,
|
||||
/// User-space stack pointer
|
||||
pub sp_el0: u64,
|
||||
/// Translation table base register for user-space
|
||||
pub ttbr0_el1: u64,
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
const fn data_abort_access_type(iss: u64) -> &'static str {
|
||||
if iss & (1 << 6) != 0 {
|
||||
"WRITE"
|
||||
} else {
|
||||
"READ"
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
const fn data_abort_access_size(iss: u64) -> &'static str {
|
||||
match (iss >> 22) & 0x3 {
|
||||
0 => "BYTE",
|
||||
1 => "HALFWORD",
|
||||
2 => "WORD",
|
||||
3 => "DOUBLEWORD",
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn __aa64_exc_irq_handler(_exc: &mut ExceptionFrame) {
|
||||
unsafe {
|
||||
let ic = IrqContext::new();
|
||||
machine::intc().handle_pending_irqs(&ic);
|
||||
}
|
||||
}
|
||||
|
||||
fn dump_data_abort(level: Level, esr: u64, far: u64) {
|
||||
let iss = esr & 0x1FFFFFF;
|
||||
println!(level, "\x1B[41;1mData Abort:");
|
||||
|
||||
print!(level, " Illegal {}", data_abort_access_type(iss),);
|
||||
if iss & (1 << 24) != 0 {
|
||||
print!(level, " of a {}", data_abort_access_size(iss));
|
||||
}
|
||||
if iss & (1 << 10) == 0 {
|
||||
print!(level, " at {:#018x}", far);
|
||||
} else {
|
||||
print!(level, " at UNKNOWN");
|
||||
}
|
||||
println!(level, "\x1B[0m");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn __aa64_exc_sync_handler(exc: &mut ExceptionFrame) {
|
||||
let esr = ESR_EL1.get();
|
||||
let err_code = esr >> 26;
|
||||
|
||||
#[allow(clippy::single_match)]
|
||||
match err_code {
|
||||
EC_DATA_ABORT_EL0 | EC_DATA_ABORT_ELX => {
|
||||
let far = FAR_EL1.get() as usize;
|
||||
let iss = esr & 0x1FFFFFF;
|
||||
|
||||
if iss & (1 << 6) != 0 && far < mem::KERNEL_OFFSET && sched::is_ready() {
|
||||
let thread = Thread::current();
|
||||
let proc = thread.owner().unwrap();
|
||||
let asid = proc.asid();
|
||||
|
||||
let res = proc.manipulate_space(|space| {
|
||||
space.try_cow_copy(far)?;
|
||||
Process::invalidate_asid(asid);
|
||||
Result::<(), Errno>::Ok(())
|
||||
});
|
||||
|
||||
if res.is_err() {
|
||||
// Kill program
|
||||
errorln!("Data abort from {:#x}", exc.elr_el1);
|
||||
dump_data_abort(Level::Error, esr, far as u64);
|
||||
proc.enter_fault_signal(thread, Signal::SegmentationFault);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
errorln!("Unresolved data abort");
|
||||
errorln!("Data abort from {:#x}", exc.elr_el1);
|
||||
dump_data_abort(Level::Error, esr, far as u64);
|
||||
}
|
||||
EC_SVC_AA64 => {
|
||||
let num = SystemCall::from_repr(exc.x[8]);
|
||||
if num.is_none() {
|
||||
todo!();
|
||||
}
|
||||
let num = num.unwrap();
|
||||
|
||||
if num == SystemCall::Fork {
|
||||
match unsafe { syscall::sys_fork(exc) } {
|
||||
Ok(pid) => exc.x[0] = u32::from(pid) as usize,
|
||||
Err(err) => {
|
||||
exc.x[0] = err.to_negative_isize() as usize;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
match syscall::syscall(num, &exc.x[..6]) {
|
||||
Ok(val) => exc.x[0] = val,
|
||||
Err(err) => {
|
||||
exc.x[0] = err.to_negative_isize() as usize;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if sched::is_ready() {
|
||||
let thread = Thread::current();
|
||||
errorln!(
|
||||
"Unhandled exception in thread {:?}, {:?}",
|
||||
thread.id(),
|
||||
thread.owner().map(|e| e.id())
|
||||
);
|
||||
}
|
||||
|
||||
errorln!(
|
||||
"Unhandled exception at ELR={:#018x}, ESR={:#010x}",
|
||||
exc.elr_el1,
|
||||
esr,
|
||||
);
|
||||
errorln!("Error code: {:#08b}", err_code);
|
||||
|
||||
panic!("Unhandled exception");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn __aa64_exc_fiq_handler(_exc: &mut ExceptionFrame) {
|
||||
todo!();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
extern "C" fn __aa64_exc_serror_handler(_exc: &mut ExceptionFrame) {
|
||||
todo!();
|
||||
}
|
||||
|
||||
global_asm!(include_str!("vectors.S"));
|
@ -1,57 +0,0 @@
|
||||
use crate::dev::irq::IrqContext;
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use tock_registers::registers::ReadWrite;
|
||||
use tock_registers::{register_bitfields, register_structs};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
CTLR [
|
||||
Enable OFFSET(0) NUMBITS(1) []
|
||||
],
|
||||
PMR [
|
||||
Priority OFFSET(0) NUMBITS(8) []
|
||||
],
|
||||
IAR [
|
||||
InterruptID OFFSET(0) NUMBITS(10) []
|
||||
],
|
||||
EOIR [
|
||||
EOINTID OFFSET(0) NUMBITS(10) []
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
pub(super) GiccRegs {
|
||||
(0x00 => CTLR: ReadWrite<u32, CTLR::Register>),
|
||||
(0x04 => PMR: ReadWrite<u32, PMR::Register>),
|
||||
(0x08 => _res0),
|
||||
(0x0C => IAR: ReadWrite<u32, IAR::Register>),
|
||||
(0x10 => EOIR: ReadWrite<u32, EOIR::Register>),
|
||||
(0x14 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct Gicc {
|
||||
regs: DeviceMemoryIo<GiccRegs>,
|
||||
}
|
||||
|
||||
impl Gicc {
|
||||
pub const unsafe fn new(regs: DeviceMemoryIo<GiccRegs>) -> Self {
|
||||
Self { regs }
|
||||
}
|
||||
|
||||
pub unsafe fn enable(&self) {
|
||||
debugln!("Enable GICC");
|
||||
self.regs.CTLR.write(CTLR::Enable::SET);
|
||||
self.regs.PMR.write(PMR::Priority.val(0xFF));
|
||||
}
|
||||
|
||||
pub fn pending_irq_number<'q>(&'q self, _ic: &IrqContext<'q>) -> usize {
|
||||
self.regs.IAR.read(IAR::InterruptID) as usize
|
||||
}
|
||||
|
||||
pub fn clear_irq<'q>(&'q self, irq: u32, _ic: &IrqContext<'q>) {
|
||||
self.regs.EOIR.write(EOIR::EOINTID.val(irq));
|
||||
}
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use tock_registers::registers::{ReadOnly, ReadWrite};
|
||||
use tock_registers::{register_bitfields, register_structs};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
CTLR [
|
||||
Enable OFFSET(0) NUMBITS(1) []
|
||||
],
|
||||
TYPER [
|
||||
ITLinesNumber OFFSET(0) NUMBITS(5) []
|
||||
],
|
||||
ITARGETSR [
|
||||
Offset3 OFFSET(24) NUMBITS(8) [],
|
||||
Offset2 OFFSET(16) NUMBITS(8) [],
|
||||
Offset1 OFFSET(8) NUMBITS(8) [],
|
||||
Offset0 OFFSET(0) NUMBITS(8) []
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
pub(super) GicdSharedRegs {
|
||||
(0x000 => CTLR: ReadWrite<u32, CTLR::Register>),
|
||||
(0x004 => TYPER: ReadWrite<u32, TYPER::Register>),
|
||||
(0x008 => _res0),
|
||||
(0x104 => ISENABLER: [ReadWrite<u32>; 31]),
|
||||
(0x180 => _res1),
|
||||
(0x820 => ITARGETSR: [ReadWrite<u32, ITARGETSR::Register>; 248]),
|
||||
(0xC00 => _res2),
|
||||
(0xC08 => ICFGR: [ReadWrite<u32>; 62]),
|
||||
(0xC0C => @END),
|
||||
}
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
pub(super) GicdBankedRegs {
|
||||
(0x000 => _res0),
|
||||
(0x100 => ISENABLER: ReadWrite<u32>),
|
||||
(0x104 => _res1),
|
||||
(0x800 => ITARGETSR: [ReadOnly<u32, ITARGETSR::Register>; 8]),
|
||||
(0x804 => _res2),
|
||||
(0xC00 => ICFGR: [ReadWrite<u32>; 2]),
|
||||
(0xC04 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
impl GicdSharedRegs {
|
||||
#[inline(always)]
|
||||
fn num_irqs(&self) -> usize {
|
||||
((self.TYPER.read(TYPER::ITLinesNumber) as usize) + 1) * 32
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn itargets_slice(&self) -> &[ReadWrite<u32, ITARGETSR::Register>] {
|
||||
assert!(self.num_irqs() >= 36);
|
||||
let itargetsr_max_index = ((self.num_irqs() - 32) >> 2) - 1;
|
||||
&self.ITARGETSR[0..itargetsr_max_index]
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct Gicd {
|
||||
shared_regs: IrqSafeSpinLock<DeviceMemoryIo<GicdSharedRegs>>,
|
||||
banked_regs: DeviceMemoryIo<GicdBankedRegs>,
|
||||
}
|
||||
|
||||
impl Gicd {
|
||||
pub const unsafe fn new(
|
||||
shared_mmio: DeviceMemoryIo<GicdSharedRegs>,
|
||||
banked_mmio: DeviceMemoryIo<GicdBankedRegs>,
|
||||
) -> Self {
|
||||
Self {
|
||||
shared_regs: IrqSafeSpinLock::new(shared_mmio),
|
||||
banked_regs: banked_mmio,
|
||||
}
|
||||
}
|
||||
|
||||
fn local_gic_target_mask(&self) -> u32 {
|
||||
self.banked_regs.ITARGETSR[0].read(ITARGETSR::Offset0)
|
||||
}
|
||||
|
||||
fn enable_irq_inner(&self, irq: usize) {
|
||||
let reg = irq >> 5;
|
||||
let bit = 1u32 << (irq & 0x1F);
|
||||
|
||||
match reg {
|
||||
// Private
|
||||
0 => {
|
||||
let reg = &self.banked_regs.ISENABLER;
|
||||
|
||||
reg.set(reg.get() | bit);
|
||||
}
|
||||
// Shared
|
||||
_ => {
|
||||
let regs = self.shared_regs.lock();
|
||||
let reg = ®s.ISENABLER[reg - 1];
|
||||
|
||||
reg.set(reg.get() | bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable_irq(&self, irq: super::IrqNumber) {
|
||||
let irq = irq.get();
|
||||
|
||||
self.enable_irq_inner(irq);
|
||||
}
|
||||
|
||||
pub unsafe fn enable(&self) {
|
||||
let mask = self.local_gic_target_mask();
|
||||
let regs = self.shared_regs.lock();
|
||||
debugln!("Enable GICD, max IRQ number: {}", regs.num_irqs());
|
||||
|
||||
regs.CTLR.write(CTLR::Enable::SET);
|
||||
|
||||
for reg in regs.itargets_slice().iter() {
|
||||
reg.write(
|
||||
ITARGETSR::Offset0.val(mask)
|
||||
+ ITARGETSR::Offset1.val(mask)
|
||||
+ ITARGETSR::Offset2.val(mask)
|
||||
+ ITARGETSR::Offset3.val(mask),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,135 +0,0 @@
|
||||
//! ARM Generic Interrupt Controller
|
||||
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource, IrqContext},
|
||||
Device,
|
||||
};
|
||||
use crate::mem::virt::{DeviceMemory, DeviceMemoryIo};
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use libsys::error::Errno;
|
||||
|
||||
mod gicc;
|
||||
use gicc::Gicc;
|
||||
mod gicd;
|
||||
use gicd::Gicd;
|
||||
|
||||
/// Maximum available IRQ number
|
||||
pub const MAX_IRQ: usize = 300;
|
||||
|
||||
/// Range-checked IRQ number type
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct IrqNumber(usize);
|
||||
|
||||
/// ARM Generic Interrupt Controller, version 2
|
||||
pub struct Gic {
|
||||
gicc: InitOnce<Gicc>,
|
||||
gicd: InitOnce<Gicd>,
|
||||
gicd_base: usize,
|
||||
gicc_base: usize,
|
||||
table: IrqSafeSpinLock<[Option<&'static (dyn IntSource + Sync)>; MAX_IRQ]>,
|
||||
}
|
||||
|
||||
impl IrqNumber {
|
||||
/// Returns numeric representation for given [IrqNumber]
|
||||
#[inline(always)]
|
||||
pub const fn get(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Checks and wraps an IRQ number
|
||||
#[inline(always)]
|
||||
pub const fn new(v: usize) -> Self {
|
||||
assert!(v < MAX_IRQ);
|
||||
Self(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Gic {
|
||||
fn name(&self) -> &'static str {
|
||||
"ARM Generic Interrupt Controller"
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
let gicd_mmio = DeviceMemory::map("GICv2 Distributor registers", self.gicd_base, 1)?;
|
||||
let gicd_mmio_shared = DeviceMemoryIo::new(gicd_mmio.clone());
|
||||
let gicd_mmio_banked = DeviceMemoryIo::new(gicd_mmio);
|
||||
let gicc_mmio = DeviceMemoryIo::map("GICv2 CPU registers", self.gicc_base, 1)?;
|
||||
|
||||
let gicd = Gicd::new(gicd_mmio_shared, gicd_mmio_banked);
|
||||
let gicc = Gicc::new(gicc_mmio);
|
||||
|
||||
gicd.enable();
|
||||
gicc.enable();
|
||||
|
||||
self.gicd.init(gicd);
|
||||
self.gicc.init(gicc);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntController for Gic {
|
||||
type IrqNumber = IrqNumber;
|
||||
|
||||
fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Errno> {
|
||||
self.gicd.get().enable_irq(irq);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_pending_irqs<'irq_context>(&'irq_context self, ic: &IrqContext<'irq_context>) {
|
||||
let gicc = self.gicc.get();
|
||||
let irq_number = gicc.pending_irq_number(ic);
|
||||
if irq_number >= MAX_IRQ {
|
||||
return;
|
||||
}
|
||||
|
||||
gicc.clear_irq(irq_number as u32, ic);
|
||||
|
||||
{
|
||||
let table = self.table.lock();
|
||||
match table[irq_number] {
|
||||
None => panic!("No handler registered for irq{}", irq_number),
|
||||
Some(handler) => {
|
||||
drop(table);
|
||||
handler.handle_irq().expect("irq handler failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn register_handler(
|
||||
&self,
|
||||
irq: Self::IrqNumber,
|
||||
handler: &'static (dyn IntSource + Sync),
|
||||
) -> Result<(), Errno> {
|
||||
let mut table = self.table.lock();
|
||||
let irq = irq.get();
|
||||
if table[irq].is_some() {
|
||||
return Err(Errno::AlreadyExists);
|
||||
}
|
||||
|
||||
debugln!("Bound irq{} to {:?}", irq, Device::name(handler));
|
||||
table[irq] = Some(handler);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Gic {
|
||||
/// Constructs an instance of GICv2.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Does not perform `gicd_base` and `gicc_base` validation.
|
||||
pub const unsafe fn new(gicd_base: usize, gicc_base: usize) -> Self {
|
||||
Self {
|
||||
gicc: InitOnce::new(),
|
||||
gicd: InitOnce::new(),
|
||||
gicd_base,
|
||||
gicc_base,
|
||||
table: IrqSafeSpinLock::new([None; MAX_IRQ]),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
//! AArch64-specific IRQ handling functionality
|
||||
|
||||
pub mod gic;
|
@ -1,214 +0,0 @@
|
||||
//! Allwinner H6 GPIO port controller driver.
|
||||
//!
|
||||
//! GPIO ports are split into two register groups:
|
||||
//!
|
||||
//! 1. CPUS-PORT (TODO PL, PM)
|
||||
//! 2. CPUX-PORT (PC, PD, PF, PG, PH)
|
||||
//!
|
||||
use crate::dev::{
|
||||
gpio::{GpioDevice, PinConfig, PinMode, PullMode},
|
||||
Device,
|
||||
};
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use libsys::error::Errno;
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use tock_registers::register_structs;
|
||||
use tock_registers::registers::ReadWrite;
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
CpuxPortRegs {
|
||||
(0x00 => CFG: [ReadWrite<u32>; 4]),
|
||||
(0x10 => DAT: ReadWrite<u32>),
|
||||
(0x14 => DRV: [ReadWrite<u32>; 2]),
|
||||
(0x1C => PUL: [ReadWrite<u32>; 2]),
|
||||
(0x24 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct CpuxGpio {
|
||||
regs: DeviceMemoryIo<[CpuxPortRegs; 8]>,
|
||||
}
|
||||
|
||||
pub struct Gpio {
|
||||
cpux: InitOnce<IrqSafeSpinLock<CpuxGpio>>,
|
||||
cpux_base: usize,
|
||||
}
|
||||
|
||||
/// Structure combining bank and pin numbers
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct PinAddress(u32);
|
||||
|
||||
impl PinAddress {
|
||||
/// Constructs a new pin address from `bank` and `pin` numbers
|
||||
#[inline(always)]
|
||||
pub const fn new(bank: u32, pin: u32) -> Self {
|
||||
// TODO sanity checks
|
||||
Self((bank << 16) | pin)
|
||||
}
|
||||
|
||||
/// Returns bank number of this pin
|
||||
#[inline(always)]
|
||||
pub const fn bank(self) -> usize {
|
||||
(self.0 >> 16) as usize
|
||||
}
|
||||
|
||||
/// Returns pin number of this pin
|
||||
#[inline(always)]
|
||||
pub const fn pin(self) -> u32 {
|
||||
self.0 & 0xFFFF
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuxPortRegs {
|
||||
#[inline]
|
||||
fn set_pin_cfg_inner(&self, pin: u32, cfg: u32) {
|
||||
let reg = pin >> 3;
|
||||
let shift = (pin & 0x7) * 4;
|
||||
let tmp = self.CFG[reg as usize].get() & !(0xF << shift);
|
||||
self.CFG[reg as usize].set(tmp | ((cfg & 0x7) << shift));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_pin_pul_inner(&self, pin: u32, pul: u32) {
|
||||
let reg = pin >> 4;
|
||||
let shift = (pin & 0xF) * 2;
|
||||
let tmp = self.PUL[reg as usize].get() & !(0x3 << shift);
|
||||
self.PUL[reg as usize].set(tmp | ((pul & 0x3) << shift));
|
||||
}
|
||||
}
|
||||
|
||||
impl CpuxGpio {
|
||||
unsafe fn set_pin_config(&self, bank: usize, pin: u32, cfg: &PinConfig) -> Result<(), Errno> {
|
||||
let regs = &self.regs[bank];
|
||||
|
||||
let pull = match cfg.pull {
|
||||
PullMode::None => 0,
|
||||
PullMode::Up => 1,
|
||||
PullMode::Down => 2,
|
||||
};
|
||||
|
||||
match cfg.mode {
|
||||
PinMode::Disable => regs.set_pin_cfg_inner(pin, 7),
|
||||
PinMode::Input => {
|
||||
regs.set_pin_cfg_inner(pin, 0);
|
||||
regs.set_pin_pul_inner(pin, pull);
|
||||
}
|
||||
PinMode::Output => {
|
||||
regs.set_pin_cfg_inner(pin, 1); // TODO is it the same for all pins?
|
||||
regs.set_pin_pul_inner(pin, pull);
|
||||
}
|
||||
PinMode::InputInterrupt => {
|
||||
todo!()
|
||||
}
|
||||
PinMode::Alt => {
|
||||
assert!(cfg.func > 1 && cfg.func < 7);
|
||||
regs.set_pin_cfg_inner(pin, cfg.func);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_pin(&self, bank: usize, pin: u32) -> bool {
|
||||
self.regs[bank].DAT.get() & (1u32 << pin) != 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn toggle_pin(&mut self, bank: usize, pin: u32) {
|
||||
self.regs[bank]
|
||||
.DAT
|
||||
.set(self.regs[bank].DAT.get() ^ (1u32 << pin))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_pin(&mut self, bank: usize, pin: u32, value: bool) {
|
||||
if value {
|
||||
self.regs[bank]
|
||||
.DAT
|
||||
.set(self.regs[bank].DAT.get() | (1u32 << pin))
|
||||
} else {
|
||||
self.regs[bank]
|
||||
.DAT
|
||||
.set(self.regs[bank].DAT.get() & !(1u32 << pin))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Gpio {
|
||||
fn name(&self) -> &'static str {
|
||||
"Allwinner H6 GPIO Controller"
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
self.cpux.init(IrqSafeSpinLock::new(CpuxGpio {
|
||||
regs: DeviceMemoryIo::map(self.name(), self.cpux_base, 1)?,
|
||||
}));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl GpioDevice for Gpio {
|
||||
type PinAddress = PinAddress;
|
||||
|
||||
unsafe fn set_pin_config(&self, pin: PinAddress, cfg: &PinConfig) -> Result<(), Errno> {
|
||||
let bank = pin.bank();
|
||||
let pin = pin.pin();
|
||||
|
||||
match bank {
|
||||
0 | 1 | 4 => unimplemented!(),
|
||||
_ => self.cpux.get().lock().set_pin_config(bank, pin, cfg),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_pin_config(&self, _pin: PinAddress) -> Result<PinConfig, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn write_pin(&self, pin: PinAddress, state: bool) {
|
||||
let bank = pin.bank();
|
||||
let pin = pin.pin();
|
||||
|
||||
match bank {
|
||||
0 | 1 | 4 => unimplemented!(),
|
||||
_ => self.cpux.get().lock().write_pin(bank, pin, state),
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_pin(&self, pin: PinAddress) {
|
||||
let bank = pin.bank();
|
||||
let pin = pin.pin();
|
||||
|
||||
match bank {
|
||||
0 | 1 | 4 => unimplemented!(),
|
||||
_ => self.cpux.get().lock().toggle_pin(bank, pin),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_pin(&self, pin: PinAddress) -> Result<bool, Errno> {
|
||||
let bank = pin.bank();
|
||||
let pin = pin.pin();
|
||||
|
||||
match bank {
|
||||
0 | 1 | 4 => unimplemented!(),
|
||||
_ => Ok(self.cpux.get().lock().read_pin(bank, pin)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Gpio {
|
||||
pub unsafe fn cfg_uart0_ph0_ph1(&self) -> Result<(), Errno> {
|
||||
self.set_pin_config(PinAddress::new(7, 0), &PinConfig::alt(2))?;
|
||||
self.set_pin_config(PinAddress::new(7, 1), &PinConfig::alt(2))
|
||||
}
|
||||
|
||||
pub const unsafe fn new(cpux_base: usize) -> Self {
|
||||
Self {
|
||||
cpux: InitOnce::new(),
|
||||
cpux_base,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
//! Xunlong Orange Pi 3, with Allwinner H6 SoC
|
||||
|
||||
use crate::arch::aarch64::{
|
||||
irq::gic::{self, Gic},
|
||||
timer::GenericTimer,
|
||||
};
|
||||
use crate::dev::{
|
||||
gpio::{GpioDevice, PinConfig},
|
||||
irq::{IntController, IntSource},
|
||||
serial::SerialDevice,
|
||||
timer::TimestampSource,
|
||||
Device,
|
||||
};
|
||||
use crate::fs::devfs::{self, CharDeviceType};
|
||||
use crate::mem::phys;
|
||||
use libsys::error::Errno;
|
||||
|
||||
mod gpio;
|
||||
mod rtc;
|
||||
mod uart;
|
||||
mod wdog;
|
||||
|
||||
pub use gic::IrqNumber;
|
||||
use gpio::Gpio;
|
||||
pub use gpio::PinAddress;
|
||||
use rtc::Rtc;
|
||||
use uart::Uart;
|
||||
use wdog::RWdog;
|
||||
|
||||
pub fn init_board_early() -> Result<(), Errno> {
|
||||
unsafe {
|
||||
UART0.enable()?;
|
||||
|
||||
phys::init_from_region(0x80000000, 0x10000000);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn init_board() -> Result<(), Errno> {
|
||||
unsafe {
|
||||
GIC.enable()?;
|
||||
GPIO.enable()?;
|
||||
|
||||
UART0.init_irqs()?;
|
||||
devfs::add_char_device(&UART0, CharDeviceType::TtySerial)?;
|
||||
|
||||
R_WDOG.enable()?;
|
||||
|
||||
GPIO.cfg_uart0_ph0_ph1()?;
|
||||
GPIO.set_pin_config(PinAddress::new(3, 26), &PinConfig::out_pull_down())?;
|
||||
|
||||
RTC.enable()?;
|
||||
RTC.init_irqs()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Performs board reset
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: may interrupt critical processes
|
||||
pub unsafe fn reset_board() -> ! {
|
||||
R_WDOG.reset_board()
|
||||
}
|
||||
|
||||
const LOCAL_TIMER_IRQ: IrqNumber = IrqNumber::new(30);
|
||||
const R_WDOG_BASE: usize = 0x07020400;
|
||||
const UART0_BASE: usize = 0x05000000;
|
||||
const RTC_BASE: usize = 0x07000000;
|
||||
const RTC_IRQ: IrqNumber = IrqNumber::new(133);
|
||||
const PIO_BASE: usize = 0x0300B000;
|
||||
const GICD_BASE: usize = 0x03021000;
|
||||
const GICC_BASE: usize = 0x03022000;
|
||||
|
||||
/// Returns primary console for this machine
|
||||
#[inline]
|
||||
pub fn console() -> &'static impl SerialDevice {
|
||||
&UART0
|
||||
}
|
||||
|
||||
/// Returns the timer used as CPU-local periodic IRQ source
|
||||
#[inline]
|
||||
pub fn local_timer() -> &'static GenericTimer {
|
||||
&LOCAL_TIMER
|
||||
}
|
||||
|
||||
/// Returns CPU's interrupt controller device
|
||||
#[inline]
|
||||
pub fn intc() -> &'static impl IntController<IrqNumber = IrqNumber> {
|
||||
&GIC
|
||||
}
|
||||
|
||||
static R_WDOG: RWdog = unsafe { RWdog::new(R_WDOG_BASE) };
|
||||
static UART0: Uart = unsafe { Uart::new(UART0_BASE, IrqNumber::new(32)) };
|
||||
static LOCAL_TIMER: GenericTimer = GenericTimer::new(LOCAL_TIMER_IRQ);
|
||||
pub(super) static GPIO: Gpio = unsafe { Gpio::new(PIO_BASE) };
|
||||
static RTC: Rtc = unsafe { Rtc::new(RTC_BASE, RTC_IRQ) };
|
||||
static GIC: Gic = unsafe { Gic::new(GICD_BASE, GICC_BASE) };
|
@ -1,114 +0,0 @@
|
||||
use crate::arch::machine::{self, IrqNumber};
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource},
|
||||
rtc::RtcDevice,
|
||||
Device,
|
||||
};
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use libsys::error::Errno;
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite},
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
ALARM0_IRQ_EN [
|
||||
ALARM0_IRQ_EN OFFSET(0) NUMBITS(1) []
|
||||
],
|
||||
ALARM0_ENABLE [
|
||||
ALM_0_EN OFFSET(0) NUMBITS(1) []
|
||||
],
|
||||
ALARM0_IRQ_STA [
|
||||
ALARM0_IRQ_PEND OFFSET(0) NUMBITS(1) []
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
(0x00 => LOSC_CTRL: ReadWrite<u32>),
|
||||
(0x04 => LOSC_AUTO_SWT_STA: ReadWrite<u32>),
|
||||
(0x08 => INTOSC_CLK_PRESCAL: ReadWrite<u32>),
|
||||
(0x0C => INTOSC_CLK_AUTO_CALI: ReadWrite<u32>),
|
||||
(0x10 => RTC_YY_MM_DD: ReadWrite<u32>),
|
||||
(0x14 => RTC_HH_MM_SS: ReadWrite<u32>),
|
||||
(0x18 => _res0),
|
||||
(0x20 => ALARM0_COUNTER: ReadWrite<u32>),
|
||||
(0x24 => ALARM0_CUR_VLU: ReadOnly<u32>),
|
||||
(0x28 => ALARM0_ENABLE: ReadWrite<u32, ALARM0_ENABLE::Register>),
|
||||
(0x2C => ALARM0_IRQ_EN: ReadWrite<u32, ALARM0_IRQ_EN::Register>),
|
||||
(0x30 => ALARM0_IRQ_STA: ReadWrite<u32, ALARM0_IRQ_STA::Register>),
|
||||
(0x34 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Rtc {
|
||||
regs: InitOnce<IrqSafeSpinLock<DeviceMemoryIo<Regs>>>,
|
||||
base: usize,
|
||||
irq: IrqNumber,
|
||||
}
|
||||
|
||||
impl Regs {
|
||||
fn arm_alarm0_irq(&self, sec: u32) {
|
||||
// Clear IRQ pending status
|
||||
if sec == 0 {
|
||||
return;
|
||||
}
|
||||
self.ALARM0_IRQ_STA
|
||||
.write(ALARM0_IRQ_STA::ALARM0_IRQ_PEND::SET);
|
||||
self.ALARM0_IRQ_EN.write(ALARM0_IRQ_EN::ALARM0_IRQ_EN::SET);
|
||||
self.ALARM0_COUNTER.set(self.ALARM0_CUR_VLU.get() + sec - 1);
|
||||
self.ALARM0_ENABLE.write(ALARM0_ENABLE::ALM_0_EN::SET);
|
||||
}
|
||||
}
|
||||
|
||||
impl RtcDevice for Rtc {}
|
||||
|
||||
impl IntSource for Rtc {
|
||||
fn handle_irq(&self) -> Result<(), Errno> {
|
||||
self.regs.get().lock().arm_alarm0_irq(1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_irqs(&'static self) -> Result<(), Errno> {
|
||||
machine::intc().register_handler(self.irq, self)?;
|
||||
self.regs.get().lock().arm_alarm0_irq(1);
|
||||
machine::intc().enable_irq(self.irq)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Rtc {
|
||||
fn name(&self) -> &'static str {
|
||||
"Allwinner H6 RTC"
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
self.regs.init(IrqSafeSpinLock::new(DeviceMemoryIo::map(
|
||||
self.name(),
|
||||
self.base,
|
||||
1,
|
||||
)?));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Rtc {
|
||||
/// Constructs an instance of RTC device.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Does not perform `base` validation.
|
||||
pub const unsafe fn new(base: usize, irq: IrqNumber) -> Self {
|
||||
Self {
|
||||
regs: InitOnce::new(),
|
||||
base,
|
||||
irq,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,165 +0,0 @@
|
||||
use crate::arch::machine::{self, IrqNumber};
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource},
|
||||
serial::SerialDevice,
|
||||
tty::{CharRing, TtyDevice},
|
||||
Device,
|
||||
};
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use libsys::error::Errno;
|
||||
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
|
||||
use tock_registers::registers::{Aliased, ReadOnly, ReadWrite};
|
||||
use tock_registers::{register_bitfields, register_structs};
|
||||
|
||||
register_bitfields! [
|
||||
u32,
|
||||
IER [
|
||||
PTIME OFFSET(7) NUMBITS(1) [],
|
||||
RS485_INT_EN OFFSET(4) NUMBITS(1) [],
|
||||
EDSSI OFFSET(3) NUMBITS(1) [],
|
||||
ELSI OFFSET(2) NUMBITS(1) [],
|
||||
ETBEI OFFSET(1) NUMBITS(1) [],
|
||||
ERBFI OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
IIR [
|
||||
FEFLAG OFFSET(6) NUMBITS(2) [
|
||||
Enable = 3,
|
||||
Disable = 0
|
||||
],
|
||||
IID OFFSET(0) NUMBITS(4) [
|
||||
ModemStatus = 0,
|
||||
NoInterrupt = 1,
|
||||
ThrEmpty = 2,
|
||||
Rs485Interrupt = 3,
|
||||
ReceivedDataAvailable = 4,
|
||||
ReceiverLineStatus = 6,
|
||||
BusyDetect = 7,
|
||||
CharacterTimeout = 12
|
||||
]
|
||||
],
|
||||
LSR [
|
||||
FIFOERR OFFSET(7) NUMBITS(1) [],
|
||||
TEMT OFFSET(6) NUMBITS(1) [],
|
||||
THRE OFFSET(5) NUMBITS(1) [],
|
||||
BI OFFSET(4) NUMBITS(1) [],
|
||||
FE OFFSET(3) NUMBITS(1) [],
|
||||
PE OFFSET(2) NUMBITS(1) [],
|
||||
OE OFFSET(1) NUMBITS(1) [],
|
||||
DR OFFSET(0) NUMBITS(1) []
|
||||
]
|
||||
];
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
(0x0000 => DR_DLL: Aliased<u32>),
|
||||
(0x0004 => IER_DLH: ReadWrite<u32, IER::Register>),
|
||||
(0x0008 => IIR_FCR: Aliased<u32, IIR::Register, ()>),
|
||||
(0x000C => LCR: ReadWrite<u32>),
|
||||
(0x0010 => MCR: ReadWrite<u32>),
|
||||
(0x0014 => LSR: ReadOnly<u32, LSR::Register>),
|
||||
(0x0018 => MSR: ReadOnly<u32>),
|
||||
(0x001C => SCH: ReadWrite<u32>),
|
||||
(0x0020 => _res0),
|
||||
(0x007C => USR: ReadOnly<u32>),
|
||||
(0x0080 => TFL: ReadWrite<u32>),
|
||||
(0x0084 => RFL: ReadWrite<u32>),
|
||||
(0x0088 => HSK: ReadWrite<u32>),
|
||||
(0x008C => _res1),
|
||||
(0x00A4 => HALT: ReadWrite<u32>),
|
||||
(0x00D0 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct UartInner {
|
||||
regs: DeviceMemoryIo<Regs>,
|
||||
}
|
||||
|
||||
#[derive(TtyCharDevice)]
|
||||
pub(super) struct Uart {
|
||||
inner: InitOnce<IrqSafeSpinLock<UartInner>>,
|
||||
ring: CharRing<16>,
|
||||
base: usize,
|
||||
irq: IrqNumber,
|
||||
}
|
||||
|
||||
impl Device for Uart {
|
||||
fn name(&self) -> &'static str {
|
||||
"Allwinner H6 UART"
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
let mut inner = UartInner {
|
||||
regs: DeviceMemoryIo::map(self.name(), self.base, 1)?,
|
||||
};
|
||||
// TODO
|
||||
self.inner.init(IrqSafeSpinLock::new(inner));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SerialDevice for Uart {
|
||||
fn send(&self, byte: u8) -> Result<(), Errno> {
|
||||
if !self.inner.is_initialized() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let inner = self.inner.get().lock();
|
||||
while !inner.regs.LSR.matches_all(LSR::THRE::SET) {
|
||||
cortex_a::asm::nop();
|
||||
}
|
||||
inner.regs.DR_DLL.set(byte as u32);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn recv(&self, _blocking: bool) -> Result<u8, Errno> {
|
||||
let inner = self.inner.get().lock();
|
||||
while !inner.regs.LSR.matches_all(LSR::DR::SET) {
|
||||
cortex_a::asm::nop();
|
||||
}
|
||||
Ok(inner.regs.DR_DLL.get() as u8)
|
||||
}
|
||||
}
|
||||
|
||||
impl TtyDevice<16> for Uart {
|
||||
fn ring(&self) -> &CharRing<16> {
|
||||
&self.ring
|
||||
}
|
||||
}
|
||||
|
||||
impl IntSource for Uart {
|
||||
fn handle_irq(&self) -> Result<(), Errno> {
|
||||
let byte = self.inner.get().lock().regs.DR_DLL.get();
|
||||
|
||||
if byte == 0x1B {
|
||||
debugln!("Received ESC, resetting");
|
||||
unsafe {
|
||||
machine::reset_board();
|
||||
}
|
||||
}
|
||||
|
||||
self.recv_byte(byte as u8);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_irqs(&'static self) -> Result<(), Errno> {
|
||||
machine::intc().register_handler(self.irq, self)?;
|
||||
self.inner.get().lock().regs.IER_DLH.modify(IER::ERBFI::SET);
|
||||
machine::intc().enable_irq(self.irq)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Uart {
|
||||
pub const unsafe fn new(base: usize, irq: IrqNumber) -> Self {
|
||||
Self {
|
||||
inner: InitOnce::new(),
|
||||
ring: CharRing::new(),
|
||||
base,
|
||||
irq,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
use crate::dev::Device;
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use libsys::error::Errno;
|
||||
use tock_registers::{
|
||||
interfaces::Writeable, register_bitfields, register_structs, registers::ReadWrite,
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
CTRL [
|
||||
KEY OFFSET(1) NUMBITS(12) [
|
||||
Value = 0xA57
|
||||
],
|
||||
RESTART OFFSET(0) NUMBITS(1) []
|
||||
],
|
||||
CFG [
|
||||
CONFIG OFFSET(0) NUMBITS(2) [
|
||||
System = 1
|
||||
]
|
||||
],
|
||||
MODE [
|
||||
EN OFFSET(0) NUMBITS(1) []
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
RWdogRegs {
|
||||
(0x00 => IRQ_EN: ReadWrite<u32>),
|
||||
(0x04 => IRQ_STA: ReadWrite<u32>),
|
||||
(0x08 => _res0),
|
||||
(0x10 => CTRL: ReadWrite<u32, CTRL::Register>),
|
||||
(0x14 => CFG: ReadWrite<u32, CFG::Register>),
|
||||
(0x18 => MODE: ReadWrite<u32, MODE::Register>),
|
||||
(0x1C => @END),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct RWdog {
|
||||
inner: InitOnce<IrqSafeSpinLock<DeviceMemoryIo<RWdogRegs>>>,
|
||||
base: usize,
|
||||
}
|
||||
|
||||
impl Device for RWdog {
|
||||
fn name(&self) -> &'static str {
|
||||
"Allwinner H6 R_WDOG"
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
self.inner.init(IrqSafeSpinLock::new(DeviceMemoryIo::map(
|
||||
self.name(),
|
||||
self.base,
|
||||
1,
|
||||
)?));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl RWdog {
|
||||
/// Performs board reset
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: may interrupt critical processes
|
||||
pub unsafe fn reset_board(&self) -> ! {
|
||||
let regs = self.inner.get().lock();
|
||||
|
||||
regs.CFG.write(CFG::CONFIG::System);
|
||||
regs.MODE.write(MODE::EN::SET);
|
||||
regs.CTRL.write(CTRL::KEY::Value + CTRL::RESTART::SET);
|
||||
|
||||
loop {
|
||||
asm!("wfe");
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs an instance of R_WDOG peripheral.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Does not perform `base` validation.
|
||||
pub const unsafe fn new(base: usize) -> Self {
|
||||
Self {
|
||||
inner: InitOnce::new(),
|
||||
base,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,83 +1,11 @@
|
||||
//! QEMU virt machine
|
||||
use crate::dev::serial::{pl011::Pl011, SerialDevice};
|
||||
use crate::sync::Spin;
|
||||
|
||||
use crate::arch::aarch64::{
|
||||
irq::gic::{self, Gic},
|
||||
timer::GenericTimer,
|
||||
};
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource},
|
||||
pci::pcie::gpex::GenericPcieHost,
|
||||
rtc::pl031::Pl031,
|
||||
serial::{pl011::Pl011, SerialDevice},
|
||||
Device,
|
||||
};
|
||||
use crate::fs::devfs::{self, CharDeviceType};
|
||||
use crate::mem::phys;
|
||||
use libsys::error::Errno;
|
||||
pub const UART0_BASE: usize = 0x09000000;
|
||||
|
||||
pub use gic::IrqNumber;
|
||||
|
||||
// TODO extract this from device tree
|
||||
const LOCAL_TIMER_IRQ: IrqNumber = IrqNumber::new(30);
|
||||
const UART0_BASE: usize = 0x09000000;
|
||||
const UART0_IRQ: IrqNumber = IrqNumber::new(33);
|
||||
const RTC_BASE: usize = 0x09010000;
|
||||
const RTC_IRQ: IrqNumber = IrqNumber::new(34);
|
||||
const GICD_BASE: usize = 0x08000000;
|
||||
const GICC_BASE: usize = 0x08010000;
|
||||
const ECAM_BASE: usize = 0x4010000000;
|
||||
|
||||
const PHYS_BASE: usize = 0x40000000;
|
||||
const PHYS_SIZE: usize = 0x10000000;
|
||||
|
||||
/// Performs early board initialization (debug output and physical memory)
|
||||
pub fn init_board_early() -> Result<(), Errno> {
|
||||
unsafe {
|
||||
// Enable UART early on
|
||||
UART0.enable()?;
|
||||
|
||||
phys::init_from_region(PHYS_BASE, PHYS_SIZE);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Performs board hardware init
|
||||
pub fn init_board() -> Result<(), Errno> {
|
||||
unsafe {
|
||||
GIC.enable()?;
|
||||
|
||||
UART0.init_irqs()?;
|
||||
devfs::add_char_device(&UART0, CharDeviceType::TtySerial)?;
|
||||
|
||||
RTC.enable()?;
|
||||
RTC.init_irqs()?;
|
||||
|
||||
PCIE.enable()?;
|
||||
// PCIE.map()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns primary console for this machine
|
||||
#[inline]
|
||||
pub fn console() -> &'static impl SerialDevice {
|
||||
pub fn console() -> &'static Spin<impl SerialDevice> {
|
||||
&UART0
|
||||
}
|
||||
|
||||
/// Returns the timer used as CPU-local periodic IRQ source
|
||||
#[inline]
|
||||
pub fn local_timer() -> &'static GenericTimer {
|
||||
&LOCAL_TIMER
|
||||
}
|
||||
|
||||
/// Returns CPU's interrupt controller device
|
||||
#[inline]
|
||||
pub fn intc() -> &'static impl IntController<IrqNumber = IrqNumber> {
|
||||
&GIC
|
||||
}
|
||||
|
||||
static UART0: Pl011 = unsafe { Pl011::new(UART0_BASE, UART0_IRQ) };
|
||||
static RTC: Pl031 = unsafe { Pl031::new(RTC_BASE, RTC_IRQ) };
|
||||
static GIC: Gic = unsafe { Gic::new(GICD_BASE, GICC_BASE) };
|
||||
static PCIE: GenericPcieHost = unsafe { GenericPcieHost::new(ECAM_BASE, 8) };
|
||||
static LOCAL_TIMER: GenericTimer = GenericTimer::new(LOCAL_TIMER_IRQ);
|
||||
static UART0: Spin<Pl011> = Spin::new(unsafe { Pl011::new(UART0_BASE) });
|
||||
|
@ -1,476 +0,0 @@
|
||||
use crate::arch::machine::{self, Bcm283xMailbox};
|
||||
use crate::dev::sd::{
|
||||
SdCardIdentification, SdCardStatus, SdCommand, SdCommandNumber, SdCommandTransfer,
|
||||
SdHostController, SdResponse, SdResponseType,
|
||||
};
|
||||
use crate::dev::Device;
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use libsys::error::Errno;
|
||||
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
|
||||
use tock_registers::registers::{ReadOnly, ReadWrite};
|
||||
use tock_registers::{register_bitfields, register_structs};
|
||||
use vfs::BlockDevice;
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
BLKSIZECNT [
|
||||
BLKCNT OFFSET(16) NUMBITS(16) [],
|
||||
BLKSIZE OFFSET(0) NUMBITS(10) [],
|
||||
],
|
||||
CMDTM [
|
||||
CMD_INDEX OFFSET(24) NUMBITS(6) [],
|
||||
CMD_TYPE OFFSET(22) NUMBITS(23) [
|
||||
Normal = 0,
|
||||
Suspend = 1,
|
||||
Resume = 2,
|
||||
Abort = 3,
|
||||
],
|
||||
CMD_ISDATA OFFSET(21) NUMBITS(1) [],
|
||||
CMD_IXCHK_EN OFFSET(20) NUMBITS(1) [],
|
||||
CMD_CRCCHK_EN OFFSET(19) NUMBITS(1) [],
|
||||
CMD_RSPNS_TYPE OFFSET(16) NUMBITS(2) [
|
||||
None = 0,
|
||||
Bits136 = 1,
|
||||
Bits48 = 2,
|
||||
Bits48Busy = 3
|
||||
],
|
||||
TM_MULTI_BLOCK OFFSET(5) NUMBITS(1) [],
|
||||
TM_DAT_DIR OFFSET(4) NUMBITS(1) [
|
||||
HostToCard = 0,
|
||||
CardToHost = 1,
|
||||
],
|
||||
TM_AUDO_CMD_EN OFFSET(2) NUMBITS(2) [
|
||||
None = 0,
|
||||
Cmd12 = 1,
|
||||
Cmd23 = 2
|
||||
],
|
||||
TM_BLKCNT_EN OFFSET(1) NUMBITS(1) [],
|
||||
],
|
||||
STATUS [
|
||||
READ_TRANSFER OFFSET(9) NUMBITS(1) [],
|
||||
WRITE_TRANSFER OFFSET(8) NUMBITS(1) [],
|
||||
MISC_INSERTED OFFSET(16) NUMBITS(1) [],
|
||||
DAT_ACTIVE OFFSET(2) NUMBITS(1) [],
|
||||
DAT_INHIBIT OFFSET(1) NUMBITS(1) [],
|
||||
CMD_INHIBIT OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
INTERRUPT [
|
||||
ACMD_ERR 24,
|
||||
DEND_ERR 22,
|
||||
DCRC_ERR 21,
|
||||
DTO_ERR 20,
|
||||
CBAD_ERR 19,
|
||||
CEND_ERR 18,
|
||||
CCRC_ERR 17,
|
||||
CTO_ERR 16,
|
||||
ERR 15,
|
||||
ENDBOOT 14,
|
||||
BOOTACK 13,
|
||||
RETUNE 12,
|
||||
CARD 8,
|
||||
READ_RDY 5,
|
||||
WRITE_RDY 4,
|
||||
BLOCK_GAP 2,
|
||||
DATA_DONE 1,
|
||||
CMD_DONE 0,
|
||||
],
|
||||
CONTROL1 [
|
||||
SRST_DATA OFFSET(26) NUMBITS(1) [],
|
||||
SRST_CMD OFFSET(25) NUMBITS(1) [],
|
||||
SRST_HC OFFSET(24) NUMBITS(1) [],
|
||||
DATA_TOUNIT OFFSET(16) NUMBITS(4) [],
|
||||
CLK_FREQ8 OFFSET(8) NUMBITS(8) [],
|
||||
CLK_FREQ_MS2 OFFSET(6) NUMBITS(2) [],
|
||||
CLK_GENSEL OFFSET(5) NUMBITS(1) [
|
||||
Divided = 0,
|
||||
Programmable = 1
|
||||
],
|
||||
CLK_EN OFFSET(2) NUMBITS(1) [],
|
||||
CLK_STABLE OFFSET(1) NUMBITS(1) [],
|
||||
CLK_INTLEN OFFSET(0) NUMBITS(1) [],
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
(0x00 => ARG2: ReadWrite<u32>),
|
||||
(0x04 => BLKSIZECNT: ReadWrite<u32, BLKSIZECNT::Register>),
|
||||
(0x08 => ARG1: ReadWrite<u32>),
|
||||
(0x0C => CMDTM: ReadWrite<u32, CMDTM::Register>),
|
||||
(0x10 => RESP0: ReadOnly<u32>),
|
||||
(0x14 => RESP1: ReadOnly<u32>),
|
||||
(0x18 => RESP2: ReadOnly<u32>),
|
||||
(0x1C => RESP3: ReadOnly<u32>),
|
||||
(0x20 => DATA: ReadWrite<u32>),
|
||||
(0x24 => STATUS: ReadOnly<u32, STATUS::Register>),
|
||||
(0x28 => CONTROL0: ReadWrite<u32>),
|
||||
(0x2C => CONTROL1: ReadWrite<u32, CONTROL1::Register>),
|
||||
(0x30 => INTERRUPT: ReadWrite<u32, INTERRUPT::Register>),
|
||||
(0x34 => IRPT_MASK: ReadWrite<u32, INTERRUPT::Register>),
|
||||
(0x38 => IRPT_EN: ReadWrite<u32>),
|
||||
(0x3C => CONTROL2: ReadWrite<u32>),
|
||||
(0x40 => _res0),
|
||||
(0x50 => FORCE_IRPT: ReadWrite<u32>),
|
||||
(0x54 => _res1),
|
||||
(0x70 => BOOT_TIMEOUT: ReadWrite<u32>),
|
||||
(0x74 => DBG_SEL: ReadWrite<u32>),
|
||||
(0x78 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct MmcInner {
|
||||
regs: DeviceMemoryIo<Regs>,
|
||||
status: SdCardStatus,
|
||||
}
|
||||
|
||||
pub struct MassMediaController {
|
||||
inner: InitOnce<IrqSafeSpinLock<MmcInner>>,
|
||||
base: usize,
|
||||
}
|
||||
|
||||
fn clock_divider(f_base: u32, f_target: u32) -> u32 {
|
||||
let mut target_div;
|
||||
let mut div = 0;
|
||||
if f_target <= f_base {
|
||||
target_div = f_base / f_target;
|
||||
if f_base % f_target != 0 {
|
||||
target_div -= 1;
|
||||
}
|
||||
} else {
|
||||
target_div = 1;
|
||||
}
|
||||
for first_bit in (0..31).rev() {
|
||||
if target_div & (1 << first_bit) != 0 {
|
||||
div = first_bit;
|
||||
target_div &= !(1 << first_bit);
|
||||
if target_div != 0 {
|
||||
div += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if div == u32::MAX {
|
||||
div = 31;
|
||||
}
|
||||
if div >= 32 {
|
||||
div = 31;
|
||||
}
|
||||
if div != 0 {
|
||||
div = 1 << (div - 1);
|
||||
}
|
||||
if div >= 0x400 {
|
||||
div = 0x3FF;
|
||||
}
|
||||
|
||||
div
|
||||
}
|
||||
|
||||
impl MmcInner {
|
||||
fn power_on(&mut self) -> Result<(), Errno> {
|
||||
machine::BCM_MBOX.set_power_state(
|
||||
Bcm283xMailbox::POWER_SD_CARD,
|
||||
Bcm283xMailbox::POWER_STATE_ON | Bcm283xMailbox::POWER_STATE_WAIT,
|
||||
)
|
||||
}
|
||||
|
||||
fn base_clock(&mut self) -> Result<u32, Errno> {
|
||||
machine::BCM_MBOX.clock_rate(Bcm283xMailbox::CLOCK_EMMC)
|
||||
}
|
||||
|
||||
// TODO generalize flag setting
|
||||
fn send_cmd_inner(&mut self, cmd: &mut SdCommand) -> Result<SdResponse, Errno> {
|
||||
const TIMEOUT: u64 = 10000;
|
||||
let info = cmd.info_struct();
|
||||
let mut cmdtm = CMDTM::CMD_INDEX.val(cmd.number as u32);
|
||||
|
||||
// Wait until CMD lines free up
|
||||
crate::block!(
|
||||
self.regs.STATUS.matches_all(STATUS::CMD_INHIBIT::CLEAR),
|
||||
TIMEOUT
|
||||
);
|
||||
|
||||
if info.response_type.is_busy() {
|
||||
// TODO check if this is an ABORT command
|
||||
// Wait until DAT lines free up after busy cmd
|
||||
crate::block!(
|
||||
self.regs.STATUS.matches_all(STATUS::DAT_INHIBIT::CLEAR),
|
||||
TIMEOUT
|
||||
);
|
||||
}
|
||||
|
||||
let (block_count, block_size) = match &cmd.transfer {
|
||||
SdCommandTransfer::Write(buf, blk_size) => {
|
||||
let sz = *blk_size as usize;
|
||||
assert!(buf.len() % sz == 0);
|
||||
cmdtm += CMDTM::CMD_ISDATA::SET + CMDTM::TM_DAT_DIR::HostToCard;
|
||||
((buf.len() / sz) as u32, *blk_size)
|
||||
}
|
||||
SdCommandTransfer::Read(buf, blk_size) => {
|
||||
let sz = *blk_size as usize;
|
||||
assert!(buf.len() % sz == 0);
|
||||
cmdtm += CMDTM::CMD_ISDATA::SET + CMDTM::TM_DAT_DIR::CardToHost;
|
||||
((buf.len() / sz) as u32, *blk_size)
|
||||
}
|
||||
SdCommandTransfer::None => (0, 512),
|
||||
};
|
||||
|
||||
let size_136 = match info.response_type {
|
||||
SdResponseType::R2 | SdResponseType::R4 => {
|
||||
cmdtm += CMDTM::CMD_RSPNS_TYPE::Bits136;
|
||||
true
|
||||
}
|
||||
SdResponseType::R1b | SdResponseType::R5b => {
|
||||
cmdtm += CMDTM::CMD_RSPNS_TYPE::Bits48Busy;
|
||||
false
|
||||
}
|
||||
SdResponseType::None => false,
|
||||
_ => {
|
||||
cmdtm += CMDTM::CMD_RSPNS_TYPE::Bits48;
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
self.regs.BLKSIZECNT.write(
|
||||
BLKSIZECNT::BLKCNT.val(block_count) + BLKSIZECNT::BLKSIZE.val(block_size as u32),
|
||||
);
|
||||
self.regs.ARG1.set(cmd.argument);
|
||||
self.regs.CMDTM.write(cmdtm);
|
||||
crate::block!(
|
||||
self.regs
|
||||
.INTERRUPT
|
||||
.matches_any(INTERRUPT::ERR::SET + INTERRUPT::CMD_DONE::SET),
|
||||
10000
|
||||
);
|
||||
|
||||
let irq_status = self.regs.INTERRUPT.get();
|
||||
self.regs.INTERRUPT.set(0xFFFF0001);
|
||||
|
||||
if irq_status & 0xFFFF0000 != 0 {
|
||||
warnln!("SD error: irq_status={:#x}", irq_status);
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
if !INTERRUPT::CMD_DONE.is_set(irq_status) {
|
||||
warnln!("SD command did not report properly");
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
let response = if size_136 {
|
||||
SdResponse::Four([
|
||||
self.regs.RESP0.get(),
|
||||
self.regs.RESP1.get(),
|
||||
self.regs.RESP2.get(),
|
||||
self.regs.RESP3.get(),
|
||||
])
|
||||
} else {
|
||||
SdResponse::One(self.regs.RESP0.get())
|
||||
};
|
||||
|
||||
match &mut cmd.transfer {
|
||||
SdCommandTransfer::Write(_, _) => {
|
||||
todo!()
|
||||
}
|
||||
SdCommandTransfer::Read(buf, _) => {
|
||||
debugln!("Reading {} data blocks", block_count);
|
||||
for i in 0..block_count {
|
||||
crate::block!(
|
||||
self.regs
|
||||
.INTERRUPT
|
||||
.matches_any(INTERRUPT::ERR::SET + INTERRUPT::READ_RDY::SET),
|
||||
10000
|
||||
);
|
||||
let irq_status = self.regs.INTERRUPT.get();
|
||||
self.regs.INTERRUPT.set(0xFFFF0000 | (1 << 5));
|
||||
|
||||
if irq_status & 0xFFFF0000 != 0 {
|
||||
warnln!("SD error during data read: irq_status={:#x}", irq_status);
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
if !INTERRUPT::READ_RDY.is_set(irq_status) {
|
||||
warnln!("SD did not respond with data blocks");
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
assert!(block_size % 4 == 0);
|
||||
for j in (0..block_size).step_by(4) {
|
||||
let word = self.regs.DATA.get();
|
||||
let base = (i * block_size as u32) as usize + j as usize;
|
||||
buf[base + 0] = (word & 0xFF) as u8;
|
||||
buf[base + 1] = ((word >> 8) & 0xFF) as u8;
|
||||
buf[base + 2] = ((word >> 16) & 0xFF) as u8;
|
||||
buf[base + 3] = (word >> 24) as u8;
|
||||
}
|
||||
}
|
||||
}
|
||||
SdCommandTransfer::None => {}
|
||||
}
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
fn phys_reset(&mut self) -> Result<(), Errno> {
|
||||
self.status.phys_inserted = false;
|
||||
|
||||
self.regs
|
||||
.CONTROL1
|
||||
.modify(CONTROL1::SRST_HC::SET + CONTROL1::CLK_EN::CLEAR + CONTROL1::CLK_INTLEN::CLEAR);
|
||||
|
||||
// Wait for SRST_HC to clear
|
||||
crate::block!(
|
||||
self.regs.CONTROL1.matches_all(CONTROL1::SRST_HC::CLEAR),
|
||||
10000
|
||||
);
|
||||
|
||||
//let mut tmp;
|
||||
|
||||
debugln!("Checking for a card");
|
||||
crate::block!(
|
||||
self.regs.STATUS.matches_all(STATUS::MISC_INSERTED::SET),
|
||||
10000,
|
||||
{
|
||||
warnln!("No card inserted");
|
||||
return Ok(());
|
||||
}
|
||||
);
|
||||
|
||||
self.status.phys_inserted = true;
|
||||
|
||||
self.regs.CONTROL2.set(0);
|
||||
|
||||
let mut f_base = self.base_clock()?;
|
||||
if f_base == 0 {
|
||||
f_base = 100000000;
|
||||
}
|
||||
|
||||
let div = clock_divider(f_base, 400000);
|
||||
debugln!("Switching to ID frequency");
|
||||
|
||||
let div_lsb = div & 0xFF;
|
||||
let div_msb = (div >> 8) & 0x3;
|
||||
self.regs.CONTROL1.modify(
|
||||
CONTROL1::CLK_FREQ8.val(div_lsb)
|
||||
+ CONTROL1::CLK_FREQ_MS2.val(div_msb)
|
||||
+ CONTROL1::DATA_TOUNIT.val(11)
|
||||
+ CONTROL1::CLK_EN::SET
|
||||
+ CONTROL1::CLK_INTLEN::SET,
|
||||
);
|
||||
|
||||
crate::block!(
|
||||
self.regs.CONTROL1.matches_all(CONTROL1::CLK_STABLE::SET),
|
||||
100000,
|
||||
{
|
||||
warnln!("Controller clock did not stabilize in time");
|
||||
return Err(Errno::TimedOut);
|
||||
}
|
||||
);
|
||||
|
||||
// Do not forward any IRQs to ARM side
|
||||
self.regs.IRPT_EN.set(0);
|
||||
// Ack and unmask all interrupts to the controller
|
||||
self.regs.INTERRUPT.set(u32::MAX);
|
||||
self.regs.IRPT_MASK.set(u32::MAX);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send_cmd(&mut self, cmd: &mut SdCommand) -> Result<SdResponse, Errno> {
|
||||
if cmd.is_acmd() {
|
||||
let arg = if let Some(rca) = self.status.address {
|
||||
(rca as u32) << 16
|
||||
} else {
|
||||
0
|
||||
};
|
||||
self.send_cmd_inner(&mut SdCommand {
|
||||
number: SdCommandNumber::Cmd55,
|
||||
argument: arg,
|
||||
transfer: SdCommandTransfer::None,
|
||||
})?;
|
||||
}
|
||||
self.send_cmd_inner(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockDevice for MassMediaController {
|
||||
fn read(&self, pos: usize, data: &mut [u8]) -> Result<(), Errno> {
|
||||
// TODO check card status
|
||||
if data.len() % 512 != 0 || pos % 512 != 0 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
for i in 0..(data.len() / 512) {
|
||||
let s = i * 512;
|
||||
self.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Cmd17,
|
||||
argument: (pos / 512 + i) as u32,
|
||||
transfer: SdCommandTransfer::Read(&mut data[s..(s + 512)], 512),
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write(&self, _pos: usize, _data: &[u8]) -> Result<(), Errno> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl SdHostController for MassMediaController {
|
||||
fn send_cmd(&self, cmd: &mut SdCommand) -> Result<SdResponse, Errno> {
|
||||
self.inner.get().lock().send_cmd(cmd)
|
||||
}
|
||||
|
||||
fn phys_reset(&self) -> Result<(), Errno> {
|
||||
let mut inner = self.inner.get().lock();
|
||||
inner.status.address = None;
|
||||
inner.status.id = None;
|
||||
inner.phys_reset()
|
||||
}
|
||||
|
||||
fn is_phys_inserted(&self) -> bool {
|
||||
self.inner.get().lock().status.phys_inserted
|
||||
}
|
||||
|
||||
fn set_card_address(&self, rca: u16) -> Result<(), Errno> {
|
||||
self.inner.get().lock().status.address = Some(rca);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_card_identification(&self, id: SdCardIdentification) -> Result<(), Errno> {
|
||||
self.inner.get().lock().status.id = Some(id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reset_card_identification(&self) -> Result<(), Errno> {
|
||||
self.inner.get().lock().status.id = None;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for MassMediaController {
|
||||
fn name(&self) -> &'static str {
|
||||
"BCM283x External Mass Media Controller"
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
let mut inner = MmcInner {
|
||||
regs: DeviceMemoryIo::map(self.name(), self.base, 1)?,
|
||||
status: SdCardStatus::invalid(),
|
||||
};
|
||||
inner.power_on()?;
|
||||
self.inner.init(IrqSafeSpinLock::new(inner));
|
||||
|
||||
self.reset_card()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl MassMediaController {
|
||||
pub const unsafe fn new(base: usize) -> Self {
|
||||
Self {
|
||||
inner: InitOnce::new(),
|
||||
base,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,241 +0,0 @@
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource, IrqContext},
|
||||
Device,
|
||||
};
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use core::fmt;
|
||||
use cortex_a::registers::MPIDR_EL1;
|
||||
use libsys::error::Errno;
|
||||
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use tock_registers::register_structs;
|
||||
use tock_registers::registers::{ReadOnly, ReadWrite};
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
pub(super) BcmRegs {
|
||||
(0x000 => _res0),
|
||||
(0x200 => PENDING_BASIC: ReadOnly<u32>),
|
||||
(0x204 => PENDING1: ReadOnly<u32>),
|
||||
(0x208 => PENDING2: ReadOnly<u32>),
|
||||
(0x20C => _res1),
|
||||
(0x210 => ENABLE1: ReadWrite<u32>),
|
||||
(0x214 => ENABLE2: ReadWrite<u32>),
|
||||
(0x218 => ENABLE_BASIC: ReadWrite<u32>),
|
||||
(0x21C => @END),
|
||||
}
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
pub(super) Qa7Regs {
|
||||
(0x000 => _res0),
|
||||
(0x040 => CORE_IRQ_EN: [ReadWrite<u32>; 4]),
|
||||
(0x050 => _res1),
|
||||
(0x060 => CORE_IRQ_SRC: [ReadWrite<u32>; 4]),
|
||||
(0x070 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct IrqNumber(u32);
|
||||
|
||||
impl IrqNumber {
|
||||
pub const MAX: u32 = 64 + 32;
|
||||
|
||||
pub const fn bcm_irq(n: u32) -> Self {
|
||||
assert!(n < 64);
|
||||
Self(n + 32)
|
||||
}
|
||||
|
||||
pub const fn qa7_irq(n: u32) -> Self {
|
||||
assert!(n < 32);
|
||||
Self(n)
|
||||
}
|
||||
|
||||
pub const fn is_bcm_irq(self) -> bool {
|
||||
self.0 >= 32
|
||||
}
|
||||
|
||||
pub const fn number(self) -> u32 {
|
||||
if self.is_bcm_irq() {
|
||||
self.0 - 32
|
||||
} else {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn index(self) -> usize {
|
||||
self.0 as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for IrqNumber {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}_irq{}",
|
||||
if self.is_bcm_irq() { "bcm" } else { "qa7" },
|
||||
self.number()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct BcmIrqchipInner {
|
||||
regs: DeviceMemoryIo<BcmRegs>,
|
||||
}
|
||||
struct Qa7IrqchipInner {
|
||||
regs: DeviceMemoryIo<Qa7Regs>,
|
||||
}
|
||||
|
||||
pub struct Bcm283xIrqchip {
|
||||
bcm_inner: InitOnce<IrqSafeSpinLock<BcmIrqchipInner>>,
|
||||
qa7_inner: InitOnce<IrqSafeSpinLock<Qa7IrqchipInner>>,
|
||||
table: IrqSafeSpinLock<[Option<&'static (dyn IntSource + Sync)>; IrqNumber::MAX as usize]>,
|
||||
}
|
||||
|
||||
impl BcmIrqchipInner {
|
||||
fn enable_irq(&self, n: u32) -> Result<(), Errno> {
|
||||
let (reg, bit) = if n < 32 {
|
||||
(&self.regs.ENABLE1, 1 << n)
|
||||
} else if n < 64 {
|
||||
(&self.regs.ENABLE2, 1 << (n - 32))
|
||||
} else {
|
||||
todo!();
|
||||
};
|
||||
reg.set(reg.get() | bit);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pending_irq(&self) -> Option<u32> {
|
||||
let status = self.regs.PENDING2.get();
|
||||
for bit in 0..32 {
|
||||
if status & (1 << bit) != 0 {
|
||||
return Some(bit + 32);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Qa7IrqchipInner {
|
||||
fn enable_irq(&self, n: u32) -> Result<(), Errno> {
|
||||
// TODO check this code in SMP setup
|
||||
let core_id = MPIDR_EL1.get() & 0x3;
|
||||
// Timer IRQ control
|
||||
let (reg, bit) = if n < 4 {
|
||||
(&self.regs.CORE_IRQ_EN[core_id as usize], 1 << n)
|
||||
} else {
|
||||
todo!()
|
||||
};
|
||||
reg.set(reg.get() | bit);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pending_irq(&self) -> Option<u32> {
|
||||
let core_id = MPIDR_EL1.get() & 0x3;
|
||||
let reg = &self.regs.CORE_IRQ_SRC[core_id as usize];
|
||||
let value = reg.get();
|
||||
for bit in 0..8 {
|
||||
if value & (1 << bit) != 0 {
|
||||
return Some(bit);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn clear_irq(&self) {
|
||||
let core_id = MPIDR_EL1.get() & 0x3;
|
||||
let reg = &self.regs.CORE_IRQ_SRC[core_id as usize];
|
||||
reg.set(0);
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Bcm283xIrqchip {
|
||||
fn name(&self) -> &'static str {
|
||||
"BCM283x/QA7 Interrupt Controller"
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
self.bcm_inner.init(IrqSafeSpinLock::new(BcmIrqchipInner {
|
||||
regs: DeviceMemoryIo::map("BCM283x Peripheral Interrupt Controller", 0x3F00B000, 1)?,
|
||||
}));
|
||||
self.qa7_inner.init(IrqSafeSpinLock::new(Qa7IrqchipInner {
|
||||
regs: DeviceMemoryIo::map("QA7 Core Interrupt Controller", 0x40000000, 1)?,
|
||||
}));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntController for Bcm283xIrqchip {
|
||||
type IrqNumber = IrqNumber;
|
||||
|
||||
fn register_handler(
|
||||
&self,
|
||||
irq: IrqNumber,
|
||||
handler: &'static (dyn IntSource + Sync),
|
||||
) -> Result<(), Errno> {
|
||||
let mut table = self.table.lock();
|
||||
let irqi = irq.index();
|
||||
if table[irqi as usize].is_some() {
|
||||
return Err(Errno::AlreadyExists);
|
||||
}
|
||||
|
||||
debugln!("Bound {:?} to {:?}", irq, Device::name(handler));
|
||||
table[irqi as usize] = Some(handler);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn enable_irq(&self, irq: IrqNumber) -> Result<(), Errno> {
|
||||
if irq.is_bcm_irq() {
|
||||
self.bcm_inner.get().lock().enable_irq(irq.number())
|
||||
} else {
|
||||
self.qa7_inner.get().lock().enable_irq(irq.number())
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_pending_irqs<'q>(&'q self, _ic: &IrqContext<'q>) {
|
||||
let qa7 = self.qa7_inner.get().lock();
|
||||
let bcm = self.bcm_inner.get().lock();
|
||||
|
||||
let irq_number = if let Some(irq) = qa7.pending_irq() {
|
||||
irq as usize
|
||||
} else if let Some(irq) = bcm.pending_irq() {
|
||||
irq as usize + 32
|
||||
} else {
|
||||
panic!("No IRQ pending");
|
||||
};
|
||||
|
||||
drop(bcm);
|
||||
|
||||
if irq_number < 32 {
|
||||
qa7.clear_irq();
|
||||
drop(qa7);
|
||||
}
|
||||
|
||||
{
|
||||
let table = self.table.lock();
|
||||
match table[irq_number] {
|
||||
None => panic!("No handler registered for irq{}", irq_number),
|
||||
Some(handler) => {
|
||||
drop(table);
|
||||
handler.handle_irq().expect("irq handler failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Bcm283xIrqchip {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
bcm_inner: InitOnce::new(),
|
||||
qa7_inner: InitOnce::new(),
|
||||
table: IrqSafeSpinLock::new([None; IrqNumber::MAX as usize]),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,184 +0,0 @@
|
||||
use crate::dev::Device;
|
||||
use crate::mem::{self, virt::DeviceMemoryIo};
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use libsys::error::Errno;
|
||||
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use tock_registers::registers::{ReadOnly, WriteOnly};
|
||||
use tock_registers::{register_bitfields, register_structs};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
STATUS [
|
||||
FULL OFFSET(31) NUMBITS(1) [],
|
||||
EMPTY OFFSET(30) NUMBITS(1) [],
|
||||
],
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
(0x00 => READ: ReadOnly<u32>),
|
||||
(0x04 => _res0),
|
||||
(0x18 => STATUS: ReadOnly<u32, STATUS::Register>),
|
||||
(0x1C => _res1),
|
||||
(0x20 => WRITE: WriteOnly<u32>),
|
||||
(0x24 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C, align(16))]
|
||||
struct MboxBuffer([u32; 36]);
|
||||
|
||||
struct Inner {
|
||||
regs: DeviceMemoryIo<Regs>,
|
||||
buf: MboxBuffer,
|
||||
}
|
||||
|
||||
pub struct Bcm283xMailbox {
|
||||
inner: InitOnce<IrqSafeSpinLock<Inner>>,
|
||||
base: usize,
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
const RESPONSE: u32 = 1 << 31;
|
||||
const REQUEST: u32 = 0;
|
||||
|
||||
const PROP_ARM_MEMORY: u32 = 0x10005;
|
||||
const PROP_SET_POWER_STATE: u32 = 0x28001;
|
||||
const PROP_GET_CLOCK_RATE: u32 = 0x30002;
|
||||
|
||||
fn call(&self, ch: u8) -> Result<(), Errno> {
|
||||
let ptr_virt = &self.buf as *const _ as usize;
|
||||
let ptr_phys = ptr_virt - mem::KERNEL_OFFSET;
|
||||
assert!(ptr_phys < 0x100000000);
|
||||
|
||||
while self.regs.STATUS.matches_all(STATUS::FULL::SET) {
|
||||
cortex_a::asm::nop();
|
||||
}
|
||||
|
||||
let val = (ptr_phys as u32) | (ch as u32);
|
||||
self.regs.WRITE.set(val);
|
||||
|
||||
loop {
|
||||
while self.regs.STATUS.matches_all(STATUS::EMPTY::SET) {
|
||||
cortex_a::asm::nop();
|
||||
}
|
||||
|
||||
if self.regs.READ.get() == val {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn memory_split(&mut self) -> Result<usize, Errno> {
|
||||
self.buf.0[0] = 8 * 4;
|
||||
self.buf.0[1] = Self::REQUEST;
|
||||
|
||||
self.buf.0[2] = Self::PROP_ARM_MEMORY;
|
||||
self.buf.0[3] = 8;
|
||||
self.buf.0[4] = 0;
|
||||
self.buf.0[5] = 0x12345678;
|
||||
self.buf.0[6] = 0x87654321;
|
||||
self.buf.0[7] = 0;
|
||||
|
||||
self.call(8)?;
|
||||
|
||||
if self.buf.0[1] != Self::RESPONSE {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
Ok(self.buf.0[6] as usize)
|
||||
}
|
||||
|
||||
fn set_power_state(&mut self, dev: u32, cmd: u32) -> Result<(), Errno> {
|
||||
self.buf.0[0] = 8 * 4;
|
||||
self.buf.0[1] = Self::REQUEST;
|
||||
|
||||
self.buf.0[2] = Self::PROP_SET_POWER_STATE;
|
||||
self.buf.0[3] = 8;
|
||||
self.buf.0[4] = 0;
|
||||
self.buf.0[5] = dev;
|
||||
self.buf.0[6] = cmd;
|
||||
self.buf.0[7] = 0;
|
||||
|
||||
self.call(8)?;
|
||||
|
||||
if self.buf.0[1] != Self::RESPONSE {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
if self.buf.0[6] & 1 << 1 != 0 {
|
||||
return Err(Errno::DoesNotExist);
|
||||
}
|
||||
|
||||
if self.buf.0[6] & 1 << 0 == 0 {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clock_rate(&mut self, clk: u32) -> Result<u32, Errno> {
|
||||
self.buf.0[0] = 8 * 4;
|
||||
self.buf.0[1] = Self::REQUEST;
|
||||
|
||||
self.buf.0[2] = Self::PROP_GET_CLOCK_RATE;
|
||||
self.buf.0[3] = 8;
|
||||
self.buf.0[4] = 0;
|
||||
self.buf.0[5] = clk;
|
||||
self.buf.0[6] = 0;
|
||||
self.buf.0[7] = 0;
|
||||
|
||||
self.call(8)?;
|
||||
|
||||
if self.buf.0[1] != Self::RESPONSE {
|
||||
return Err(Errno::InvalidArgument);
|
||||
}
|
||||
|
||||
Ok(self.buf.0[6])
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Bcm283xMailbox {
|
||||
fn name(&self) -> &'static str {
|
||||
"BCM283x Mailbox"
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
self.inner.init(IrqSafeSpinLock::new(Inner {
|
||||
regs: DeviceMemoryIo::map(self.name(), self.base, 1)?,
|
||||
buf: MboxBuffer([0; 36]),
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Bcm283xMailbox {
|
||||
pub const POWER_STATE_ON: u32 = 1 << 0;
|
||||
pub const POWER_STATE_WAIT: u32 = 1 << 1;
|
||||
pub const POWER_SD_CARD: u32 = 0;
|
||||
|
||||
pub const CLOCK_EMMC: u32 = 1;
|
||||
|
||||
pub fn memory_split(&self) -> Result<usize, Errno> {
|
||||
self.inner.get().lock().memory_split()
|
||||
}
|
||||
|
||||
pub fn set_power_state(&self, dev: u32, cmd: u32) -> Result<(), Errno> {
|
||||
self.inner.get().lock().set_power_state(dev, cmd)
|
||||
}
|
||||
|
||||
pub fn clock_rate(&self, clk: u32) -> Result<u32, Errno> {
|
||||
self.inner.get().lock().clock_rate(clk)
|
||||
}
|
||||
|
||||
pub const unsafe fn new(base: usize) -> Self {
|
||||
Self {
|
||||
inner: InitOnce::new(),
|
||||
base,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
use crate::arch::aarch64::timer::GenericTimer;
|
||||
use crate::dev::{
|
||||
irq::IntSource,
|
||||
serial::{pl011::Pl011, SerialDevice},
|
||||
Device,
|
||||
};
|
||||
use crate::mem::phys;
|
||||
use libsys::error::Errno;
|
||||
|
||||
pub mod irqchip;
|
||||
pub use irqchip::{Bcm283xIrqchip, IrqNumber};
|
||||
pub mod emmc;
|
||||
pub use emmc::MassMediaController;
|
||||
pub mod mailbox;
|
||||
pub use mailbox::Bcm283xMailbox;
|
||||
|
||||
const UART_BASE: usize = 0x3F201000;
|
||||
const EMMC_BASE: usize = 0x3F300000;
|
||||
const BCM_MBOX_BASE: usize = 0x3F00B880;
|
||||
const UART_IRQ: IrqNumber = IrqNumber::bcm_irq(57);
|
||||
const LOCAL_TIMER_IRQ: IrqNumber = IrqNumber::qa7_irq(1);
|
||||
|
||||
pub fn init_board_early() -> Result<(), Errno> {
|
||||
unsafe {
|
||||
UART.enable()?;
|
||||
BCM_MBOX.enable()?;
|
||||
|
||||
let memory = BCM_MBOX.memory_split()?;
|
||||
infoln!("Memory split: {:#x}", memory);
|
||||
|
||||
phys::init_from_region(0, memory);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn init_board() -> Result<(), Errno> {
|
||||
unsafe {
|
||||
IRQCHIP.enable()?;
|
||||
UART.init_irqs()?;
|
||||
|
||||
EMMC.enable()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn intc() -> &'static Bcm283xIrqchip {
|
||||
&IRQCHIP
|
||||
}
|
||||
|
||||
/// Returns primary console for this machine
|
||||
#[inline]
|
||||
pub fn console() -> &'static impl SerialDevice {
|
||||
&UART
|
||||
}
|
||||
|
||||
/// Returns the timer used as CPU-local periodic IRQ source
|
||||
#[inline]
|
||||
pub fn local_timer() -> &'static GenericTimer {
|
||||
&LOCAL_TIMER
|
||||
}
|
||||
|
||||
static IRQCHIP: Bcm283xIrqchip = Bcm283xIrqchip::new();
|
||||
pub static EMMC: MassMediaController = unsafe { MassMediaController::new(EMMC_BASE) };
|
||||
static UART: Pl011 = unsafe { Pl011::new(UART_BASE, UART_IRQ) };
|
||||
pub(self) static BCM_MBOX: Bcm283xMailbox = unsafe { Bcm283xMailbox::new(BCM_MBOX_BASE) };
|
||||
static LOCAL_TIMER: GenericTimer = GenericTimer::new(LOCAL_TIMER_IRQ);
|
@ -1,51 +1,9 @@
|
||||
//! aarch64 architecture implementation
|
||||
|
||||
use cortex_a::registers::DAIF;
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
use core::arch::asm;
|
||||
|
||||
pub mod boot;
|
||||
pub mod context;
|
||||
pub mod exception;
|
||||
pub mod irq;
|
||||
pub mod reg;
|
||||
pub mod timer;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "mach_qemu")] {
|
||||
pub mod mach_qemu;
|
||||
|
||||
pub use mach_qemu as machine;
|
||||
} else if #[cfg(feature = "mach_orangepi3")] {
|
||||
pub mod mach_orangepi3;
|
||||
|
||||
pub use mach_orangepi3 as machine;
|
||||
} else if #[cfg(feature = "mach_rpi3")] {
|
||||
pub mod mach_rpi3;
|
||||
|
||||
pub use mach_rpi3 as machine;
|
||||
}
|
||||
}
|
||||
|
||||
/// Masks IRQs and returns previous IRQ mask state
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: disables IRQ handling temporarily
|
||||
#[inline(always)]
|
||||
pub unsafe fn irq_mask_save() -> u64 {
|
||||
let state = DAIF.get();
|
||||
asm!("msr daifset, {bits}", bits = const 2, options(nomem, nostack, preserves_flags));
|
||||
state
|
||||
}
|
||||
|
||||
/// Restores IRQ mask state
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: modifies interrupt behavior. Must only be used in
|
||||
/// conjunction with [irq_mask_save]
|
||||
#[inline(always)]
|
||||
pub unsafe fn irq_restore(state: u64) {
|
||||
DAIF.set(state);
|
||||
}
|
||||
|
@ -1,50 +0,0 @@
|
||||
//! CNTKCTL_EL1 register
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use core::arch::asm;
|
||||
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_bitfields,
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
u64,
|
||||
/// Counter-timer Kernel Control Register
|
||||
pub CNTKCTL_EL1 [
|
||||
/// If set, disables CNTPCT and CNTFRQ trapping from EL0
|
||||
EL0PCTEN OFFSET(0) NUMBITS(1) []
|
||||
]
|
||||
}
|
||||
|
||||
/// CNTKCTL_EL1 register
|
||||
pub struct Reg;
|
||||
|
||||
impl Readable for Reg {
|
||||
type T = u64;
|
||||
type R = CNTKCTL_EL1::Register;
|
||||
|
||||
#[inline(always)]
|
||||
fn get(&self) -> Self::T {
|
||||
let mut tmp;
|
||||
unsafe {
|
||||
asm!("mrs {}, cntkctl_el1", out(reg) tmp);
|
||||
}
|
||||
tmp
|
||||
}
|
||||
}
|
||||
|
||||
impl Writeable for Reg {
|
||||
type T = u64;
|
||||
type R = CNTKCTL_EL1::Register;
|
||||
|
||||
#[inline(always)]
|
||||
fn set(&self, value: Self::T) {
|
||||
unsafe {
|
||||
asm!("msr cntkctl_el1, {}", in(reg) value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// CNTKCTL_EL1 register
|
||||
pub const CNTKCTL_EL1: Reg = Reg;
|
@ -1,58 +0,0 @@
|
||||
//! CPACR_EL1 register
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use core::arch::asm;
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_bitfields,
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
u64,
|
||||
/// EL1 Architectural Feature Access Control Register
|
||||
pub CPACR_EL1 [
|
||||
/// Enable EL0 and EL1 SIMD/FP accesses to EL1
|
||||
FPEN OFFSET(20) NUMBITS(2) [
|
||||
/// Trap both EL0 and EL1
|
||||
TrapAll = 0,
|
||||
/// Trap EL0
|
||||
TrapEl0 = 1,
|
||||
/// Trap EL1
|
||||
TrapEl1 = 2,
|
||||
/// Do not trap any SIMD/FP instructions
|
||||
TrapNone = 3
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
/// CPACR_EL1 register
|
||||
pub struct Reg;
|
||||
|
||||
impl Readable for Reg {
|
||||
type T = u64;
|
||||
type R = CPACR_EL1::Register;
|
||||
|
||||
#[inline(always)]
|
||||
fn get(&self) -> Self::T {
|
||||
let mut tmp;
|
||||
unsafe {
|
||||
asm!("mrs {}, cpacr_el1", out(reg) tmp);
|
||||
}
|
||||
tmp
|
||||
}
|
||||
}
|
||||
|
||||
impl Writeable for Reg {
|
||||
type T = u64;
|
||||
type R = CPACR_EL1::Register;
|
||||
|
||||
#[inline(always)]
|
||||
fn set(&self, value: Self::T) {
|
||||
unsafe {
|
||||
asm!("msr cpacr_el1, {}", in(reg) value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// CPACR_EL1 register
|
||||
pub const CPACR_EL1: Reg = Reg;
|
@ -1,7 +0,0 @@
|
||||
//! AArch64 architectural registers
|
||||
|
||||
pub mod cpacr_el1;
|
||||
pub use cpacr_el1::CPACR_EL1;
|
||||
|
||||
pub mod cntkctl_el1;
|
||||
pub use cntkctl_el1::CNTKCTL_EL1;
|
@ -1,68 +0,0 @@
|
||||
//! ARM generic timer implementation
|
||||
|
||||
use crate::arch::machine::{self, IrqNumber};
|
||||
use crate::proc;
|
||||
use crate::dev::{
|
||||
pseudo,
|
||||
irq::{IntController, IntSource},
|
||||
timer::TimestampSource,
|
||||
Device,
|
||||
};
|
||||
use core::time::Duration;
|
||||
use cortex_a::registers::{CNTFRQ_EL0, CNTPCT_EL0, CNTP_CTL_EL0, CNTP_TVAL_EL0};
|
||||
use libsys::error::Errno;
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
|
||||
/// Generic timer struct
|
||||
pub struct GenericTimer {
|
||||
irq: IrqNumber,
|
||||
}
|
||||
|
||||
/// Duration of a single timer period
|
||||
pub const TIMER_TICK: u64 = 1000000;
|
||||
|
||||
impl Device for GenericTimer {
|
||||
fn name(&self) -> &'static str {
|
||||
"ARM Generic Timer"
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntSource for GenericTimer {
|
||||
fn handle_irq(&self) -> Result<(), Errno> {
|
||||
CNTP_TVAL_EL0.set(TIMER_TICK);
|
||||
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET);
|
||||
proc::wait::tick();
|
||||
proc::switch();
|
||||
pseudo::RANDOM.set_state(CNTPCT_EL0.get() as u32);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_irqs(&'static self) -> Result<(), Errno> {
|
||||
machine::intc().register_handler(self.irq, self)?;
|
||||
CNTP_TVAL_EL0.set(TIMER_TICK);
|
||||
machine::intc().enable_irq(self.irq)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl TimestampSource for GenericTimer {
|
||||
fn timestamp(&self) -> Result<Duration, Errno> {
|
||||
let cnt = (CNTPCT_EL0.get() as u128) * 1_000_000_000u128;
|
||||
let frq = CNTFRQ_EL0.get() as u128;
|
||||
let secs = ((cnt / frq) / 1_000_000_000) as u64;
|
||||
let nanos = ((cnt / frq) % 1_000_000_000) as u32;
|
||||
Ok(Duration::new(secs, nanos))
|
||||
}
|
||||
}
|
||||
|
||||
impl GenericTimer {
|
||||
/// Constructs a new instance of ARM Generic Timer
|
||||
pub const fn new(irq: IrqNumber) -> Self {
|
||||
Self { irq }
|
||||
}
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
// vi:ft=a64asm:
|
||||
|
||||
.set PT_REGS_SIZE, (16 * 16 + 16 * 2)
|
||||
|
||||
.macro EXC_SAVE_STATE
|
||||
sub sp, sp, #PT_REGS_SIZE
|
||||
|
||||
// TODO only save all registers if doing fork()?
|
||||
stp x0, x1, [sp, #16 * 0]
|
||||
stp x2, x3, [sp, #16 * 1]
|
||||
stp x4, x5, [sp, #16 * 2]
|
||||
stp x6, x7, [sp, #16 * 3]
|
||||
stp x8, x9, [sp, #16 * 4]
|
||||
stp x10, x11, [sp, #16 * 5]
|
||||
stp x12, x13, [sp, #16 * 6]
|
||||
stp x14, x15, [sp, #16 * 7]
|
||||
stp x16, x17, [sp, #16 * 8]
|
||||
stp x18, x19, [sp, #16 * 9]
|
||||
stp x20, x21, [sp, #16 * 10]
|
||||
stp x22, x23, [sp, #16 * 11]
|
||||
stp x24, x25, [sp, #16 * 12]
|
||||
stp x26, x27, [sp, #16 * 13]
|
||||
stp x28, x29, [sp, #16 * 14]
|
||||
stp x30, x31, [sp, #16 * 15]
|
||||
|
||||
mrs x0, spsr_el1
|
||||
mrs x1, elr_el1
|
||||
mrs x2, sp_el0
|
||||
// mrs x3, ttbr0_el1
|
||||
|
||||
stp x0, x1, [sp, #16 * 16]
|
||||
stp x2, x3, [sp, #16 * 17]
|
||||
.endm
|
||||
|
||||
.macro EXC_RESTORE_STATE
|
||||
ldp x0, x1, [sp, #16 * 16]
|
||||
ldp x2, x3, [sp, #16 * 17]
|
||||
msr spsr_el1, x0
|
||||
msr elr_el1, x1
|
||||
msr sp_el0, x2
|
||||
// msr ttbr0_el1, x3
|
||||
|
||||
ldp x0, x1, [sp, #16 * 0]
|
||||
ldp x2, x3, [sp, #16 * 1]
|
||||
ldp x4, x5, [sp, #16 * 2]
|
||||
ldp x6, x7, [sp, #16 * 3]
|
||||
ldp x8, x9, [sp, #16 * 4]
|
||||
ldp x10, x11, [sp, #16 * 5]
|
||||
ldp x12, x13, [sp, #16 * 6]
|
||||
ldp x14, x15, [sp, #16 * 7]
|
||||
ldp x16, x17, [sp, #16 * 8]
|
||||
ldp x18, x19, [sp, #16 * 9]
|
||||
ldp x20, x21, [sp, #16 * 10]
|
||||
ldp x22, x23, [sp, #16 * 11]
|
||||
ldp x24, x25, [sp, #16 * 12]
|
||||
ldp x26, x27, [sp, #16 * 13]
|
||||
ldp x28, x29, [sp, #16 * 14]
|
||||
ldp x30, x31, [sp, #16 * 15]
|
||||
|
||||
add sp, sp, #PT_REGS_SIZE
|
||||
.endm
|
||||
|
||||
.macro EXC_VECTOR el, ht, bits, kind
|
||||
.p2align 7
|
||||
b __aa\bits\()_el\el\ht\()_\kind
|
||||
.endm
|
||||
|
||||
.macro EXC_ENTRY_HANDLER el, ht, bits, kind
|
||||
__aa\bits\()_el\el\ht\()_\kind:
|
||||
.if \bits == 32
|
||||
// TODO AArch32?
|
||||
b .
|
||||
.endif
|
||||
|
||||
EXC_SAVE_STATE
|
||||
mov x0, sp
|
||||
mov lr, xzr
|
||||
bl __aa64_exc_\kind\()_handler
|
||||
EXC_RESTORE_STATE
|
||||
eret
|
||||
.endm
|
||||
|
||||
.section .text
|
||||
.global aa64_el1_vectors
|
||||
.p2align 12
|
||||
aa64_el1_vectors:
|
||||
EXC_VECTOR 1, t, 64, sync
|
||||
EXC_VECTOR 1, t, 64, irq
|
||||
EXC_VECTOR 1, t, 64, fiq
|
||||
EXC_VECTOR 1, t, 64, serror
|
||||
|
||||
EXC_VECTOR 1, h, 64, sync
|
||||
EXC_VECTOR 1, h, 64, irq
|
||||
EXC_VECTOR 1, h, 64, fiq
|
||||
EXC_VECTOR 1, h, 64, serror
|
||||
|
||||
EXC_VECTOR 0, t, 64, sync
|
||||
EXC_VECTOR 0, t, 64, irq
|
||||
EXC_VECTOR 0, t, 64, fiq
|
||||
EXC_VECTOR 0, t, 64, serror
|
||||
|
||||
EXC_VECTOR 0, t, 32, sync
|
||||
EXC_VECTOR 0, t, 32, irq
|
||||
EXC_VECTOR 0, t, 32, fiq
|
||||
EXC_VECTOR 0, t, 32, serror
|
||||
|
||||
.p2align 7
|
||||
EXC_ENTRY_HANDLER 1, t, 64, sync
|
||||
EXC_ENTRY_HANDLER 1, t, 64, irq
|
||||
EXC_ENTRY_HANDLER 1, t, 64, fiq
|
||||
EXC_ENTRY_HANDLER 1, t, 64, serror
|
||||
|
||||
EXC_ENTRY_HANDLER 1, h, 64, sync
|
||||
EXC_ENTRY_HANDLER 1, h, 64, irq
|
||||
EXC_ENTRY_HANDLER 1, h, 64, fiq
|
||||
EXC_ENTRY_HANDLER 1, h, 64, serror
|
||||
|
||||
EXC_ENTRY_HANDLER 0, t, 64, sync
|
||||
EXC_ENTRY_HANDLER 0, t, 64, irq
|
||||
EXC_ENTRY_HANDLER 0, t, 64, fiq
|
||||
EXC_ENTRY_HANDLER 0, t, 64, serror
|
||||
|
||||
EXC_ENTRY_HANDLER 0, t, 32, sync
|
||||
EXC_ENTRY_HANDLER 0, t, 32, irq
|
||||
EXC_ENTRY_HANDLER 0, t, 32, fiq
|
||||
EXC_ENTRY_HANDLER 0, t, 32, serror
|
@ -1,14 +1,3 @@
|
||||
//! Architecture-specific detail module
|
||||
//!
|
||||
//! Contains two module aliases, which may or may not point
|
||||
//! the same architecture module:
|
||||
//!
|
||||
//! * [platform] - architecture details (e.g. aarch64)
|
||||
//! * [machine] - particular machine implementation (e.g. bcm2837)
|
||||
//!
|
||||
//! Modules visible in the documentation will depend on
|
||||
//! build target platform.
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(target_arch = "aarch64")] {
|
||||
pub mod aarch64;
|
||||
@ -19,34 +8,28 @@ cfg_if! {
|
||||
}
|
||||
|
||||
// TODO move to mod io
|
||||
// use core::marker::PhantomData;
|
||||
// use core::ops::Deref;
|
||||
//
|
||||
// /// Wrapper for setting up memory-mapped registers and IO
|
||||
// pub struct MemoryIo<T> {
|
||||
// base: usize,
|
||||
// _pd: PhantomData<fn() -> T>,
|
||||
// }
|
||||
//
|
||||
// impl<T> MemoryIo<T> {
|
||||
// /// Constructs a new instance of MMIO region.
|
||||
// ///
|
||||
// /// # Safety
|
||||
// ///
|
||||
// /// Does not perform `base` validation.
|
||||
// pub const unsafe fn new(base: usize) -> Self {
|
||||
// Self {
|
||||
// base,
|
||||
// _pd: PhantomData,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl<T> Deref for MemoryIo<T> {
|
||||
// type Target = T;
|
||||
//
|
||||
// #[inline(always)]
|
||||
// fn deref(&self) -> &Self::Target {
|
||||
// unsafe { &*(self.base as *const _) }
|
||||
// }
|
||||
// }
|
||||
use core::ops::Deref;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
pub struct MemoryIo<T> {
|
||||
base: usize,
|
||||
_pd: PhantomData<fn() -> T>,
|
||||
}
|
||||
|
||||
impl<T> MemoryIo<T> {
|
||||
pub const unsafe fn new(base: usize) -> Self {
|
||||
Self {
|
||||
base,
|
||||
_pd: PhantomData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for MemoryIo<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*(self.base as *const _) }
|
||||
}
|
||||
}
|
||||
|
@ -1,114 +0,0 @@
|
||||
//! Kernel command-line handling and configuration
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use core::fmt;
|
||||
|
||||
/// Kernel configuration data
|
||||
#[derive(Debug)]
|
||||
pub struct Config {
|
||||
cmdline: ConfigString<256>,
|
||||
console: ConfigString<16>,
|
||||
mem_limit: usize,
|
||||
initrd_base: usize,
|
||||
initrd_size: usize,
|
||||
}
|
||||
|
||||
/// Kernel parameter keys
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ConfigKey {
|
||||
Cmdline,
|
||||
Console,
|
||||
MemLimit,
|
||||
InitrdBase,
|
||||
InitrdSize,
|
||||
}
|
||||
|
||||
struct ConfigString<const N: usize> {
|
||||
buf: [u8; N],
|
||||
len: usize,
|
||||
}
|
||||
|
||||
/// Kernel config instance
|
||||
pub static CONFIG: IrqSafeSpinLock<Config> = IrqSafeSpinLock::new(Config::default());
|
||||
|
||||
impl const Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
cmdline: ConfigString::empty(),
|
||||
console: ConfigString::empty(),
|
||||
mem_limit: usize::MAX,
|
||||
initrd_base: 0,
|
||||
initrd_size: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Sets a config key to [usize] value
|
||||
pub fn set_usize(&mut self, key: ConfigKey, value: usize) {
|
||||
match key {
|
||||
ConfigKey::InitrdBase => self.initrd_base = value,
|
||||
ConfigKey::InitrdSize => self.initrd_size = value,
|
||||
ConfigKey::MemLimit => self.mem_limit = value,
|
||||
_ => panic!("Invalid usize key: {:?}", key),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets a config key to [str] value
|
||||
pub fn set_str(&mut self, key: ConfigKey, value: &str) {
|
||||
match key {
|
||||
ConfigKey::Cmdline => self.cmdline.set_from_str(value),
|
||||
_ => panic!("Invalid str key: {:?}", key),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an [usize] value for given `key`
|
||||
pub fn get_usize(&self, key: ConfigKey) -> usize {
|
||||
match key {
|
||||
ConfigKey::InitrdBase => self.initrd_base,
|
||||
ConfigKey::InitrdSize => self.initrd_size,
|
||||
ConfigKey::MemLimit => self.mem_limit,
|
||||
_ => panic!("Invalid usize key: {:?}", key),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a [str] value for given `key`
|
||||
pub fn get_str(&self, key: ConfigKey) -> &str {
|
||||
match key {
|
||||
ConfigKey::Cmdline => self.cmdline.as_str(),
|
||||
ConfigKey::Console => self.console.as_str(),
|
||||
_ => panic!("Invalid str key: {:?}", key),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses command line options provided to the kernel and
|
||||
/// sets appropriate config keys
|
||||
pub fn set_cmdline(&self, _cmdline: &str) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> ConfigString<N> {
|
||||
pub const fn empty() -> Self {
|
||||
Self {
|
||||
buf: [0; N],
|
||||
len: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
core::str::from_utf8(&self.buf[..self.len]).unwrap()
|
||||
}
|
||||
|
||||
pub fn set_from_str(&mut self, data: &str) {
|
||||
let bytes = data.as_bytes();
|
||||
self.buf[..bytes.len()].copy_from_slice(bytes);
|
||||
self.len = bytes.len();
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> fmt::Debug for ConfigString<N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self.as_str())
|
||||
}
|
||||
}
|
@ -1,147 +1,37 @@
|
||||
//! Debug output module.
|
||||
//!
|
||||
//! The module provides [print!] and [println!] macros
|
||||
//! which can be used in similar way to print! and
|
||||
//! println! from std.
|
||||
//!
|
||||
//! Level-specific debugging macros are provided as well:
|
||||
//!
|
||||
//! * [debugln!]
|
||||
//! * [infoln!]
|
||||
//! * [warnln!]
|
||||
//! * [errorln!]
|
||||
|
||||
use crate::dev::serial::SerialDevice;
|
||||
use libsys::{debug::TraceLevel, error::Errno};
|
||||
use core::convert::TryFrom;
|
||||
use crate::sync::Spin;
|
||||
use core::fmt;
|
||||
|
||||
pub static LEVEL: Level = Level::Debug;
|
||||
|
||||
/// Kernel logging levels
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
#[repr(u32)]
|
||||
pub enum Level {
|
||||
/// Debugging information
|
||||
Debug = 1,
|
||||
/// General informational messages
|
||||
Info = 2,
|
||||
/// Non-critical warnings
|
||||
Warn = 3,
|
||||
/// Critical errors
|
||||
Error = 4,
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for Level {
|
||||
type Error = Errno;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(l: u32) -> Result<Level, Errno> {
|
||||
match l {
|
||||
1 => Ok(Level::Debug),
|
||||
2 => Ok(Level::Info),
|
||||
3 => Ok(Level::Warn),
|
||||
4 => Ok(Level::Error),
|
||||
_ => Err(Errno::InvalidArgument)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TraceLevel> for Level {
|
||||
#[inline(always)]
|
||||
fn from(l: TraceLevel) -> Self {
|
||||
match l {
|
||||
TraceLevel::Debug => Self::Debug,
|
||||
TraceLevel::Info => Self::Info,
|
||||
TraceLevel::Warn => Self::Warn,
|
||||
TraceLevel::Error => Self::Error,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SerialOutput<T: 'static + SerialDevice> {
|
||||
inner: &'static T,
|
||||
inner: &'static Spin<T>,
|
||||
}
|
||||
|
||||
impl<T: SerialDevice> fmt::Write for SerialOutput<T> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
let mut lock = self.inner.lock();
|
||||
for &byte in s.as_bytes() {
|
||||
if byte == b'\n' {
|
||||
self.inner.send(b'\r').ok();
|
||||
unsafe {
|
||||
// TODO check for errors
|
||||
drop(lock.send(byte));
|
||||
}
|
||||
// TODO check for errors
|
||||
self.inner.send(byte).ok();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a formatted message to output stream
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($level:expr, $($it:tt)+) => ($crate::debug::_debug($level, format_args!($($it)+)))
|
||||
macro_rules! debug {
|
||||
($($it:tt)+) => ($crate::debug::_debug(format_args!($($it)+)))
|
||||
}
|
||||
|
||||
/// Writes a formatted message, followed by a newline, to output stream
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
($level:expr, $($it:tt)+) => (print!($level, "{}\n", format_args!($($it)+)))
|
||||
}
|
||||
|
||||
/// Writes a message, annotated with current file and line, with a newline, to
|
||||
/// debug level output.
|
||||
///
|
||||
/// See [println!].
|
||||
#[macro_export]
|
||||
macro_rules! debugln {
|
||||
($($it:tt)+) => (
|
||||
print!($crate::debug::Level::Debug, "[{}:{}] {}\n", file!(), line!(), format_args!($($it)+))
|
||||
)
|
||||
($($it:tt)+) => (debug!("{}\n", format_args!($($it)+)))
|
||||
}
|
||||
|
||||
/// Writes a message, annotated with current file and line, with a newline, to
|
||||
/// info level output.
|
||||
///
|
||||
/// See [println!].
|
||||
#[macro_export]
|
||||
macro_rules! infoln {
|
||||
($($it:tt)+) => (
|
||||
print!($crate::debug::Level::Info, "\x1B[1m[{}:{}] {}\x1B[0m\n", file!(), line!(), format_args!($($it)+))
|
||||
)
|
||||
}
|
||||
|
||||
/// Writes a message, annotated with current file and line, with a newline, to
|
||||
/// warning level output.
|
||||
///
|
||||
/// See [println!].
|
||||
#[macro_export]
|
||||
macro_rules! warnln {
|
||||
($($it:tt)+) => (
|
||||
print!($crate::debug::Level::Warn, "\x1B[33;1m[{}:{}] {}\x1B[0m\n", file!(), line!(), format_args!($($it)+))
|
||||
)
|
||||
}
|
||||
|
||||
/// Writes a message, annotated with current file and line, with a newline, to
|
||||
/// error level output.
|
||||
///
|
||||
/// See [println!].
|
||||
#[macro_export]
|
||||
macro_rules! errorln {
|
||||
($($it:tt)+) => (
|
||||
print!($crate::debug::Level::Error, "\x1B[41;1m[{}:{}] {}\x1B[0m\n", file!(), line!(), format_args!($($it)+))
|
||||
)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn _debug(level: Level, args: fmt::Arguments) {
|
||||
pub fn _debug(args: fmt::Arguments) {
|
||||
use crate::arch::machine;
|
||||
use fmt::Write;
|
||||
|
||||
if level >= LEVEL {
|
||||
SerialOutput {
|
||||
inner: machine::console(),
|
||||
}
|
||||
.write_fmt(args)
|
||||
.ok();
|
||||
}
|
||||
drop(SerialOutput { inner: machine::console() }.write_fmt(args));
|
||||
}
|
||||
|
@ -1,133 +0,0 @@
|
||||
//! Device tree facilities
|
||||
use crate::debug::Level;
|
||||
use fdt_rs::prelude::*;
|
||||
use fdt_rs::{
|
||||
base::DevTree,
|
||||
index::{DevTreeIndex, DevTreeIndexNode, DevTreeIndexProp},
|
||||
};
|
||||
use libsys::{error::Errno, path::path_component_left};
|
||||
|
||||
#[repr(align(16))]
|
||||
struct Wrap {
|
||||
data: [u8; 65536],
|
||||
}
|
||||
|
||||
static mut INDEX_BUFFER: Wrap = Wrap { data: [0; 65536] };
|
||||
|
||||
type INode<'a> = DevTreeIndexNode<'a, 'a, 'a>;
|
||||
type IProp<'a> = DevTreeIndexProp<'a, 'a, 'a>;
|
||||
|
||||
/// Device tree manager structure
|
||||
#[allow(dead_code)]
|
||||
pub struct DeviceTree {
|
||||
tree: DevTree<'static>,
|
||||
index: DevTreeIndex<'static, 'static>,
|
||||
}
|
||||
|
||||
fn tab(level: Level, depth: usize) {
|
||||
for _ in 0..depth {
|
||||
print!(level, "\t");
|
||||
}
|
||||
}
|
||||
|
||||
fn dump_node(level: Level, node: &INode, depth: usize) {
|
||||
if node.name().unwrap().starts_with("virtio_mmio@") {
|
||||
return;
|
||||
}
|
||||
|
||||
tab(level, depth);
|
||||
println!(level, "{:?} {{", node.name().unwrap());
|
||||
|
||||
for prop in node.props() {
|
||||
tab(level, depth + 1);
|
||||
let name = prop.name().unwrap();
|
||||
print!(level, "{:?} = ", name);
|
||||
|
||||
match name {
|
||||
"compatible" => print!(level, "{:?}", prop.str().unwrap()),
|
||||
"#address-cells" | "#size-cells" => print!(level, "{}", prop.u32(0).unwrap()),
|
||||
"reg" => {
|
||||
print!(level, "<");
|
||||
let len = prop.length() / 4;
|
||||
for i in 0..len {
|
||||
print!(level, "{:#010x}", prop.u32(i).unwrap());
|
||||
if i < len - 1 {
|
||||
print!(level, ", ");
|
||||
}
|
||||
}
|
||||
print!(level, ">");
|
||||
}
|
||||
_ => print!(level, "..."),
|
||||
}
|
||||
println!(level, ";");
|
||||
}
|
||||
|
||||
if node.children().next().is_some() {
|
||||
println!(level, "");
|
||||
}
|
||||
|
||||
for child in node.children() {
|
||||
dump_node(level, &child, depth + 1);
|
||||
}
|
||||
|
||||
tab(level, depth);
|
||||
println!(level, "}}");
|
||||
}
|
||||
|
||||
fn find_node<'a>(at: INode<'a>, path: &str) -> Option<INode<'a>> {
|
||||
let (item, path) = path_component_left(path);
|
||||
if item.is_empty() {
|
||||
assert_eq!(path, "");
|
||||
Some(at)
|
||||
} else {
|
||||
let child = at.children().find(|c| c.name().unwrap() == item)?;
|
||||
if path.is_empty() {
|
||||
Some(child)
|
||||
} else {
|
||||
find_node(child, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Looks up a node's property by its name
|
||||
pub fn find_prop<'a>(at: INode<'a>, name: &str) -> Option<IProp<'a>> {
|
||||
at.props().find(|p| p.name().unwrap() == name)
|
||||
}
|
||||
|
||||
// fn read_cells(prop: &IProp, off: usize, cells: u32) -> Option<u64> {
|
||||
// Some(match cells {
|
||||
// 1 => prop.u32(off).ok()? as u64,
|
||||
// 2 => (prop.u32(off).ok()? as u64) | ((prop.u32(off + 1).ok()? as u64) << 32),
|
||||
// _ => todo!(),
|
||||
// })
|
||||
// }
|
||||
|
||||
impl DeviceTree {
|
||||
/// Dumps contents of the device tree
|
||||
pub fn dump(&self, level: Level) {
|
||||
dump_node(level, &self.index.root(), 0);
|
||||
}
|
||||
|
||||
/// Looks up given `path` in the tree
|
||||
pub fn node_by_path(&self, path: &str) -> Option<INode> {
|
||||
find_node(self.index.root(), path.trim_start_matches('/'))
|
||||
}
|
||||
|
||||
/// Loads a device tree from physical `base` address and
|
||||
/// creates an index for it
|
||||
pub fn from_phys(base: usize) -> Result<DeviceTree, Errno> {
|
||||
// TODO virtualize address
|
||||
let tree = unsafe { DevTree::from_raw_pointer(base as *const _) }
|
||||
.map_err(|_| Errno::InvalidArgument)?;
|
||||
let layout = DevTreeIndex::get_layout(&tree).unwrap();
|
||||
if layout.size() + layout.align() >= unsafe { INDEX_BUFFER.data.len() } {
|
||||
return Err(Errno::OutOfMemory);
|
||||
}
|
||||
let index = DevTreeIndex::new(tree, unsafe {
|
||||
&mut INDEX_BUFFER.data[0..layout.size() + layout.align()]
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
Ok(DeviceTree { tree, index })
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
//! GPIO and pin control interfaces
|
||||
|
||||
use crate::dev::Device;
|
||||
use libsys::error::Errno;
|
||||
|
||||
/// Pin function mode
|
||||
pub enum PinMode {
|
||||
/// Do not use pin
|
||||
Disable = 0,
|
||||
/// Use pin as a GPIO input
|
||||
Input,
|
||||
/// Use pin as a GPIO output
|
||||
Output,
|
||||
/// Use pin as an external interrupt trigger source
|
||||
InputInterrupt,
|
||||
/// Use pin for peripheral functionality
|
||||
Alt,
|
||||
}
|
||||
|
||||
/// Input/output pin pull mode
|
||||
pub enum PullMode {
|
||||
/// No pull
|
||||
None,
|
||||
/// Pull up
|
||||
Up,
|
||||
/// Pull down
|
||||
Down,
|
||||
}
|
||||
|
||||
/// Pin configuration for [GpioDevice::set_pin_config]
|
||||
pub struct PinConfig {
|
||||
/// Pin function
|
||||
pub mode: PinMode,
|
||||
/// Pin pull mode, only used for Input/Output pins
|
||||
pub pull: PullMode,
|
||||
/// Alternate pin function, only used when mode == [PinMode::Alt]
|
||||
pub func: u32,
|
||||
}
|
||||
|
||||
/// Generic GPIO controller interface
|
||||
pub trait GpioDevice: Device {
|
||||
/// Controller-specific address type for a single pin,
|
||||
/// may include its bank and pin numbers
|
||||
type PinAddress;
|
||||
|
||||
/// Initializes configuration for given pin
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: changes physical pin configuration
|
||||
unsafe fn set_pin_config(&self, pin: Self::PinAddress, cfg: &PinConfig) -> Result<(), Errno>;
|
||||
/// Returns current configuration of given pin
|
||||
fn get_pin_config(&self, pin: Self::PinAddress) -> Result<PinConfig, Errno>;
|
||||
|
||||
/// Sets `pin` to HIGH/LOW `state`
|
||||
fn write_pin(&self, pin: Self::PinAddress, state: bool);
|
||||
/// Toggles `pin`'s HIGH/LOW state
|
||||
fn toggle_pin(&self, pin: Self::PinAddress);
|
||||
/// Returns `true` if input `pin` is in HIGH state
|
||||
fn read_pin(&self, pin: Self::PinAddress) -> Result<bool, Errno>;
|
||||
}
|
||||
|
||||
impl PinConfig {
|
||||
/// Alternative (peripheral) pin configuration
|
||||
pub const fn alt(func: u32) -> Self {
|
||||
Self {
|
||||
mode: PinMode::Alt,
|
||||
pull: PullMode::None,
|
||||
func,
|
||||
}
|
||||
}
|
||||
|
||||
/// Pull-down output
|
||||
pub const fn out_pull_down() -> Self {
|
||||
Self {
|
||||
mode: PinMode::Output,
|
||||
pull: PullMode::Down,
|
||||
func: 0,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
//! Interrupt controller and handler interfaces
|
||||
use crate::dev::Device;
|
||||
use core::marker::PhantomData;
|
||||
use libsys::error::Errno;
|
||||
|
||||
/// Token to indicate the local core is running in IRQ context
|
||||
pub struct IrqContext<'irq_context> {
|
||||
_0: PhantomData<&'irq_context ()>,
|
||||
}
|
||||
|
||||
/// Interrupt controller interface
|
||||
pub trait IntController: Device {
|
||||
/// Implementation-specific definition for "IRQ line"
|
||||
type IrqNumber;
|
||||
|
||||
/// Binds a handler [IntSource] to a specific `irq` line
|
||||
fn register_handler(
|
||||
&self,
|
||||
irq: Self::IrqNumber,
|
||||
handler: &'static (dyn IntSource + Sync),
|
||||
) -> Result<(), Errno>;
|
||||
|
||||
/// Enables/unmasks `irq` line
|
||||
fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Errno>;
|
||||
|
||||
/// Handles all pending IRQs for this interrupt controller
|
||||
fn handle_pending_irqs<'irq_context>(&'irq_context self, ic: &IrqContext<'irq_context>);
|
||||
}
|
||||
|
||||
/// Interface for peripherals capable of emitting IRQs
|
||||
pub trait IntSource: Device {
|
||||
/// Handles pending IRQs, if any, of this [IntSource].
|
||||
///
|
||||
/// If no IRQ is pending, returns [Errno::DoesNotExist]
|
||||
fn handle_irq(&self) -> Result<(), Errno>;
|
||||
|
||||
///
|
||||
fn init_irqs(&'static self) -> Result<(), Errno>;
|
||||
}
|
||||
|
||||
impl<'q> IrqContext<'q> {
|
||||
/// Constructs an IRQ context token
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only allowed to be constructed in top-level IRQ handlers
|
||||
#[inline(always)]
|
||||
pub unsafe fn new() -> Self {
|
||||
Self { _0: PhantomData }
|
||||
}
|
||||
}
|
@ -1,29 +1,9 @@
|
||||
//! Module for device interfaces and drivers
|
||||
use error::Errno;
|
||||
|
||||
use libsys::error::Errno;
|
||||
|
||||
// Device classes
|
||||
pub mod fdt;
|
||||
pub mod gpio;
|
||||
pub mod irq;
|
||||
pub mod pci;
|
||||
pub mod rtc;
|
||||
pub mod sd;
|
||||
pub mod serial;
|
||||
pub mod timer;
|
||||
pub mod pseudo;
|
||||
pub mod tty;
|
||||
|
||||
/// Generic device trait
|
||||
pub trait Device {
|
||||
/// Returns device type/driver name
|
||||
fn name(&self) -> &'static str;
|
||||
fn name() -> &'static str;
|
||||
|
||||
/// Performs device initialization logic.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Marked unsafe as it may cause direct hardware-specific side-effects.
|
||||
/// Additionally, may be called twice with undefined results.
|
||||
unsafe fn enable(&self) -> Result<(), Errno>;
|
||||
unsafe fn enable(&mut self) -> Result<(), Errno>;
|
||||
}
|
||||
|
@ -1,134 +0,0 @@
|
||||
//! PCI bus host and device interfaces
|
||||
|
||||
use crate::dev::Device;
|
||||
use core::fmt;
|
||||
use libsys::error::Errno;
|
||||
|
||||
pub mod pcie;
|
||||
|
||||
macro_rules! ecam_field {
|
||||
($getter:ident, $off:expr, u16) => {
|
||||
#[inline(always)]
|
||||
#[allow(missing_docs)]
|
||||
fn $getter(&self) -> u16 {
|
||||
self.readw($off)
|
||||
}
|
||||
};
|
||||
($getter:ident, $off:expr, u8) => {
|
||||
#[inline(always)]
|
||||
#[allow(missing_docs)]
|
||||
fn $getter(&self) -> u8 {
|
||||
self.readb($off)
|
||||
}
|
||||
};
|
||||
($getter:ident, $setter:ident, $off:expr, u16) => {
|
||||
#[inline(always)]
|
||||
#[allow(missing_docs)]
|
||||
unsafe fn $setter(&self, v: u16) {
|
||||
self.writew($off, v)
|
||||
}
|
||||
|
||||
ecam_field! { $getter, $off, u16 }
|
||||
};
|
||||
}
|
||||
|
||||
/// PCI endpoint address struct, combining bus:dev:func parts
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct PciAddress {
|
||||
value: u32,
|
||||
}
|
||||
|
||||
/// Generic PCI device configuration space interface
|
||||
pub trait PciCfgSpace {
|
||||
// TODO change readl to readl_unchecked() and perform checks at trait level
|
||||
/// Reads an [u32] from device config space.
|
||||
/// `off` must be aligned at a 4-byte boundary.
|
||||
fn readl(&self, off: usize) -> u32;
|
||||
|
||||
/// Writes an [u32] to device config space.
|
||||
/// `off` must be aligned at a 4-byte boundary.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: allows arbitrary value writes to PCI config space.
|
||||
unsafe fn writel(&self, off: usize, val: u32);
|
||||
|
||||
/// Reads an [u16] from device config space.
|
||||
/// `off` must be aligned at a 2-byte boundary.
|
||||
#[inline(always)]
|
||||
fn readw(&self, off: usize) -> u16 {
|
||||
assert!(off & 0x1 == 0);
|
||||
(self.readl(off & !0x3) >> ((off & 0x3) * 8)) as u16
|
||||
}
|
||||
|
||||
/// Reads an [u8] from device config space
|
||||
#[inline(always)]
|
||||
fn readb(&self, off: usize) -> u8 {
|
||||
(self.readl(off & !0x3) >> ((off & 0x3) * 8)) as u8
|
||||
}
|
||||
|
||||
ecam_field! { vendor_id, 0x00, u16 }
|
||||
ecam_field! { device_id, 0x02, u16 }
|
||||
ecam_field! { header_type, 0x0E, u8 }
|
||||
|
||||
/// Returns `true` if device this config describes is
|
||||
/// present on the bus
|
||||
#[inline(always)]
|
||||
fn is_valid(&self) -> bool {
|
||||
self.readl(0) != 0xFFFFFFFF
|
||||
}
|
||||
}
|
||||
|
||||
/// PCI host controller interface
|
||||
pub trait PciHostDevice: Device {
|
||||
/// Initializes and enables devices attached to the bus
|
||||
fn map(&self) -> Result<(), Errno>;
|
||||
}
|
||||
|
||||
impl PciAddress {
|
||||
/// Constructs a [PciAddress] instance from its components
|
||||
#[inline(always)]
|
||||
pub const fn new(bus: u8, dev: u8, func: u8) -> Self {
|
||||
Self {
|
||||
value: ((bus as u32) << 8) | ((dev as u32) << 3) | (func as u32),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `bus` field of [PciAddress]
|
||||
#[inline(always)]
|
||||
pub const fn bus(self) -> u8 {
|
||||
(self.value >> 8) as u8
|
||||
}
|
||||
|
||||
/// Returns `dev` field of [PciAddress]
|
||||
#[inline(always)]
|
||||
pub const fn dev(self) -> u8 {
|
||||
((self.value >> 3) as u8) & 0x1F
|
||||
}
|
||||
|
||||
/// Returns `func` field of [PciAddress]
|
||||
#[inline(always)]
|
||||
pub const fn func(self) -> u8 {
|
||||
(self.value as u8) & 0x7
|
||||
}
|
||||
|
||||
/// Returns a new [PciAddress], constructed from `self`, but with
|
||||
/// specified `func` number
|
||||
#[inline(always)]
|
||||
pub const fn with_func(self, func: u8) -> Self {
|
||||
Self::new(self.bus(), self.dev(), func)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PciAddress {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{:02x}:{:02x}:{:02x}",
|
||||
self.bus(),
|
||||
self.dev(),
|
||||
self.func()
|
||||
)
|
||||
}
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
//! Generic PCIe host driver
|
||||
|
||||
use crate::dev::{
|
||||
pci::{pcie::EcamCfgSpace, PciAddress, PciCfgSpace, PciHostDevice},
|
||||
Device,
|
||||
};
|
||||
use crate::mem::virt::DeviceMemory;
|
||||
use crate::util::InitOnce;
|
||||
use libsys::error::Errno;
|
||||
|
||||
/// GPEX host controller struct
|
||||
pub struct GenericPcieHost {
|
||||
ecam_base: usize,
|
||||
ecam: InitOnce<DeviceMemory>,
|
||||
// TODO
|
||||
#[allow(dead_code)]
|
||||
bus_count: u8,
|
||||
}
|
||||
|
||||
impl Device for GenericPcieHost {
|
||||
fn name(&self) -> &'static str {
|
||||
"Generic PCIe Host Controller"
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
self.ecam
|
||||
.init(DeviceMemory::map(self.name(), self.ecam_base, 512 * 512)?);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PciHostDevice for GenericPcieHost {
|
||||
fn map(&self) -> Result<(), Errno> {
|
||||
let bus0 = self.get_ecam(PciAddress::new(0, 0, 0));
|
||||
|
||||
if bus0.header_type() & 0x80 == 0 {
|
||||
self.map_bus(0)?;
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl GenericPcieHost {
|
||||
fn get_ecam(&self, addr: PciAddress) -> EcamCfgSpace {
|
||||
assert!(addr.value < 512 * 512);
|
||||
unsafe { EcamCfgSpace::new(self.ecam.get().base(), addr) }
|
||||
}
|
||||
|
||||
fn map_function(&self, addr: PciAddress, cfg: EcamCfgSpace) -> Result<(), Errno> {
|
||||
infoln!(
|
||||
"{:?}: {:04x}:{:04x}",
|
||||
addr,
|
||||
cfg.vendor_id(),
|
||||
cfg.device_id()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn map_device(&self, addr: PciAddress) -> Result<(), Errno> {
|
||||
let fn0 = self.get_ecam(addr);
|
||||
if !fn0.is_valid() {
|
||||
return Ok(());
|
||||
}
|
||||
let ty = fn0.header_type();
|
||||
|
||||
self.map_function(addr, fn0)?;
|
||||
|
||||
// Check if device is a multi-function one
|
||||
if ty & 0x80 != 0 {
|
||||
for func in 1..8 {
|
||||
let addr = addr.with_func(func);
|
||||
let f = self.get_ecam(addr);
|
||||
if f.is_valid() {
|
||||
self.map_function(addr, f)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn map_bus(&self, bus: u8) -> Result<(), Errno> {
|
||||
for dev in 0u8..=255 {
|
||||
self.map_device(PciAddress::new(bus, dev, 0))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Constructs an instance of GPEX device.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Does not perform `ecam_base` validation.
|
||||
pub const unsafe fn new(ecam_base: usize, bus_count: u8) -> Self {
|
||||
Self {
|
||||
ecam: InitOnce::new(),
|
||||
ecam_base,
|
||||
bus_count,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
//! PCI Express access interfaces and drivers
|
||||
|
||||
use crate::dev::pci::{PciAddress, PciCfgSpace};
|
||||
|
||||
pub mod gpex;
|
||||
|
||||
/// Enhanced configuration space from PCI Express
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct EcamCfgSpace {
|
||||
base: usize,
|
||||
}
|
||||
|
||||
impl EcamCfgSpace {
|
||||
/// Constructs an instance of ECAM struct describing PCI endpoint `addr`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `ecam_base` is not validated.
|
||||
pub const unsafe fn new(ecam_base: usize, addr: PciAddress) -> Self {
|
||||
Self {
|
||||
base: ecam_base + (addr.value as usize) * 4096,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PciCfgSpace for EcamCfgSpace {
|
||||
#[inline(always)]
|
||||
fn readl(&self, off: usize) -> u32 {
|
||||
assert!(off & 0x3 == 0);
|
||||
unsafe { core::ptr::read_volatile((self.base + off) as *const u32) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn writel(&self, off: usize, val: u32) {
|
||||
assert!(off & 0x3 == 0);
|
||||
core::ptr::write_volatile((self.base + off) as *mut u32, val);
|
||||
}
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
use crate::arch::machine::{self, IrqNumber};
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource},
|
||||
serial::SerialDevice,
|
||||
tty::{CharRing, TtyDevice},
|
||||
Device,
|
||||
};
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use libsys::{error::Errno, ioctl::IoctlCmd};
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
use vfs::CharDevice;
|
||||
|
||||
pub struct Random {
|
||||
state: AtomicU32
|
||||
}
|
||||
pub struct Zero;
|
||||
|
||||
impl Device for Random {
|
||||
fn name(&self) -> &'static str {
|
||||
"Pseudo-random device"
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CharDevice for Random {
|
||||
fn read(&self, _blocking: bool, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
for byte in data.iter_mut() {
|
||||
*byte = self.read_single() as u8;
|
||||
}
|
||||
Ok(data.len())
|
||||
}
|
||||
|
||||
fn write(&self, _blocking: bool, _data: &[u8]) -> Result<usize, Errno> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn is_ready(&self, _write: bool) -> Result<bool, Errno> {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn ioctl(&self, _cmd: IoctlCmd, _ptr: usize, _lim: usize) -> Result<usize, Errno> {
|
||||
Err(Errno::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Device for Zero {
|
||||
fn name(&self) -> &'static str {
|
||||
"Zero device"
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CharDevice for Zero {
|
||||
fn read(&self, _blocking: bool, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
data.fill(0);
|
||||
Ok(data.len())
|
||||
}
|
||||
|
||||
fn write(&self, _blocking: bool, _data: &[u8]) -> Result<usize, Errno> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn is_ready(&self, _write: bool) -> Result<bool, Errno> {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn ioctl(&self, _cmd: IoctlCmd, _ptr: usize, _lim: usize) -> Result<usize, Errno> {
|
||||
Err(Errno::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
impl Random {
|
||||
pub fn set_state(&self, state: u32) {
|
||||
self.state.store(state, Ordering::Release);
|
||||
}
|
||||
|
||||
pub fn read_single(&self) -> u32 {
|
||||
let mut x = self.state.load(Ordering::Acquire);
|
||||
x ^= x << 13;
|
||||
x ^= x >> 7;
|
||||
x ^= x << 17;
|
||||
self.state.store(x, Ordering::Release);
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
pub static RANDOM: Random = Random { state: AtomicU32::new(0) };
|
||||
pub static ZERO: Zero = Zero;
|
@ -1,11 +0,0 @@
|
||||
//! Interfaces and drivers for real-time clock devices
|
||||
|
||||
use crate::dev::Device;
|
||||
|
||||
#[cfg(feature = "pl031")]
|
||||
pub mod pl031;
|
||||
|
||||
// TODO define what RTC devices can do
|
||||
// alarms? read real time?
|
||||
/// Interface for generic RTC device
|
||||
pub trait RtcDevice: Device {}
|
@ -1,110 +0,0 @@
|
||||
//! PL031 - ARM PrimeCell real-time clock implementation
|
||||
use crate::arch::machine::{self, IrqNumber};
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource},
|
||||
rtc::RtcDevice,
|
||||
Device,
|
||||
};
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use libsys::error::Errno;
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
CR [
|
||||
RTCStart OFFSET(0) NUMBITS(1) []
|
||||
],
|
||||
IMSC [
|
||||
RTCIMSC OFFSET(0) NUMBITS(1) []
|
||||
],
|
||||
ICR [
|
||||
RTCICR OFFSET(0) NUMBITS(1) []
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
(0x00 => DR: ReadOnly<u32>),
|
||||
(0x04 => MR: ReadWrite<u32>),
|
||||
(0x08 => LR: ReadWrite<u32>),
|
||||
(0x0C => CR: ReadWrite<u32, CR::Register>),
|
||||
(0x10 => IMSC: ReadWrite<u32, IMSC::Register>),
|
||||
(0x14 => RIS: ReadOnly<u32>),
|
||||
(0x18 => MIS: ReadOnly<u32>),
|
||||
(0x1C => ICR: WriteOnly<u32, ICR::Register>),
|
||||
(0x20 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct Pl031Inner {
|
||||
regs: DeviceMemoryIo<Regs>,
|
||||
}
|
||||
|
||||
/// Device struct for PL031
|
||||
pub struct Pl031 {
|
||||
inner: InitOnce<IrqSafeSpinLock<Pl031Inner>>,
|
||||
base: usize,
|
||||
irq: IrqNumber,
|
||||
}
|
||||
|
||||
impl RtcDevice for Pl031 {}
|
||||
|
||||
impl IntSource for Pl031 {
|
||||
fn handle_irq(&self) -> Result<(), Errno> {
|
||||
let inner = self.inner.get().lock();
|
||||
inner.regs.ICR.write(ICR::RTCICR::SET);
|
||||
let data = inner.regs.DR.get();
|
||||
inner.regs.MR.set(data + 1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_irqs(&'static self) -> Result<(), Errno> {
|
||||
machine::intc().register_handler(self.irq, self)?;
|
||||
self.inner.get().lock().regs.IMSC.modify(IMSC::RTCIMSC::SET);
|
||||
machine::intc().enable_irq(self.irq)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Pl031 {
|
||||
fn name(&self) -> &'static str {
|
||||
"PL031 RTC"
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
let inner = Pl031Inner {
|
||||
regs: DeviceMemoryIo::map(self.name(), self.base, 1)?,
|
||||
};
|
||||
|
||||
inner.regs.CR.modify(CR::RTCStart::CLEAR);
|
||||
inner.regs.MR.set(inner.regs.DR.get() + 1);
|
||||
inner.regs.CR.modify(CR::RTCStart::SET);
|
||||
|
||||
self.inner.init(IrqSafeSpinLock::new(inner));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Pl031 {
|
||||
/// Constructs an instance of PL031 device.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Does not perform `base` validation.
|
||||
pub const unsafe fn new(base: usize, irq: IrqNumber) -> Self {
|
||||
Self {
|
||||
inner: InitOnce::new(),
|
||||
base,
|
||||
irq,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,436 +0,0 @@
|
||||
//! SD host controller interface and card operation facilities
|
||||
use crate::dev::Device;
|
||||
use libsys::error::Errno;
|
||||
use vfs::BlockDevice;
|
||||
|
||||
/// Generic SD/MMC host controller interface
|
||||
pub trait SdHostController: Device + BlockDevice {
|
||||
// Physical layer
|
||||
/// Sends `cmd` to the card using controller's physical layer
|
||||
fn send_cmd(&self, cmd: &mut SdCommand) -> Result<SdResponse, Errno>;
|
||||
/// Performs controller reset
|
||||
fn phys_reset(&self) -> Result<(), Errno>;
|
||||
/// Returns `true` if the card is physically present
|
||||
fn is_phys_inserted(&self) -> bool;
|
||||
|
||||
// Data layer
|
||||
/// Sets card relative address
|
||||
fn set_card_address(&self, rca: u16) -> Result<(), Errno>;
|
||||
/// Sets card identification data
|
||||
fn set_card_identification(&self, id: SdCardIdentification) -> Result<(), Errno>;
|
||||
/// Resets card to unidentified state
|
||||
fn reset_card_identification(&self) -> Result<(), Errno>;
|
||||
|
||||
/// Resets the inserted card and waits for it to respond
|
||||
fn reset_card_probe(&self) -> Result<(), Errno> {
|
||||
self.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Cmd0,
|
||||
argument: 0,
|
||||
transfer: SdCommandTransfer::None,
|
||||
})?;
|
||||
|
||||
if self
|
||||
.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Cmd8,
|
||||
argument: 0x1AA,
|
||||
transfer: SdCommandTransfer::None,
|
||||
})?
|
||||
.unwrap_one()
|
||||
!= 0x1AA
|
||||
{
|
||||
warnln!("Card did not respond to CMD8");
|
||||
return Err(Errno::DeviceError);
|
||||
}
|
||||
|
||||
// Set operating conditions
|
||||
for _ in 0..10 {
|
||||
let res = self
|
||||
.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Acmd41,
|
||||
argument: 0x00FF8000,
|
||||
transfer: SdCommandTransfer::None,
|
||||
})?
|
||||
.unwrap_one();
|
||||
|
||||
if res & (1 << 31) != 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for _ in 0..1000000 {
|
||||
cortex_a::asm::nop();
|
||||
}
|
||||
}
|
||||
|
||||
warnln!("Card did not respond to Acmd41");
|
||||
Err(Errno::DeviceError)
|
||||
}
|
||||
|
||||
/// Performs controller reset and attempts SD card initialization sequence
|
||||
/// if it is physically present
|
||||
fn reset_card(&self) -> Result<(), Errno> {
|
||||
let mut buf = [0u8; 16];
|
||||
|
||||
self.reset_card_identification()?;
|
||||
|
||||
// Reset physical interface
|
||||
self.phys_reset()?;
|
||||
|
||||
// Bail out if card is not physically present
|
||||
if !self.is_phys_inserted() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Probe for card
|
||||
self.reset_card_probe()?;
|
||||
|
||||
// Perform init sequence
|
||||
let cmd2 = self
|
||||
.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Cmd2,
|
||||
argument: 0,
|
||||
transfer: SdCommandTransfer::None,
|
||||
})?
|
||||
.unwrap_four();
|
||||
|
||||
let cmd3 = self
|
||||
.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Cmd3,
|
||||
argument: 0,
|
||||
transfer: SdCommandTransfer::None,
|
||||
})?
|
||||
.unwrap_one();
|
||||
|
||||
let rca = (cmd3 >> 16) as u16;
|
||||
|
||||
if cmd3 & (1 << 14) != 0 {
|
||||
warnln!("Illegal command");
|
||||
return Err(Errno::DeviceError);
|
||||
}
|
||||
|
||||
if cmd3 & (1 << 13) != 0 {
|
||||
warnln!("Card reported error");
|
||||
return Err(Errno::DeviceError);
|
||||
}
|
||||
|
||||
if cmd3 & (1 << 8) == 0 {
|
||||
warnln!("Card is not ready for data mode");
|
||||
return Err(Errno::DeviceError);
|
||||
}
|
||||
let cmd9 = self
|
||||
.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Cmd9,
|
||||
argument: cmd3 & 0xFFFF0000,
|
||||
transfer: SdCommandTransfer::None,
|
||||
})?
|
||||
.unwrap_four();
|
||||
|
||||
debugln!("cmd9 = {:#x?}", cmd9);
|
||||
let csd_structure = (cmd9[3] >> 16) & 0x3;
|
||||
let capacity = match csd_structure {
|
||||
0 => {
|
||||
let c_size = ((cmd9[2] & 0x3) << 10) | ((cmd9[1] >> 22) & 0x3FF);
|
||||
let c_size_mult = (cmd9[1] >> 7) & 0x7;
|
||||
((c_size + 1) as u64) << (c_size_mult + 9 /* Block size is 512 */ + 2)
|
||||
}
|
||||
1 => todo!(),
|
||||
_ => {
|
||||
warnln!("Invalid CSD version: {}", csd_structure);
|
||||
return Err(Errno::DeviceError);
|
||||
}
|
||||
};
|
||||
|
||||
let cmd7 = self
|
||||
.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Cmd7,
|
||||
argument: cmd3 & 0xFFFF0000,
|
||||
transfer: SdCommandTransfer::None,
|
||||
})?
|
||||
.unwrap_one();
|
||||
|
||||
let status = (cmd7 >> 9) & 0xF;
|
||||
if status != 3 && status != 4 {
|
||||
warnln!("Card reported invalid status: {}", status);
|
||||
return Err(Errno::DeviceError);
|
||||
}
|
||||
|
||||
// Set block size
|
||||
self.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Cmd16,
|
||||
argument: 512,
|
||||
transfer: SdCommandTransfer::None,
|
||||
})?;
|
||||
|
||||
self.set_card_address(rca)?;
|
||||
self.send_cmd(&mut SdCommand {
|
||||
number: SdCommandNumber::Acmd51,
|
||||
argument: 0,
|
||||
transfer: SdCommandTransfer::Read(&mut buf[..8], 8),
|
||||
})?;
|
||||
|
||||
let sd_spec = buf[0] & 0xF;
|
||||
|
||||
let version = match sd_spec {
|
||||
0 => SdCardVersion::Ver10,
|
||||
1 => SdCardVersion::Ver11,
|
||||
2 => {
|
||||
// FIXME check for 3.0/4.0
|
||||
SdCardVersion::Ver20
|
||||
}
|
||||
_ => panic!("Invalid version: {:#x}", sd_spec),
|
||||
};
|
||||
|
||||
let card_id = SdCardIdentification {
|
||||
id: cmd2,
|
||||
version,
|
||||
capacity,
|
||||
};
|
||||
|
||||
self.set_card_identification(card_id)?;
|
||||
infoln!("Found a valid SD card of capacity {}B", capacity);
|
||||
|
||||
// TODO High speed support
|
||||
// if version >= SdCardVersion::Ver11 {
|
||||
// let mut buf = [0u8; 64];
|
||||
// self.send_cmd(&mut SdCommand {
|
||||
// number: SdCommandNumber::Cmd6,
|
||||
// argument: 0x00FFFFF0,
|
||||
// transfer: SdCommandTransfer::Read(&mut buf, 64)
|
||||
// })?;
|
||||
|
||||
// // Check HS support
|
||||
// let hs_support = buf[13] >> 1 != 0;
|
||||
// if hs_support {
|
||||
// debugln!("Switching to high speed mode");
|
||||
// self.send_cmd(&mut SdCommand {
|
||||
// number: SdCommandNumber::Cmd6,
|
||||
// argument: 0x80FFFFF1,
|
||||
// transfer: SdCommandTransfer::None
|
||||
// })?;
|
||||
|
||||
// todo!();
|
||||
// }
|
||||
// }
|
||||
|
||||
// let dbus_widths = buf[0] & 0xF;
|
||||
// TODO 4 bit mode support
|
||||
|
||||
// TODO switch clock rate
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// List of possible response types by SD cards
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum SdResponseType {
|
||||
/// No response
|
||||
None,
|
||||
R1,
|
||||
R1b,
|
||||
R2,
|
||||
R3,
|
||||
R4,
|
||||
R5,
|
||||
R5b,
|
||||
R6,
|
||||
R7,
|
||||
}
|
||||
|
||||
/// List of possible SD card versions
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
|
||||
pub enum SdCardVersion {
|
||||
Ver10 = 0x10,
|
||||
Ver11 = 0x11,
|
||||
Ver20 = 0x20,
|
||||
Ver30 = 0x30,
|
||||
Ver40 = 0x40,
|
||||
}
|
||||
|
||||
/// SD card identification data
|
||||
pub struct SdCardIdentification {
|
||||
/// Manufacturer's/device ID
|
||||
pub id: [u32; 4],
|
||||
/// SD card version
|
||||
pub version: SdCardVersion,
|
||||
/// SD card capacity in bytes
|
||||
pub capacity: u64,
|
||||
}
|
||||
|
||||
/// SD card status data
|
||||
pub struct SdCardStatus {
|
||||
/// If `true`, SD card is physically detected by the controller
|
||||
pub phys_inserted: bool,
|
||||
/// SD card's RCA (relative card address)
|
||||
pub address: Option<u16>,
|
||||
/// Identification data
|
||||
pub id: Option<SdCardIdentification>,
|
||||
}
|
||||
|
||||
/// List of possible SD command responses
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum SdResponse {
|
||||
/// Single-word response
|
||||
One(u32),
|
||||
/// Four-word response
|
||||
Four([u32; 4]),
|
||||
}
|
||||
|
||||
/// List of SD card commands
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[repr(u32)]
|
||||
pub enum SdCommandNumber {
|
||||
/// GO_IDLE_STATE
|
||||
///
|
||||
/// Resets the SD card
|
||||
Cmd0 = 0,
|
||||
/// ALL_SEND_CID
|
||||
///
|
||||
/// Requests card's unique card ID
|
||||
Cmd2 = 2,
|
||||
/// SEND_RELATIVE_ADDR
|
||||
///
|
||||
/// Requests card's RCA
|
||||
Cmd3 = 3,
|
||||
/// SWITCH_FUNC
|
||||
///
|
||||
/// Checks switchable function and/or switches card function
|
||||
Cmd6 = 6,
|
||||
/// SELECT/DESELECT_CARD
|
||||
///
|
||||
/// Selects or deselects a card
|
||||
Cmd7 = 7,
|
||||
/// SEND_IF_COND
|
||||
///
|
||||
/// Sends SD card interface conditions (voltage range)
|
||||
Cmd8 = 8,
|
||||
/// SEND_CSD
|
||||
///
|
||||
/// Sends SD card-specific data
|
||||
Cmd9 = 9,
|
||||
/// SET_BLOCKLEN
|
||||
///
|
||||
/// Sets SD card logical block length
|
||||
Cmd16 = 16,
|
||||
/// READ_SINGLE_BLOCK
|
||||
///
|
||||
/// Reads a single block from the card
|
||||
Cmd17 = 17,
|
||||
/// SD_SEND_OP_COND
|
||||
///
|
||||
/// Sends host capacity support info and requests card's operating
|
||||
/// conditions info
|
||||
Acmd41 = 41,
|
||||
/// SEND_SCR
|
||||
///
|
||||
/// Requests SD configuration register
|
||||
Acmd51 = 51,
|
||||
/// APP_CMD
|
||||
///
|
||||
/// Notifies the card that the following command is
|
||||
/// an "A-cmd" (application specific command)
|
||||
Cmd55 = 55,
|
||||
}
|
||||
|
||||
/// Information structure for SD controller drivers
|
||||
pub struct SdCommandInfo {
|
||||
/// Which response type to expect from this command
|
||||
pub response_type: SdResponseType,
|
||||
}
|
||||
|
||||
/// Struct describing expected data transfer for a command
|
||||
pub enum SdCommandTransfer<'a> {
|
||||
/// No transfer occurs
|
||||
None,
|
||||
/// Read from card (second param is block size)
|
||||
Read(&'a mut [u8], u16),
|
||||
/// Write to card (second param is block size)
|
||||
Write(&'a [u8], u16),
|
||||
}
|
||||
|
||||
/// Generic SD card command structure
|
||||
pub struct SdCommand<'a> {
|
||||
/// Command index
|
||||
pub number: SdCommandNumber,
|
||||
/// Argument value (0 if marked as 'stuff bits' in spec)
|
||||
pub argument: u32,
|
||||
/// Expected data transfer
|
||||
pub transfer: SdCommandTransfer<'a>,
|
||||
}
|
||||
|
||||
impl SdCardStatus {
|
||||
/// Initial state for a SD card
|
||||
pub const fn invalid() -> Self {
|
||||
Self {
|
||||
phys_inserted: false,
|
||||
address: None,
|
||||
id: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SdResponseType {
|
||||
/// Returns `true` if response has 'busy' status
|
||||
pub const fn is_busy(self) -> bool {
|
||||
matches!(self, Self::R1b | Self::R5b)
|
||||
}
|
||||
}
|
||||
|
||||
impl SdResponse {
|
||||
/// Returns single-word response or panics
|
||||
pub fn unwrap_one(&self) -> u32 {
|
||||
match *self {
|
||||
SdResponse::One(v) => v,
|
||||
_ => panic!("Unexpected response type"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns four-word response or panics
|
||||
pub fn unwrap_four(&self) -> [u32; 4] {
|
||||
match *self {
|
||||
SdResponse::Four(v) => v,
|
||||
_ => panic!("Unexpected response type"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SdCommandInfo {
|
||||
/// Constructs a new command info struct
|
||||
pub const fn new(response_type: SdResponseType) -> Self {
|
||||
Self { response_type }
|
||||
}
|
||||
}
|
||||
|
||||
impl SdCommand<'_> {
|
||||
/// Returns command information
|
||||
pub const fn info_struct(&self) -> SdCommandInfo {
|
||||
match self.number {
|
||||
SdCommandNumber::Cmd0 => SdCommandInfo::new(SdResponseType::None),
|
||||
SdCommandNumber::Cmd2 => SdCommandInfo::new(SdResponseType::R2),
|
||||
SdCommandNumber::Cmd3 => SdCommandInfo::new(SdResponseType::R6),
|
||||
SdCommandNumber::Cmd6 => SdCommandInfo::new(SdResponseType::R1),
|
||||
SdCommandNumber::Cmd7 => SdCommandInfo::new(SdResponseType::R1b),
|
||||
SdCommandNumber::Cmd8 => SdCommandInfo::new(SdResponseType::R7),
|
||||
SdCommandNumber::Cmd9 => SdCommandInfo::new(SdResponseType::R2),
|
||||
SdCommandNumber::Cmd16 => SdCommandInfo::new(SdResponseType::R1),
|
||||
SdCommandNumber::Cmd17 => SdCommandInfo::new(SdResponseType::R1),
|
||||
SdCommandNumber::Acmd41 => SdCommandInfo::new(SdResponseType::R3),
|
||||
SdCommandNumber::Acmd51 => SdCommandInfo::new(SdResponseType::R1),
|
||||
SdCommandNumber::Cmd55 => SdCommandInfo::new(SdResponseType::R1),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if cmd is application-specific
|
||||
pub const fn is_acmd(&self) -> bool {
|
||||
matches!(
|
||||
self.number,
|
||||
SdCommandNumber::Acmd41 | SdCommandNumber::Acmd51
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the command index
|
||||
pub const fn number(&self) -> u32 {
|
||||
self.number as u32
|
||||
}
|
||||
}
|
@ -1,18 +1,9 @@
|
||||
//! Module for serial device drivers
|
||||
|
||||
use crate::dev::Device;
|
||||
use libsys::error::Errno;
|
||||
use error::Errno;
|
||||
|
||||
#[cfg(feature = "pl011")]
|
||||
pub mod pl011;
|
||||
|
||||
/// Generic interface for serial devices
|
||||
pub trait SerialDevice: Device {
|
||||
/// Transmits (blocking) a byte through the serial device
|
||||
fn send(&self, byte: u8) -> Result<(), Errno>;
|
||||
/// Receives a byte through the serial interface.
|
||||
///
|
||||
/// If `blocking` is `false` and there's no data in device's queue,
|
||||
/// will return [Errno::WouldBlock].
|
||||
fn recv(&self, blocking: bool) -> Result<u8, Errno>;
|
||||
unsafe fn send(&mut self, byte: u8) -> Result<(), Errno>;
|
||||
unsafe fn recv(&mut self, blocking: bool) -> Result<u8, Errno>;
|
||||
}
|
||||
|
@ -1,223 +1,90 @@
|
||||
//! PL011 - ARM PrimeCell UART implementation
|
||||
|
||||
use crate::arch::machine::{self, IrqNumber};
|
||||
use crate::dev::{
|
||||
irq::{IntController, IntSource},
|
||||
serial::SerialDevice,
|
||||
tty::{CharRing, TtyDevice},
|
||||
Device,
|
||||
};
|
||||
use crate::mem::virt::DeviceMemoryIo;
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use libsys::error::Errno;
|
||||
use crate::arch::MemoryIo;
|
||||
use crate::dev::{serial::SerialDevice, Device};
|
||||
use error::Errno;
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
interfaces::{Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Pl011 {
|
||||
regs: MemoryIo<Regs>,
|
||||
}
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
/// Flag register
|
||||
FR [
|
||||
/// Transmit FIFO full
|
||||
TXFF OFFSET(5) NUMBITS(1) [],
|
||||
/// Receive FIFO empty
|
||||
RXFE OFFSET(4) NUMBITS(1) [],
|
||||
/// UART busy
|
||||
BUSY OFFSET(3) NUMBITS(1) [],
|
||||
],
|
||||
/// Control register
|
||||
CR [
|
||||
/// Enable UART receiver
|
||||
RXE OFFSET(9) NUMBITS(1) [],
|
||||
/// Enable UART transmitter
|
||||
TXE OFFSET(8) NUMBITS(1) [],
|
||||
/// Enable UART
|
||||
UARTEN OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
/// Interrupt clear register
|
||||
ICR [
|
||||
/// Writing this to ICR clears all IRQs
|
||||
ALL OFFSET(0) NUMBITS(11) []
|
||||
],
|
||||
/// Interrupt mask set/clear register
|
||||
IMSC [
|
||||
RXIM OFFSET(4) NUMBITS(1) []
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
/// PL011 registers
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
/// Data register
|
||||
(0x00 => DR: ReadWrite<u32>),
|
||||
(0x04 => _res1),
|
||||
/// Flag register
|
||||
(0x18 => FR: ReadOnly<u32, FR::Register>),
|
||||
(0x1C => _res2),
|
||||
/// Line control register
|
||||
(0x2C => LCR_H: ReadWrite<u32>),
|
||||
/// Control register
|
||||
(0x30 => CR: ReadWrite<u32, CR::Register>),
|
||||
(0x34 => IFLS: ReadWrite<u32>),
|
||||
(0x38 => IMSC: ReadWrite<u32, IMSC::Register>),
|
||||
(0x3C => _res3),
|
||||
/// Interrupt clear register
|
||||
(0x44 => ICR: WriteOnly<u32, ICR::Register>),
|
||||
(0x04 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct Pl011Inner {
|
||||
regs: DeviceMemoryIo<Regs>,
|
||||
}
|
||||
|
||||
/// Device struct for PL011
|
||||
#[derive(TtyCharDevice)]
|
||||
pub struct Pl011 {
|
||||
inner: InitOnce<IrqSafeSpinLock<Pl011Inner>>,
|
||||
ring: CharRing<16>,
|
||||
base: usize,
|
||||
irq: IrqNumber,
|
||||
}
|
||||
|
||||
impl Pl011Inner {
|
||||
#[inline(always)]
|
||||
pub unsafe fn send(&mut self, byte: u8) {
|
||||
impl SerialDevice for Pl011 {
|
||||
unsafe fn send(&mut self, byte: u8) -> Result<(), Errno> {
|
||||
while self.regs.FR.matches_all(FR::TXFF::SET) {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
self.regs.DR.set(byte as u32);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub unsafe fn recv(&mut self, blocking: bool) -> Result<u8, Errno> {
|
||||
unsafe fn recv(&mut self, blocking: bool) -> Result<u8, Errno> {
|
||||
if self.regs.FR.matches_all(FR::RXFE::SET) {
|
||||
if !blocking {
|
||||
return Err(Errno::WouldBlock);
|
||||
}
|
||||
while self.regs.FR.matches_all(FR::RXFE::SET) {
|
||||
// TODO allow IRQs here?
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self.regs.DR.get() as u8)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn enable(&mut self) {
|
||||
impl Device for Pl011 {
|
||||
fn name() -> &'static str {
|
||||
"PL011 UART"
|
||||
}
|
||||
|
||||
unsafe fn enable(&mut self) -> Result<(), Errno> {
|
||||
self.regs.CR.set(0);
|
||||
self.regs.ICR.write(ICR::ALL::CLEAR);
|
||||
self.regs
|
||||
.CR
|
||||
.write(CR::UARTEN::SET + CR::TXE::SET + CR::RXE::SET);
|
||||
}
|
||||
}
|
||||
|
||||
// impl fmt::Write for Pl011Inner {
|
||||
// fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
// for &c in s.as_bytes() {
|
||||
// unsafe {
|
||||
// self.send(c);
|
||||
// }
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
|
||||
impl IntSource for Pl011 {
|
||||
fn handle_irq(&self) -> Result<(), Errno> {
|
||||
let inner = self.inner.get().lock();
|
||||
inner.regs.ICR.write(ICR::ALL::CLEAR);
|
||||
|
||||
let byte = inner.regs.DR.get();
|
||||
drop(inner);
|
||||
|
||||
self.recv_byte(byte as u8);
|
||||
// self.ring.putc(byte as u8, false).ok();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_irqs(&'static self) -> Result<(), Errno> {
|
||||
machine::intc().register_handler(self.irq, self)?;
|
||||
self.inner.get().lock().regs.IMSC.modify(IMSC::RXIM::SET);
|
||||
machine::intc().enable_irq(self.irq)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SerialDevice for Pl011 {
|
||||
fn send(&self, byte: u8) -> Result<(), Errno> {
|
||||
if !self.inner.is_initialized() {
|
||||
// TODO early output here
|
||||
return Ok(());
|
||||
}
|
||||
unsafe {
|
||||
self.inner.get().lock().send(byte);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn recv(&self, blocking: bool) -> Result<u8, Errno> {
|
||||
unsafe { self.inner.get().lock().recv(blocking) }
|
||||
}
|
||||
}
|
||||
|
||||
// impl CharDevice for Pl011 {
|
||||
// fn read(&self, blocking: bool, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
// assert!(blocking);
|
||||
// self.line_read(data)
|
||||
// }
|
||||
//
|
||||
// fn write(&self, blocking: bool, data: &[u8]) -> Result<usize, Errno> {
|
||||
// assert!(blocking);
|
||||
// self.line_write(data)
|
||||
// }
|
||||
//
|
||||
// fn ioctl(&self, cmd: IoctlCmd, ptr: usize, len: usize) -> Result<usize, Errno> {
|
||||
// self.tty_ioctl(cmd, ptr, len)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl TtyDevice<16> for Pl011 {
|
||||
fn ring(&self) -> &CharRing<16> {
|
||||
&self.ring
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Pl011 {
|
||||
fn name(&self) -> &'static str {
|
||||
"PL011 UART"
|
||||
}
|
||||
|
||||
unsafe fn enable(&self) -> Result<(), Errno> {
|
||||
let mut inner = Pl011Inner {
|
||||
regs: DeviceMemoryIo::map(self.name(), self.base, 1)?,
|
||||
};
|
||||
inner.enable();
|
||||
|
||||
self.inner.init(IrqSafeSpinLock::new(inner));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Pl011 {
|
||||
/// Constructs an instance of PL011 device.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Does not perform `base` validation.
|
||||
pub const unsafe fn new(base: usize, irq: IrqNumber) -> Self {
|
||||
pub const unsafe fn new(base: usize) -> Self {
|
||||
Self {
|
||||
inner: InitOnce::new(),
|
||||
ring: CharRing::new(),
|
||||
base,
|
||||
irq,
|
||||
regs: MemoryIo::new(base),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
//! Timer interface
|
||||
|
||||
use crate::dev::Device;
|
||||
use core::time::Duration;
|
||||
use libsys::error::Errno;
|
||||
|
||||
/// Interface for generic timestamp source
|
||||
pub trait TimestampSource: Device {
|
||||
/// Reads current timestamp as a [Duration] from system start time
|
||||
fn timestamp(&self) -> Result<Duration, Errno>;
|
||||
}
|
@ -1,333 +0,0 @@
|
||||
//! Teletype (TTY) device facilities
|
||||
use crate::dev::serial::SerialDevice;
|
||||
use crate::proc::{Process, wait::{Wait, WAIT_SELECT}};
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use libsys::error::Errno;
|
||||
use libsys::{
|
||||
termios::{Termios, TermiosIflag, TermiosLflag, TermiosOflag},
|
||||
proc::Pid,
|
||||
signal::Signal,
|
||||
ioctl::IoctlCmd
|
||||
};
|
||||
use core::mem::size_of;
|
||||
use crate::syscall::arg;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CharRingInner<const N: usize> {
|
||||
rd: usize,
|
||||
wr: usize,
|
||||
data: [u8; N],
|
||||
flags: u8,
|
||||
fg_pgid: Option<Pid>,
|
||||
}
|
||||
|
||||
/// Ring buffer for TTYs
|
||||
pub struct CharRing<const N: usize> {
|
||||
wait_read: Wait,
|
||||
wait_write: Wait,
|
||||
config: IrqSafeSpinLock<Termios>,
|
||||
inner: IrqSafeSpinLock<CharRingInner<N>>,
|
||||
}
|
||||
|
||||
/// Generic teletype device interface
|
||||
pub trait TtyDevice<const N: usize>: SerialDevice {
|
||||
/// Returns a reference to character device's ring buffer
|
||||
fn ring(&self) -> &CharRing<N>;
|
||||
|
||||
/// Returns `true` if the TTY is ready for an operation
|
||||
fn is_ready(&self, write: bool) -> Result<bool, Errno> {
|
||||
let ring = self.ring();
|
||||
if write {
|
||||
todo!()
|
||||
} else {
|
||||
Ok(ring.is_readable())
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs a TTY control request
|
||||
fn tty_ioctl(&self, cmd: IoctlCmd, ptr: usize, _len: usize) -> Result<usize, Errno> {
|
||||
match cmd {
|
||||
IoctlCmd::TtyGetAttributes => {
|
||||
// TODO validate size
|
||||
let res = arg::struct_mut::<Termios>(ptr)?;
|
||||
*res = self.ring().config.lock().clone();
|
||||
Ok(size_of::<Termios>())
|
||||
},
|
||||
IoctlCmd::TtySetAttributes => {
|
||||
let src = arg::struct_ref::<Termios>(ptr)?;
|
||||
*self.ring().config.lock() = src.clone();
|
||||
Ok(size_of::<Termios>())
|
||||
},
|
||||
IoctlCmd::TtySetPgrp => {
|
||||
let src = arg::struct_ref::<u32>(ptr)?;
|
||||
self.ring().inner.lock().fg_pgid = Some(Pid::try_from(*src)?);
|
||||
Ok(0)
|
||||
},
|
||||
_ => Err(Errno::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes and writes output an output byte
|
||||
fn line_send(&self, byte: u8) -> Result<(), Errno> {
|
||||
let config = self.ring().config.lock();
|
||||
|
||||
if byte == b'\n' && config.oflag.contains(TermiosOflag::ONLCR) {
|
||||
self.send(b'\r').ok();
|
||||
}
|
||||
|
||||
self.send(byte)
|
||||
}
|
||||
|
||||
/// Receives input bytes and processes them
|
||||
fn recv_byte(&self, mut byte: u8) {
|
||||
let ring = self.ring();
|
||||
let config = ring.config.lock();
|
||||
|
||||
if byte == b'@' {
|
||||
use crate::mem::phys;
|
||||
let stat = phys::statistics();
|
||||
debugln!("Physical memory stats:");
|
||||
debugln!("{:#?}", stat);
|
||||
return;
|
||||
}
|
||||
|
||||
if byte == b'\r' && config.iflag.contains(TermiosIflag::ICRNL) {
|
||||
byte = b'\n';
|
||||
}
|
||||
|
||||
if byte == b'\n' {
|
||||
if config.lflag.contains(TermiosLflag::ECHO)
|
||||
|| (config.is_canon() && config.lflag.contains(TermiosLflag::ECHONL))
|
||||
{
|
||||
if byte == b'\n' && config.oflag.contains(TermiosOflag::ONLCR) {
|
||||
self.send(b'\r').ok();
|
||||
}
|
||||
self.send(byte).ok();
|
||||
}
|
||||
} else if config.lflag.contains(TermiosLflag::ECHO) {
|
||||
let echoe = (byte == config.chars.erase || byte == config.chars.werase)
|
||||
&& config.lflag.contains(TermiosLflag::ECHOE);
|
||||
let echok = byte == config.chars.kill && config.lflag.contains(TermiosLflag::ECHOE);
|
||||
|
||||
if byte.is_ascii_control() {
|
||||
if !echoe && !echok {
|
||||
self.send(b'^').ok();
|
||||
self.send(byte + 0x40).ok();
|
||||
}
|
||||
} else {
|
||||
self.send(byte).ok();
|
||||
}
|
||||
}
|
||||
|
||||
if byte == 0x3 && config.lflag.contains(TermiosLflag::ISIG) {
|
||||
drop(config);
|
||||
let pgid = ring.inner.lock().fg_pgid;
|
||||
if let Some(pgid) = pgid {
|
||||
// TODO send to pgid
|
||||
let proc = Process::get(pgid);
|
||||
if let Some(proc) = proc {
|
||||
proc.set_signal(Signal::Interrupt);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
self.ring().putc(byte, false).ok();
|
||||
}
|
||||
|
||||
/// Line discipline function
|
||||
fn line_read(&self, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
let ring = self.ring();
|
||||
let mut config = ring.config.lock();
|
||||
|
||||
if data.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
if !config.is_canon() {
|
||||
drop(config);
|
||||
let byte = ring.getc()?;
|
||||
data[0] = byte;
|
||||
Ok(1)
|
||||
} else {
|
||||
let mut rem = data.len();
|
||||
let mut off = 0;
|
||||
// Perform canonical read
|
||||
while rem != 0 {
|
||||
drop(config);
|
||||
let byte = ring.getc()?;
|
||||
config = ring.config.lock();
|
||||
|
||||
if byte == config.chars.eof && config.is_canon() {
|
||||
break;
|
||||
}
|
||||
if byte == config.chars.erase && config.is_canon() {
|
||||
if off > 0 && config.lflag.contains(TermiosLflag::ECHOE) {
|
||||
self.raw_write(b"\x1B[D \x1B[D").ok();
|
||||
off -= 1;
|
||||
rem += 1;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
if byte == config.chars.werase && config.is_canon() {
|
||||
if off > 0 && config.lflag.contains(TermiosLflag::ECHOE) {
|
||||
let idx = data[..off].iter().rposition(|&ch| ch == b' ').unwrap_or(0);
|
||||
let len = off;
|
||||
|
||||
for _ in idx..len {
|
||||
self.raw_write(b"\x1B[D \x1B[D").ok();
|
||||
off -= 1;
|
||||
rem += 1;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
if byte == config.chars.kill && config.is_canon() {
|
||||
if off > 0 && config.lflag.contains(TermiosLflag::ECHOK) {
|
||||
while off != 0 {
|
||||
self.raw_write(b"\x1B[D \x1B[D").ok();
|
||||
off -= 1;
|
||||
rem += 1;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
data[off] = byte;
|
||||
off += 1;
|
||||
rem -= 1;
|
||||
|
||||
if byte == b'\n' || byte == b'\r' {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(off)
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes and writes string bytes
|
||||
fn line_write(&self, data: &[u8]) -> Result<usize, Errno> {
|
||||
for &byte in data.iter() {
|
||||
self.line_send(byte)?;
|
||||
}
|
||||
Ok(data.len())
|
||||
}
|
||||
|
||||
/// Writes string bytes without any processing
|
||||
fn raw_write(&self, data: &[u8]) -> Result<usize, Errno> {
|
||||
for &byte in data.iter() {
|
||||
self.send(byte)?;
|
||||
}
|
||||
Ok(data.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> CharRingInner<N> {
|
||||
#[inline]
|
||||
const fn is_readable(&self) -> bool {
|
||||
if self.rd <= self.wr {
|
||||
(self.wr - self.rd) > 0
|
||||
} else {
|
||||
(self.wr + (N - self.rd)) > 0
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_unchecked(&mut self) -> u8 {
|
||||
let res = self.data[self.rd];
|
||||
self.rd = (self.rd + 1) % N;
|
||||
res
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_unchecked(&mut self, ch: u8) {
|
||||
self.data[self.wr] = ch;
|
||||
self.wr = (self.wr + 1) % N;
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> CharRing<N> {
|
||||
/// Returns a new fixed-size ring buffer
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
inner: IrqSafeSpinLock::new(CharRingInner {
|
||||
fg_pgid: None,
|
||||
rd: 0,
|
||||
wr: 0,
|
||||
data: [0; N],
|
||||
flags: 0,
|
||||
}),
|
||||
config: IrqSafeSpinLock::new(Termios::new()),
|
||||
wait_read: Wait::new("tty_read"),
|
||||
wait_write: Wait::new("tty_write"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if a character/line is available for reception
|
||||
pub fn is_readable(&self) -> bool {
|
||||
let inner = self.inner.lock();
|
||||
let config = self.config.lock();
|
||||
if config.lflag.contains(TermiosLflag::ICANON) {
|
||||
// TODO optimize this somehow?
|
||||
let mut rd = inner.rd;
|
||||
let mut count = 0usize;
|
||||
loop {
|
||||
let readable = if rd <= inner.wr {
|
||||
(inner.wr - rd) > 0
|
||||
} else {
|
||||
(inner.wr + (N - rd)) > 0
|
||||
};
|
||||
|
||||
if !readable {
|
||||
break;
|
||||
}
|
||||
|
||||
let byte = inner.data[rd];
|
||||
if byte == b'\n' || byte == config.chars.eof {
|
||||
count += 1;
|
||||
}
|
||||
|
||||
rd = (rd + 1) % N;
|
||||
}
|
||||
|
||||
count != 0 || inner.flags != 0
|
||||
} else {
|
||||
inner.is_readable() || inner.flags != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs a blocking read of a single byte from the buffer
|
||||
pub fn getc(&self) -> Result<u8, Errno> {
|
||||
let mut lock = self.inner.lock();
|
||||
loop {
|
||||
if !lock.is_readable() && lock.flags == 0 {
|
||||
drop(lock);
|
||||
self.wait_read.wait(None)?;
|
||||
lock = self.inner.lock();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let byte = lock.read_unchecked();
|
||||
drop(lock);
|
||||
self.wait_write.wakeup_one();
|
||||
WAIT_SELECT.wakeup_all();
|
||||
Ok(byte)
|
||||
}
|
||||
|
||||
/// Puts a single byte to the buffer
|
||||
pub fn putc(&self, ch: u8, blocking: bool) -> Result<(), Errno> {
|
||||
let mut lock = self.inner.lock();
|
||||
if blocking {
|
||||
todo!()
|
||||
}
|
||||
lock.write_unchecked(ch);
|
||||
drop(lock);
|
||||
self.wait_read.wakeup_one();
|
||||
WAIT_SELECT.wakeup_all();
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
//! Device list pseudo-filesystem
|
||||
use crate::util::InitOnce;
|
||||
use alloc::boxed::Box;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use libsys::{stat::FileMode, error::Errno};
|
||||
use vfs::{CharDevice, CharDeviceWrapper, Vnode, VnodeKind, VnodeRef};
|
||||
|
||||
/// Possible character device kinds
|
||||
#[derive(Debug)]
|
||||
pub enum CharDeviceType {
|
||||
/// Serial TTY (ttyS*)
|
||||
TtySerial,
|
||||
}
|
||||
|
||||
static DEVFS_ROOT: InitOnce<VnodeRef> = InitOnce::new();
|
||||
|
||||
/// Initializes devfs
|
||||
pub fn init() {
|
||||
let node = Vnode::new("", VnodeKind::Directory, Vnode::CACHE_READDIR | Vnode::CACHE_STAT);
|
||||
node.props_mut().mode = FileMode::default_dir();
|
||||
DEVFS_ROOT.init(node);
|
||||
}
|
||||
|
||||
/// Returns devfs root node reference
|
||||
pub fn root() -> &'static VnodeRef {
|
||||
DEVFS_ROOT.get()
|
||||
}
|
||||
|
||||
pub fn add_named_char_device(dev: &'static dyn CharDevice, name: &str) -> Result<(), Errno> {
|
||||
infoln!("Add char device: {}", name);
|
||||
|
||||
let node = Vnode::new(name, VnodeKind::Char, Vnode::CACHE_STAT);
|
||||
node.props_mut().mode = FileMode::from_bits(0o600).unwrap() | FileMode::S_IFCHR;
|
||||
node.set_data(Box::new(CharDeviceWrapper::new(dev)));
|
||||
|
||||
DEVFS_ROOT.get().attach(node);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds a character device node to the filesystem
|
||||
pub fn add_char_device(dev: &'static dyn CharDevice, kind: CharDeviceType) -> Result<(), Errno> {
|
||||
static TTYS_COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||
let mut buf = [0u8; 32];
|
||||
|
||||
let (count, prefix) = match kind {
|
||||
CharDeviceType::TtySerial => (&TTYS_COUNT, b"ttyS"),
|
||||
};
|
||||
|
||||
let value = count.fetch_add(1, Ordering::Relaxed);
|
||||
if value > 9 {
|
||||
panic!("Too many character devices of type {:?}", kind);
|
||||
}
|
||||
buf[..prefix.len()].copy_from_slice(prefix);
|
||||
buf[prefix.len()] = (value as u8) + b'0';
|
||||
|
||||
let name = core::str::from_utf8(&buf[..=prefix.len()]).map_err(|_| Errno::InvalidArgument)?;
|
||||
|
||||
add_named_char_device(dev, name)
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
//! Kernel filesystem facilities
|
||||
use crate::mem::{
|
||||
self,
|
||||
phys::{self, PageUsage},
|
||||
};
|
||||
use libsys::{error::Errno, stat::MountOptions};
|
||||
use vfs::VnodeRef;
|
||||
use memfs::BlockAllocator;
|
||||
|
||||
pub mod devfs;
|
||||
pub mod sysfs;
|
||||
|
||||
/// Allocator implementation for memfs
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct MemfsBlockAlloc;
|
||||
|
||||
unsafe impl BlockAllocator for MemfsBlockAlloc {
|
||||
fn alloc(&self) -> *mut u8 {
|
||||
if let Ok(page) = phys::alloc_page(PageUsage::Filesystem) {
|
||||
mem::virtualize(page) as *mut u8
|
||||
} else {
|
||||
core::ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, data: *mut u8) {
|
||||
let phys = (data as usize) - mem::KERNEL_OFFSET;
|
||||
phys::free_page(phys).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a filesystem instance based on `options`
|
||||
pub fn create_filesystem(options: &MountOptions) -> Result<VnodeRef, Errno> {
|
||||
let fs_name = options.fs.unwrap();
|
||||
|
||||
match fs_name {
|
||||
"devfs" => Ok(devfs::root().clone()),
|
||||
"sysfs" => Ok(sysfs::root().clone()),
|
||||
_ => todo!()
|
||||
}
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
use crate::util::InitOnce;
|
||||
use alloc::boxed::Box;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use fs_macros::auto_inode;
|
||||
use libsys::{
|
||||
error::Errno,
|
||||
stat::{FileMode, OpenFlags, Stat},
|
||||
};
|
||||
use vfs::{CharDevice, CharDeviceWrapper, Vnode, VnodeImpl, VnodeKind, VnodeRef};
|
||||
use core::fmt::{self, Write};
|
||||
use core::str::FromStr;
|
||||
use crate::debug::{self, Level};
|
||||
|
||||
struct NodeData<
|
||||
R: Fn(&mut [u8]) -> Result<usize, Errno>,
|
||||
W: Fn(&[u8]) -> Result<usize, Errno>,
|
||||
> {
|
||||
read_func: R,
|
||||
write_func: W,
|
||||
}
|
||||
|
||||
struct BufferWriter<'a> {
|
||||
dst: &'a mut [u8],
|
||||
pos: usize
|
||||
}
|
||||
|
||||
impl<'a> fmt::Write for BufferWriter<'a> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
for byte in s.bytes() {
|
||||
if self.pos == self.dst.len() {
|
||||
todo!();
|
||||
}
|
||||
self.dst[self.pos] = byte;
|
||||
self.pos += 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BufferWriter<'a> {
|
||||
pub const fn new(dst: &'a mut [u8]) -> Self {
|
||||
Self { dst, pos: 0 }
|
||||
}
|
||||
|
||||
pub const fn count(&self) -> usize {
|
||||
self.pos
|
||||
}
|
||||
}
|
||||
|
||||
#[auto_inode]
|
||||
impl<
|
||||
R: Fn(&mut [u8]) -> Result<usize, Errno>,
|
||||
W: Fn(&[u8]) -> Result<usize, Errno>,
|
||||
> VnodeImpl for NodeData<R, W>
|
||||
{
|
||||
fn open(&mut self, _node: VnodeRef, _mode: OpenFlags) -> Result<usize, Errno> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
|
||||
if pos != 0 {
|
||||
// TODO handle this
|
||||
Ok(0)
|
||||
} else {
|
||||
(self.read_func)(data)
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, _node: VnodeRef, pos: usize, data: &[u8]) -> Result<usize, Errno> {
|
||||
if pos != 0 {
|
||||
todo!();
|
||||
}
|
||||
(self.write_func)(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
R: Fn(&mut [u8]) -> Result<usize, Errno>,
|
||||
W: Fn(&[u8]) -> Result<usize, Errno>,
|
||||
> NodeData<R, W>
|
||||
{
|
||||
pub const fn new(read_func: R, write_func: W) -> Self {
|
||||
Self {
|
||||
read_func,
|
||||
write_func,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static SYSFS_ROOT: InitOnce<VnodeRef> = InitOnce::new();
|
||||
static TEST_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
// TODO subdirs
|
||||
fn add_generic_node<R, W>(parent: Option<VnodeRef>, name: &str, mode: FileMode, read: R, write: W)
|
||||
where
|
||||
R: Fn(&mut [u8]) -> Result<usize, Errno> + 'static,
|
||||
W: Fn(&[u8]) -> Result<usize, Errno> + 'static,
|
||||
{
|
||||
let node = Vnode::new(name, VnodeKind::Regular, Vnode::CACHE_STAT);
|
||||
node.props_mut().mode = mode | FileMode::S_IFREG;
|
||||
node.set_data(Box::new(NodeData::new(read, write)));
|
||||
|
||||
if let Some(parent) = parent {
|
||||
parent.attach(node);
|
||||
} else {
|
||||
SYSFS_ROOT.get().attach(node);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_read_write_node<R, W>(parent: Option<VnodeRef>, name: &str, read: R, write: W)
|
||||
where
|
||||
R: Fn(&mut [u8]) -> Result<usize, Errno> + 'static,
|
||||
W: Fn(&[u8]) -> Result<usize, Errno> + 'static,
|
||||
{
|
||||
add_generic_node(parent, name, FileMode::from_bits(0o600).unwrap(), read, write)
|
||||
}
|
||||
|
||||
pub fn add_read_node<R>(parent: Option<VnodeRef>, name: &str, read: R) where R: Fn(&mut [u8]) -> Result<usize, Errno> + 'static {
|
||||
add_generic_node(parent, name, FileMode::from_bits(0o400).unwrap(), read, |_| Err(Errno::ReadOnly))
|
||||
}
|
||||
|
||||
pub fn add_directory(parent: Option<VnodeRef>, name: &str) -> Result<VnodeRef, Errno> {
|
||||
let node = Vnode::new(name, VnodeKind::Directory, Vnode::CACHE_READDIR | Vnode::CACHE_STAT);
|
||||
node.props_mut().mode = FileMode::from_bits(0o500).unwrap() | FileMode::S_IFDIR;
|
||||
|
||||
if let Some(parent) = parent {
|
||||
parent.attach(node.clone());
|
||||
} else {
|
||||
SYSFS_ROOT.get().attach(node.clone());
|
||||
}
|
||||
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
pub fn root() -> &'static VnodeRef {
|
||||
SYSFS_ROOT.get()
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
let node = Vnode::new("", VnodeKind::Directory, Vnode::CACHE_READDIR | Vnode::CACHE_STAT);
|
||||
node.props_mut().mode = FileMode::default_dir();
|
||||
SYSFS_ROOT.init(node);
|
||||
|
||||
let debug_dir = add_directory(None, "debug").unwrap();
|
||||
|
||||
add_read_write_node(Some(debug_dir.clone()), "level", |buf| {
|
||||
let mut writer = BufferWriter::new(buf);
|
||||
write!(&mut writer, "{}\n", debug::LEVEL as u32).map_err(|_| Errno::InvalidArgument)?;
|
||||
Ok(writer.count())
|
||||
}, |buf| {
|
||||
let s = core::str::from_utf8(buf).map_err(|_| Errno::InvalidArgument)?;
|
||||
let value = u32::from_str(s).map_err(|_| Errno::InvalidArgument).and_then(Level::try_from)?;
|
||||
todo!()
|
||||
});
|
||||
|
||||
add_read_node(None, "uptime", |buf| {
|
||||
use crate::arch::machine;
|
||||
use crate::dev::timer::TimestampSource;
|
||||
|
||||
let mut writer = BufferWriter::new(buf);
|
||||
let time = machine::local_timer().timestamp()?;
|
||||
write!(&mut writer, "{} {}\n", time.as_secs(), time.subsec_nanos()).map_err(|_| Errno::InvalidArgument)?;
|
||||
Ok(writer.count())
|
||||
});
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
//! Kernel initialization process
|
||||
|
||||
use crate::config::{ConfigKey, CONFIG};
|
||||
use crate::fs::{devfs, MemfsBlockAlloc};
|
||||
use crate::mem;
|
||||
use crate::proc::{elf, Process};
|
||||
use libsys::stat::{FileDescriptor, OpenFlags, UserId, GroupId};
|
||||
use memfs::Ramfs;
|
||||
use vfs::{Filesystem, Ioctx};
|
||||
|
||||
/// Kernel init process function
|
||||
#[inline(never)]
|
||||
pub extern "C" fn init_fn(_arg: usize) -> ! {
|
||||
let proc = Process::current();
|
||||
|
||||
debugln!("Running kernel init process");
|
||||
|
||||
let cfg = CONFIG.lock();
|
||||
let initrd_start = cfg.get_usize(ConfigKey::InitrdBase);
|
||||
let initrd_size = cfg.get_usize(ConfigKey::InitrdSize);
|
||||
let console = cfg.get_str(ConfigKey::Console);
|
||||
|
||||
if initrd_start == 0 {
|
||||
panic!("No initrd specified");
|
||||
}
|
||||
|
||||
let initrd_start = mem::virtualize(initrd_start);
|
||||
let fs =
|
||||
unsafe { Ramfs::open(initrd_start as *mut u8, initrd_size, MemfsBlockAlloc {}).unwrap() };
|
||||
let root = fs.root().unwrap();
|
||||
|
||||
let ioctx = Ioctx::new(root, UserId::root(), GroupId::root());
|
||||
|
||||
let node = ioctx.find(None, "/init", true).unwrap();
|
||||
let file = node.open(OpenFlags::O_RDONLY | OpenFlags::O_EXEC).unwrap();
|
||||
|
||||
proc.io.lock().set_ioctx(ioctx);
|
||||
|
||||
// Open stdin/stdout/stderr
|
||||
{
|
||||
let devfs_root = devfs::root();
|
||||
let tty_node = if console.is_empty() {
|
||||
devfs_root.lookup("ttyS0")
|
||||
} else {
|
||||
devfs_root.lookup(console)
|
||||
}
|
||||
.expect("Failed to open stdout for init process");
|
||||
|
||||
let mut io = proc.io.lock();
|
||||
let stdin = tty_node.open(OpenFlags::O_RDONLY).unwrap();
|
||||
let stdout = tty_node.open(OpenFlags::O_WRONLY).unwrap();
|
||||
let stderr = stdout.clone();
|
||||
|
||||
io.set_file(FileDescriptor::STDIN, stdin).unwrap();
|
||||
io.set_file(FileDescriptor::STDOUT, stdout).unwrap();
|
||||
io.set_file(FileDescriptor::STDERR, stderr).unwrap();
|
||||
io.set_ctty(tty_node);
|
||||
}
|
||||
|
||||
drop(cfg);
|
||||
|
||||
Process::execve(|space| elf::load_elf(space, file), &["/init"]).unwrap();
|
||||
panic!("Unreachable");
|
||||
}
|
@ -1,51 +1,24 @@
|
||||
//! osdve5 crate (lol)
|
||||
#![feature(
|
||||
global_asm,
|
||||
const_for,
|
||||
const_mut_refs,
|
||||
const_fn_fn_ptr_basics,
|
||||
const_fn_trait_bound,
|
||||
const_trait_impl,
|
||||
panic_info_message,
|
||||
alloc_error_handler,
|
||||
linked_list_cursors,
|
||||
const_btree_new,
|
||||
asm_const,
|
||||
const_raw_ptr_deref,
|
||||
const_fn_fn_ptr_basics
|
||||
)]
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate kernel_macros;
|
||||
#[macro_use]
|
||||
extern crate cfg_if;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
extern crate alloc;
|
||||
|
||||
#[macro_use]
|
||||
pub mod debug;
|
||||
|
||||
pub mod arch;
|
||||
pub mod config;
|
||||
pub mod dev;
|
||||
pub mod fs;
|
||||
pub mod init;
|
||||
pub mod mem;
|
||||
pub mod proc;
|
||||
pub mod sync;
|
||||
pub mod syscall;
|
||||
pub mod util;
|
||||
|
||||
use core::arch::asm;
|
||||
|
||||
#[panic_handler]
|
||||
fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
|
||||
unsafe {
|
||||
asm!("msr daifset, #2");
|
||||
}
|
||||
|
||||
errorln!("Panic: {:?}", pi);
|
||||
// TODO
|
||||
fn panic_handler(_pi: &core::panic::PanicInfo) -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
@ -1,65 +0,0 @@
|
||||
//! Kernel heap allocation facilities
|
||||
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use core::alloc::{GlobalAlloc, Layout};
|
||||
use core::ptr::null_mut;
|
||||
|
||||
struct SystemAlloc;
|
||||
|
||||
struct Heap {
|
||||
base: usize,
|
||||
size: usize,
|
||||
ptr: usize,
|
||||
}
|
||||
|
||||
unsafe impl GlobalAlloc for SystemAlloc {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
HEAP.get().lock().alloc(layout)
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||
HEAP.get().lock().dealloc(ptr, layout)
|
||||
}
|
||||
}
|
||||
|
||||
impl Heap {
|
||||
unsafe fn alloc(&mut self, layout: Layout) -> *mut u8 {
|
||||
// Simple bump allocation
|
||||
assert!(layout.align() <= 16);
|
||||
let size = (layout.size() + 15) & !15;
|
||||
if self.ptr + size >= self.size {
|
||||
return null_mut();
|
||||
}
|
||||
|
||||
let ptr = self.ptr;
|
||||
self.ptr += size;
|
||||
|
||||
(self.base + ptr) as *mut u8
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&mut self, _ptr: *mut u8, _layout: Layout) {}
|
||||
}
|
||||
|
||||
#[alloc_error_handler]
|
||||
fn alloc_error_handler(layout: Layout) -> ! {
|
||||
panic!("Allocation failed: {:?}", layout)
|
||||
}
|
||||
|
||||
#[global_allocator]
|
||||
static SYSTEM_ALLOC: SystemAlloc = SystemAlloc;
|
||||
|
||||
static HEAP: InitOnce<IrqSafeSpinLock<Heap>> = InitOnce::new();
|
||||
|
||||
/// Initializes kernel heap with virtual `base` address and `size`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: accepts arbitrary `base` and `size` parameters.
|
||||
pub unsafe fn init(base: usize, size: usize) {
|
||||
let heap = Heap { base, size, ptr: 0 };
|
||||
|
||||
infoln!("Kernel heap: {:#x}..{:#x}", base, base + size);
|
||||
|
||||
HEAP.init(IrqSafeSpinLock::new(heap));
|
||||
}
|
@ -1,28 +1,8 @@
|
||||
//! Memory management and functions module
|
||||
|
||||
pub mod heap;
|
||||
pub mod phys;
|
||||
pub mod virt;
|
||||
|
||||
/// Virtual offset applied to kernel address space
|
||||
pub const KERNEL_OFFSET: usize = 0xFFFFFF8000000000;
|
||||
|
||||
/// Default page size used by the kernel
|
||||
pub const PAGE_SIZE: usize = 4096;
|
||||
|
||||
/// Returns input `addr` with [KERNEL_OFFSET] applied.
|
||||
///
|
||||
/// Will panic if `addr` is not mapped by kernel's
|
||||
/// direct translation tables.
|
||||
pub fn virtualize(addr: usize) -> usize {
|
||||
assert!(addr < (256 << 30));
|
||||
addr + KERNEL_OFFSET
|
||||
}
|
||||
|
||||
/// Returns the physical address of kernel's end in memory.
|
||||
pub fn kernel_end_phys() -> usize {
|
||||
extern "C" {
|
||||
static __kernel_end: u8;
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn memcpy(dst: *mut u8, src: *mut u8, mut len: usize) -> *mut u8 {
|
||||
while len != 0 {
|
||||
len -= 1;
|
||||
*dst.add(len) = *src.add(len);
|
||||
}
|
||||
unsafe { &__kernel_end as *const _ as usize - KERNEL_OFFSET }
|
||||
dst
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user